AOP的代理方式主要分为两种,静态代理 和 动态代理。
在AspectJ 1.5 之后,引入了 @Aspect 形式的注解风格开发,Spring也非常快地跟进了这种方式,在Spring 2.0之后便使用了与Aspect 1.5 一样的注解。
注意:Spring只是使用了AspectJ的注解,而没有使用AspectJ的编译器,低层还是使用动态代理技术实现。
AspectJ使用静态代理
静态代理的缺点很明显:
一个代理类只能对一个业务接口的实现类进行包装,如果有多个业务接口的话,就要定义很多实现类进行代理才行。
而且如果代理类对业务方法的预处理,调用后处理都是一样的(例如调用前输出提示,调用后关闭连接),则多个
代理类就会有很多重复的代码。
Spring使用动态代理,主要有JDK实现和cglib实现。
Spring创建代理的规则为:
默认使用JDK动态代理来创建AOP代理
当需要代理的类没有实现接口的时候,Spring会切换为使用cglib创建AOP代理
可以在配置文件制定,强制使用cglib代理
Spring使用动态代理(详解)
JDK实现动态代理
JDK动态代理的原理,是利用反射机制,生成一个和目标类继承了一样接口的匿名代理类。在调用具体方法
前使InvocationHandler来处理。
JDK动态代理的对象在创建时,需要使用业务实现类的接口作为参数(因为在后面代理方法时需要根据接口内的方
法名进行调用)。
如果业务实现类没有实现接口,就无法使用JDK动态代理了
关键接口:java.lang.reflect.InvocationHandler
关键类:java.lang.reflect.Proxy 和 java.lang.reflect.Method
具体代码:
//接口
public interface Jiekou {
void run();
}
//拓展类
public class Jiekoushixian implements Jiekou {
public void run() {
System.out.println("接口实现了");
}
}
//调用处理类invocationhandler
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class Diaoyongchuli implements InvocationHandler {
private final Jiekou jiekou;
public diaoyongchuli(Jiekou jiekou){
this.jiekou = jiekou;
}
//invoke三个参数:
//proxy:就是代理对象,newProxyInstance方法的返回对象
//method:调用的方法
//args: 方法中的参数
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("---------之前-------");
Object invoke = method.invoke(jiekou, args);
System.out.println("---------之后-------");
return invoke;
}
}
//最终
import java.lang.reflect.Proxy;
public class App {
public static void main(String[] args) {
Jiekou jiekoushixian = new Jiekoushixian();
//newProxyInstance,方法有三个参数:
//loader: 用哪个类加载器去加载代理对象
//interfaces:动态代理类需要实现的接口
//h:动态代理方法在执行时,会调用h里面的invoke方法去执行
Jiekou jiekou = (Jiekou)Proxy.newProxyInstance(jiekoushixian.getClass().
getClassLoader(), Jiekoushixian.class.getInterfaces(),
new Diaoyongchuli (jiekoushixian));
//执行
jiekou.run();
}
cglib 动态代理
cglib动态代理的原理是继承需要代理的类,生成的代理类是目标类的子类。
用cglib生成的代理类重写了父类的各个方法。
关键接口:org.springframework.cglib.proxy.MethodInterceptor
关键类:org.springframework.cglib.proxy.Enhancer 和
org.springframework.cglib.proxy.MethodProxy
public class CGlibDaili implements MethodInterceptor {
private Object proxyObject;
public CGlibDaili(Object proxyObject) {
this.proxyObject = proxyObject;
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("---------之前-------");
Object ret = method.invoke(proxyObject, objects);
System.out.println("---------之后-------");
return ret;
}
}
// 工厂方法,获取代理对象
public class ProxyFactory {
public static Object createCGlibDailiInstance(Object proxyObject){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(proxyObject.getClass());
enhancer.setCallback(new CGlibDaili(proxyObject));
return enhancer.create();
}
public static void main(String[] args) {
Jiekoushixian jiekoushixian = new Jiekoushixian();
Jiekoushixian jiekou=(Jiekoushixian)ProxyFactory.createCGlibDailiInstance(jiekoushixian);
//执行
jiekou.run();
}
}
两者的区别
JDK 使用继承接口的方式生成代理类。(实现接口,管理代理实例)
cglib 使用继承目标类的方式生成代理类。(生成目标类的子类,重写方法)
JDK只能对继承了接口的类代理。
cglib 除了 final 类都可以代理。
JDK代理类生成快,但是运行效率较cglib代理差。
cglib代理类生成慢,但是运行效率较JDK代理快。
Spring如何选择这两种代理
目标对象实现了接口,默认使用JDK代理。
目标对象实现了接口,可以选择强制使用 cglib 代理。
目标对象没有实现接口,只能选择使用 cglib 代理。