1、前言
java中代理方式分为静态代理和动态代理,静态代理的代理关系在编译时就确定了,它需要为每一个目标类创建一个代理类,在代理类数量较少时可以选择使用。当代理类较多时,需要使用动态代理,动态代理相对来说提供了很大的灵活性,以下讲解下动态代理的两种实现方式,即JDK原生动态代理和CGLIB动态代理。
2、JDK原生动态代理
2.1 示例代码
定义接口:
public interface Fruit {
String name(final String name);
double price(double price);
}
实现接口的具体代理对象类:
public class Apple implements Fruit {
@Override
public String name(String name) {
System.out.println("this is " + name);
return name;
}
@Override
public double price(double price) {
System.out.println("Apple price is " + price);
return price;
}
}
定义实现接口InvocationHandler的类:
public class FruitDynamicProxy implements InvocationHandler {
private Fruit fruit;
public FruitDynamicProxy(Fruit fruit) {
this.fruit = fruit;
}
/**
* 获取代理对象实例
* newProxyInstance()会返回一个实现了指定接口的代理对象,对该对象的所有方法调用都会转发给InvocationHandler.invoke()方法
*
* @return
*/
public Object getProxyInstance() {
return Proxy.newProxyInstance(
//代理对象的类加载器
fruit.getClass().getClassLoader(),
//代理对象需要实现的接口,可以是多个
fruit.getClass().getInterfaces(),
//方法调用的实际对象,
this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("FruitDynamicProxy方法名 " + method.getName() + " ---参数 " + Arrays.toString(args));
//代理对象调用相应方法的返回结果
Object invoke = method.invoke(fruit, args);
return invoke;
}
}
测试:
FruitDynamicProxy fruitDynamicProxy = new FruitDynamicProxy(new Apple());
Fruit banana = (Fruit) fruitDynamicProxy.getProxyInstance();
//调用该方法时转到FruitDynamicProxy类的invoke方法
banana.name("apple");
banana.price(12.6);
2.2 说明
实现接口InvocationHandler类,代理对象在调用方法时就会转到该类的invoke()方法。之后在获取代理对象时使用Proxy的newProxyInstance()方法,该方法会返回一个实现具体接口的代理对象,该代理对象的所有方法调用都会转发到InvocationHandler.invoke()方法,具体是使用了java反射机制。
newProxyInstance()方法的三个参数:
loader,指定代理对象的类加载器;interfaces,代理对象需要实现的接口,可以同时指定多个接口;handler,方法调用的实际处理者,代理对象的方法调用都会转发到这里。
3、CGLIB动态代理
3.1 代码示例
代理对象类:
public class CglibFruit {
public String name(String name) {
System.out.println("this is " + name);
return name;
}
public double price(double price) {
System.out.println("price is " + price);
return price;
}
//final修饰的方法非子类无法访问
public final void hello(String msg) {
System.out.println("final method " + msg);
}
}
实现MethodInterceptor类:
public class CglibMethodInterceptor implements MethodInterceptor {
private CglibFruit fruit;
public CglibMethodInterceptor(CglibFruit fruit) {
this.fruit = fruit;
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("CglibMethodInterceptor method " + method.getName() + " --- 参数 " + objects);
Object o1 = methodProxy.invokeSuper(o, objects);
return o1;
}
/**
* 获取代理对象实例,通过Enhancer来指定要代理的目标对象、实际处理代理逻辑的对象,最后通过调用create()方法得到代理对象
* @return
*/
public Object getProxyInstance() {
Enhancer enhancer = new Enhancer();
//设置代理对象class字节
enhancer.setSuperclass(this.fruit.getClass());
//设置回调方法
enhancer.setCallback(this);
//创建代理对象
Object o = enhancer.create();
return o;
}
}
测试:
CglibMethodInterceptor cmi = new CglibMethodInterceptor(new CglibFruit());
CglibFruit fruit = (CglibFruit) cmi.getProxyInstance();
fruit.name("orange");
fruit.price(12.666);
3.2 说明
实现MethodInterceptor类,代理对象的方法调用会被转发到该类的intercept()方法。在获取代理对象时通过Enhancer来指定要代理的目标对象、实际处理代理逻辑的对象,最后通过调用create()方法得到代理对象。对这个对象所有非final方法的调用都会转发给MethodInterceptor.intercept()方法。
4、两者区别
java动态代理是领用反射机制生成一个代理接口匿名类,在调用具体的方法前调用InvocationHandler.invoke()方法来处理。CGLIB动态代理是对代理对象类的class文件加载进来,通过修改器字节码成成子类来处理。
两者使用:
如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
如果目标对象实现了接口,可以强制使用CGLIB实现AOP
如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换
参照原码 Github
来源:https://www.cnblogs.com/kingsonfu/p/10633687.html