自Java 1.3以后,Java提供了动态代理技术,允许开发者在运行期创建接口的代理实例,后来这项技术被用到了Spring的很多地方。
JDK动态代理主要涉及java.lang.reflect包下边的两个类:Proxy和InvocationHandler。
其中,InvocationHandler是一个接口,可以通过实现该接口定义切面逻辑,并通过反射机制调用目标类的代码,动态地将切面逻辑和业务逻辑编织在一起。
但是JDK动态代理的话,他有一个限制,就是它只能为接口创建代理实例,而对于没有通过接口定义业务方法的类,如何创建动态代理实例哪?答案就是CGLib。
JDK动态代理示例代码:
1、新建一个接口
package com.demo.rpc.proxy;
public interface NewsService {
String queryById(long newsId);
}
2、新建一个接口的实现类
package com.demo.rpc.proxy;
public class NewsServiceImpl implements NewsService {
@Override
public void queryById(long newsId) {
System.out.println("hello");
}
}
3、新建一个代理类
package com.demo.rpc.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class JDKProxy implements InvocationHandler {
Object targetObject;
/**
* 传入目标对象进行代理
* @param targetObject
* @return
*/
public Object newProxy(Object targetObject) {
this.targetObject = targetObject;
return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理之前");
Object invoke = method.invoke(targetObject, args);
System.out.println("代理之后");
return invoke;
}
}
4、调用测试
@Test
public void jdkProxy(){
NewsServiceImpl newsServiceImpl = new NewsServiceImpl();
JDKProxy proxy = new JDKProxy();
NewsService newsService = (NewsService)proxy.newProxy(newsServiceImpl);
newsService.queryById(1);
}
CGLIB(Code Generation Library)是一个基于ASM的字节码生成库,它允许我们在运行时对字节码进行修改和动态生成。
CGLIB通过继承方式实现代理,在子类中采用方法拦截的技术拦截所有父类方法的调用并顺势织入横切逻辑。
CGLIB动态代理示例代码:
1、创建一个实现类
package com.demo.rpc.proxy;
public class HelloService {
public void sayHello(){
System.out.println("hello");
}
}
2、新建一个代理类
package com.demo.rpc.proxy;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CGLIBProxy implements MethodInterceptor {
Object targetObject;
public Object newProxy(Object targetObject){
this.targetObject = targetObject;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(targetObject.getClass());
enhancer.setCallback(this);
Object proxyObj = enhancer.create();
return proxyObj;
}
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("代理之前");
Object invoke = method.invoke(targetObject, args);
System.out.println("代理之后");
return invoke;
}
}
3、调用测试
@Test
public void CGLIBProxy(){
CGLIBProxy proxy = new CGLIBProxy();
HelloService helloService = (HelloService)proxy.newProxy(new HelloService());
helloService.sayHello();
}
JDK和CGLib动态代理区别
1、JDK动态代理具体实现原理:
通过实现InvocationHandler接口创建自己的调用处理器;
通过为Proxy类指定ClassLoader对象和一组interface来创建动态代理;
通过反射机制获取动态代理类的构造函数,其唯一参数类型就是调用处理器接口类型;
通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数参入;
JDK动态代理是面向接口的代理模式,如果被代理目标没有接口那么Spring也无能为力,Spring通过Java的反射机制生产被代理接口的新的匿名实现类,重写了其中AOP的增强方法。
2、CGLib动态代理:
利用ASM开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
3、两者对比:
JDK动态代理是面向接口的。
CGLib动态代理是通过字节码底层继承要代理类来实现,因此如果被代理类被final关键字所修饰,会失败。
4、使用注意:
如果要被代理的对象是个实现类,那么Spring会使用JDK动态代理来完成操作(Spirng默认采用JDK动态代理实现机制);
如果要被代理的对象不是个实现类那么,Spring会强制使用CGLib来实现动态代理。
网友回复