Aop is finally on the agenda to write about.

Series directory

This series is divided into three parts: first, middle and second. The previous article mainly introduced how to use AOP, provided demo and configuration instructions; The middle part analyzes the technical principle of implementing AOP; The next chapter is mainly for Spring implementation of AOP source code analysis.

  • Let’s start with an example
    • Agent-based approach
    • Pure POJO section configuration
    • AspectJ annotations
    • AspectJ XML configuration
    • Expression specification
  • Basic concept
    • AOP concepts
    • Target Object
    • Weave, Weave
    • Proxy
    • Introduction
    • Aspect
    • Joinpoint
    • Pointcut
    • Advice
      • concept
      • classification
    • Relationship between
  • Some of the pit

The project address

Project address: Glmapper-SSM-parent

This project contains all the code for the following AOP implementations, so you can fork around if you are interested. This demo lists the implementation of 4 methods:

  • Code based approach
  • Based on pure POJO classes
  • Aspect annotation-based approach
  • An approach based on injection aspects

The approach we often use today is based on the Aspect annotation approach. Let’s look at the different forms of expression one by one.

Agent-based approach

This approach seems straightforward, but is cumbersome to configure; You can refer to the project. Only the key process code is posted here.

First, define an interface: GoodsService

public interface GoodsService {
	/** * query all merchandise information **@paramOffset Indicates the start position *@paramLimit Number of queries *@return* /
	List<Goods> queryAll(int offset,int limit);
}
Copy the code

GoodsService implementation class

@Service
@Qualifier("goodsService")
public class GoodsServiceImpl implements GoodsService {
	@Autowired 
	private GoodsDao goodsDao;
	
	public List<Goods> queryAll(int offset, int limit) {
		System.out.println("QueryAll method executed");
		List<Goods> list = new ArrayList<Goods>();
		returnlist; }}Copy the code

3. Define a notification class LoggerHelper that inherits MethodBeforeAdvice and AfterReturningAdvice.

// Notify the LoggerHelper class
public class LoggerHelper implements MethodBeforeAdvice.AfterReturningAdvice {

    private static final Logger LOGGER = LoggerFactory.getLogger(LoggerHelper.class);
    // before method implementation for MethodBeforeAdvice
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        LOGGER.info("before current time:"+System.currentTimeMillis());
    }
    AfterReturning method for AfterReturningAdvice
    public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
        LOGGER.info("afterReturning current time:"+System.currentTimeMillis()); }}Copy the code

4, important, this configuration needs to pay attention to. In this project I am configured in the applicationContext.xml file.

<! -- Define proxy -->
<bean id="goodsServiceImpl" class="com.glmapper.framerwork.service.impl.GoodsServiceImpl"></bean>

<! Define the notification content, that is, what needs to be done before and after the pointcut executes.
<bean id="loggerHelper" class="com.glmapper.framerwork.aspect.LoggerHelper"></bean>

<! Define pointcut location -->
<bean id="loggerPointcut" class="org.springframework.aop.support.JdkRegexpMethodPointcut">
	<property name="pattern" value=".*query.*"></property>
</bean>

<! Associate pointcuts with advice to complete aspect configuration -->
<! Advisors, advice, pointcut, pointcut
<! Adivce and Pointcut are two properties of Advisor
<bean id="loggerHelperAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
	<property name="advice" ref="loggerHelper"></property>
	<property name="pointcut" ref="loggerPointcut"></property>
</bean>

<! -- Set proxy -->
<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
	<! -- The object of the proxy, i.e. the target class -->
	<property name="target" ref="goodsServiceImpl"></property>
	<! -- Use section -->
	<property name="interceptorNames" value="loggerHelperAdvisor"></property>
	<! -- Proxy interface, commodity interface -->
	<property name="proxyInterfaces" value="com.glmapper.framerwork.service.GoodsService"></property>
</bean>
Copy the code

5. Use: Annotation injection

@Controller
@RequestMapping("/buy")
public class BuyController {
    @Autowired
    private OrderService orderService;
    // Since we have already configured proxy in the configuration file,
    // So we can inject directly to get our proxy class
    @Autowired
    private GoodsService proxy;
    
    @RequestMapping("/initPage")
    public ModelAndView initPage(HttpServletRequest request, HttpServletResponse response, ModelAndView view) {
    // Use proxy to execute *query*,
    List<Goods> goods = proxy.queryAll(10.10);
    view.addObject("goodsList", goods);
    view.setViewName("goodslist");
    returnview; }}Copy the code

6. Get the bean manually using: utility class method

This approach uses a SpringContextUtil utility class to get the proxy object.

@RequestMapping("/initPage") public ModelAndView initPage(HttpServletRequest request, HttpServletResponse response, ModelAndView view) {// Use the tool class to get the same effect. GoodsService proxy= (GoodsService) SpringContextUtil.getBean("proxy"); A List < Goods > Goods = proxy. QueryAll (10, 10); view.addObject("goodsList", goods);
    view.setViewName("goodslist");
    return view;
}
Copy the code

Definition of the SpringContextUtil class

First, SpringContextUtil inherits the ApplicationContextAware interface. We want SpringContextUtil to be managed directly by the Spring container, so we need the @Component annotation. The most important thing is that it should be able to be scanned by the injection scan that we configured. (For the pit I stomped on myself, I put it under an unscanned package, and the debug is always null; Almost hit the computer…)

@Component
public class SpringContextUtil implements ApplicationContextAware {

    // Spring application context
    private static ApplicationContext applicationContext;

    /** * Implements the ApplicationContextAware interface callback method to set the context **@param applicationContext
     */
    public void setApplicationContext(ApplicationContext applicationContext) {
        SpringContextUtil.applicationContext = applicationContext;
    }

    / * * *@return ApplicationContext
     */
    public static ApplicationContext getApplicationContext(a) {
        return applicationContext;
    }

    /** * get the object * overriding the bean method to play the main role *@param name
     * @returnObject An instance of a bean registered with the given name *@throws BeansException
     */
    public static Object getBean(String name) throws BeansException {
        returnapplicationContext.getBean(name); }}Copy the code

8. Running results

21:04:47. [HTTP - nio - 8080-940 the exec] INFO C.G.F ramerwork. Aspect. LoggerHelper - before the current time: 1529413487940 Perform the 21:04:47 queryAll method. 940 [HTTP - nio - 8080 - exec] INFO C.G.F ramerwork. Aspect. LoggerHelper - afterReturning current time:1529413487940Copy the code

The above is the classic way of implementing AOP through proxies.

Pure POJO sectionaop:config

Note the difference between LoggerHelper and LoggerHelper, where LoggerAspect does not inherit any interfaces or abstract classes.

POJO class definition

/ * * *@description: [Description text] *@email: <a href="[email protected]"></a>
 * @author: guolei.sgl
 * @date: 18/6/20 * /
public class LoggerAspect {
    private static final Logger LOGGER =
    LoggerFactory.getLogger(LoggerHelper.class);

    public void before(a){
        LOGGER.info("before current time:"+System.currentTimeMillis());
    }

    public void afterReturning(a) {
        LOGGER.info("afterReturning current time:"+System.currentTimeMillis()); }}Copy the code

2. Configuration files

<! Define the notification content, that is, what needs to be done before and after the pointcut executes.
<bean id="loggerAspect"  
    class="com.glmapper.framerwork.aspect.LoggerAspect">
</bean>

<aop:config>
    <! -- Define the section -->
    <aop:aspect ref="loggerAspect">
    	<aop:pointcut id="loggerPointCut"  expression=
    	"execution(* com.glmapper.framerwork.service.impl.*.*(..)) " />
    	<! -- Define Advice -->
    	<! -- Pre-notification -->
    	<aop:before pointcut-ref="loggerPointCut" method="before" />
    	<! -- Post-notification -->
    	<aop:after-returning pointcut-ref="loggerPointCut"
    	method="afterReturning"/>
    </aop:aspect>
</aop:config>
Copy the code

Note that LoggerAspect needs to handle 0 formal unbound in pointcut if there are parameters in before and afterReturning.

AspectJ annotation-driven approach

This is the simplest implementation, simply annotating our Aspect class with the @Aspect annotation.

Define the Aspect class and annotate it with @aspect

/ * * *@description: Use Aspect annotation-driven approach *@email: <a href="[email protected]"></a>
 * @author: guolei.sgl
 * @date: 18/6/20 * /
@Aspect
public class LoggerAspectInject {

    private static final Logger LOGGER = LoggerFactory.getLogger(LoggerAspectInject.class);

    @Pointcut("execution(* com.glmapper.framerwork.service.impl.*.*(..) )")
    public void cutIn(a){}

    @Before("cutIn()")
    public void before(a){
        LOGGER.info("before current time:"+System.currentTimeMillis());
    }

    @AfterReturning("cutIn()")
    public void AfterReturning(a){
        LOGGER.info("afterReturning current time:"+System.currentTimeMillis()); }}Copy the code

2. Usage Mode 1: Declare bean in configuration file mode

<aop:aspectj-autoproxy />
<! Define the notification content, that is, what needs to be done before and after the pointcut executes.
<bean id="loggerAspectInject"
    class="com.glmapper.framerwork.aspect.LoggerAspectInject">
</bean>
<! -- Define proxy -->
<bean id="goodsServiceImpl"
    class="com.glmapper.framerwork.service.impl.GoodsServiceImpl">
</bean>
Copy the code

3. Client application:

@Controller
@RequestMapping("/buy")
public class BuyController {

    @Autowired
    private OrderService orderService;
    
    @RequestMapping("/initPage")
    public ModelAndView initPage(HttpServletRequest request, HttpServletResponse response, ModelAndView view) {
        // Manually fetch the proxy bean via SpringContextUtil
    	GoodsService goodsService=(GoodsService)
    	SpringContextUtil.getBean("goodsServiceImpl");
    
    	List<Goods> goods = goodsService.queryAll(10.10);
    	view.addObject("goodsList", goods);
    	view.setViewName("goodslist");
    	returnview; }}Copy the code

4. Usage 2: Hosting to IOC with @Component annotation

@Aspect
@Component // Add the Component annotation here so you don't need to configure it in XML
public class LoggerAspectInject {

    private static final Logger LOGGER =
    LoggerFactory.getLogger(LoggerAspectInject.class);

    @Pointcut("execution(* com.glmapper.framerwork.service.impl.*.*(..) )")
    public void cutIn(a){}

    @Before("cutIn()")
    public void before(a){
        LOGGER.info("before current time:"+System.currentTimeMillis());
    }

    @AfterReturning("cutIn()")
    public void AfterReturning(a){
        LOGGER.info("afterReturning current time:"+System.currentTimeMillis()); }}Copy the code

5. Client code:

@Controller
@RequestMapping("/buy")
public class BuyController {

    @Autowired
    private OrderService orderService;
    // Direct injection
    @Autowired
    private GoodsService goodsService;
    
    @RequestMapping("/initPage")
    public ModelAndView initPage(HttpServletRequest request, HttpServletResponse response, ModelAndView view) {
    	
    	List<Goods> goods = goodsService.queryAll(10.10);
    	view.addObject("goodsList", goods);
    	view.setViewName("goodslist");
    	returnview; }}Copy the code

6, a relatively complete LoggerAspectInject, in the actual project can be directly referenced


/ * * *@description: aop
 * @email: < a href = "[email protected]" > < / a > *@author: glmapper@lei *@date: 18/6/4 * /
@Aspect
@Component
public class LoggerAspectInject {
    private static final Logger LOGGER= LoggerFactory.getLogger(LoggerAspectInject.class);
    
    @Pointcut("execution(* com.glmapper.book.web.controller.*.*(..) )")
    public void cutIn(a){}@Around("cutIn()")   // Define Pointcut with the label "aroundAdvice"
    public Object aroundAdvice(ProceedingJoinPoint poin){
        System.out.println("Circular notification");
        Object object = null;
        try{
            object = poin.proceed();
        }catch (Throwable e){
            e.printStackTrace();
        }
        return object;
    }

    / / define advise
    // This method is just an identifier, equivalent to the id of the pointcut defined in the configuration file. This method returns no values and parameters
    @Before("cutIn()")
    public void beforeAdvice(a){
        System.out.println("Advance notice");
    }

    @After("cutIn()")
    public void afterAdvice(a){
        System.out.println("Post notification");
    }

    @AfterReturning("cutIn()")
    public void afterReturning(a){
        System.out.println("Post return");
    }

    @AfterThrowing("cutIn()")
    public void afterThrowing(a){
        System.out.println("Post anomaly"); }}Copy the code

About named pointcuts: The cutIn methods in the above example can be called named pointcuts, which can be referenced by other pointcuts, while anonymous pointcuts cannot. Only @AspectJ supports named pointcuts, while the Schema style does not. As shown below, @AspectJ references named pointcuts as follows:

@Pointcut("execution(* com.glmapper.book.web.controller.*.*(..) )")
public void cutIn(a){}// Introduce named pointcuts
@Before("cutIn()")
public void beforeAdvice(a){
    System.out.println("Advance notice");
}
Copy the code

Injection AspectJ facets

This way I feel is a way of combining the second and the third.

1. Define the aspect class

/ * * *@descriptionInjection is also a way to configure through XML@email: <a href="[email protected]"></a>
* @author: guolei.sgl
* @date: 18/6/20 * /
public class LoggerAspectHelper {

    private static final Logger LOGGER = LoggerFactory.getLogger(LoggerAspectHelper.class);
    
    /** * execute * before transferring method@param point
     * @throws Throwable
     */
    public void doBefore(JoinPoint point) throws Throwable {
        LOGGER.info("before current time:"+System.currentTimeMillis());
    }
    
    /** * is executed before and after the method is called@param point
     * @return
     * @throws Throwable
     */
    public Object doAround(ProceedingJoinPoint point) throws Throwable
    {
        LOGGER.info("around current time:"+System.currentTimeMillis());
        if(point.getArgs().length>0) {
            return point.proceed(point.getArgs());
        }else{
            returnpoint.proceed(); }}/** * is executed after the method is called@param point
     * @throws Throwable
     */
    public void doAfter(JoinPoint point) throws Throwable
    {
        LOGGER.info("after current time:"+System.currentTimeMillis());
    }
    
    /** * Exception notification *@param point
     * @param ex
     */
    public void doThrowing(JoinPoint point, Throwable ex)
    {
        LOGGER.info("throwing current time:"+System.currentTimeMillis()); }}Copy the code

2. XML configuration

<bean id="loggerAspectHelper"    
    class="com.glmapper.framerwork.aspect.LoggerAspectHelper">
</bean>

<aop:config>
    <aop:aspect id="configAspect" ref="loggerAspectHelper">
    	<! - to configure com. Glmapper. Framerwork. Service. Imp package under all of the class or interface methods -- -- >
    	<aop:pointcut id="cutIn" expression=
    	"execution(* com.glmapper.framerwork.service.impl.*.*(..) )" />
    	
    	<aop:before   pointcut-ref="cutIn" method="doBefore" />
    	<aop:after    pointcut-ref="cutIn" method="doAfter" />
    	<aop:around   pointcut-ref="cutIn" method="doAround" />
    	<aop:after-throwing pointcut-ref="cutIn" 
    	    method="doThrowing" throwing="ex" />
    	
    </aop:aspect>
</aop:config>
Copy the code

3, the results

23:39:48. 756 [HTTP - nio - 8080 - exec - 4] INFO C.G.F.A spect. LoggerAspectHelper - before the current time: 1529509188756 23:39:48. [HTTP - nio - 8080-757 the exec - 4] INFO C.G.F.A spect. LoggerAspectHelper - around the current time: 1529509188757 excute queryAll method... 23:39:48. 757 [HTTP - nio - 8080 - exec - 4] INFO C.G.F.A spect. LoggerAspectHelper - after the current time: 1529509188757Copy the code

expression


From the examples above we use regular expressions to specify our pointcuts. In practice, there are many other types of expressions, not just execution. Here are a few:

1, the execution

Join points for matching method execution;

execution(* com.glmapper.book.web.controller.*.*(..) )Copy the code
  • The execution () expression body;
  • The first “*” symbol indicates that the return value is of any type;
  • Com. Glmapper. Book. Web. Controller AOP cut by the service package, that is, part of our business
  • The “.” following the package name indicates the current package and its subpackages
  • The second “*” indicates the class name, that is, all classes
  • . * (..) Any method name, parentheses for parameters, and two dots for any parameter type

2, the within

Matches method execution within the specified type;

/ / if the com glmapper. Book. Web. The controller the outside package or any child in the package
// With this type defined, there is a join point in the Web layer.within(com.glmapper.book.web.controller.. *)@Pointcut("within(com.glmapper.book.web.controller.. *)")
public void cutIn(a){}
Copy the code

@within: matches all methods held within the specified annotation type;

/ * * *@description: Annotation definition *@email: < a href = "[email protected]" > < / a > *@author: glmapper@lei *@date: 18/6/4 * /
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.FIELD})
public @interface AuthAnnotation {
}
Copy the code

A class method that holds an AuthAnnotation annotation for any target object’s type; The annotation must be declared on the target object; declarations on the interface have no effect on it.

@within(com.glmapper.book.common.annotaion.AuthAnnotation)

// All classes annotated by @AdviceAnnotation will match
@Pointcut("@within(com.glmapper.book.common.annotaion.AuthAnnotation)") 
public void cutIn(a){}
Copy the code

3, this

The execution method to match the current AOP proxy object type; Note that the AOP proxy object is type-matching, which may include importing interfaces as well as type-matching; The expression used in this must be a fully qualified name of the type. Wildcards are not supported.

// The current target object (a non-AOP object) implements any method of the UserService interface
this(com.glmapper.book.web.service.UserService)

// Used to pass a reference to the proxy object into the notification method.
@Before("cutIn() && this(proxy)")
public void beforeAdvice(ProceedingJoinPoint poin,Object proxy){
    System.out.println("Advance notice");
}
Copy the code

4, target

The execution method to match the current target object type; Note that the target object type matches, so that does not include imported interface type match; The expression used in target must be a fully qualified name of the type. Wildcards are not supported.

// The current target object (a non-AOP object) implements any method of the UserService interface
target(com.glmapper.book.web.service.UserService)

// Used to pass a reference to the proxy object into the notification method.
@Before("cutIn() && target(proxy)")
public void beforeAdvice(ProceedingJoinPoint poin,Object proxy){
    System.out.println("Advance notice");
}
Copy the code

@target: the execution method used to match the current target object type, where the target object holds the specified annotation; Class methods that hold Secure annotations for any target object; This annotation, like @within, must be declared on the target object; declarations on the interface do not apply to it either.

@target(com.glmapper.book.common.annotaion.AuthAnnotation)

@Pointcut("@target(com.glmapper.book.common.annotaion.AuthAnnotation)")
public void cutIn(a){}
Copy the code

5, the args

An execution method of the specified type is passed in as an argument to match the currently executing method; Parameters in the parameter type list must be fully qualified names of types. Wildcards are not supported. Args are dynamic pointcuts, which are very expensive and should not be used except in special cases.

// Any one that starts with accepting "parameter type java.io.Serializable",
// And can then be executed with any method of any type of parameter,
// Args specifies parameter types that are dynamically matched at run time
args (java.io.Serializable,..)

// Used to pass parameters into the notification method.
@Before("cutIn() && args(age,username)")
public void beforeAdvide(JoinPoint point, int age, String username){
  / /...
}
Copy the code

@args: execution that holds the specified annotation for the argument passed to the currently executing method; Any method that takes only one argument and that is passed in at runtime with an AuthAnnotation; Dynamic pointcuts, similar to ARG indicators;

@args (com.glmapper.book.common.annotaion.AuthAnnotation)

@Before("@args(com.glmapper.book.common.annotaion.AuthAnnotation)")
public void beforeAdvide(JoinPoint point){
  / /...
}
Copy the code

6, @ the annotation

Matches the method that the currently executing method holds the specified annotation with the @annotation type. The annotation type must also be a fully qualified type name;

// The AuthAnnotation holding on the current execution method will be matched
@annotation(com.glmapper.book.common.annotaion.AuthAnnotation)

// The method that matches the AuthAnnotation annotation whose join point is specified by its argument.
That is, all methods specified for annotation annotation will match.
@Pointcut("@annotation(com.glmapper.book.common.annotaion.AuthAnnotation)")
public void cutIn(a){}
Copy the code

There is also the bean approach, which has not been used. Have a look if you are interested.

Examples are given in the basic concepts section below.

Basic concept

The basic Concepts section gives a general overview of some of the conceptual points in AOP, with reference to explanations on the official web site.

AOP

AOP(aspect-oriented Programming), namely Aspect Oriented Programming, is complementary to OOP(Object-oriented Programming) and provides a different perspective of abstract software structure from OOP. In OOP, we use a class as our basic unit, whereas in AOP, the basic unit is an Aspect.

Cross Cutting Concern: Independent services, such as system logging. Crosscutting cannot be done if it is not a stand-alone service (that is, a service that is strongly coupled to the business). Often these independent services need to be distributed throughout the system, throughout the business process.

Target Object

Target object. Weave into the target object for advice. The target object is also known as the advised Object. Because Spring AOP implements aspects in a run-time proxy manner, advICED Object is always a proxy object; Note that AdvIced Object does not refer to the original class, but rather to the proxy class that is woven into the Advice.

Weave in

Advice is applied in JoinPoint in a process called weaving. Another way of looking at it is the process of concatenating aspects with other objects and creating AdvICED Objects.

AOP weaving can be done in three ways, depending on the implementation technology:

  • Compiler weaving, which requires specialJavaThe compiler
  • Class load time is woven in, which requires a special class loader
  • Dynamic proxy weaving, adding enhancements to the target class at run timeAdvice) to generate subclasses.

Spring uses dynamic proxy weaving, while AspectJ uses compiler weaving and class loading

The agent

Spring AOP defaults to using proxies with standard JDK dynamic proxies. This allows any interface (or group of interfaces) to be proxied.

Spring AOP can also use the CGLIB proxy. If the business object does not implement an interface, CGLIB is used by default. It is good practice to program interfaces rather than classes; Business classes typically implement one or more business interfaces. It is possible to enforce CGLIB in special cases where there is no declared method on the interface that needs to be notified, or where a proxy object needs to be passed to a specific type of method.

Introductions

We know that the Java language itself is not dynamic, and that once our classes are compiled, it is difficult to add new functionality to them. But in the first example, we did not add new methods to the object, but we did add new functionality to it. This is adding new functionality to an existing method, but can you add new methods to an object? The answer is definitely yes, and can be achieved using Introduction.

Introduction: Dynamically add or subtract methods for a class. Add additional methods or fields to a type. Spring AOP allows us to introduce new interfaces (and corresponding implementations) to target objects.

Aspect

Aspect: a combination of advice and pointcuts.

The cutting plane implements the cross-cutting function. Is one of the most common logging module, the methods perform time-consuming module, in this way, the program according to the function is divided into several layers, if according to the traditional inheritance, business model inherit log module need to insert in the changes so much, and by creating a section of AOP can be used to realize the same function, we can make different aspects according to different requirements.

Cross-cutting concerns scattered among various business objects are collected to design independent reusable objects, which are called aspects. In the example above we defined four different forms of section depending on the configuration.

Joinpoint

The point or moment at which an Aspect joins a business process during application execution is called a Joinpoint. Specifically, it is the time when Advice is called to be executed within an application, either before or after a method is called (or both), or when an exception occurs.

Joinpoint & ProceedingJoinPoint

Surround notification = pre + target method execution + post notification. The PROCEED method is used to start target method execution.

The surround advice ProceedingJoinPoint performs the proceed method to cause the target method to execute, which is one of the biggest differences between the surround advice and the pre – and post-advice methods.

Proceedingjoinpoint inherits JoinPoint. The method proceed is exposed on the basis of JoinPoint. Proceed is important; this is the method that the AOP proxy chain executes; By exposing this method, you can support AOP :around aspects (the other aspects only need JoinPoint, depending on the type of aspect) that can decide whether to go through the proxy chain or the other logic that you intercept.

In the method surrounding the notification, an Object of type should be returned. If the method surrounding the notification returns type void, some unexpected conditions will occur, such as 404.

Pointcut

Matches the predicate of join points. Advice is associated with a pointcut expression and runs at any join point that the pointcut matches. (For example, the execution of a method with a specific name). The concept of join points matched by pointcut expressions is at the heart of AOP, and Spring uses the AspectJ pointcut expression language by default.

In Spring, all methods can be considered Joinpoint, but we don’t want to add Advice to all methods. The Pointcut provides a set of rules (described using AspectJ Pointcut Expression Language) that match joinPoints and adds Advice to joinPoints that meet the rules.

Pointcut and Joinpoint

In Spring AOP, all method implementations are Join points. The Point cut is a description, which modifies the join point. Through the point cut, we can determine which join points can be woven into Advice. So join point and Point cut are essentially two different dimensions.

Advice is performed on join points, and the Point Cut specifies which join points can perform which advice.

Advice

concept

Advice is the implementation of our aspect function, and it is where the pointcuts are actually executed. For example, several methods like the one in the previous example print the time (any method marked with an annotation like @before is a notification); Advice inserts code into the application at Jointpoint.

classification

BeforeAdvice, AfterAdvice, the difference is whether Advice is called before or after the target method, Throw Advice means that Advice is called when an exception occurs on the target.

  • Before advice: advice to be executed before join point. Although before advice is executed before join point, it does not prevent the join point from executing unless an exception occurs (i.e., in the before Advice code, You cannot artificially decide whether to continue executing code in join Point.)
  • After return advice: Advice performed after a join point returns normally
  • After Throwing Advice: Advice performed when a join point throws an exception
  • After (final) advice: Advice that is executed whether a join point exits normally or if an exception occurs.
  • Around advice: advice that is performed both before the join point and after the joint point exits. This is the most commonly used advice.

Advice, JoinPoint, and PointCut relationships

The graph below, found on the blog of one of the most influential people on the Internet, helps us better understand the relationship between these concepts.

The above is a brief overview of some of the basic concepts involved in AOP and their relationships.

Some of the pit

A record of some problems that occurred during debugging

Error 404 was reported when intercepting the controller layer service using AOP

@Around("cutIn()")
public void aroundAdvice(ProceedingJoinPoint poin) {
    System.out.println("Circular notification");
}
Copy the code

The important thing to note here is that when you use wrap notification again, you need to give the method a return value.

@Around("cutIn()")
public Object aroundAdvice(ProceedingJoinPoint poin) throws Throwable {
    System.out.println("Circular notification");
    return poin.proceed();
}
Copy the code

2, 0 formal unbound in pointcut

Aop annotations with arguments are provided in Spring 4.x. Look at the following example:

@Pointcut(value = "execution(* com.glmapper.framerwork.service.impl.*(int,int)) && args(i,j)")  
public void cutIn(int i, int j) {}  
  
@Before(value="cutIn(i, j)",argNames = "i,j")  
public void beforeMethod( int i, int j) {  
    System.out.println("---------begins with " + i + "-" +j);  
}  
Copy the code

Before, for example here has two parameters of type int, if we don’t have to specify parameter when use, then will be thrown: under Caused by: Java. Lang. IllegalArgumentException: Error at ::0 Formal unbound in pointcut Error information.

It was meant to be in one piece, but it's too long, so let's separate it; Update over the weekend