Java设计模式-代理模式

设计模式之代理模式

代理模式是设计模式的一种,简单解释就是不直接访问目标对象,通过访问代理对象就可以实现对目标对象的访问。就像现在买火车票,不用直接去火车站买,可以直接去各个代售点或者APP上购买,这里的代售点或者APP就是火车站的代理,这样做的好处是,不用修改目标对象,可以在代理对象中增加额外的操作,达到扩展目标对象的目的

Java中主要有三种方式:静态代理,JDK动态代理,cglib代理。前两种代理方式都是通过接口代理,cglib可以实现代理类。

静态代理

静态代理中代理对象和被代理对象都需要实现同一个接口,才能达到代理的目的。

这样做就会存在不好的地方,当修改接口时,代理对象和被代理对象都需要修改,耦合性太大,不容易维护,同时可能会产生过多的代理类。

静态代理代码实现

定义一个接口:

1
2
3
4
public interface IBaseDao {
//存储数据方法
void save();
}

需要代理的目标类,需要实现IBaseDao接口:

1
2
3
4
5
6
public class StaticProxyTarget implements IBaseDao {
@Override
public void save() {
System.out.println("save data");
}
}

代理类,需要实现IBaseDao接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class StaticProxy implements IBaseDao {
//代理目标对象
private StaticProxyTarget target;
//通过构造函数传入代理对象
public StaticProxy(StaticProxyTarget target) {
this.target = target;
}

@Override
public void save() {
System.out.println("start transaction");
target.save();
System.out.println("commit transaction");
}
}

测试静态代理:

1
2
3
4
5
6
7
8
9
public class ProxyTest {
public static void main(String[] args) {
//目标对象
StaticProxyTarget staticProxyTarget = new StaticProxyTarget();
//代理对象
StaticProxy staticProxy = new StaticProxy(staticProxyTarget);
staticProxy.save();
}
}

输出结果:

1
2
3
start transaction
save data
commit transaction

JDK动态代理

JDK动态代理是利用Java API,动态的生成代理对象,达到代理目标对象的作用。

与静态代理不同的是,动态代理是在Java运行时动态生成字节码,并加载到jvm中运行,没有.class文件,静态代理编译后会产生.class文件。

定义一个接口:

1
2
3
public interface IBaseDao {
void save();
}

需要代理的目标类,需要实现IBaseDao接口:

1
2
3
4
5
6
7
public class DynamicProxyTarget implements IBaseDao{

@Override
public void save() {
System.out.println("save data");
}
}

动态代理类:
其中Proxy提供了用于创建动态代理对象的static方法。
主要用到了下面这个方法,可以直接创建一个动态代理对象,该代理对象的实现类实现了interfaces指定的系列接口,执行代理对象的每个方法时都会被替换成InvocationHandler的invoke方法。

1
2
3
static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)

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
public class DynamicProxy {
//需要代理的目标对象
private Object target;

public DynamicProxy(Object target) {
this.target = target;
}

public Object getProxyInstance() {
/**
* 执行动态代理对象的方法时,将会执行InvocationHandler的invoke方法
* 其中三个参数为
* proxy:动态代理对象
* method:代表正在执行的方法
* args:调用目标对象方法时传入的参数
*/
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), (proxy, method, args) -> {
System.out.println("start transaction");
//调用目标对象的方法
Object result = method.invoke(target, args);
System.out.println("commit transaction");
return result;
});
}
}

测试JDK动态代理:

1
2
3
4
5
6
7
8
public class ProxyTest {
public static void main(String[] args) {
//动态代理测试
IBaseDao baseDao = new DynamicProxyTarget();
IBaseDao dynamicProxy = (IBaseDao) new DynamicProxy(baseDao).getProxyInstance();
dynamicProxy.save();
}
}

结果输出:

1
2
3
start transaction
save data
commit transaction

cglib代理

cglib (Code Generation Library )是一个第三方代码生成类库,可以在运行时在内存中动态生成一个子类对象,从而实现对目标对象功能的扩展。Spring框架中的AOP就使用了cglib。

cglib和上面两种代理最大的不同就是,被代理类不需要实现接口,就可以实现对目标对象的代理,代码侵入性更小。

需要代理的目标类:

1
2
3
4
5
public class CglibProxyTarget {
public void save() {
System.out.println("save data");
}
}

cglib代理类,需要实现MethodInterceptor接口:

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
public class CglibProxy implements MethodInterceptor {
private CglibProxyTarget target;

public CglibProxy(CglibProxyTarget target) {
this.target = target;
}

//生成代理对象
public Object getProxyInstance() {
Enhancer enhancer = new Enhancer();
//指定父类
enhancer.setSuperclass(target.getClass());
//设置回调
enhancer.setCallback(this);

return enhancer.create();
}

@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("start transaction");
//调用目标对象的方法
Object result = method.invoke(target, args);
System.out.println("commit transaction");
return result;
}
}

测试结果:

1
2
3
4
5
6
7
8
public class ProxyTest {
public static void main(String[] args) {
//cglib代理测试
CglibProxyTarget cglibProxyTarget = new CglibProxyTarget();
cglibProxyTarget = (CglibProxyTarget) new CglibProxy(cglibProxyTarget).getProxyInstance();
cglibProxyTarget.save();
}
}

结果输出:

1
2
3
start transaction
save data
commit transaction

总结

  • 静态代理代理对象和被代理对象都需要实现同一个接口,实现简单,但是耦合性太大,不便于维护。
  • JDK动态代理需要被代理对象实现业务接口,代理对象中实现InvocationHandler接口,通过Java反射生成代理,但是动态生成的代理更加灵活。
  • 静态代理编译后产生.class文件,比JDK反射性能好,cglib是通过字节码生成代理,性能高于反射,但是cglib会生成子类继承被代理对象,所以被代理对象不能为final。

代码很简单,需要理解的是思想,代理模式运用广泛,很多框架中都使用了代理模式,只是设计更复杂,个人笔记,如有不对请指出。