• 《Spring》(十二) ---- AOP 实现机制


    动态代理 

      JDK动态代理(参考《疯狂Java讲义》(三十六)---- 类加载机制与反射

       JDK动态代理机制可以在运行期间,为相应的接口动态生成对应的代理对象。所以,可以将横切关注点逻辑封装到动态代理的InvacationHandler中,然后在系统运行期间,根据横切关注点需要植入的模块位置,将横切逻辑织入到相应的代理类中。

      这种方式实现的唯一缺点是,所有需要织入横切关注点逻辑的模块类都得实现相应的接口,因为动态代理只针对接口有效。

    示例:

      ForumService.class

    package com.ivy.aop;
    
    public interface ForumService {
    
        void removeTopic(int topicId);
        void removeForum(int forumId);
    }

      ForumServiceImpl.class

    package com.ivy.aop;
    
    public class ForumServiceImpl implements ForumService {
    
        @Override
        public void removeTopic(int topicId) {
            // TODO Auto-generated method stub
            System.out.println("remove topic record : " + topicId);
            try {
                Thread.currentThread().sleep(20);
            } catch (Exception ex) {
                throw new RuntimeException();
            }
        }
    
        @Override
        public void removeForum(int forumId) {
            System.out.println("remove forum record : " + forumId);
            try {
                Thread.currentThread().sleep(40);
            } catch (Exception ex) {
                throw new RuntimeException();
            }
    
        }
    
    }

      PerformanceMonitor.class

    package com.ivy.aop;
    
    public class PerformanceMonitor {
    
        private static ThreadLocal<MethodPerformance> performanceRecord = new ThreadLocal<MethodPerformance>();
        
        public static void begin(String method) {
            System.out.println("begin monitor...");
            MethodPerformance mp = new MethodPerformance(method);
            performanceRecord.set(mp);
        }
        
        public static void end() {
            System.out.println("end monitor...");
            MethodPerformance mp = performanceRecord.get();
            mp.printPerformance();
        }
    }

      MethodPerformance.class

    package com.ivy.aop;
    
    public class MethodPerformance {
    
        private long begin;
        private long end;
        private String serviceMethod;
        public MethodPerformance(String serviceMethod) {
            this.serviceMethod = serviceMethod;
            this.begin = System.currentTimeMillis();
        }
        
        public void printPerformance() {
            end = System.currentTimeMillis();
            long elapse = end - begin;
            
            System.out.println(serviceMethod + " cost " + elapse + "ms.");
        }
    }

      
      PerformanceHandler.class

    package com.ivy.aop;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    
    public class PerformanceHandler implements InvocationHandler{
    
        private Object target;
        public PerformanceHandler(Object target) {
            this.target = target;
        }
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
            PerformanceMonitor.begin(target.getClass().getName() + "." + method.getName());
            Object object = method.invoke(target, args);
            PerformanceMonitor.end();
            return object;
        }
    }

      TestForumServiceByJDKProxy.class

    package com.ivy.aop;
    
    import java.lang.reflect.Proxy;
    
    public class TestForumServiceByJDKProxy {
    
        public static void main(String[] args) {
            ForumService target = new ForumServiceImpl();
            PerformanceHandler handler = new PerformanceHandler(target);
        // 只能对接口进行代理 ForumService proxy = (ForumService)Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), handler); proxy.removeForum(10); proxy.removeTopic(1012); } }

      运行结果:

    begin monitor...
    remove forum record : 10
    end monitor...
    com.ivy.aop.ForumServiceImpl.removeForum cost 41ms.
    begin monitor...
    remove topic record : 1012
    end monitor...
    com.ivy.aop.ForumServiceImpl.removeTopic cost 20ms.
    

      

    动态字节码增强 -- CGLib动态代理

      java虚拟机加载的class文件都是符合一定规范的,所以,只要交给虚拟机运行的文件符合Java Class规范,程序的运行就么有问题。通常的class文件都是从Java源代码文件使用Javac编译器编译而成的,但只要符合Java Class规范,我们也可以使用CGLib等Java工具类,在程序运行期间,动态构建字节码的class文件。在这样的前提下,我们可以为需要织入横切逻辑的模块类在运行期间,通过动态字节码增强技术,为这些系统模块类生成相应的子类,而将横切逻辑加到这些子类中,让应用程序在执行期间使用的是这些动态生成的子类,从而达到将横切逻辑织入系统的目的。使用动态字节码增强技术,即使模块类没有实现相应的接口,我们依然可以对其进行扩展,而不用像动态代理那样受限于接口。不过这种实现的不足是,如果需要扩展的类以及类中的实例方法等声名为final,则无法对其进行子类化扩展。

      Spring AOP在无法使用动态代理机制进行AOP扩展的时候,会使用CGLIB库德动态字节码增强来实现AOP的功能扩展。

      

      使用JDK创建代理有一个限制,它只能够为接口创建代理实例,对于没有通过接口定义业务方法的类,就需要CGLib动态创建代理了。

      CGLib采用非常底层的字节码技术,可以为一个类创建子类,并在子类中采用方法拦截的技术拦截所有弗雷方法的调用,并顺势织入横切逻辑。

      CglibProxy.class

    package com.ivy.aop;
    
    import java.lang.reflect.Method;
    
    import org.springframework.aop.interceptor.PerformanceMonitorInterceptor;
    
    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    
    public class CglibProxy implements MethodInterceptor{
    
        private Enhancer enhancer = new Enhancer();
        public Object getProxy(Class clazz) {
            // 设置需要创建子类的类
            enhancer.setSuperclass(clazz);
            enhancer.setCallback(this);
            // 通过字节码结束动态创建子类实例
            return enhancer.create();
        }
        @Override
        // 拦截父类所有方法的调用
        public Object intercept(Object obj, Method method, Object[] args,
                MethodProxy proxy) throws Throwable {
            // TODO Auto-generated method stub
            PerformanceMonitor.begin(obj.getClass().getName() + "." + method.getName());
            // 通过代理类调用父类中的方法
            Object result = proxy.invokeSuper(obj, args);
            PerformanceMonitor.end();
            return result;
        }
    
    }

      TestForumService.class

    package com.ivy.aop;
    
    public class TestForumService {
    
        public static void main(String[] args) {
            // TODO Auto-generated method stub
    
            CglibProxy proxy = new CglibProxy();
        // 可以对实现类进行代理 ForumServiceImpl forumService = (ForumServiceImpl)proxy.getProxy(ForumServiceImpl.class); forumService.removeForum(10); forumService.removeTopic(1023); } }

      运行结果:

    begin monitor...
    remove forum record : 10
    end monitor...
    com.ivy.aop.ForumServiceImpl$$EnhancerByCGLIB$$2d2c5cf3.removeForum cost 41ms.
    begin monitor...
    remove topic record : 1023
    end monitor...
    com.ivy.aop.ForumServiceImpl$$EnhancerByCGLIB$$2d2c5cf3.removeTopic cost 20ms.
  • 相关阅读:
    centos7 主从dns配置 bind服务
    Linux的启动流程
    shell脚本编程数组
    shell脚本编程进阶
    计划任务
    进程管理
    Linux网络配置
    计算机网络基础
    磁盘存储和文件系统
    软件包管理
  • 原文地址:https://www.cnblogs.com/IvySue/p/6529100.html
Copyright © 2020-2023  润新知