zoukankan      html  css  js  c++  java
  • 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代理

  • 相关阅读:
    {POJ}{3903}{Stock Exchange}{nlogn 最长上升子序列}
    {HDU}{2516}{取石子游戏}{斐波那契博弈}
    {POJ}{3925}{Minimal Ratio Tree}{最小生成树}
    {ICIP2014}{收录论文列表}
    {Reship}{KMP字符串匹配}
    kettle系列-[KettleUtil]kettle插件,类似kettle的自定义java类控件
    kettle系列-kettle管理平台部署说明
    kettle系列-我的开源kettle调度、管理平台[kettle-manager]介绍
    技术杂记-改造具有监控功能的数据库连接池阿里Druid,支持simple-jndi,kettle
    技术杂记-日期时间字符串解析识别
  • 原文地址:https://www.cnblogs.com/msi-chen/p/10801816.html
Copyright © 2011-2022 走看看