Spring思惟导图,让Spring再也不难懂(AOP 篇)

转载自:
一、微信公众号【Java思惟导图
二、若谷先生html

1、什么是 AOP ?

AOP(Aspect-Oriented Programming,面向方面编程),对 OOP(Object-Oriented Programming,面向对象编程)
clipboard.png
  • 【OOP与AOP】java

    • 概念spring

      • AOP(Aspect-Oriented Programming,面向方面编程)
      • OOP(Object-Oriented Programming,面向对象编程)
    • 方向express

      • OOP 定义从上到下的关系
      • AOP 定义从左到右的关系
  • 【两个部分】编程

    • 核心关注点缓存

      • 业务处理的主要流程
    • 横切关注点微信

      • 与业务主要流程关系不大的部分
      • 常常发生在核心关注点的多处,而各处都是基本类似的功能
      • 如权限认证、日志、事务处理

2、AOP 使用场景

一、AOP框架种类

  • AspectJ
  • JBoss AOP
  • Spring AOP

二、使用 AOP 场景

  • 性能监控:在方法调用先后记录调用事件,方法执行太长或超时报警。
  • 缓存代理:缓存某方法的返回值,下次执行该方法时,直接从缓存里获取。
  • 软件破解:使用AOP修改软件的验证类的判断逻辑。
  • 记录日志:在方法执行先后记录系统日志。
  • 工做流系统:工做流系统须要将业务代码和流畅引擎代码混合在一块儿执行,那么可使用 AOP 将其分离,并动态挂载业务。
  • 权限验证:方法执行前验证是否有权限执行当前方法,没有则抛出没有权限执行异常,由业务代码捕获。

三、传统编码与AOP区别

clipboard.png

3、核心概念

clipboard.png
【术语】mybatis

  • 切面(Aspect):
    一个关注点的模块化,这个关注点可能会横切多个对象。
    事务管理是 J2EE 应用中一个关于横切关注点的很好列子。
    在 Spring AOP 中,切面可使用基于模式或者基于 @Aspect 注解的方式来实现。
  • 链接点(Joinpoint):
    在程序执行过程当中某个特定的点,好比某方法调用的时候或者处理异常的时候。
    在 Spring AOP 中,一个链接点老是表示一个方法的执行。
  • 切入点(Pointcut):
    匹配链接点的断言。通知和一个切入点表达式关联,并在知足这个切入点的链接点上运行(例如:当执行某个特定名称的方法时)。
    切入点表达式如何和链接点匹配是 AOP 的核心:Spring 缺省使用 Aspect 切入点语法。
  • 引入(Introduction):
    用来给一个类型声明额外的方法或属性(也被称为链接类型声明(inter-type declaration))
    Spring 容许引入新的接口(以及一个对应的实现)到任何被代理的对象。例如,可使用引入来使一个 bean 实现 isModified 接口,以便简化缓存机制。
  • 目标对象(Target Object):
    被一个或者多个切面所通知的对象,也被称为通知(advised)对象。
    既然 Spring AOP 是经过运行时代理实现的。这个对象永远是一个被代理(proxied)对象。
  • AOP 代理(AOP Proxy):
    AOP 框架建立的对象,用来实现切面契约(例如,通知方法执行等等)。
    在 Spring 中,AOP 代理能够是 JDK 动态代理或者 CGLIB 代理。
  • 织入(Weaving):
    把切面链接到其它的应用程序类型或者对象上,并建立一个被通知的对象。这些能够在编译时(例如使用 AspectJ 编译器),类加载时和运行时完成。Spring 和其余纯 Java AOP 框架同样,在运行时完成织入。
  • 通知(Advice):
    在切面的某个特定的链接点上执行的动做。其中包括“around”、“before” 和 “after” 等不一样类型的通知。
    许多 AOP 框架(包括 Spring)都是以拦截器作通知模型,并维护一个以链接点为中心的拦截器链。

【通知类型】app

  • 前置通知(Before advice):
    @Before
    在某链接点以前执行的通知,但这个通知不能阻止了链接点以前的执行流程(除非它抛出一个异常)。
  • 后置通知(After returning advice):
    @After
    在某链接点正常完成后执行的通知,例如,一个方法么有抛出任何异常,正常返回。
  • 异常通知(After throwing advice):
    @After-returning
    在方法抛出异常退出时执行的通知。
  • 最终通知(After(finally)advice):
    @After-throwing
    当某链接点退出的时候执行的通知(不管是正常退出仍是异常退出);
  • 环绕通知(Around advice):
    @Around
    包围一个链接点的通知,如方法调用,这是最强大的一种通知类型。

3、简单例子

一、基于注解的方式

@Aspectj 
public class TransactionDemo {
    @Pointcut(value="execution(* com.yangxin.core.service.*.*.*(..))")
    public void point() { //... }
    
    @Before(value="point()")
    public void before() {
        System.out.println("transaction begin");
    }
    
    @AfterReturning(value = "point()")
    public void after() {
        System.out.println("transaction commit");
    }
    
    @Around("point()")
    public void around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("transaction begin");
        joinPoint.proceed();
        System.out.println("transaction commit");
    }
}

在 applicationContext.xml 中配置:框架

<aop:aspectj-autoproxy />
    <bean id="transactionDemo" class="com.yangxin.core.transaction.TransactionDemo" />

二、基于 XML 的方式

<aop:config>
    <aop:aspect ref="log">
        <aop:pointcut
                expression="(execution(* spring.ch3.topic1.Chief.*(..)))"
                id="chiefPointCut" />
        <aop:before method="washOven" pointcut-ref="chiefPointCut" />
        <aop:before method="prepare" pointcut-ref="chiefPointCut" />
        <aop:after method="after" pointcut-ref="chiefPointCut" />
    </aop:aspect>
</aop:config>

4、Spring AOP 原理

AOP 代理实际上是由 AOP 框架动态生成的一个对象,该对象可做为目标对象使用。
AOP 代理包含了目标对象的所有方法,但 AOP 代理中的方法与目标对象的方法存在差别:AOP 方法在特定切入点添加了加强处理,并回调了目标对象的方法。
clipboard.png

Spring 的 AOP 代理由 Spring 的 IoC 容器负责生成、管理,其依赖关系也由 IoC 容器负责管理。所以,AOP 代理能够直接使用容器中的其余 Bean 实例做为目标,这种关系可由 IoC 容器的依赖注入提供。

aop开发时,其中须要参与开发的只有 3 个部分:

  • 定义普通业务组件。
  • 定义切入点,一个切入点可能横切多个业务组件。
  • 定义加强处理,加强处理就是在 AOP 框架为普通业务组件织入的处理动做。

5、两种动态代理方式

Spring默认采起的动态代理机制实现AOP,当动态代理不可用时(代理类无接口)会使用CGlib机制。

Spring提供了两种方式来生成代理对象: JDKProxy和Cglib,具体使用哪一种方式生成由AopProxyFactory根据AdvisedSupport对象的配置来决定。默认的策略是若是目标类是接口,则使用JDK动态代理技术,不然使用Cglib来生成代理。

一、JDK 动态代理

  • JDK动态代理主要涉及到java.lang.reflect包中的两个类:Proxy和InvocationHandler。InvocationHandler是一个接口,经过实现该接口定义横切逻辑,并经过反射机制调用目标类的代码,动态将横切逻辑和业务逻辑编制在一块儿。
  • Proxy利用InvocationHandler动态建立一个符合某一接口的实例,生成目标类的代理对象。

二、CGLib动态代理

  • CGLib全称为Code Generation Library,是一个强大的高性能,高质量的代码生成类库,能够在运行期扩展Java类与实现Java接口,CGLib封装了asm,能够再运行期动态生成新的class。和JDK动态代理相比较:JDK建立代理有一个限制,就是只能为接口建立代理实例,而对于没有经过接口定义业务方法的类,则能够经过CGLib建立动态代理。

6、补充实例

clipboard.png

package springMVCmybatis.com.my.aop;
import org.aspectj.lang.annotation.Aspect; 
import org.aspectj.lang.annotation.Around; 
import org.aspectj.lang.annotation.Before; 
import org.aspectj.lang.annotation.Pointcut; 
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint; 
import org.aspectj.lang.annotation.After; 
import org.aspectj.lang.annotation.AfterReturning; 
import org.aspectj.lang.annotation.AfterThrowing; 
import org.springframework.core.annotation.Order;

@Aspect
@Order(3)
public class MyAopTest {
    @Pointcut("execution(* springMVCmybatis.addController.addEmp(..))")
    private void pointCutMethod() { //... }
    
    @Pointcut("execution(* springMVCmybatis.com.my.aop.UserServiceImp.*(..))") 
    private void testAOP() { //... }
    
    /**
    * 声明前置通知 ,JoinPont是srpring提供的静态变量,
    * 经过joinPoint参数能够得到目标方法的类名,方法参数,方法名等信息,这个参数无关紧要。
    */
    @Before("pointCutMethod() || testAOP()") 
    public void doBefore(JoinPoint joinPoint) { 
        System.out.println("@Before:开始添加--order=3"); 
    } 
    
    /**
    * 声明后置通知 ,若是result的类型与proceed执行的方法返回的参数类型不匹配那么就不会执行这个方法
    */
    @AfterReturning(pointcut = "pointCutMethod()  || testAOP()", returning = "result") 
    public void doAfterReturning(String result) { 
        System.out.println("@AfterReturning:后置通知--order=3"); 
        System.out.println("---" + result + "---"); 
    } 
    
    /**
    * 声明例外通知 
    */
    @AfterThrowing(pointcut = "pointCutMethod() || testAOP()", throwing = "e") 
    public void doAfterThrowing(Exception e) { 
        System.out.println("@AfterThrowing:例外通知--order=3"); 
        System.out.println(e.getMessage()); 
    }
    
    /**
    * 声明最终通知
    */
    @After("pointCutMethod() || testAOP()") 
    public void doAfter() { 
        System.out.println("@After:最终通知--order=3"); 
    } 

    /**
    * 声明环绕通知
    * 参数必须是ProceedingJoinPoint,经过该对象的proceed()方法来执行目标函数,
    * proceed()的返回值就是环绕通知的返回值,proceedingJoinPoint是个接口,
    * implement JoinPoint,因此也能够得到目标函数的类名,方法名等参数。
    */
    @Around("pointCutMethod() || testAOP()") 
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable { 
        System.out.println("@Around:进入方法---环绕通知--order=3"); 
        Object o = pjp.proceed(); 
        System.out.println("@Around:退出方法---环绕通知--order=3"); 
        return o; 
    }
}

7、切点表达式

一、方法签名表达式

execution(<修饰符模式>?<返回类型模式><方法所在类的彻底限定名称模式>(<参数模式>)<异常模式>?)
execution(modifiers-pattern? ret-type-pattern fully-qualified-class-name (param-pattern) throws-pattern?)   
对比方法的定义来记忆,一个java方法的所有定义方式能够表示成下面的方式:
public String springMVCmybatic.com.my.aop.UserServiceImp(String a, int b) throw Exception{}
  • modifier-pattern?:表示方法的修饰符,无关紧要;对应的就是 public
  • ret-type-pattern:表示方法的返回值;对应的就是 String
  • fully-qualified-class-name 方法所在类的彻底限定名称;对应的就是 springMVCmybatic.com.my.aop.UserServiceImp
  • param-pattern:表示方法的参数;对应的就是 String a, int b
  • throws-pattern:表示方法抛出的异常,无关紧要;对应的就是 throw Exception

二、&&,||,!(表达式之间能够采用与,或,非的方式来过滤。)

@Around("pointCutMethod() || testAOP()") 
public Object doAround(ProceedingJoinPoint pjp) throws Throwable { 
    System.out.println("@Around:进入方法---环绕通知"); 
    Object o = pjp.proceed(); 
    System.out.println("@Around:退出方法---环绕通知"); 
    return o; 
}

8、多个切点的执行顺序

上面的例子中,定义了order=3,从新建立一个切面,定义order=6,执行的结果是:
@Around:进入方法---环绕通知--order=3
@Before:开始添加--order=3
@Around:进入方法---环绕通知--order=6
@Before:开始添加--order=6
============执行业务方法findUser,查找的用户是:张三=============
@Around:退出方法---环绕通知--order=6
@After:最终通知--order=6
@AfterReturning:后置通知--order=6
---张三---
@Around:退出方法---环绕通知--order=3
@After:最终通知--order=3
@AfterReturning:后置通知--order=3
---张三---
 
 
@Around:进入方法---环绕通知--order=3
@Before:开始添加--order=3
@Around:进入方法---环绕通知--order=6
@Before:开始添加--order=6
============执行业务方法addUser=============
@After:最终通知--order=6
@AfterThrowing:例外通知--order=6
null
@After:最终通知--order=3
@AfterThrowing:例外通知--order=3
null
 从结果中能够看出order越小越先执行,执行完了以后就order越小就越后推出。总结为下面的图:
clipboard.png
相关文章
相关标签/搜索