spring boot aop的使用

spring boot aop的使用

1.aop的官网介绍

AOP concepts
Let us begin by defining some central AOP concepts and terminology. These terms are not Spring-specific…​ unfortunately, AOP terminology is not particularly intuitive; however, it would be even more confusing if Spring used its own terminology.
  ● Aspect: a modularization of a concern that cuts across multiple classes. Transaction management is a good example of a crosscutting concern in enterprise Java applications. In Spring AOP, aspects are implemented using regular classes (the schema-based approach) or regular classes annotated with the @Aspectannotation (the @AspectJ style).
  ● 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 alwaysrepresents 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 below.) Many AOP frameworks, including Spring, model an advice as an interceptor, maintaining a chain of interceptors around the join point.
  ● 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). The concept of join points as matched by pointcut expressions is central to AOP, and Spring uses the AspectJ pointcut expression language by default.
  ● Introduction: declaring additional methods or fields on behalf of a type. Spring AOP allows you to introduce new interfaces (and a corresponding implementation) to any advised object. For example, you could use an introduction to make a bean implement an IsModified interface, to simplify caching. (An introduction is known as an inter-type declaration in the AspectJ community.)
  ● Target object: object being advised by one or more aspects. Also referred to as the advised object. Since Spring AOP is implemented using runtime proxies, this object will always be a proxied object.
  ● 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 will be a JDK dynamic proxy or a CGLIB proxy.
  ● Weaving: linking aspects with other application types or objects to create an advised object. This can be done at compile time (using the AspectJ compiler, for example), load time, or at runtime. Spring AOP, like other pure Java AOP frameworks, performs weaving at runtime.


1.1 AOP概念
我们首先定义一些中央AOP概念和术语。这些术语不是Spring特定的...不幸的是,AOP术语不是特别直观的; 然而,如果Spring使用自己的术语,会更加混乱。
  ● 方面:跨越多个类的关注模块化。事务管理是企业Java应用程序中横向关注的一个很好的例子。在Spring AOP中,使用常规类(基于模式的方法)或使用 @Aspect注释(@AspectJ样式)注释的常规类来实现方面。
  ● 联接点:程序执行过程中的一点,例如执行方法或处理异常。在Spring AOP中,连接点总是 表示方法执行。
  ● 建议:特定连接点的方面采取的行动。不同类型的建议包括“周围”,“之前”和“之后”咨询。(建议类型将在下面讨论。)许多AOP框架(包括Spring)将建议作为拦截器建模,在连接点周围维护一系列拦截器。
  ● 切入点:匹配连接点的谓词。建议与切入点表达式相关联,并在与切入点匹配的任何连接点运行(例如,执行具有特定名称的方法)。连接点与切入点表达式匹配的概念是AOP的核心,默认情况下Spring使用AspectJ切入点表达式语言。
  ● 简介:代表类型声明附加的方法或字段。Spring AOP允许您向任何建议的对象引入新的接口(以及相应的实现)。例如,您可以使用简介来使一个bean实现一个IsModified接口,以简化缓存。(介绍被称为AspectJ社区中的类型间声明。)
  ● 目标对象:由一个或多个方面建议的对象。也称为建议对象。由于Spring AOP是使用运行时代理实现的,因此该对象将始终是一个代理对象。
  ● AOP代理:由AOP框架创建的一个对象来实现方面的合同(建议方法执行等等)。在Spring框架中,AOP代理将是JDK动态代理或CGLIB代理。
  ● 编织:与其他应用程序类型或对象链接方面以创建建议的对象。这可以在编译时完成(例如使用AspectJ编译器),加载时间或运行时。与其他纯Java AOP框架一样,Spring AOP在运行时执行编织。


Types of advice:
  ● Before advice: Advice that executes before a join point, but which does not have the ability to prevent execution flow proceeding to the join point (unless it throws an exception).
  ● After returning advice: Advice to be executed after a join point completes normally: for example, if a method returns without throwing an exception.
  ● After throwing advice: Advice to be executed if a method exits by throwing an exception.
  ● After (finally) advice: Advice to be executed regardless of the means by which a join point exits (normal or exceptional return).
  ● Around advice: Advice that surrounds a join point such as a method invocation. This is the most powerful kind of advice. Around advice can perform custom behavior before and after the method invocation. It is also responsible for choosing whether to proceed to the join point or to shortcut the advised method execution by returning its own return value or throwing an exception.


建议类型:
  ● 建议之前:在连接点之前执行的建议,但是没有能力阻止执行流程进行到连接点(除非引发异常)。
  ● 返回建议后:在连接点正常完成后要执行的建议:例如,如果方法返回而不抛出异常。
  ● 抛出建议后:如果方法通过抛出异常退出,则执行的建议。
  ● 之后(最后)建议:建议执行,无论连接点退出的方式(正常或异常返回)。
  ● 周围建议:围绕连接点的建议,如方法调用。这是最强大的建议。周围的建议可以在方法调用之前和之后执行自定义行为。它还负责选择是否继续进行连接点,或者通过返回自己的返回值或抛出异常来快速建立方法执行。

2.引入aop的相关依赖:

3.具体使用代码如下:

package com.zxl.examples.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

/**
 * Created by Administrator on 2017/8/3.
 */
@Aspect
@Component
public class LogAspect {

    /**
     *
     * execution(* com.sample.service.impl..*.*(..))
     解释如下:
     符号	含义
     execution()
     表达式的主体;
     第一个”*“符号
     表示返回值的类型任意;
     com.sample.service.impl	AOP所切的服务的包名,即,我们的业务部分
     包名后面的”..“	表示当前包及子包
     第二个”*“	表示类名,*即所有类。此处可以自定义,下文有举例
     .*(..)	表示任何方法名,括号表示参数,两个点表示任何参数类型

     execution(<修饰符模式>?<返回类型模式><方法名模式>(<参数模式>)<异常模式>?)  除了返回类型模式、方法名模式和参数模式外,其它项都是可选的。
     *
     *
     *
     * @param joinPoint
     */


    @AfterReturning("execution(* com.zxl.examples.service..*.*(..))")
    public void logServiceAccessAfterReturning(JoinPoint joinPoint) {
        System.out.println("Completed: " + joinPoint);
    }

//    @Before("execution(* com.zxl.examples.service..*.*(..))")
    public void logServiceAccessBefore(JoinPoint joinPoint){
        System.out.println("Before: " + joinPoint);
    }

    @After("execution(* com.zxl.examples.service..*.*(..))")
    public void logServiceAccessAfter(JoinPoint joinPoint){
        System.out.println("After: " + joinPoint);
    }

    /**
     * around 开启后会覆盖before
     * @param joinPoint
     */
//    @Around("execution(* com.zxl.examples.service..*.*(..))")
    public void logServiceAccessAround(JoinPoint joinPoint){
        System.out.println("Around: " + joinPoint);
    }

    //声明一个切入点
    @Pointcut("execution(* com.zxl.examples.service..*.*(..))")
    public void logServiceAccessPointCut(){
    }

    @Before("logServiceAccessPointCut()")
    public void losgUseThePointCut(JoinPoint joinPoint){
        System.out.println("THIS IS USE THE POINT CUT: " + joinPoint);
    }



}

效果如下图所示(当调用service中的方式时,aop配置的规则生效了,具体看控制台):


4.  注解详解-->摘自官网:

组合切入点表达式
切入点表达式可以使用'&&','||' 和'!'。也可以通过名称引用切入点表达式。以下示例显示三个切入点表达式:(anyPublicOperation如果方法执行连接点表示任何公共方法的执行,则匹配); inTrading(如果交易模块tradingOperation中的方法执行相匹配)和(如果方法执行代表交易模块中的任何公共方法,则匹配)。
@Pointcut(“execution(public * *(..))”)
 private  void anyPublicOperation(){}


@Pointcut(“in(com.xyz.someapp.trading .. *)”)
 private  void inTrading(){}


@Pointcut(“anyPublicOperation()&& inTrading()”)
 private  void tradingOperation(){}
如上所示,从较小的命名组件构建更复杂的切入点表达式是一个最佳做法。当通过名称引用切入点时,应用正常的Java可见性规则(您可以看到相同类型的私有切入点,层次结构中的受保护切入点,任何地方的公共切入点等)。可视性不会影响切入点 匹配。
共享切入点定义
在使用企业应用程序时,您通常需要从几个方面参考应用程序的模块和特定的操作集。我们建议定义一个“SystemArchitecture”方面,为此目的捕获常见的切入点表达式。典型的这个方面如下所示:
package com.xyz.someapp;


import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;


@Aspect
 public  class SystemArchitecture {


    / **
     *如果定义了方法,则连接点位于Web层中
     *在com.xyz.someapp.web包或任何子包中的类型
     *根据。
     * / 
    @Pointcut(“in(com.xyz.someapp.web .. *)”)
     public  void inWebLayer(){}


    / **
     *如果定义了方法,则连接点位于服务层
     *在com.xyz.someapp.service包或任何子包中输入
     *根据。
     * / 
    @Pointcut(“in(com.xyz.someapp.service .. *)”)
     public  void inServiceLayer(){}


    / **
     *如果定义了方法,则连接点位于数据访问层
     *在com.xyz.someapp.dao包或任何子包中的类型
     *根据。
     * / 
    @Pointcut(“in(com.xyz.someapp.dao .. *)”)
     public  void inDataAccessLayer(){}


    / **
     *业务服务是执行服务上定义的任何方法
     *界面。这个定义假定接口被放置在
     *“服务”包,并且实现类型在子包中。
     *
     *如果按功能区域分组服务接口(例如,
     *在包com.xyz.someapp.abc.service和com.xyz.someapp.def.service)然后
     *切入点表达式“执行(* com.xyz.someapp..service。*。*(..))”
     *可以代替。
     *
     *或者,您可以使用'bean'编写表达式
     * PCD,像这样“bean(* Service)”。(这假设你有
     *以一致的方式命名为您的Spring服务bean。)
     * / 
    @Pointcut(“execution(* com.xyz.someapp..service。*。*(..))”)
     public  void businessService(){}


    / **
     *数据访问操作是在a上定义的任何方法的执行
     * dao界面。这个定义假定接口被放置在
     *“dao”包,这些实现类型在子包中。
     * / 
    @Pointcut(“execution(* com.xyz.someapp.dao。*。*(..))”)
     public  void dataAccessOperation(){}


}


Combining pointcut expressions
Pointcut expressions can be combined using '&&', '||' and '!'. It is also possible to refer to pointcut expressions by name. The following example shows three pointcut expressions: anyPublicOperation (which matches if a method execution join point represents the execution of any public method); inTrading (which matches if a method execution is in the trading module), and tradingOperation (which matches if a method execution represents any public method in the trading module).
@Pointcut("execution(public * *(..))")
private void anyPublicOperation() {}


@Pointcut("within(com.xyz.someapp.trading..*)")
private void inTrading() {}


@Pointcut("anyPublicOperation() && inTrading()")
private void tradingOperation() {}
It is a best practice to build more complex pointcut expressions out of smaller named components as shown above. When referring to pointcuts by name, normal Java visibility rules apply (you can see private pointcuts in the same type, protected pointcuts in the hierarchy, public pointcuts anywhere and so on). Visibility does not affect pointcut matching.
Sharing common pointcut definitions
When working with enterprise applications, you often want to refer to modules of the application and particular sets of operations from within several aspects. We recommend defining a "SystemArchitecture" aspect that captures common pointcut expressions for this purpose. A typical such aspect would look as follows:
package com.xyz.someapp;


import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;


@Aspect
public class SystemArchitecture {


    /**
     * A join point is in the web layer if the method is defined
     * in a type in the com.xyz.someapp.web package or any sub-package
     * under that.
     */
    @Pointcut("within(com.xyz.someapp.web..*)")
    public void inWebLayer() {}


    /**
     * A join point is in the service layer if the method is defined
     * in a type in the com.xyz.someapp.service package or any sub-package
     * under that.
     */
    @Pointcut("within(com.xyz.someapp.service..*)")
    public void inServiceLayer() {}


    /**
     * A join point is in the data access layer if the method is defined
     * in a type in the com.xyz.someapp.dao package or any sub-package
     * under that.
     */
    @Pointcut("within(com.xyz.someapp.dao..*)")
    public void inDataAccessLayer() {}


    /**
     * A business service is the execution of any method defined on a service
     * interface. This definition assumes that interfaces are placed in the
     * "service" package, and that implementation types are in sub-packages.
     *
     * If you group service interfaces by functional area (for example,
     * in packages com.xyz.someapp.abc.service and com.xyz.someapp.def.service) then
     * the pointcut expression "execution(* com.xyz.someapp..service.*.*(..))"
     * could be used instead.
     *
     * Alternatively, you can write the expression using the 'bean'
     * PCD, like so "bean(*Service)". (This assumes that you have
     * named your Spring service beans in a consistent fashion.)
     */
    @Pointcut("execution(* com.xyz.someapp..service.*.*(..))")
    public void businessService() {}


    /**
     * A data access operation is the execution of any method defined on a
     * dao interface. This definition assumes that interfaces are placed in the
     * "dao" package, and that implementation types are in sub-packages.
     */
    @Pointcut("execution(* com.xyz.someapp.dao.*.*(..))")
    public void dataAccessOperation() {}


}
The pointcuts defined in such an aspect can be referred to anywhere that you need a pointcut expression. For example, to make the service layer transactional, you could write:
<aop:config>
    <aop:advisor
        pointcut="com.xyz.someapp.SystemArchitecture.businessService()"
        advice-ref="tx-advice"/>
</aop:config>


<tx:advice id="tx-advice">
    <tx:attributes>
        <tx:method name="*" propagation="REQUIRED"/>
    </tx:attributes>
</tx:advice>
The <aop:config> and <aop:advisor> elements are discussed in Section 11.3, “Schema-based AOP support”. The transaction elements are discussed inChapter 17, Transaction Management.




Examples
Spring AOP users are likely to use the execution pointcut designator the most often. The format of an execution expression is:
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern)
            throws-pattern?)
All parts except the returning type pattern (ret-type-pattern in the snippet above), name pattern, and parameters pattern are optional. The returning type pattern determines what the return type of the method must be in order for a join point to be matched. Most frequently you will use * as the returning type pattern, which matches any return type. A fully-qualified type name will match only when the method returns the given type. The name pattern matches the method name. You can use the * wildcard as all or part of a name pattern. If specifying a declaring type pattern then include a trailing . to join it to the name pattern component. The parameters pattern is slightly more complex: () matches a method that takes no parameters, whereas (..) matches any number of parameters (zero or more). The pattern (*)matches a method taking one parameter of any type, (*,String) matches a method taking two parameters, the first can be of any type, the second must be a String. Consult the Language Semantics section of the AspectJ Programming Guide for more information.
Some examples of common pointcut expressions are given below.
  ● the execution of any public method:
execution(public * *(..))
  ● the execution of any method with a name beginning with "set":
execution(* set*(..))
  ● the execution of any method defined by the AccountService interface:
execution(* com.xyz.service.AccountService.*(..))
  ● the execution of any method defined in the service package:
execution(* com.xyz.service.*.*(..))
  ● the execution of any method defined in the service package or a sub-package:
execution(* com.xyz.service..*.*(..))
  ● any join point (method execution only in Spring AOP) within the service package:
within(com.xyz.service.*)
  ● any join point (method execution only in Spring AOP) within the service package or a sub-package:
within(com.xyz.service..*)
  ● any join point (method execution only in Spring AOP) where the proxy implements the AccountService interface:
this(com.xyz.service.AccountService)
'this' is more commonly used in a binding form :- see the following section on advice for how to make the proxy object available in the advice body.


  ● any join point (method execution only in Spring AOP) where the target object implements the AccountService interface:
target(com.xyz.service.AccountService)
'target' is more commonly used in a binding form :- see the following section on advice for how to make the target object available in the advice body.


  ● any join point (method execution only in Spring AOP) which takes a single parameter, and where the argument passed at runtime is Serializable:
args(java.io.Serializable)
'args' is more commonly used in a binding form :- see the following section on advice for how to make the method arguments available in the advice body.


Note that the pointcut given in this example is different to execution(* *(java.io.Serializable)): the args version matches if the argument passed at runtime is Serializable, the execution version matches if the method signature declares a single parameter of type Serializable.
  ● any join point (method execution only in Spring AOP) where the target object has an @Transactional annotation:
@target(org.springframework.transaction.annotation.Transactional)
'@target' can also be used in a binding form :- see the following section on advice for how to make the annotation object available in the advice body.


  ● any join point (method execution only in Spring AOP) where the declared type of the target object has an @Transactional annotation:
@within(org.springframework.transaction.annotation.Transactional)
'@within' can also be used in a binding form :- see the following section on advice for how to make the annotation object available in the advice body.


  ● any join point (method execution only in Spring AOP) where the executing method has an @Transactional annotation:
@annotation(org.springframework.transaction.annotation.Transactional)
'@annotation' can also be used in a binding form :- see the following section on advice for how to make the annotation object available in the advice body.


  ● any join point (method execution only in Spring AOP) which takes a single parameter, and where the runtime type of the argument passed has the @Classifiedannotation:
@args(com.xyz.security.Classified)
'@args' can also be used in a binding form :- see the following section on advice for how to make the annotation object(s) available in the advice body.


  ● any join point (method execution only in Spring AOP) on a Spring bean named tradeService:
bean(tradeService)
  ● any join point (method execution only in Spring AOP) on Spring beans having names that match the wildcard expression *Service:
bean(*Service)


Spring AOP用户最有可能使用execution切入点代号。执行表达式的格式为:
执行(modifiers-pattern?ret-type-pattern declaring -type-pattern?name-pattern(param-pattern)
             throws -pattern?)
除返回类型模式之外的所有部分(上面的代码段中的ret-type-pattern),名称模式和参数模式都是可选的。返回类型模式确定方法的返回类型必须是为了使连接点匹配。最常使用*的返回类型与任何返回类型匹配。完全限定类型名称只有当方法返回给定类型时才匹配。名称模式与方法名称相匹配。您可以使用*通配符作为名称模式的全部或部分。如果指定一个声明类型模式,则包括一个尾随.以将其连接到名称模式组件。参数模式稍微复杂一些:()匹配不需要参数的方法,而(..)匹配任意数量的参数(零个或多个)。该模式(*)匹配一个采用任何类型的参数的(*,String)方法, 匹配一个采用两个参数的方法,第一个可以是任何类型,第二个必须是一个String。有关更多信息,请参阅AspectJ编程指南中的 语言语义部分。
常见的切入点表达式的一些示例如下。
  ● 执行任何公共方法:
执行(public * *(..))
  ● 以“set”开头的任何方法的执行:
执行(* set *(..))
  ● 执行由AccountService接口定义的任何方法:
执行(* com.xyz.service.AccountService。*(..))
  ● 在服务包中定义的任何方法的执行:
执行(* com.xyz.service。*。*(..))
  ● 在服务包或子包中定义的任何方法的执行:
执行(* com.xyz.service .. *。*(..))
  ● 服务包中的任何连接点(仅在Spring AOP中执行的方法):
内(com.xyz.service。*)
  ● 服务包或子包中的任何连接点(仅在Spring AOP中执行的方法):
内(com.xyz.service。*)
  ● 代理实现AccountService接口的任何连接点(仅在Spring AOP中执行的方法) :
this(com.xyz.service.AccountService)
'this'更常用于绑定形式: - 有关如何使代理对象在建议主体中可用的建议,请参阅以下部分。


  ● 目标对象实现AccountService接口的任何连接点(仅在Spring AOP中执行的方法):
目标(com.xyz.service.AccountService)
'target'更常用于绑定形式: - 有关如何使目标对象在建议主体中可用的建议,请参阅以下部分。


  ● 任何连接点(仅在Spring AOP中执行的方法),它接受单个参数,以及在运行时传递参数的方法是Serializable:
ARGS(java.io.Serializable接口)
'args'更常用于绑定形式: - 有关如何使方法参数在建议主体中可用的建议,请参阅以下部分。


请注意,本示例中给出的切入点不同于execution(* *(java.io.Serializable)):如果在运行时传递的参数是Serializable,则args版本匹配,如果方法签名声明单个类型的参数,则执行版本匹配Serializable。
  ● 目标对象具有@Transactional注释的任何连接点(仅在Spring AOP中执行的方法) :
@target(org.springframework.transaction.annotation.Transactional)
“@target”也可以以绑定形式使用: - 有关如何使注释对象在建议主体中可用的建议,请参阅以下部分。


  ● 任何连接点(仅在Spring AOP中的方法执行),其中声明的目标对象类型具有@Transactional注释:
@within(org.springframework.transaction.annotation.Transactional)
'@within'也可以以绑定形式使用: - 有关如何使注释对象在建议主体中可用的建议,请参阅以下部分。


  ● 任何连接点(仅在Spring AOP中执行的方法),其中执行方法具有 @Transactional注释:
@annotation(org.springframework.transaction.annotation.Transactional)
'@annotation'也可以以绑定形式使用: - 有关如何使注释对象在建议主体中可用的建议,请参阅以下部分。


  ● 任何连接点(仅在Spring AOP中执行的方法),它接受单个参数,传递参数的运行时类型具有@Classified注释:
@args(com.xyz.security.Classified)
'@args'也可以以绑定形式使用: - 有关如何使注释对象在建议主体中可用的建议,请参阅以下部分。


  ● Spring bean上的任何连接点(仅在Spring AOP中执行的方法)命名为 tradeService:
豆(tradeService)
  ● Spring bean上的任何连接点(仅在Spring AOP中执行的方法)具有与通配符表达式匹配的名称*Service:
豆(*服务)







参考资料:

小提示:1.方法必须是void的

2.@Before与@Around不能对 同一个方法进行配置,否则@Before不会生效

猜你喜欢

转载自blog.csdn.net/long290046464/article/details/76731968