设计模式之代理模式
代理模式是设计模式的一种,简单解释就是不直接访问目标对象,通过访问代理对象就可以实现对目标对象的访问。就像现在买火车票,不用直接去火车站买,可以直接去各个代售点或者APP上购买,这里的代售点或者APP就是火车站的代理,这样做的好处是,不用修改目标对象,可以在代理对象中增加额外的操作,达到扩展目标对象的目的
Java中主要有三种方式:静态代理,JDK动态代理,cglib代理。前两种代理方式都是通过接口代理,cglib可以实现代理类。
静态代理
静态代理中代理对象和被代理对象都需要实现同一个接口,才能达到代理的目的。
这样做就会存在不好的地方,当修改接口时,代理对象和被代理对象都需要修改,耦合性太大,不容易维护,同时可能会产生过多的代理类。
静态代理代码实现
定义一个接口:
public interface IBaseDao {
//存储数据方法
void save();
}
需要代理的目标类,需要实现IBaseDao接口:
public class StaticProxyTarget implements IBaseDao {
@Override
public void save() {
System.out.println("save data");
}
}
代理类,需要实现IBaseDao接口:
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");
}
}
测试静态代理:
public class ProxyTest {
public static void main(String[] args) {
//目标对象
StaticProxyTarget staticProxyTarget = new StaticProxyTarget();
//代理对象
StaticProxy staticProxy = new StaticProxy(staticProxyTarget);
staticProxy.save();
}
}
输出结果:
start transaction
save data
commit transaction
JDK动态代理
JDK动态代理是利用Java API,动态的生成代理对象,达到代理目标对象的作用。
与静态代理不同的是,动态代理是在Java运行时动态生成字节码,并加载到jvm中运行,没有.class文件,静态代理编译后会产生.class文件。
定义一个接口:
public interface IBaseDao {
void save();
}
需要代理的目标类,需要实现IBaseDao接口:
public class DynamicProxyTarget implements IBaseDao{
@Override
public void save() {
System.out.println("save data");
}
}
动态代理类:
其中Proxy提供了用于创建动态代理对象的static方法。
主要用到了下面这个方法,可以直接创建一个动态代理对象,该代理对象的实现类实现了interfaces指定的系列接口,执行代理对象的每个方法时都会被替换成InvocationHandler的invoke方法。
static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
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动态代理:
public class ProxyTest {
public static void main(String[] args) {
//动态代理测试
IBaseDao baseDao = new DynamicProxyTarget();
IBaseDao dynamicProxy = (IBaseDao) new DynamicProxy(baseDao).getProxyInstance();
dynamicProxy.save();
}
}
结果输出:
start transaction
save data
commit transaction
cglib代理
cglib (Code Generation Library )是一个第三方代码生成类库,可以在运行时在内存中动态生成一个子类对象,从而实现对目标对象功能的扩展。Spring框架中的AOP就使用了cglib。
cglib和上面两种代理最大的不同就是,被代理类不需要实现接口,就可以实现对目标对象的代理,代码侵入性更小。
需要代理的目标类:
public class CglibProxyTarget {
public void save() {
System.out.println("save data");
}
}
cglib代理类,需要实现MethodInterceptor接口:
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;
}
}
测试结果:
public class ProxyTest {
public static void main(String[] args) {
//cglib代理测试
CglibProxyTarget cglibProxyTarget = new CglibProxyTarget();
cglibProxyTarget = (CglibProxyTarget) new CglibProxy(cglibProxyTarget).getProxyInstance();
cglibProxyTarget.save();
}
}
结果输出:
start transaction
save data
commit transaction
总结
- 静态代理代理对象和被代理对象都需要实现同一个接口,实现简单,但是耦合性太大,不便于维护。
- JDK动态代理需要被代理对象实现业务接口,代理对象中实现InvocationHandler接口,通过Java反射生成代理,但是动态生成的代理更加灵活。
- 静态代理编译后产生.class文件,比JDK反射性能好,cglib是通过字节码生成代理,性能高于反射,但是cglib会生成子类继承被代理对象,所以被代理对象不能为final。
代码很简单,需要理解的是思想,代理模式运用广泛,很多框架中都使用了代理模式,只是设计更复杂,个人笔记,如有不对请指出。