本文内容整理自 博学谷狂野架构师
动态代理简介
? Proxy模式是常用的设计模式,其特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。
用户可以更加结构图,自己编码完成Proxy模式。这种实现称为静态代理。
? Java提供了java.lang.reflect.Proxy类与InvocationHandler接口,配合反射,可以实现动态代理。静态代理的代理类与代理操作,都是事先编码,运行过程种无法修改代理结构。动态代理的代理与代理操作,都是在运行过程中,动态生成,可以在运行过程中,修改代理结构,符合面向对象的开闭原则。
? 最最最主要的原因就是,在不改变目标对象方法的情况下对方法进行增强,比如,我们希望对方法的调用增加日志记录,或者对方法的调用进行拦截,等等...
? 动态代理用于将在不需要修改原代码的情况下进行代码的增加,spring中的AOP,事务,都是使用动态代理来实现的,我们天天都在使用动态代理只是自己不知道而已。
动态代理三大要素
-
需要定义一个接口,java动态代理类只能代理接口(不支持抽象类),如果没有接口就要使用cjlib
-
需要一个实现类继承这个接口
-
编写一个增强类实现 InvocationHandler接口,代理类都需要实现InvocationHandler接口的invoke方法
一个例子
先定义一个接口
定义一个海外代购的接口
/**
* 海外代购
*/
public interface Buying {
public String buy();
}
编写一个实现类
实现类实现接口
public class BuyingImpl implements Buying {
@Override
public String buy() {
System.out.println("开始逻辑处理");
return "买了个锤子";
}
}
编写一个增将类
编写一个增强类,主要要包裹一个需要需要增强的对象也就是我们的BuyingImpl,并实现InvocationHandler接口,在invoke方法中写增强实现
/**
* 海外代购增强类
* 注意实现 InvocationHandler
* 动态代理类只能代理接口(不支持抽象类),代理类都需要实现InvocationHandler类,实现invoke方法。
* 该invoke方法就是调用被代理接口的所有方法时需要调用的 。
*/
public class BuingHandler implements InvocationHandler {
/**
* 包裹一个需要增强的目标对象
*/
private Object targetObject;
public BuingHandler(Object targetObject){
this.targetObject = targetObject;
}
/**
* 获取代理类
*
* @return
*/
public Object getProxy() {
/**
* 该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
* 第一个参数指定产生代理对象的类加载器,需要将其指定为和目标对象同一个类加载器
* 第二个参数要实现和目标对象一样的接口,所以只需要拿到目标对象的实现接口
* 第三个参数表明这些被拦截的方法在被拦截时需要执行哪个InvocationHandler的invoke方法
* 根据传入的目标返回一个代理对象
*/
return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
targetObject.getClass().getInterfaces(), this);
}
/**
* 关联的这个实现类的方法被调用时将被执行
* InvocationHandler接口的方法
*
* @param proxy 表示代理对象
* @param method 示原对象被调用的方法
* @param args 表示方法的参数
* @return 返回的是对象的一个接口
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("前置增强");
//反射调用原始的需要增强的方法
Object value = method.invoke(targetObject, args);
System.out.println("后置增强");
return value;
}
}
这里面要注意 method 是我们需要增强的方法,args 是我们需要增强的参数数组
编写Main方法
public static void main(String[] args) {
//创建BuingHandler 类
BuingHandler buingHandler = new BuingHandler(new BuyingImpl());
//获取代理对象
Buying buying = (Buying) buingHandler.getProxy();
//调用具体接口
String value = buying.buy();
System.out.println(value);
}
输出
前置增强
开始逻辑处理
后置增强
买了个锤子
我们就这样实现了动态代理,我们没有修改原有代码的情况下做了增强
我们实现了 其那只以及后置增强
我们运行下看下接口对象
我们看到实际对象是$Proxy0,我们发现动态代理给我们换了一个对象,我们要研究下他是怎么实现的
源码实现
读源码首先找到入口,没有不得入口就像无头的苍蝇,苍蝇还不叮无缝的蛋呢
下面内容有点多,也有点绕,请跟着思路来一点点解析
1、首先找到入口
我们创建代理对象调用的是
Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
targetObject.getClass().getInterfaces(), this);
所以我们先从Proxy.newProxyInstance开始入手
2、newProxyInstance方法
进入newProxyInstance方法内部
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException {
//增强实现不能为空,为空就抛出异常
Objects.requireNonNull(h);
//对接口数组进行clone
final Class<?>[] intfs = interfaces.clone();
//进项权限检查
final SecurityManager sm = System.getSecurityManager();
if (sm != null