重温jdk动态代理和cglib代理的区别:
jdk动态代理
- 实现InvocationHandler接口,重写invoke方法,加上自己想要的逻辑代码。
- 创建代理目标对象
- 通过Proxy.newInstance(classLoader,interfaces,InvocationHandler子类);去创建代理类,之后强转或者使用泛型方法。
- 通过代理类来进行方法的调用。
要求:代理类和目标对象需要实现同一个接口!!!
可以看出,jdk动态代理依赖于其实现了接口,并且对接口进行“增强”。若类不实现一个接口时,则无法使用jdk动态代理。
cglib就是弥补了jdk动态代理的不足。
CGLib
- 实现MethodInterceptor接口,重写intercept方法对代理对象的方法进行拦截。
- 通过创建Enhance(增强)来设置超类,去通过enhance.create()创建代理对象的子类,对超类中的方法进行拦截,从而实现自己额外的业务逻辑(性能检测,权限控制,事务,日志等)。
原理:通过动态生成复杂的字节码,创建目标类的子类,进行代理实现。final修饰的类不能进行代理。
JDK动态代理通过反射调用目标对象,cglib采用类似索引的方式直接调用目标对象中的方法。
JAVA实现
JDKDynamic 代理:
package com.logan;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* JDK 动态代理:被代理的对象需要实现一个接口,代理类对被代理类的方法进行拦截,在实际执行被代理类的方法前后,加上日志,事务,权限控制等。。
*
* 1.事务,日志,权限控制是和业务逻辑不相干的,它们是垂直正交的关系。则可看做AOP中的"切面"。
* 2.切入的方法,比如在Dao层所有的add*,update*,delete*之前的方法加上事务,成功了就提交事务,失败了就回滚。
* 可称之为切点 CutPoint
*
*
* JDK 动态代理 : spring 中默认使用JDK动态代理,若想使用CGLib,则需要配置 <aop:config proxy-target-class="true"> 默认为false
* 随着JDK版本的不断升级,最开始jdk动态代理的效率逐渐在提高。
*
* 优点:
* 1.对比静态代理来说,动态代理可以代码复用。
* 2.使用静态代理时,如果代理类和被代理类同时实现了一个接口,当接口方法有变动时,代理类也必须同时修改。
*
* 缺点:
* 1.只能代理实现了接口的类,否则将不能使用JDK 动态代理。
*
*
* @author logan
*
* @Date 2019年04月20日15:12:42
*
*/
public class JDKDynamicProxy implements InvocationHandler {
private Object target;
public JDKDynamicProxy(Object target){
this.target = target;
}
public <T> T getProxy(){
return (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
this.target.getClass().getInterfaces(), this);
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("jdk dynamic proxy before .....");
long startTime = System.currentTimeMillis();
Object object = method.invoke(target, args);
long endTime = System.currentTimeMillis();
System.out.println("jdk dynamic proxy after .....");
System.out.println("jdk dynamic proxy spend time is " + (endTime - startTime) );
return object;
}
}
CGLib Demo :
package com.logan;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
*
* CGLibProxy demo
*
* 为了弥补JDK动态代理的不足,cglib代理在底层字节码的基础上创建类,因为需要动态的去创建类,所以创建类所花费的时间要大于JDK动态代理
* 若一次创建,多次服务,那么效果还是比较好的。
*
*
* @author logan
*/
public class CGLibProxy implements MethodInterceptor {
private Object target;
private Enhancer enhancer = new Enhancer();
public CGLibProxy(Object target){
this.target = target;
}
/**
* get proxy object
* @param <T>
* @return
*/
public <T> T getProxy(){
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(this);
return (T) enhancer.create();
}
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("proxy before .....");
long startTime = System.currentTimeMillis();
//Object object = methodProxy.invoke(target, args);
Object object = methodProxy.invokeSupper(o, args);
long endTime = System.currentTimeMillis();
System.out.println("proxy after .....");
System.out.println("spend time is " + (endTime - startTime) );
return object;
}
}
测试接口:
package com.logan;
public interface IMaster {
void sayHello(String name);
String select(String name);
}
测试实现类:
package com.logan;
public class Master implements IMaster {
public void sayHello(String name){
System.out.println("Hello, " + name);
}
public String select(String name){
return "name = " + name;
}
}
Tester类:
package com.logan;
/**
* 测试类
*/
public class Tester {
public static void main(String[] args) {
IMaster master = new CGLibProxy(new Master()).getProxy();
master.sayHello("Logan");
System.out.println(master.select("Nancy"));
System.out.println("=============");
IMaster proxyMaster = new JDKDynamicProxy(new Master()).getProxy();
proxyMaster.sayHello("xiaoma");
System.out.println(proxyMaster.select("123"));
}
}