Preface introduces

Appendix: Spring source learning column

In the previous chapters, we have a certain understanding of the Springframework IOC implementation source code, and then this paper continues to learn a core technology of Springframework AOP technology.

Were you familiar enough with AOP before you learned the Spring AOP source code? Learning the source code before you are familiar with the application is certainly difficult to understand, so this article will not describe the implementation of the source code, first through this blog to familiarize yourself with Spring AOP, and then learn the source code

1. What is AOP technology?

The Spring website provides an overview of AOP techniques:

Aspect-Oriented Programming (AOP) complements Object-Oriented Programming (OOP) by providing another way of thinking about program structure. The key unit of modularity in OOP is the class, whereas in AOP the unit of modularity is the aspect. Aspects enable the modularization of concerns such as transaction management that cut across multiple types and objects. (Such concerns are often termed crosscutting concerns in AOP literature.)

In particular, AOP (aspect-oriented Programming), also known as aspect-oriented Programming, is an object-oriented technology that realizes modularization through the separation of cross-domain concerns.

  • Transaction management across domains that is, across multiple types and objects, and so on;
  • Concerns are often referred to as crosscutting concerns, and the key unit of modularity in OOP is classes, whereas modularity is aspect concerns in AOP is often referred to as crosscutting concerns

2. AOP’s essential purpose

AOP essence: Enhance crosscutting logic without changing the original business logic, which can be permission verification logic, log monitoring, transaction control, and so on

For more information about AOP, refer to the Spring AOP official documentation

3. AOP terminology

noun describe
Joinpoint A join point is a point in a program’s execution, such as the execution of a method or the handling of an exception
Pointcut This refers to the join point after the enhancement code is woven into the business mainline
Advice Advice, which can be translated as notification or enhancement, refers to the methods used in the aspect class to provide enhancement
Target Object Refers to the target object of the proxy, namely the proxied object
Proxy Proxy object refers to a proxy class created after a class is woven into AOP for enhancement
Weaving This refers to the process of applying Advice to a Target object to generate a Proxy. Ps: AspectJ uses compile-time weaving and class-load weaving, while Spring AOP uses dynamic proxy weaving
Aspect Aspects are AOP concerns, that is, Advice code concerns, and are a synthesis of the above concepts. Put this Advice code in a class called the aspect class, which is a modular class that spans the concerns of multiple classes

You may not understand these theories very well, so use illustrations from foreign websites to illustrate AOP conceptslink

Above all, the goal is to lock in the Pointcut and Weaving the Advice

Spring AOP and AspectJ

With the previous overview of AOP concepts, we have a general understanding of AOP, but this article will clarify the relationship between Spring AOP and AspectJ

The Spring website points out the relationship between Spring AOP and AspectJ. The website clearly states its position that Spring AOP does not compete with AspectJ projects to provide a more mature AOP solution. Spring AOP and AspectJ are complementary. Spring AOP seamlessly combines IOC and AspectJ, enabling Spring AOP to meet most AOP requirements, which is a pattern that provides proxy objects

From the official website can also know the general relationship between Spring AOP and AspectJ, in fact, this is two KINDS of AOP implementation technology, Spring AOP is integrated with AspectJ part of the function, at the same time combined with IOC implementation, AspectJ is a relatively perfect and mature AOP solution, Here’s a simple comparison

  • Spring AOP

    • Spring AOP is a component of SpringFramework, belonging to a relatively core function of SpringFramework, Spring AOP is a combination of AspectJ and IOC implementation, providing AspectJ functions
    • Spring AOP addresses the most common AOP requirement (method weaving) in enterprise development, and Spring AOP does not compete with AspectJ
    • Spring AOP can only work with beans in the Spring container
    • Performance-wise, Spring AOP is dynamically woven at run time, so it can’t match AspectJ in performance
    • Spring AOP uses dynamic proxy for method weaving. There are two dynamic proxy methods, CGLIB and the DYNAMIC proxy provided by the JDK

    referencewww.baeldung.com/spring-aop-…For illustration

  • AspectJ

    • AspectJ is an open source project from Eclipse, linked to www.eclipse.org/aspectj
    • AspectJ is a mature AOP solution that provides more AOP functionality than Spring AOP
    • AspectJ method weaving is static weaving and can be done at compile-time, post-compile, or load-time when the JVM class loader is loading.
    • AspectJ performs better than Spring AOP because it does method weaving at compile time

Ok, now that I’ve outlined the main differences between Spring AOP and AspectJ, I can use a table to compare the differences.

contrast Spring AOP AspectJ
Implementation language Use the Java language Extended implementation using the Java programming language
The build process No separate compilation process is required The AspectJ compiler (AJC) is required unless LTW is set up
Woven into the timing Dynamic proxy, woven in at run time Static weaving, which is woven during compilation, when compile-time (compile time), post-compile (compile time), load-time (JVM classloader load time)
function Basic method of weaving in You can weave fields, methods, constructors, static initializers, final classes/methods…
The scope of Works only on Spring container-managed beans Can be applied to all domain objects
performance Much slower than AspectJ Better performance (compile-time weaving is much faster than run-time weaving)
learning Easy to learn and apply More complex than Spring AOP

In addition, AspectJ static knitting timing:

  • Compile -time weaving: weaving is the method weaving that takes place at compile time and generates the.class file containing the weaving code
  • Post-compile weaving: also known as binary weaving, weaving Advice into existing class files and JAR files
  • Load-time Weaving (LTW) : Weaving at class Load time, exactly the same as binary weaving before, except that weaving is deferred until the class loader loads the class file into the JVM

5. AOP proxy selection in Spring

In the previous knowledge, we know that Spring AOP is the use of dynamic proxy technology in Spring AOP proxy selection, method weaving implementation has two methods, one is JDK dynamic proxy, one is CGLIB

  • By default, the Spring framework selects JDK or CGLIB based on whether the proxied Target Object implements an interface. If the proxied object does not implement any interface, Spring selects CGLIB. If the proxied object does, Spring selects the dynamic proxy provided by the JDK.
  • Ps: Although this is the default, we can customize dynamic proxy by configuration

6. Prepare reference for experimental environment

Now that you have learned the above theoretical knowledge, you are ready to practice by example. Before practice, you need to prepare the following environment for your experiment:

  • SpringFramework version
    • Springframework5.0. X
  • The development environment
    • JAR management: Gradle 4.9/ Maven3.+
    • IDE development: IntelliJ IDEA 2018.2.5
    • The JDK: jdk1.8.0 _31
    • Git fro Window 2.8.3
    • Git Client: SmartGit18.1.5 (Optional)

7, Spring AOP implementation

In Spring AOP, there are three main configurations available:

  • Interface based configuration for Spring1.2: The earliest AOP implementations of Spring were based on the AOP interfaces provided by Spring to implement Advice logic coding, and so on
  • Spring2.0+ schema-based configuration: after Spring2.0, schema-based configuration, that is, xml-type configuration, is provided, using namespaces<aop>
  • Spring2.0+ @aspect configuration: after Spring2.0, it is also available@AspectThis approach,@AspectThe AspectJ JAR is used, but the implementation is Spring AOP’s own

8, Spring AOP example reference

Spring AOP can be implemented in three ways described earlier, and this article verifies with code examples:

Maven configuration

<properties>
    <springframework.version>5.0.19. RELEASE</springframework.version>
    <aspectj.version>1.9.4</aspectj.version>
</properties>

<dependencies>
	<! Spring AOP configuration -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
        <version>${springframework.version}</version>
    </dependency>
    <! The test class for this article requires an IOC configuration.
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${springframework.version}</version>
    </dependency>
    <! -- @aspect is required to add -->
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>${aspectj.version}</version>
    </dependency>


</dependencies>
Copy the code

8.1. Spring1.2 interface-based configuration

8.1.1 Basic class writing

User.java

package com.example.spring.aop.bean;

public class User {
    private String username;
    private String password;

    public String getUsername(a) {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword(a) {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString(a) {
        return "User{" +
                "username='" + username + '\' ' +
                ", password='" + password + '\' ' +
                '} '; }}Copy the code

UserService. Java:

package com.example.spring.aop.service;

import com.example.spring.aop.bean.User;

/**
 * <pre>
 *      UserService
 * </pre>
 *
 * <pre>
 * @authorMazq * Modified Record * Modified by: Date: 2020/11/20 18:02 Modified content: * </pre> */
public interface UserService {

    User addUser(User user);

    User getUser(a);
}

Copy the code

UserServiceImpl .java

package com.example.spring.aop.service.impl;

import com.example.spring.aop.bean.User;
import com.example.spring.aop.service.UserService;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

/**
 * <pre>
 *		UserServiceImpl 
 * </pre>
 *
 * <pre>
 * @authorMazq * Modified Record * Modified by: Date: 2020/11/20 17:57 Modified content: * </pre> */
@Service
public class UserServiceImpl implements UserService {

    private static User user = null;

    @Override
    public User addUser(User userDto) {
        user = new User();
        BeanUtils.copyProperties(userDto,user);
        return user;
    }

    @Override
    public User getUser(a) {
        returnuser; }}Copy the code

8.1.2. Use Advice interface

LogMethodBeforeAdvice .java

package com.example.spring.aop.core.advice;

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * <pre>
 *      LogMethodBeforeAdvice
 * </pre>
 *
 * <pre>
 * @authorMazq * Modified Record * Modified by: Date: 2020/11/20 17:38 Modified content: * </pre> */
public class LogMethodBeforeAdvice implements MethodBeforeAdvice {
	@Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println(String.format("Execution method: %s, parameter list: %s", method.getName(), Arrays.toString(args) )); }}Copy the code

LogAfterReturningAdvice .java

package com.example.spring.aop.core.advice;

import org.springframework.aop.AfterReturningAdvice;

import java.lang.reflect.Method;

/**
 * <pre>
 *      LogAfterReturningAdvice
 * </pre>
 *
 * <pre>
 * @authorMazq * Modified Record * Modified by: Date: 2020/11/20 17:41 Modified content: * </pre> */
public class LogAfterReturningAdvice implements AfterReturningAdvice {
	@Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println(String.format("Method return: %s", returnValue )); }}Copy the code

Spring_interfaces_config. XML:


      
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <! -- Target Object -->
    <bean id="userServiceTarget" class="com.example.spring.aop.service.impl.UserServiceImpl"></bean>

    <! - implement MethodBeforeAdvice -- -- >
    <bean id="logMethodBeforeAdvice" class="com.example.spring.aop.core.advice.LogMethodBeforeAdvice"></bean>
    <! - implement AfterReturningAdvice -- -- >
    <bean id = "logAfterReturningAdvice" class="com.example.spring.aop.core.advice.LogAfterReturningAdvice"></bean>

    <bean id="userServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <! Proxy interface -->
        <property name="proxyInterfaces">
            <list>
                <value>com.example.spring.aop.service.UserService</value>
            </list>
        </property>
        <! -- Configure the target object, that is, the proxied class, the concrete business implementation class -->
        <property name="target" ref="userServiceTarget"></property>
        <! Advice, Advisor, interceptor -->
        <property name="interceptorNames">
            <list>
                <value>logMethodBeforeAdvice</value>
                <value>logAfterReturningAdvice</value>
            </list>
        </property>
    </bean>

</beans>
Copy the code

TestApplication. Java:

package com.example.spring.aop;


import com.example.spring.aop.bean.User;
import com.example.spring.aop.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestApplication {

    public static void testAopProxy(a) {
        ApplicationContext ioc = new ClassPathXmlApplicationContext("classpath:spring_interfaces_config.xml");
        UserService userService = (UserService) ioc.getBean("userServiceProxy");
        User userDto = new User();
        userDto.setUsername("tom");
        userDto.setPassword("11");
        userService.addUser(userDto);
        System.out.println(String.format("User data printing :%s",userService.getUser().toString()));
    }

    public static void main(String[] args) { testAopProxy(); }}Copy the code

[User{username=’ Tom ‘, password=’11’}]

User{username=’ Tom ‘, password=’11’} User{username=’ Tom ‘, password=’11’}

8.1.3 use the Advisor interface

  • NameMatchMethodPointcutAdvisor use

Define an Advisor that intercepts only query methods and modify the configuration:

<! Define an Advisor that intercepts only query methods
<bean id="logOnlyObtainQueryAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
    <! Advice class -->
    <property name="advice" ref="logMethodBeforeAdvice"></property>
    <! -- only query methods are blocked -->
    <property name="mappedNames" value="getUser"></property>
</bean>

<bean id="userServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
    <! Proxy interface -->
    <property name="proxyInterfaces">
        <list>
            <value>com.example.spring.aop.service.UserService</value>
        </list>
    </property>
    <! -- Configure the target object, that is, the proxied class, the concrete business implementation class -->
    <property name="target" ref="userServiceTarget"></property>
    <! Advice, Advisor, interceptor -->
    <property name="interceptorNames">
        <list>
            <value>logOnlyObtainQueryAdvisor</value>
        </list>
    </property>
</bean>
Copy the code

Execute method: getUser, parameter list: []

User{username=’ Tom ‘, password=’11’}

  • RegexpMethodPointcutAdvisor use

Introduces the NameMatchMethodPointcutAdvisor front, but general enough, so Spring aop also provides RegexpMethodPointcutAdvisor, can support a regular expression

 <! Define advisors that support regular matching and only intercept query methods.
 <bean id="regexpMethodAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
     <property name="advice" ref="logMethodBeforeAdvice"></property>
     <property name="pattern" value="com.example.spring.aop.*.service.*.get.*"></property>
 </bean>

<bean id="userServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
   <! Proxy interface -->
   <property name="proxyInterfaces">
       <list>
           <value>com.example.spring.aop.service.UserService</value>
       </list>
   </property>
   <! -- Configure the target object, that is, the proxied class, the concrete business implementation class -->
   <property name="target" ref="userServiceTarget"></property>
   <! Advice, Advisor, interceptor -->
   <property name="interceptorNames">
       <list>
           <value>regexpMethodAdvisor</value>
       </list>
   </property>
</bean>
Copy the code

Execute method: getUser, parameter list: []

User{username=’ Tom ‘, password=’11’}

8.1.4. Use the Interceptor interface

TestMethodInterceptor. Java:

package com.example.spring.aop.core.interceptor;


import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

/**
 * <pre>
 *      TestMethodInterceptor
 * </pre>
 *
 * <pre>
 * @authorMazq * Modified Record * Modified by: Date: 2020/11/23 10:28 Modified content: * </pre> */
public class TestMethodInterceptor  implements MethodInterceptor {

    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        System.out.println(String.format("Before method invoke: %s",methodInvocation));
        Object implObj = methodInvocation.proceed();
        System.out.println(String.format("After method invoke: %s",implObj));
        returnimplObj; }}Copy the code

Modifying a configuration file:

<! MethodInterceptor -->
<bean id="methodInterceptor" class="com.example.spring.aop.core.interceptor.TestMethodInterceptor"></bean>

<bean id="userServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
   <! Proxy interface -->
    <property name="proxyInterfaces">
        <list>
            <value>com.example.spring.aop.service.UserService</value>
        </list>
    </property>
    <! -- Configure the target object, that is, the proxied class, the concrete business implementation class -->
    <property name="target" ref="userServiceTarget"></property>
    <! Advice, Advisor, interceptor -->
    <property name="interceptorNames">
        <list>
            <value>logMethodBeforeAdvice</value>
            <value>logAfterReturningAdvice</value>
            <value>methodInterceptor</value>
        </list>
    </property>
</bean>
Copy the code

Select addUser method log information:

Before the method call (before method invoke) : ReflectiveMethodInvocation: public abstract com.example.spring.aop.bean.User com.example.spring.aop.service.UserService.addUser(com.example.spring.aop.bean.User); Target is of class [. Com. Example. Spring aop. Service. Impl. UserServiceImpl] after the method call (after method invoke) : User{username=’tom’, password=’11’}

8.1.5 Use of beanNameAutoProxy

Spring also provides beanNameAutoProxy. This method automatically matches the beanName ID and does not require each business to be ProxyFactoryBean

Create a new configuration file, spring_beannameAutoproxy_config.xml:


      
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <! -- Target Object -->
    <bean id="userServiceTarget" class="com.example.spring.aop.service.impl.UserServiceImpl"></bean>

    <! - implement MethodBeforeAdvice -- -- >
    <bean id="logMethodBeforeAdvice" class="com.example.spring.aop.core.advice.LogMethodBeforeAdvice"></bean>
    <! - implement AfterReturningAdvice -- -- >
    <bean id = "logAfterReturningAdvice" class="com.example.spring.aop.core.advice.LogAfterReturningAdvice"></bean>

    <! MethodInterceptor -->
    <bean id="methodInterceptor" class="com.example.spring.aop.core.interceptor.TestMethodInterceptor"></bean>

    <! -- Define BeannameAutoXyCreator -->
    <bean id="autoProxyCreator" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
        <! Advice, Advisor, interceptor-->
        <property name="interceptorNames">
            <list>
                <value>logMethodBeforeAdvice</value>
                <value>logAfterReturningAdvice</value>
                <value>methodInterceptor</value>
            </list>
        </property>
        <! Eg: userServiceTarget-->
        <property name="beanNames" value="*ServiceTarget"></property>
    </bean>


</beans>
Copy the code

TestApplication. Java:

package com.example.spring.aop;


import com.example.spring.aop.bean.User;
import com.example.spring.aop.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestApplication {

    public static void testBeanNameAutoProxy(a) {
        ApplicationContext ioc = new ClassPathXmlApplicationContext("classpath:spring_beanNameAutoProxy_config.xml");
        UserService userService = ioc.getBean(UserService.class);
        User userDto = new User();
        userDto.setUsername("tom");
        userDto.setPassword("11");
        userService.addUser(userDto);
        System.out.println(String.format("User data printing :%s",userService.getUser().toString()));
    }

    public static void main(String[] args) {
        // BeanNameAutoProxyCreatortestBeanNameAutoProxy(); }}Copy the code

8.2 Spring2.0+ @aspect configuration

@aspect this way is more commonly used, POM needs to add AspectJweaver configuration, Spring AOP references aspectJ API, but the implementation is the implementation of spring extension

8.2.1 enable @AspectJ support

Annotated by @enableAspectJAutoProxy

@Configuration
@EnableAspectJAutoProxy
public class AppConfig {}Copy the code

XML, can be enabled using < AOP: Aspectj-autoproxy />


      
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
    <aop:aspectj-autoproxy/>
</beans>
Copy the code

8.2.2 Declaration

XML:

<bean id="myAspect" class="org.xyz.NotVeryUsefulAspect">
    <! -- configure properties of aspect here as normal -->
</bean>
Copy the code

Java way, using the @aspect annotation:

package org.xyz;
import org.aspectj.lang.annotation.Aspect;

@Aspect
public class NotVeryUsefulAspect {}Copy the code

8.2.3 Declare pointcuts

The type of Pointcut,Spring’s official websiteA more detailed introduction is given:The advice on the official website is stated as a generic oneSystemArchitecture :

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) {}

    /** * 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) {}

    /** * 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) {}

    /** * 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) {}

    /** * 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(a) {}}Copy the code

Because the introduction of the official website is more detailed, so this blog only selected some of the more important introduction:

  • execution

This is the most basic type of pointcut:

// Match any method in UserService
 @Pointcut("execution(* com.example.spring.aop.service.UserService.*(..) )"
    public void regexpExecution(a){}
Copy the code

Ps: the first * matches any return value, the second * matches any method, (..) Matches any number of method parameters

Eg: the execution (* *.. find*(Long,..) ) used to match methods named find… The first argument is of type long

 @Pointcut("within(com.example.spring.aop.. *) && execution(* *.. find*(Long,..) )"
    public void regexpExecutionByMethodName(a){}
Copy the code
  • within

Within: represents any join point in the service bundle (method execution only in Spring AOP)

@Pointcut("within(com.example.spring.aop.. *) ")
Copy the code
  • This and target

The former comes into play when Spring AOP creates a Cglib-based proxy, while the latter is used when creating a JDK-based proxy

Example code:

public class UserServiceImpl implements UserService {
    / /...
}
Copy the code

For UserService, use target, which is a Cglib-based proxy

@Pointcut("target(com.example.spring.aop.service.UserService)")
Copy the code

For UserService, use this, which is JDK based proxy

@Pointcut("this(com.example.spring.aop.service.impl.UserServiceImpl)")
Copy the code
  • args

Join points that restrict matching points (the execution of methods with Spring AOP) where the argument is an instance of a given type

  • @target

@target not to be confused with target, where the class implements an annotation of the object of the given type

@Pointcut("@target(org.springframework.stereotype.Repository)")
Copy the code
  • @args

The runtime type of the actual parameter passed in @args has an annotation of the given type

package com.example.spring.aop.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
// apply to class
@Target(ElementType.TYPE)
public @interface Entity {
}

Copy the code
@Pointcut("within(com.example.spring.aop.. *) && @args(com.example.spring.aop.annotation.Entity)")
    public void argsMethod(a){}
Copy the code
  • @within

Restrict matching to join points within a type with a given annotation

Pointcut("@within(org.springframework.stereotype.Repository)")
Copy the code

Equivalent to:

@Pointcut("within(@org.springframework.stereotype.Repository *)")
Copy the code
  • @annotation

This @annotation is used to match annotations. It’s a common annotation class. We can write our own annotation class:

package com.example.spring.aop.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * <pre>
 *      EnableLog
 * </pre>
 *
 * <pre>
 * @authorMazq * Modified Record * Modified by: Date: 2020/11/23 18:27 Modified content: * </pre> */
@Retention(RetentionPolicy.RUNTIME)
// apply to the method
@Target(ElementType.METHOD)
public @interface EnableLog {
}

Copy the code

Methods that add @enablelog to the corresponding method can then be blocked:

@Pointcut("within(com.example.spring.aop.. *) && @annotation(com.example.spring.aop.annotation.EnableLog)")
    public void annotationMethod(a){}
Copy the code
  • Combined expression

Combination of expression can use &&, | | and! Are combined

 @Pointcut("within(com.example.spring.aop.. *) && execution(public String com.example.spring.aop.service.UserService.*(..) )"
Copy the code

8.2.4 declare the Advice type

Advice type,Spring’s official websiteThere are also more detailed introduction:To summarize the notification types:

  • Pre-notification (@Before) : Before the method is executed, the @before annotation is declared
  • Post notification (@AfterReturning) : Run the suggestion when the matching method executes a normal return. It USES@AfterReturningAnnotation declaration
  • Exception notification (@AfterThrowing) : the program throws an exception after execution, does not throw an exception will not call, use@AfterThrowingAnnotation declaration
  • Final Notice (@After) : matching methods execute exit, if there is a try… Catch, typically used after the finally execution is complete@AfterAnnotation declaration
  • Circular notification (@Around) : Runs “around” the suggestion during the execution of the matching method. Does it have the opportunity to work before and after the method is executed, and to determine when, how, and even when the method is fully executable, to use@AroundAnnotation declaration
package com.example.spring.aop.config;

import com.example.spring.aop.service.UserService;
import com.example.spring.aop.service.impl.UserServiceImpl;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;

/**
 * <pre>
 *      SpringAspectJConfiguration
 * </pre>
 *
 * <pre>
 * @authorMazq * Modified Record * Modified by: Date: 2020/11/24 10:52 Modified content: * </pre> */
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = false)
@Aspect
public class SpringAspectJConfiguration {

    @Bean
    public UserService userService(a){
        return new UserServiceImpl();
    }

    private ThreadLocal<SimpleDateFormat> simpleDateFormat =new ThreadLocal<SimpleDateFormat>(){
        @Override
        protected SimpleDateFormat initialValue(a) {
            //return super.initialValue();
            return new SimpleDateFormat("[yyyy-mm-dd hh:mm:ss:SSS]"); }};//---------------------------------------------------------------------
    // Types of pointcut
    //---------------------------------------------------------------------

    @Pointcut("within(com.example.spring.aop.. *) && execution(public String com.example.spring.aop.service.UserService.*(..) )"
    public void regexpExecution(a){}

    @Pointcut("within(com.example.spring.aop.. *) && execution(* *.. find*(Long,..) )"
    public void regexpExecutionByMethodName(a){}

    @Pointcut("within(com.example.spring.aop.. *) && target(com.example.spring.aop.service.UserService)")
    public void targetInterface(a){}

    @Pointcut("within(com.example.spring.aop.. *) && @args(com.example.spring.aop.annotation.Entity)")
    public void argsMethod(a){}

    @Pointcut("within(com.example.spring.aop.. *) && @annotation(com.example.spring.aop.annotation.EnableLog)")
    public void annotationMethod(a){}

    //---------------------------------------------------------------------
    // Types of advice
    //---------------------------------------------------------------------

    @Before(value = "regexpExecution()")
    public void beforeAdvice(JoinPoint joinPoint){
        Object[] args = joinPoint.getArgs();
        String methodName = joinPoint.getSignature().getName();
        System.out.println(String.format("Pre-notification: beforeAdvice, parameter: %s", Arrays.toString(args)));
        System.out.println(simpleDateFormat.get().format(new Date()) + methodName);
    }

    @AfterReturning(value = "regexpExecution()", returning = "returnVal")
    public void afterReturningAdvice(Object returnVal){
        System.out.println(String.format("AfterReturningAdvice, return parameter: %s", returnVal));
    }

    @AfterThrowing(value = "regexpExecution()", throwing = "e")
    public void afterThrowingAdvice(Throwable e) {
        System.out.println(String.format("Exception notification: afterThrowingAdvice, exception message: %s", e));
    }

    @After(value = "regexpExecution()")
    public void afterAdvice(a) {
        System.out.println(String.format("Final notice: afterAdvice"));
    }

    @Around(value = "regexpExecution()")
    public Object aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) {
        Object rtValue = null;
        try {
            System.out.println("AroundAdvice pre-notification!");
            // Get parameters
            Object[] args = proceedingJoinPoint.getArgs();
            // Execute the pointcut method
            rtValue = proceedingJoinPoint.proceed(args);

            System.out.println("AroundAdvice post notification!);
        } catch (Throwable e) {
            System.out.println("AroundAdvice abnormal notification!");
            e.printStackTrace();
        } finally {
            System.out.println("AroundAdvice last!);
        }
        returnrtValue; }}Copy the code

AroundAdvice pre – notification!

BeforeAdvice. The parameter is: [1] [2020-13-24 02:13:48.509]findUserNameById aroundAdvice BackaroundAdvice! AroundAdvice Last! AfterAdvice afterReturningAdvice. The returned parameter is Tom

8.2.5. Example: Log monitoring

Service adds a method: AopConfiguration. Java:

package com.example.spring.aop.config;

import com.example.spring.aop.core.interceptor.TestMonitoringInterceptor;
import com.example.spring.aop.service.UserService;
import com.example.spring.aop.service.impl.UserServiceImpl;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

/** * <pre> * AOP log monitoring configuration class * </pre> ** <pre> *@authorMazq * Modified Record * Modified by: V1.0.0 Date: 2020/11/23 14:30 Modified Content: Added Configuration * </pre> */
@Configuration
@Aspect
@EnableAspectJAutoProxy
public class AopLogMonitorConfiguration {

    @Pointcut("within(com.example.spring.aop.. *) && execution(public String com.example.spring.aop.service.UserService.findUserNameById(Long, ..) )"
    public void monitor(a){}@Bean
    public TestMonitoringInterceptor monitoringInterceptor(a) {
        return new TestMonitoringInterceptor(true);
    }

    @Bean
    public Advisor monitoringAdvisor(a) {
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression("com.example.spring.aop.config.AopConfiguration.monitor()");
        return new DefaultPointcutAdvisor(pointcut, monitoringInterceptor());
    }

    @Bean
    public UserService userService(a){
        return newUserServiceImpl(); }}Copy the code

TestMonitoringInterceptor.java

package com.example.spring.aop.core.interceptor;

import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.springframework.aop.interceptor.AbstractMonitoringInterceptor;

* TestMonitoringInterceptor * / * * * < pre > < / pre > < pre > * * * * change records modified version: the modifier: modification date: 2020/11/23 desired changes: * < / pre > * /
public class TestMonitoringInterceptor extends AbstractMonitoringInterceptor {

    public TestMonitoringInterceptor(a){}

    public TestMonitoringInterceptor (boolean useDynamicLogger) {
        setUseDynamicLogger(useDynamicLogger);
    }

    @Override
    protected Object invokeUnderTrace(MethodInvocation methodInvocation, Log log) throws Throwable {
        String name = createInvocationTraceName(methodInvocation);
        long start = System.currentTimeMillis();
        try {
            return methodInvocation.proceed();
        } finally {
            long end = System.currentTimeMillis();
            long time = end - start;
            log.info(String.format("Method name: %s, execution time: %s ms",name,time));
            if (time > 10) {
                log.warn(String.format("Method name: %s, execution time over 10 ms! ",name)); }}}}Copy the code

Pom.xml with logback configuration:

<properties>
    <slf4j.version>1.7.25</slf4j.version>
    <logback.version>1.2.3</logback.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>${slf4j.version}</version>
    </dependency>
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-core</artifactId>
        <version>${logback.version}</version>
    </dependency>
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-access</artifactId>
        <version>${logback.version}</version>
    </dependency>
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
        <version>${logback.version}</version>
    </dependency>
</dependencies>
Copy the code

Logback. XML, copy @ github.com/eugenp/tuto…


      
<configuration>
    <! -- copy @https://github.com/eugenp/tutorials/blob/master/spring-aop/src/main/resources/logback.xml-->

    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
            </pattern>
        </encoder>
    </appender>

    <logger name="org.springframework" level="WARN" />
    <logger name="org.springframework.transaction" level="WARN" />

    <! -- in order to debug some marshalling issues, this needs to be TRACE -->
    <logger name="org.springframework.web.servlet.mvc" level="WARN" />

    <logger name="com.example.spring.aop.core.interceptor.TestMonitoringInterceptor" level="INFO" />

    <logger name="org.springframework.aop.interceptor.PerformanceMonitorInterceptor" level="TRACE" />

    <root level="TRACE">
        <appender-ref ref="STDOUT" />
    </root>
</configuration>
Copy the code
package com.example.spring.aop;


import com.example.spring.aop.bean.User;
import com.example.spring.aop.config.AopConfiguration;
import com.example.spring.aop.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class TestApplication {

    public static void testLogMonitoring(a) {
        AnnotationConfigApplicationContext ioc = new AnnotationConfigApplicationContext();
        // Register the configuration class
        ioc.register(AopConfiguration.class);
        // Start IOC container
        ioc.refresh();
        UserService userService = (UserService) ioc.getBean("userService");
        System.out.println(userService.findUserNameById(1L));
    }

    public static void main(String[] args) {
        // logging monitoringtestLogMonitoring(); }}Copy the code

17:54:05. 553. [the main] INFO C.E.S.A.S ervice. Impl. UserServiceImpl – method names: Com. Example. Spring. Aop. Service. UserService. FindUserNameById, execution time: 2531 ms

17:54:05. 559. [the main] WARN C.E.S.A.S ervice. Impl. UserServiceImpl – method names: Com. Example. Spring. Aop. Service. UserService. FindUserNameById, execution time more than 10 ms!

8.3. Spring2.0+ Schema-based configuration

Spring2.0 has since provided XML configuration based on the
namespace, which is the schema-based configuration described in this article

SchemaBasedAspect. Java:

package com.example.spring.aop.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;

import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;

public class SchemaBasedAspect {

    private ThreadLocal<SimpleDateFormat> simpleDateFormat =new ThreadLocal<SimpleDateFormat>(){
        @Override
        protected SimpleDateFormat initialValue(a) {
            //return super.initialValue();
            return new SimpleDateFormat("[yyyy-mm-dd hh:mm:ss:SSS]"); }};public void beforeAdvice(JoinPoint joinPoint){
        Object[] args = joinPoint.getArgs();
        String methodName = joinPoint.getSignature().getName();
        System.out.println(String.format("Pre-notification: beforeAdvice, parameter: %s", Arrays.toString(args)));
        System.out.println(simpleDateFormat.get().format(new Date()) + methodName);
    }

    public void afterReturningAdvice(Object returnVal){
        System.out.println(String.format("AfterReturningAdvice, return parameter: %s", returnVal));
    }

    public void afterThrowingAdvice(Throwable e) {
        System.out.println(String.format("Exception notification: afterThrowingAdvice, exception message: %s", e));
    }

    public void afterAdvice(a) {
        System.out.println(String.format("Final notice: afterAdvice"));
    }

    public Object aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) {
        Object rtValue = null;
        try {
            System.out.println("AroundAdvice pre-notification!");
            // Get parameters
            Object[] args = proceedingJoinPoint.getArgs();
            // Execute the pointcut method
            rtValue = proceedingJoinPoint.proceed(args);

            System.out.println("AroundAdvice post notification!);
        } catch (Throwable e) {
            System.out.println("AroundAdvice abnormal notification!");
            e.printStackTrace();
        } finally {
            System.out.println("AroundAdvice last!);
        }
        returnrtValue; }}Copy the code

Spring_schemaBased_config. XML:


      
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

    <! -- Target Object -->
    <bean id="userService" class="com.example.spring.aop.service.impl.UserServiceImpl"></bean>
    <! -- SchemaBased Aspect-->
    <bean id="LoggingAspect" class="com.example.spring.aop.aspect.SchemaBasedAspect"></bean>

    <aop:aspectj-autoproxy/>

    <! Start aop configuration -->
    <aop:config>
        <aop:pointcut id="executionPointcut" expression="execution(* com.example.spring.aop.service.UserService.*(..) )" />
        <! -- Configuration section -->
        <aop:aspect id="logAspect" ref="LoggingAspect">
            <! -- Configure pre-notification -->
            <aop:before method="beforeAdvice"
                        pointcut-ref="executionPointcut"></aop:before>
            <! -- Configure post-notification -->
            <aop:after-returning method="afterReturningAdvice"
                                 pointcut-ref="executionPointcut" returning="returnVal"></aop:after-returning>
            <! -- Configure exception notification -->
            <aop:after-throwing method="afterThrowingAdvice" pointcut-ref="executionPointcut" throwing="e"
            ></aop:after-throwing>
            <! -- Configure last notification -->
            <aop:after method="afterAdvice" pointcut-ref="executionPointcut"></aop:after>
            <! -- Configure surround notification -->
            <aop:around method="aroundAdvice" pointcut-ref="executionPointcut"></aop:around>
        </aop:aspect>
    </aop:config>

</beans>
Copy the code

TestApplication. Java:

package com.example.spring.aop;

import com.example.spring.aop.bean.User;
import com.example.spring.aop.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestApplication {

    public static void testSchemaBasedAop(a){
        ApplicationContext ioc = new ClassPathXmlApplicationContext("classpath:spring_schemaBased_config.xml");
        UserService userService = (UserService) ioc.getBean("userService");
        User userDto = new User();
        userDto.setUsername("tom");
        userDto.setPassword("11");
        userService.addUser(userDto);
        System.out.println(String.format("User data printing :%s",userService.getUser().toString()));
    }

    public static void main(String[] args) {
        // schema Based configtestSchemaBasedAop(); }}Copy the code

Examples of the code for this article can be found at Github: Link

The appendix reference

Good blog references:

  • www.baeldung.com/spring-aop
  • www.baeldung.com/spring-perf…
  • www.javadoop.com/post/spring…