jdk和cglib动态代理

时间:2021-05-22 15:21:18

什么是代理模式?

当一个对象(客户端)不能或者不想直接引用另一个对象(目标对象),这时可以应用代理模式在这两者之间构建一个桥梁--代理对象。按照代理对象的创建时期不同,可以分为两种:

静态代理:事先写好代理类,在程序运行前就已经存在了;
动态代理:在程序运行中创建代理对象(反射、类编译和类动态加载)。

此时代理对象和目标对象实现了相同的接口,目标对象作为代理对象的一个属性,具体接口实现中,可以在调用目标对象相应方法前后加上其他业务处理逻辑。
代理模式在实际使用时需要指定具体的目标对象,如果为每个类都添加一个代理类的话,会导致类很多,同时如果事先不知道需要代理的目标类的话,怎样实现代理模式呢?这就引出动态代理。

其中动态代理又可分为:
1.JDK动态代理
JDK动态代理生成的代理类与目标类实现了相同的接口,重写了接口中的方法
 
2.CGLIB动态代理
CGLIB(CODE GENERLIZE LIBRARY)动态代理的代理类是目标类的子类,覆盖其中的所有方法,所以该类或方法不能声明称final的。

spring中如果目标对象没有实现接口,则默认会采用CGLIB代理;
如果目标对象实现了接口,可以强制使用CGLIB实现代理(添加CGLIB库,并在spring配置中加入<aop:aspectj-autoproxy proxy-target-class="true"/>)。

AOP包括切面(aspect)、通知(advice)、连接点(joinpoint),实现方式就是通过对目标对象的代理在连接点前后加入通知,完成统一的切面操作。


下面贴上简单的测试代码,对两种方式进行简单的使用

package dynamicProxyByJDK;

/**
* writer: holien
* Time: 2017-09-26 18:26
* Intent: 被代理的用户类接口
*/
public interface User {
void saySomething(String name);
double getMoney();
}
package dynamicProxyByJDK;/** * writer: holien * Time: 2017-09-24 22:06 * Intent: 被代理的用户类 */public class UserImpl implements User {    public void saySomething(String name) {        System.out.println(name + "你好,我是用户类");    }    public double getMoney() {        System.out.println("获取金额");        return 100.2;    }}
package dynamicProxyByJDK;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;/** * writer: holien * Time: 2017-09-24 22:10 * Intent: 自定义的调用处理器,重写invoke方法,里面包含了原来的方法还有利用动态编译加上逻辑,比如添加事务, * 记录日志等 */public class LogInvocationHandler implements InvocationHandler {    // 被代理对象    private Object targetObj;    public LogInvocationHandler(Object targetObj) {        this.targetObj = targetObj;    }    // 此方法在构建代理类时被调用    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        System.out.println("开始日志记录...");        // 调用被代理对象自身的方法        Object result = method.invoke(targetObj, args);        System.out.println("结束日志记录...");        return result;    }}
package dynamicProxyByJDK;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;/** * writer: holien * Time: 2017-09-24 22:10 * Intent: 自定义的调用处理器,重写invoke方法,里面包含了原来的方法还有利用动态编译加上逻辑,比如添加事务, * 记录日志等 */public class TransactionInvocationHandler implements InvocationHandler {    // 被代理对象    private Object targetObj;    public TransactionInvocationHandler(Object targetObj) {        this.targetObj = targetObj;    }    // 此方法在构建代理类时被调用    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        System.out.println("开始事务...");        // 调用被代理对象自身的方法        Object result = method.invoke(targetObj, args);        System.out.println("结束事务...");        return result;    }}
package dynamicProxyByJDK;import java.lang.reflect.Proxy;/** * writer: holien * Time: 2017-09-24 22:47 * Intent: 代理测试 */public class ProxyTest {    public static void main(String[] args) {        User user = new UserImpl();        LogInvocationHandler logIH = new LogInvocationHandler(user);        User userLogProxy = (User)Proxy.newProxyInstance(User.class.getClassLoader(), new Class[]{User.class}, logIH);        // 测试双重aop        TransactionInvocationHandler tranIH = new TransactionInvocationHandler(userLogProxy);        User userTranProxy = (User)Proxy.newProxyInstance(User.class.getClassLoader(), new Class[]{User.class}, tranIH);        userTranProxy.saySomething("pens");        System.out.println("\n-----------------------------\n");        System.out.println("金额为:" + userTranProxy.getMoney());      }}
上面这个jdk动态代理例子中,我实现了两个InvocationHandler类,一个用来添加日志逻辑,一个用来添加事务逻辑,也就是双重aop。


下面这个例子是cglib的动态代理

package dynamicProxyByCglib;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
* writer: holien
* Time: 2017-09-26 18:30
* Intent: 日志代理,用于为目标对象方法添加日志逻辑
*/
public class LogProxy implements MethodInterceptor {

private Object target;

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

public Object newProxyInstance() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
// 因为MethodInterceptor接口继承了callable接口,而LogProxy类又实现了MethodInterceptor
// 回调函数需要传一个callable接口的实现类对象,所以可以把自身传进去
// 还有FixedValue、dispatcher等接口
enhancer.setCallback(this);
return enhancer.create();
}

// 参数1:代理对象
// 参数2:被代理的方法
// 参数3:方法参数类型列表
// 参数4:方法代理(作用未知)
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("日志操作开始...");
Object result = method.invoke(target, objects);
System.out.println("日志操作结束...");
return result;
}

public static void main(String[] args) {
UserImpl user = new UserImpl();
// 生成添加日志逻辑的代理对象
UserImpl userLogProxy = (UserImpl)new LogProxy(user).newProxyInstance();
// 再上面对象的基础上再生成添加时间逻辑的代理对象
userLogProxy.saySomething();
System.out.println("\n-----------------------\n");
System.out.println("返回金额:" + userLogProxy.getMoney());
}

}
cglib的代理方式为:自定义一个实现了MethodInterceptor的接口,重写intercept方法,与重写InvocationHandler的invoke方法类似,我们还要自己封装一个方法,比如叫newProxyInstance,里面包含了enhancer的处理逻辑,真正创建代理对象的是enhancer的create方法。其中intercept方法的第四个参数,目前还不知道有何作用,至此两种方式都会简单使用了,原理也掌握了个大概...