代理模式就是提供一个代理对象,代理对象持有被代理对象的引用,通过代理对象对被代理对象进行增强。比如你想要买房子,你只想买个房子,并不想关注买房子附带的其他的事,那么就可以找中介,中介给你找房子,带你签合同。这个例子中你就是被代理对象,你的行为只有一个那就是交易,中介呢是代理对象,他要在你买房子前给你找房子,做清洁啊什么的,买房子后带你签合同,这些附加的行为都是代理对象对你行为的增强。
代理模式一共有两类:
- 静态代理
- 动态代理
在java中常见的动态代理又有两种分别是基于JDK的动态代理和基于CGlib的动态代理,下面就用代码理解一下这几种代理模式。
现在有一个交易的接口与一个房屋交易的实现类:
public interface TransactionAble {
void transaction();
}
public class TransactionHouse implements TransactionAble {
@Override
public void transaction() {
System.out.println("交易房屋");
}
}
现在就可以通过代理类对房屋交易进行增强。
静态代理
静态代理就是手动编写代理类对被代理类进行增强:
public class StaticProxyTransaction implements TransactionAble{
private TransactionAble transactionAble;
public StaticProxyTransaction(TransactionAble transactionAble) {
this.transactionAble = transactionAble;
}
@Override
public void transaction() {
System.out.println("交易前清洁");
transactionAble.transaction();
System.out.println("交易后签合同");
}
}
使用时直接使用代理对象进行操作即可:
public class Main {
public static void main(String[] args) {
StaticProxyTransaction staticProxyTransaction = new StaticProxyTransaction(new TransactionHouse());
staticProxyTransaction.transaction();
}
}
运行结果如下图所示:
基于JDK的动态代理
基于JDK的动态代理是通过jdk提供的工具方法Proxy.newProxyInstance动态构建全新的代理类,我们只需编写一个动态处理器就可以了。真正的代理对象由JDK再运行时为我们动态的来创建:
public class JdkProxyTransaction implements InvocationHandler {
private Object object;
public JdkProxyTransaction(Object object) {
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("交易前清洁");
Object invoke = method.invoke(object, args);
System.out.println("交易后签合同");
return invoke;
}
}
使用时使用Proxy.newProxyInstance即可动态的创建出代理对象:
public class Main {
public static void main(String[] args) {
TransactionAble transactionAble = new TransactionHouse();
TransactionAble proxyBuyHouse = (TransactionAble) Proxy.newProxyInstance(
TransactionAble.class.getClassLoader(),
new Class[]{TransactionAble.class},
new JdkProxyTransaction(transactionAble)
);
proxyBuyHouse.transaction();
}
}
运行结果如下图所示:
基于CGlib的动态代理
可以看到Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h)的第二个参数为接口的数组,那就是说必须是实现了接口的类才能使用Jdk动态代理,那没有实现接口的类该怎么动态代理呢?这里基于CGlib的动态代理就出场了,CGLib采用了底层的字节码技术,其原理是通过字节码技术为一个类创建子类,在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势将横切逻辑织入目标对象。由于基于CGlib的动态代理采用的是继承,所以不能对final修饰的类进行代理。
使用CGlib动态代理首先要引入依赖:
<dependencies>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.8</version>
</dependency>
</dependencies>
编写CGlib动态代理的拦截器:
public class CglibProxyTransaction implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("交易前清洁");
Object invoke = methodProxy.invokeSuper(o, objects);
System.out.println("交易后签合同");
return invoke;
}
}
创建代理对象,并执行目标方法:
public class Main {
public static void main(String[] args) {
//用来创建代理对象的增强器
Enhancer enhancer = new Enhancer();
//将被代理的类设置为父类
enhancer.setSuperclass(TransactionHouse.class);
//设置刚才定义的拦截器
enhancer.setCallback(new CglibProxyTransaction());
//创建代理对象
TransactionHouse transactionHouse = (TransactionHouse) enhancer.create();
//代理对象执行方法
transactionHouse.transaction();
}
}
运行结果如下图所示:
通过这三种方式都可以实现代理模式,对交易方法都进行了增强。
代理模式优点:
- 职责分明,各个类只干自己的事。
- 高扩展性,符合开闭原则的情况下对现有类进行扩展。
- 动态代理减少了类的数量,使用更加灵活。
代理模式缺点:
- 静态代理:一个接口需要一种代理类,类数量增多,接口修改对应代理类也得修改。
- 基于Jdk的动态代理:只能对实现接口的类进行代理。
- 基于CGlib的动态代理:不能对final类与final方法进行代理,经过测试对final类代理会报错,对final方法代理增强的部分会失效。