代理模式

代理模式

一、代理模式(Proxy Pattern)

代理模式也称为委托模式,是一种结构性设计模式。
在代理模式中,一个类代表另一个类的功能。我们创建具有现有对象的对象,以便向外界提供功能接口。

优点
可以隐藏委托类的实现;
可以实现客户与委托类间的解耦,在不修改委托类代码的情况下能够做一些额外的处理。

二、静态代理

静态代理设计模式
* 1、真实角色
* 2、代理角色: 要持有真实角色的引用
* 3、二者要实现相同的接口

1
2


装饰者模式就是静态代理的一种体现。

三、动态代理

字节码随用随创建,随用随加载。
动态代理常用的有两种方式

  1. 基于接口
    提供者:JDK 官方的 Proxy 类。
    要求:被代理类最少实现一个接口。
  2. 基于子类
    提供者:第三方的 CGLib,如果报 asmxxxx 异常,需要导入 asm.jar。
    要求:被代理类不能用 final 修饰的类(最终类)。

基于接口的动态代理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package proxy.dynamic;
/**
* @author gavino
* 模拟厂家,厂家直销
*/
public class Manufacturers implements Market {
@Override
public void sal(Double price) {
System.out.println("收款 "+price);
}
@Override
public void afterSal() {
System.out.println("免费售后2333");
}
}

/**
* @author gavino
* 该接口提供销售规则
*/
interface Market{
/**
* 出售
*/
public void sal(Double price);
/**
* 售后
*/
public void afterSal();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package proxy.dynamic;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
* @author gavino
* 模拟消费者
*/
public class Customer {
public static void main(String[] args) {
//代理类中的真实对象(Manufacturers 制造商、厂商)
final Manufacturers manufacturers = new Manufacturers();

Market agency = (Market) Proxy.newProxyInstance(manufacturers.getClass().getClassLoader(),manufacturers.getClass().getInterfaces(),
new InvocationHandler() {

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//获取当前方法方法
String name = method.getName();
//获取方法执行的参数
Double money = (Double) args[0];
Object realValue = null;
if ("sal".equals(name)) {
realValue = method.invoke(manufacturers, money*0.8);
}
return realValue;
}
});
manufacturers.sal(13300.0);
agency.sal(13300.0);
}
}

=================================== 程序执行结果 ===============================================
收款 13300.0
收款 10640.0

相关方法和参数的详解
Proxy类就是用来创建一个代理对象的类,它提供了很多方法,但是我们最常用的是newProxyInstance方法
Proxy.newProxyInstance(loader, interfaces, h);
这个方法的作用就是创建一个代理类对象,它接收三个参数:
1、loader:一个classloader对象,定义了由哪个classloader对象对生成的代理类进行加载
2、interfaces:一个interface对象数组,表示我们将要给我们的代理对象提供一组什么样的接口,
如果我们提供了这样一个接口对象数组,那么也就是声明了代理类实现了这些接口,代理类就可以调用接口中声明的所有方法。
3、h:一个InvocationHandler对象,表示的是当动态代理对象调用方法的时候会关联到哪一个InvocationHandler对象上,并最终由其调用

  • 通过Proxy类的newProxyInstance方法创建代理对象,我们来看下方法中的参数
    • 第一个参数:manufacturers.getClass().getClassLoader(),使用handler对象的classloader对象来加载我们的代理对象
    • 第二个参数:manufacturers.getClass().getInterfaces(),这里为代理类提供的接口是真实对象实现的接口,
      这样代理对象就能像真实对象一样调用接口中的所有方法
    • 第三个参数:h(匿名内部类),我们将代理对象关联到上面的InvocationHandler对象上
1
2
3
4
5
6
7
8
9
/**
* newProxyInstance方法的参数含义:
* ClassLoader:和被代理对象使用相同的类加载器。
* 它是用于加载代理对象字节码的。
* Interfaces:Class[]:字节码数组,和被代理对象具有相同的行为。实现相同的接口。
* InvocationHandler:如何代理。用于提供增强的代码.
* 常情况下都是匿名内部类,此接口的实现类都是谁用谁写.
*/
Proxy.newProxyInstance(loader, interfaces, h);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
new InvocationHandler() {
/**
* 执行被代理对象的任何方法,都会经过该方法。
* 此方法有拦截的功能。
*
* 参数:
* proxy:代理对象的引用。不一定每次都用得到
* method:当前执行的方法
* args:当前执行方法所需的参数
* 返回值:
* 当前执行方法的返回值
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 方法增强
return null;
}
}

-----------------------------------------------------------------------------------------

method.invoke(obj, args);

基于接口的动态代理实现的总结

基于子类的动态代理

基于子类的动态代理

  • 要求:
    被代理对象不能是最终类
  • 用到的类:
    Enhancer
  • 用到的方法:
    create(Class, Callback)
  • 方法的参数:
    • Class:被代理对象的字节码
    • Callback:如何代理

导入cglib的jar包和asm的jar包(maven 工程导入 cglib 依赖即可)
去掉接口实现

1
2
3
4
5
6
7
8
9
10
11
12
13
package proxy.dynamic.cglib;
/**
* @author gavino
* 模拟厂家,厂家直销
*/
public class Manufacturers {
public void sal(Double price) {
System.out.println("收款 "+price);
}
public void afterSal() {
System.out.println("免费售后2333");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package proxy.dynamic.cglib;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

/**
* @author gavino
* 模拟消费者
*/
public class Customer {
public static void main(String[] args) {
final Manufacturers manufacturers = new Manufacturers();
Manufacturers cglibManufacturers = (Manufacturers)Enhancer.create(manufacturers.getClass(), new MethodInterceptor() {
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
//提供增强的代码
Object realValue = null;
//获取方法执行的参数
Double money = (Double)args[0];
if("sal".equals(method.getName())) {
realValue = method.invoke(manufacturers, money*0.8);
}
return realValue;
}
});
manufacturers.sal(12000.0);
cglibManufacturers.sal(12000.0);
}
}
======================================= 程序执行输出 ================================================
收款 12000.0
收款 9600.0

相关方法和参数的详解

1
2

Enhancer.create(type, callback);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Enhancer.create(type, new MethodInterceptor() {
/**
* 执行被代理对象的任何方法,都会经过该方法。在此方法内部就可以对被代理对象的任何方法进行增强。
*
* 参数:
* 前三个和基于接口的动态代理是一样的。
* MethodProxy:当前执行方法的代理对象。
* 返回值:
* 当前执行方法的返回值
*/
@Override
public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable {
// 方法的增强
return null;
}
});

评论