Table of Contents
1.3:创建InvocationHandler方法执行器 和生成代理类
1.5:或者使用内部类来标识InvocationHandler处理器;
目前java动态代理的实现分为两种
1.基于JDK的动态代理
2.基于CGILB的动态代理
在业务中使用动态代理,一般是为了给需要实现的方法添加预处理或者添加后续操作,但是不干预实现类的正常业务,把一些基本业务和主要的业务逻辑分离。我们一般所熟知的Spring的AOP原理就是基于动态代理实现的。
1:基于JDK的动态代理
基于JDK的动态代理就需要知道两个类:1.InvocationHandler(接口)、2.Proxy(类)
还要知道JDK是基于接口的动态代理,也就是说我们动态代理的被代理类,必须实现接口
1.1:创建接口
public interface UserInter {
void add();
}
1.2:创建实现类
public class UserImpl implements UserInter {
@Override
public void add() {
System.out.println("add.....");
}
}
1.3:创建InvocationHandler方法执行器 和生成代理类
package com.wkl.impl.Proxy;
import com.wkl.impl.UserImpl;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* Description:方法执行器,帮我们执行方法
* Date: 2020/6/26 - 下午 10:16
* author: wangkanglu
* version: V1.0
*/
public class MyInvocationHandler implements InvocationHandler {
private UserImpl userImpl;
public void setUserImpl(UserImpl userImpl) {
this.userImpl = userImpl;
}
/**
* Object proxy:代理对象;给jdk使用,任何时候都不要动这个对象
* Method method:当前将要执行的目标对象的方法
* Object[] args:这个方法调用时外界传入的参数值
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//参数为被代理的类的实例,和传入的参数
System.out.println("准备执行方法:"+method.getName());
Object invoke = method.invoke(userImpl, args);
System.out.println("执行方法:"+method.getName()+"完毕!");
return invoke;
}
}
1.4:编写代理类实际的调用。
UserInter userInter = new UserImpl();
//获取处理器实例
MyInvocationHandler userProxy = new MyInvocationHandler();
userProxy.setUserInter(userInter);
UserInter o = (UserInter) Proxy.newProxyInstance(userInter.getClass().getClassLoader(), userInter.getClass().getInterfaces(), userProxy);
o.add();
准备执行方法:add
add.....
执行方法:add完毕!
1.5:或者使用内部类来标识InvocationHandler处理器;
package com.wkl.impl.Proxy;
import com.wkl.impl.UserImpl;
import com.wkl.inter.UserInter;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* Description:
* Date: 2020/6/26 - 下午 10:15
* author: wangkanglu
* version: V1.0
*/
public class UserProxy {
public UserInter getProxy(UserInter userInter){
InvocationHandler i = new InvocationHandler() {
/**
* Object proxy:代理对象;给jdk使用,任何时候都不要动这个对象
* Method method:当前将要执行的目标对象的方法
* Object[] args:这个方法调用时外界传入的参数值
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("准备执行方法:"+method.getName());
Object invoke = method.invoke(userInter, args);
System.out.println("执行方法:"+method.getName()+"完毕!");
return invoke;
}
};
UserInter o = (UserInter) Proxy.newProxyInstance(userInter.getClass().getClassLoader(), userInter.getClass().getInterfaces(), i);
return o;
}
public static void main(String[] args) {
UserInter userInter = new UserImpl();
//获取处理器实例
UserProxy userProxy = new UserProxy();
UserInter proxy = userProxy.getProxy(userInter);
proxy.add();
}
}
注:处理器接口传入的是接口的实现;
2:基于CGLIB的动态代理
本文主要讲的是CGLIB的动态代理,因为基于JDK的动态代理一定要继承一个接口,而绝大部分情况是基于POJO类的动态代理,那么CGLIB就是一个很好的选择,在Hibernate框架中PO的字节码生产工作就是靠CGLIB来完成的。还是先看代码。
2.1:引入jar包
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
2.2:被代理类
public class UserImpl {
public void add() {
System.out.println("add.....");
}
}
2.3:代理了实现MethodInterceptor接口
package com.wkl.impl.Proxy;
import com.wkl.impl.UserImpl;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* Description:
* Date: 2020/6/26 - 下午 10:55
* author: wangkanglu
* version: V1.0
*/
public class UserCGLIB implements MethodInterceptor {
private UserImpl userimpl;
public void setUserimpl(UserImpl userimpl) {
this.userimpl = userimpl;
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
//参数为被代理的类的实例,和传入的参数
System.out.println("准备执行方法:"+method.getName());
用。
method无法得到父类的方法,所以使用methoproxy
Object invoke = methodProxy.invoke(userimpl, objects);
System.out.println("执行方法:"+method.getName()+"完毕!");
return invoke;
}
}
2.4:测试:
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserImpl.class);
UserCGLIB userCGLIB = new UserCGLIB();
UserImpl user = new UserImpl();
userCGLIB.setUserimpl(user);
enhancer.setCallback(userCGLIB);
UserImpl o = (UserImpl) enhancer.create();
o.add();
}
准备执行方法:add
add.....
执行方法:add完毕!
2.5:CGLIB原理
从代码可以看出,它和jdk动态代理有所不同,对外表现上看CreatProxyedObj,它只需要一个类型clazz就可以产生一个代理对象, 所以说是“类的代理”,且创造的对象通过打印类型发现也是一个新的类型。不同于jdk动态代理,jdk动态代理要求对象必须实现接口(三个参数的第二个参数),cglib对此没有要求。
cglib的原理是这样,它生成一个继承B的类型C(代理类),这个代理类持有一个MethodInterceptor,我们setCallback时传入的。 C重写所有B中的方法(方法名一致),然后在C中,构建名叫“CGLIB”+“$父类方法名$”的方法(下面叫cglib方法,所有非private的方法都会被构建),方法体里只有一句话super.方法名(),可以简单的认为保持了对父类方法的一个引用,方便调用。
这样的话,C中就有了重写方法、cglib方法、父类方法(不可见),还有一个统一的拦截方法(增强方法intercept)。其中重写方法和cglib方法肯定是有映射关系的。
C的重写方法是外界调用的入口(LSP原则),它调用MethodInterceptor的intercept方法,调用时会传递四个参数,第一个参数传递的是this,代表代理类本身,第二个参数标示拦截的方法,第三个参数是入参,第四个参数是cglib方法,intercept方法完成增强后,我们调用cglib方法间接调用父类方法完成整个方法链的调用。
为什么我们使用methodproxy,不适用method
因为如果我们通过反射 arg1.invoke(arg0, ...)这种方式是无法调用到父类的方法的,子类有方法重写,隐藏了父类的方法,父类的方法已经不可见,如果硬调arg1.invoke(arg0, ...)很明显会死循环。
所以调用的是cglib开头的方法,但是,我们使用arg3也不是简单的invoke,而是用的invokeSuper方法,这是因为cglib采用了fastclass机制,不仅巧妙的避开了调不到父类方法的问题,还加速了方法的调用。
fastclass基本原理是,给每个方法编号,通过编号找到方法执行避免了通过反射调