2

JDK动态代理
jdk动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用业务方法前调用InvocationHandler处理。代理类必须实现InvocationHandler接口,并且,JDK动态代理只能代理实现了接口的类,没有实现接口的类是不能实现JDK动态代理。
使用JDK动态代理类基本步骤:
1、需要被代理的类和接口

public interface OrderService {
    public void save(UUID orderId, String name);
}

public class OrderServiceImpl implements OrderService {
    @Override
    public void save(UUID orderId, String name) {
        System.out.println("call save()方法,save:" + name);
    }
}

2、代理工厂类,需要实现InvocationHandler接口,重写invoke方法

public class JDKProxy implements InvocationHandler {
    //需要代理的目标对象
    private Object targetObject;
    
    public Object createProxyInstance(Object targetObject) {
        this.targetObject = targetObject;
        return Proxy.newProxyInstance(this.targetObject.getClass().getClassLoader(),
                this.targetObject.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //被代理对象
        OrderServiceImpl bean = (OrderServiceImpl) this.targetObject;
        Object result = null;
        //切面逻辑(advise),此处是在目标类代码执行之前
        System.out.println("---before invoke----");
        if (bean.getUser() != null) {
            result = method.invoke(targetObject, args);
        }
        System.out.println("---after invoke----");
        return result;
    }
}

3、使用Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)动态创建代理类对象,通过代理类对象调用业务方法。

public class AOPTest {
    public static void main(String[] args) {
        JDKProxy factory = new JDKProxy();
        //Proxy为InvocationHandler实现类动态创建一个符合某一接口的代理实例  
        OrderService orderService = (OrderService) factory.createProxyInstance(new OrderServiceImpl("JDK"));
        //由动态生成的代理对象来orderService 代理执行程序
        orderService.save(UUID.randomUUID(), "aoho");
    }

}

---before invoke----
call save()方法,save:JDK
---after invoke----

Cglib动态代理
cglib是针对类来实现代理的,它会对目标类产生一个代理子类,通过方法拦截技术对过滤父类的方法调用。代理子类需要实现MethodInterceptor接口
1.被代理类,该类可以实现接口,也可以不实现接口

public class OrderManager {
    public void save(UUID orderId, String name) {
        System.out.println("call save()方法,save:" + name);
    }
}

2.代理工厂类,实现MethodInterceptor接口,重写intercept方法

public class CGLibProxy implements MethodInterceptor {
        // CGLib需要代理的目标对象
        private Object targetObject;

       public Object createProxyObject(Object obj) {
        this.targetObject = obj;
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(obj.getClass());
        //回调方法的参数为代理类对象CglibProxy,最后增强目标类调用的是代理类对象CglibProxy中的intercept方法 
        enhancer.setCallback(this);
        //增强后的目标类
        Object proxyObj = enhancer.create();
        // 返回代理对象
        return proxyObj;
    }

    @Override
    public Object intercept(Object proxy, Method method, Object[] args,
                            MethodProxy methodProxy) throws Throwable {
        Object obj = null;
        //切面逻辑(advise),此处是在目标类代码执行之前
        System.out.println("---before intercept----");
        obj = method.invoke(targetObject, args);
        System.out.println("---after intercept----");
        return obj;
    }
}

3.创建代理类对象,通过代理类对象调用业务方法

public class AOPTest {
    public static void main(String[] args) {
        OrderManager order = (OrderManager) new CGLibProxy().createProxyObject(new OrderManager("aoho"));
        order.save(UUID.randomUUID(), "CGLIB");
    }
    
---before invoke----
call save()方法,save:CGLIB
---after invoke----

JDK与Cglib动态代理对比?
1、JDK动态代理只能代理实现了接口的类,没有实现接口的类不能实现JDK的动态代理;
2、Cglib动态代理是针对类实现代理的,运行时动态生成被代理类的子类拦截父类方法调用,因此不能代理声明为final类型的类和方法;

Spring如何选择两种代理模式的?
1、如果目标对象实现了接口,则默认采用JDK动态代理;
2、如果目标对象没有实现接口,则使用Cglib代理;
3、如果目标对象实现了接口,但强制使用了Cglib,则使用Cglib进行代理
我们可以结合源码来看下,上面的选择:

image.png

参考:
深入理解Spring AOP的动态代理


WillLiaowh
71 声望8 粉丝

世界上最伟大的力量是坚持。