相信作为Java开发人员,应该都有用过或见过代理设计模式,像Spring的AOP中,就是使用的动态代理模式,Mybatis中xxxMapper接口,也是使用的动态代理来生成相应的Mapper代理对象,可见,动态代理模式的重要性。
代理模式一般分为静态代理和动态代理,目标都是为了扩展增强原有方法功能。
定义一个订单接口,添加下单方法
public interface IOrderService {
// 提交订单
void submitOrder();
}
编写订单接口实现类
public class OrderServiceImpl implements IOrderService{
// 提交订单测试
@Override
public void submitOrder() {
System.out.println("-------保存订单-------");
}
}
然后,假如当前的submitOrder()方法已经不能满足我们的需求,需要在此方法前后做一些特殊的业务处理,但我们又不希望在原有订单接口实现类上做修改(开闭原则),所以我们可以新增一个订单代理类,这个代理类定义了一个目标接口对象的成员属性,通过构造注入,从而可以操作目标对象,可以在目标对象方法前后添加自定义逻辑:
public class OrderStaticProxy implements IOrderService {
private IOrderService orderService;
// 构造注入
public OrderStaticProxy(IOrderService orderService){
this.orderService=orderService;
}
@Override
public void submitOrder() {
System.out.println("--------提交订单前,自定义逻辑");
orderService.submitOrder();
System.out.println("--------提交订单前,自定义逻辑");
}
}
测试类:
public class Test {
public static void main(String[] args) {
IOrderService orderService = new OrderServiceImpl();
OrderStaticProxy proxy = new OrderStaticProxy(orderService);
proxy.submitOrder();
}
}
分析:在不改变原有OrderServiceImpl实现类的情况下,扩展增强了原订单接口的功能
优点:
缺点:
因此,我们引出动态代理。
动态代理的角色与静态代理一样。
静态代理会为每一个业务增强都提供一个代理类, 由代理类来创建代理对象;而动态代理并不存在代理类, 代理对象直接由代理生成工具动态生成。
动态代理的代理对象是运行时动态生成的,静态代理的代理类需要我们提前写好,即代理类在编译时期就已经确定了。
动态代理又分为:
在JDK动态代理中,核心是InvocationHandler接口和Proxy类,具体可以参考JDK帮助文档。
package java.lang.reflect;
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
其中,我们需要实现InvocationHandler#invoke()方法,参数为:
通过Proxy#newProxyInstance()生成代理类对象
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
//。。。。
}
其中,该方法在Proxy
类中是静态方法,参数为:
ClassLoader loader
:指定当前目标对象使用类加载器,获取加载器的方法是固定的。Class>[] interfaces
:目标对象实现的接口类型,使用泛型方式确认类型。InvocationHandler h
:事件处理器,执行目标对象的方法时,会触发事件处理器的invoke()方法,会把当前执行目标对象的方法作为参数传入。代码实现
抽象对象角色与目标对象角色与静态代理一样(IOrderService-->OrderServiceImpl)。
自定义实现InvocationHandler#invoke(),如下
public class DynamicProxy implements InvocationHandler {
private Object target;
public DynamicProxy(Object target){
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("------动态代理,前置增强-----");
Object invoke = method.invoke(target, args);
System.out.println("------动态代理,后置增强-----");
return invoke;
}
// 生成动态代理对象
public static Object getProxy(Object target){
DynamicProxy proxy = new DynamicProxy(target);
return Proxy.newProxyInstance(proxy.getClass().getClassLoader(), target.getClass().getInterfaces(),proxy);
}
}
测试类:
/**
* @description: 测试
* @author: stwen_gan
* @date:
**/
public class Test {
public static void main(String[] args) {
// 为了在项目下生成代理类
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
// 静态代理
// IOrderService orderService = new OrderServiceImpl();
// OrderStaticProxy proxy = new OrderStaticProxy(orderService);
// proxy.submitOrder();
// 动态代理
IOrderService orderService = new OrderServiceImpl();
IOrderService dynamicProxy = (IOrderService) DynamicProxy.getProxy(orderService);
dynamicProxy.submitOrder();
}
}
输出:
其中,添加如下配置语句
//该设置用于输出cg1ib动态代理产生的类
// System.setProperty(Debuggingclasswriter.DEBUG_LOCATION_PROPERTY,"D:\\class");
//该设置用于输出jdk动态代理产生的类
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
就会在我们的当前目录下,JDK默认会生成了一个代理类,名字默认$Proxy0,继承了Proxy类
核心:一个动态代理,一般代理某一类业务,可代理多个类,代理的是接口。
从上面的代码可以看出,动态代理对象不需要实现目标对象接口,但是目标对象一定要实现接口,否则不能使用JDK动态代理。
改进:返回对象类型为泛型,抽象接口可以没有实现类,类似Mybatis中的xxxMapper接口,底层也是通过动态代理生成mapper代理对象,去拼接执行sql,我们无需编写mapper实现类
上面的静态代理和JDK动态代理模式都需要目标对象去实现一个接口,但是有的时候,目标对象可能只是一个独立的类,并没有实现任何的接口,这个时候,我们就可以使用目标对象子类的方式实现代理,这种代理方式就是:Cglib代理,使用得相对少一些。(引用)
Cglib动态代理,原理是生成目标类的子类(即这个子类对象就是代理对象),它是在内存中构件一个子类对象,从而实现对目标对象的功能拓展。
注意: 不管有没有实现接口都可以使用Cglib动态代理, 而不是只有在无接口的情况下才能使用。
interception
(拦截)。MethodInterceptor
public interface MethodInterceptor extends Callback {
Object intercept(Object var1, Method var2, Object[] var3, MethodProxy var4) throws Throwable;
}
Cglib工具类,我们只需要实现MethodInterceptor#intercept(),对目标方法进行增强
public class CglibProxy implements MethodInterceptor {
//维护目标对象
private Object target;
public CglibProxy(Object target) {
this.target = target;
}
// //获取目标对象的代理对象
// public Object getProxy() {
// //1. 实例化工具类
// Enhancer en = new Enhancer();
// //2. 设置父类对象
// en.setSuperclass(this.target.getClass());
// //3. 设置回调函数
// en.setCallback(this);
// //4. 创建子类,也就是代理对象
// return en.create();
// }
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("-------动态代理,前置增强-----");
//执行目标对象的方法
Object object = method.invoke(target, objects);
System.out.println("-------动态代理,后置增强-----");
return object;
}
}
其中,注释掉的那个方法,是为了获取目标类的代理对象,我们可以把它单独抽取出来,新增一个cglib代理工厂:
/**
* @description: cglib代理工厂
* @author: stwen_gan
* @date:
**/
public class CglibProxyFactory {
//获取目标类的代理对象
public static T createProxy(final Class> targetClass, final MethodInterceptor methodInterceptor) {
return (T) Enhancer.create(targetClass,methodInterceptor);
}
}
新增一个OrderDao类,无需实现任何接口
public class OrderDao {
void submitOrder(){
System.out.println("--------保存订单-------");
}
}
测试类:
输出:
JDK动态代理 相比较 静态代理 相同点在于都是面向接口编程,在不修改原有代码情况下增强目标方法功能,而且动态代理无需像静态代理一样,针对目标对象需要编写一一对应的代理类。使用代理模式,符合“开闭原则”,功能职责划分清晰,目标都是为了扩展增强原有方法功能,更易于后期的扩展与维护。
●史上最强Tomcat8性能优化
●阿里巴巴为什么能抗住90秒100亿?--服务端高并发分布式架构演进之路
●B2B电商平台--ChinaPay银联电子支付功能
●学会Zookeeper分布式锁,让面试官对你刮目相看
●SpringCloud电商秒杀微服务-Redisson分布式锁方案
查看更多好文,进入公众号--撩我--往期精彩
一只 有深度 有灵魂 的公众号0.0