走进Spring中AOP的世界(一)思想篇

前言

再写完《走进Spring中Bean的世界》,写这篇文章时,本来想法是把AOP的思想、原理以及源码通通囊入其中,最后发现会和上篇文章同样,读起来难以专一。因此仍是本着跳出看全景,转进去看本质的原则,将AOP的文章分为三部分,分别为思想篇、源码篇、应用篇。html

你能够了解到

  • Java程序在JVM中的运行流程
  • 面向切面编程思想
  • 代理模式的应用
  • Spring AOP的工做原理

Java程序在JVM中的运行流程

  • 先来了解下虚拟机栈(JVM stack)

    虚拟栈是一个后入先出(LIFO)栈。每个线程建立时,JVM会为这个线程建立一个私有的虚拟机栈,当线程调用某个对象的方法时,JVM会相应地建立一个栈帧(Stack Frame)放到虚拟机中,用来表示某个方法的调用。线程对方法的调用就对应一个栈帧的入栈和出栈的过程spring

  • 来看段代码了解虚拟机栈
    public class TestMethod {
            public void method1() {
                StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
            }
            public static void main(String[] args) {
                TestMethod target = new TestMethod();
                target.method1();
            }
        }
    复制代码
    上面代码咱们在method()1时,打个断点。
    当前虚拟栈的信息能够以下图所示:
  • Java程序执行机制
    图中左面是方法执行顺序,对应右面是抽象的调用流程,其实程序执行机制,把方法当作链接点,串起来就是整个执行过程。

面向切面编程思想

上面把程序执行机制以链接点的概念进行了抽象,下面来了解下面向切面以此为基准的编程思想express

  • 走进面向切面编程
    AOP将每个方法调用抽象成链接点(Join Point),链接点串起来的程序执行流就是整个程序的执行过程,“按需”选择链接点进行切入,也就是切入点(Pointcut)。那么切入点如何肯定呢?
  • 肯定切入点
    切入点(Pointcut)肯定,实际上是根据切入点表达式来匹配该方法(链接点)是否知足。

代理模式的应用

上面了解到AOP是对方法调用进行编程,那么AOP如何捕获方法的调用的?其实AOP实现的基础是代理模式的应用。编程

  • 引入代理模式Java程序执行机制
    想访问目标对象的方法时,其实执行的是代理对象的“拦截方法”。下面来了解下Spring中代理模式的两种实现方式。
  • 代理模式
    • 几大角色
      • 抽象角色(Subject):经过接口或抽象类声明真实角色实现的业务方法
      • 真实角色(TargetSubject):实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用
      • 代理角色(Proxy):实现抽象角色,是真实角色的代理,经过真实角色的业务逻辑方法来实现抽象方法,并能够附加本身的操做。

    代理模式中,由抽象角色(Subject)、真实角色(TargetSubject)、代理角色(Proxy)。其中:抽象角色(Subject)负责定义真实角色(TargetSubject)和代理角色(Proxy)应该实现的接口;真实角色(TargetSubject)来完成真正的request功能;代理角色(Proxy)负责将自身的request请求调用真实角色(TargetSubject)对应的request功能来实现业务功能,本身不真正作业务,并在调用request先后,插入代码实现具体功能。bash

    • JDK动态代理
      • 实现:JDK动态代理会根据真实角色(TargetSubject)的全部接口列表,肯定要生成的代理类的类名,默认为:com.sun.proxy.$ProxyXXX,而后根据须要实现的接口信息,在代码中动态建立 该Proxy类的字节码,将对应的字节码转换为对应的class 对象,建立InvocationHandler 实例handler,用来处理Proxy全部方法调用,Proxy 的class对象 以建立的handler对象为参数,实例化一个proxy对象。
      • 代码实现:
        public interface Subject {
            public void request();
        }
        public class TargetSubject implements Subject {
            @Override
            public void request() {
                System.out.println("TargetSubject#request...");
            }
        }
        public class InvocationHandlerImpl implements InvocationHandler {
            private TargetSubject targetSubject;
            public InvocationHandlerImpl(TargetSubject targetSubject) {
                this.targetSubject = targetSubject;
            }
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("TargetSubject#request before...");
                method.invoke(targetSubject, null);
                System.out.println("TargetSubject#request after...");
                return null;
            }
        }
        public class Test001 {
            public static void main(String[] args) {
                TargetSubject targetSubject = new TargetSubject();
                // 1.获取对应的ClassLoader
                ClassLoader classLoader = targetSubject.getClass().getClassLoader();
                // 2.获取targetSubject 所实现的全部接口
                Class[] interfaces = targetSubject.getClass().getInterfaces();
                // 3.设置一个来自代理传过来的方法调用请求处理器,处理全部的代理对象上的方法调用
                InvocationHandler handler = new InvocationHandlerImpl(targetSubject);
        	/**
                 * 4.根据上面提供的信息,建立代理对象 在这个过程当中
                 *  a.JDK会经过根据传入的参数信息动态地在内存中建立和.class 文件等同的字节码。
                 *  b.而后根据相应的字节码转换成对应的class。
                 *  而后调用newInstance()建立实例。
        	 */
                Subject proxy = (Subject)Proxy.newProxyInstance(classLoader, interfaces, handler);
                proxy.request();
            }
        }
        复制代码

      JDK动态代理提供的生成动态代理类有个特色,真实角色(TargetSubject)必须有实现的定义接口(Subject),而且只能代理该接口定义的方法,因此当某个类没有实现接口,那么这个类就不能使用动态代理了。那该如何去解决这种状况呢?请看cglib代理。ide

    • cglib代理
      • 实现:cglib(Code Generation Library),是一个强大的,高性能,高质量的Code生成类库,它能够在运行期扩展Java类与实现Java接口。它经过查找类上的全部非final的public类型的方法定义,将这些方法的定义转换成字节码,将组成的字节码转换成相应的代理的class对象,实现 MethodInterceptor接口,用来处理对代理类上全部方法的请求(这个接口和JDK动态代理InvocationHandler的功能和角色是同样的)。
      • 代码实现
        public class MethodInterceptorImpl implements MethodInterceptor {
            @Override
            public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                System.out.println("TargetSubject#request before...");
                methodProxy.invokeSuper(o, args);
                System.out.println("TargetSubject#request after...");
                return null;
            }
        }
        public class Test002 {
            public static void main(String[] args) {
                TargetSubject targetSubject = new TargetSubject();
                MethodInterceptorImpl methodInterceptor = new MethodInterceptorImpl();
                //cglib 中增强器,用来建立动态代理
                Enhancer enhancer = new Enhancer();
                //设置要建立动态代理的类
                enhancer.setSuperclass(targetSubject.getClass());
                // 设置回调,这里至关因而对于代理类上全部方法的调用,都会调用CallBack,而Callback则须要实行intercept()方法进行拦截
                enhancer.setCallback(methodInterceptor);
                TargetSubject proxy =(TargetSubject)enhancer.create();
                proxy.request();
            }
        }
        复制代码

经过上面两种代理模式的实现方式来看,有抽象角色存在时选择JDK也能实现,cglib属于全能型,有无抽象角色都可。Spring中对于“Bean”生成代理对象时,若是该“Bean”实现某个抽象角色(或者接口定义)则选择JDK动态代理生成代理对象,某则选用cglib方式生成代理对象。post

Spring AOP的工做原理

  • 工做原理全景图
    在Spring中,AOP工做原理如上图所示,定义个切面(Aspect),在切面中编写切入点(Pointcut)是什么,匹配上的类,在生成Bean的时候,实际上不会生成本来的目标对象(Target Object),并且通过Spring生成的代理对象(AOP Proxy),这样在执行目标方法(Join Point)时,其实执行的代理对象的拦截方法,而后按建议(Advice)在目标方法先后插入定制的代码。
  • 全景图中重要角色分析:
    • Aspect: A modularization of a concern that cuts across multiple classes。
    • Join point: A point during the execution of a program, such as the execution of a method or the handling of an exception. In Spring AOP, a join point always represents a method execution.
    • Advice: Action taken by an aspect at a particular join point. Different types of advice include “around”, “before” and “after” advice. (Advice types are discussed later.)
    • Pointcut: A predicate that matches join points.Advice is associated with a pointcut expression and runs at any join point matched by the pointcut (for example, the execution of a method with a certain name)
    • Target object: An object being advised by one or more aspects.
    • AOP proxy: An object created by the AOP framework in order to implement the aspect contracts (advise method executions and so on). In the Spring Framework, an AOP proxy is a JDK dynamic proxy or a CGLIB proxy.

参考文章

相关文章
相关标签/搜索