逆水行舟 —— jdk动态代理和Cglib字节码增强

Posted msi-chen

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了逆水行舟 —— jdk动态代理和Cglib字节码增强相关的知识,希望对你有一定的参考价值。

JDK动态代理

利用拦截器加上反射机制生成一个实现代理接口的匿名类,在调用具体方法时,调用InvocationHandler来处理

JDK动态代理只需要JDK环境就可以进行代理,流程为:

  1. 实现InvocationHandler

  2. 使用Proxy.newProxyInstance产生代理对象

  3. 被代理的对象必须实现接口

具体列子如下:

public class UserServiceImpl implements UserService {
    
    @Override
    public void eat() {
        System.out.println("---------吃饭");
    }
    @Override
    public void wc() {
        System.out.print("上茅房------>");
    }
}
//切面类
public class MyAspect {
    
    public void before(){
        System.out.print("先洗手再");
    }
    
    public void after(){
        System.out.print("后要洗手");
    }
}
// 产生代理对象的工厂类
public class MyFactoryBean {
    
    public static UserService getInstance(){
        // target : 目标类
        final UserService service = new UserServiceImpl();
        // Aspect : 切面类
        final MyAspect aspect = new MyAspect();
        // Weaving : 织入,也就是产生代理的过程
        UserService proxy = (UserService) Proxy.newProxyInstance(
                MyFactoryBean.class.getClassLoader(),
                new Class[]{UserService.class},
                new InvocationHandler(){
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        if (method.getName().equals("eat")){
                            aspect.before();
                        }
                        Object invoke = method.invoke(service, args);
                        if (method.getName().equals("wc")){
                            aspect.after();
                        }
                        return invoke;
                    }
                });
        return proxy;
    }
}
//测试方法
@Test
public void userTest(){
    UserService userService = MyFactoryBean.getInstance();  //使用工厂类产出代理对象
    userService.eat();
    userService.wc();
}

效果如下:

技术图片

CGLIB动态代理

  1. 通过加载对象类的class文件,修改其字节码生成子类的方式完成,不需要实现接口

  2. 但是需要第三方库:CGLIB类库的支持

public class MyProxy implements MethodInterceptor {
?
    private Object personService;
    
    public Object createProxy(Object obj){
        this.personService = obj;
        Enhancer e = new Enhancer();
        e.setSuperclass(obj.getClass());
        e.setCallback(this);
        Object proxy = e.create();
        return proxy;   //返回代理对象
    }
?
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        Object obj = null;
        if ("eat".equals(method.getName())){
            System.out.print("先洗手再----->");
        }
?
        obj = method.invoke(personService, objects);
        
        if ("wc".equals(method.getName())){
            System.out.print("---->之后要洗手");
        }
        return obj;
    }
}
public class PeopleService {
public void eat(){
System.out.println("吃饭");
}
public void wc(){
System.out.print("上厕所");
}
}
@Test
public void Test1(){
    MyProxy myProxy = new MyProxy();
    PeopleService proxy =(PeopleService) myProxy.createProxy(new PeopleService());
    proxy.eat();
  

效果如下:

      技术图片

总结

关于在Spring的AOP中采用何种代理手段,我们不强加限制的话,会根据类是否有接口来区别对待

  1. 当一个类有接口的时候,就会选用JDK的动态代理

  2. 当一个类没有实现接口的时候,就会选用CGLIB代理的方式

两种代理方式的本质:

  1. JDK动态代理是针对实现了接口的类生成代理,不是针对类

  2. CGLIB使用的是为被代理类生成一个子类,通过继承的方法覆盖并增强其方法,

    但是因为是继承所以不能声明被代理类为final,无法被继承无法实现CGLIB代理

以上是关于逆水行舟 —— jdk动态代理和Cglib字节码增强的主要内容,如果未能解决你的问题,请参考以下文章

jdk动态代理和cglib动态代理的区别

JDK / CGLIB 动态代理

JDK和CGLIB动态代理原理

Cglib和jdk动态代理的区别

CGLIB与JDK动态代理

CGLIB与JDK动态代理