1. Spring AOP 的实现主要有两种:CGLib与JDK自带的Proxy。
他们主要的区别是:
- 1.JDK动态代理它实现了被代理对象的接口,而Cglib是继承了被代理对象。
- 2.JDK和Cglib都是在运行期生成字节码,JDK是直接写Class字节码,Cglib使用ASM框架写Class字节码,Cglib代理实现更复杂,生成代理类比JDK效率低。
- 3.JDK调用代理方法,是通过反射机制调用,Cglib是通过FastClass机制直接调用方法,Cglib执行效率更高。
- 4.JDKProxy修改的类必须实现接口(因此也只能代理public方法),在创建Proxy时可以使用class.getInterfaces()获得所有接口并进行代理。而CGLib不受这个限制可以修改任何非private非final方法。
2. JDKProxy
public class JDKProxy {
/**
* 代理类,只能放回接口
* @param object
* @return
* @param <T>
*/
public static <T> T getProxy(Object object) {
Class<?> klass = object.getClass();
ClassLoader classLoder = klass.getClassLoader();
Class<?>[] interfaces = klass.getInterfaces();
return (T) Proxy.newProxyInstance(classLoder, interfaces, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("前置拦截");
Object invoke = method.invoke(object, args);
System.out.println("后置拦截");
return invoke;
}
});
}
/**
* 代理接口
* @param interfaceClass
* @return
* @param <T>
*/
public static <T> T getProxy(Class<?> interfaceClass) {
ClassLoader classLoder = interfaceClass .getClassLoader();
/**
* 进行自定义处理,或者交给其他类处理。比如 JDKProxyInvocationHandler
*/
return (T) Proxy.newProxyInstance(classLoder, new Class[]{interfaceClass}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("getProxy 前置拦截 " + method.getName());
//自定义处理逻辑
Object result;
if (method.getName().equals("getName")){
result= "getName";
}else{
result= 189;
}
System.out.println("getProxy 后置拦截 "+ method.getName());
return result;
}
});
}
/**
* 自定义方法拦截器
*/
class JDKProxyInvocationHandler implements InvocationHandler,JDKProxyInterface{
@Override
public int getAge(int age) {
return age;
}
@Override
public String getName(String name) {
return name;
}
/**
*
* @param proxy 当前代理的实例对象
*
* @param method 当前执行方法
*
* @param args 执行方法参数
*
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("JDKProxyInvocationHandler 前置拦截");
Object invoke = method.invoke(this, args);
//自定义处理逻辑
System.out.println("JDKProxyInvocationHandler 后置拦截");
return invoke;
}
}
}
3. CGLBProxy
导入依赖
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
public class CGLBProxy {
public static <T> T getProxy(T object) {
Class<?> klass = object.getClass();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(klass);
enhancer.setCallback(new InvocationHandler() {
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
System.out.println("CGLB前置拦截");
Object result = method.invoke(object, objects);
System.out.println("CGLB后置拦截");
return result;
}
});
return (T)enhancer.create();
}
}
4、两者优缺点
JDK动态代理和CGLIB代理都有它们自己的优缺点。
JDK动态代理的优点:
- JDK动态代理是Java标准库的一部分,因此它不需要引入任何外部依赖。
- JDK动态代理只需要实现接口即可生成代理对象,不需要改变原有类的结构。
- 由于JDK动态代理是基于接口实现的,因此它更适合用于代理接口实现类的场景。
JDK动态代理的缺点:
- JDK动态代理只能代理实现了接口的类,无法代理没有实现接口的类。
- JDK动态代理在生成代理对象时,需要使用反射机制,因此它的效率相对较低。
CGLIB代理的优点:
- CGLIB代理是基于字节码技术实现的,因此它的效率比JDK动态代理更高。
- CGLIB代理可以代理没有实现接口的类。
CGLIB代理的缺点:
- CGLIB代理需要引入外部依赖。
- CGLIB代理在生成代理对象时,需要改变原有类的结构,因此它可能会引起一些问题,例如无法代理final类或final方法等问题。
综上所述,JDK动态代理适用于代理接口实现类的场景,而CGLIB代理适用于代理没有实现接口的类的场景。如果你需要代理接口实现类,而且不想引入额外的依赖,那么JDK动态代理是一个不错的选择;如果你需要代理没有实现接口的类,那么CGLIB代理可能更适合你的需求。