A blog to understand the principles of AOP

The underlying principle of AOP

What is AOP?

  • AOP—Aspect Oriented Programming Aspect Oriented Programming.
  • AOP adopts a horizontal extraction mechanism to replace the repetitive code (performance monitoring, transaction management, security check, cache) of the traditional vertical inheritance system.
  • After Spring AOP is implemented in pure Java, no special compilation project and class loader are required, and the code is enhanced to the target class organization through proxy mode during runtime.
  • AspectJ is an AOP framework based on the Java language. Starting from Spring 2.0, Spring AOP introduces support for Aspect. AspectJ extends the Java language and provides a special compiler that provides horizontal code organization during compilation.

The underlying principle of AOP

AOP is the proxy mechanism, dynamic proxy: (used in JDK) JDK dynamic proxy, which generates proxies for classes that implement interfaces.

AOP proxy in Spring: one is JDK dynamic proxy, and the other is CGLib proxy mechanism (generating proxy for classes).

AOP related terms:

  • Joinpoint (connection point): The so-called connection point refers to those points that can be intercepted. In Spring, these points refer to methods, since Spring only supports method-type join points. That is, join points refer to those hairs that can be intercepted.
  • Pointcut (entry point): The so-called entry point refers to which Joinpoints we want to intercept.
  • Advice (notification/enhancement): The so-called notification means that the thing to do after intercepting the Joinpoint is to notify. Notifications are divided into pre-notifications, post-notifications, exception notifications, final notifications, and surround notifications, that is, the enhancement is the function to be completed by the aspect (it is a method-level enhancement).
  • Introduction (introduction): Introduction is a special notification. On the premise of not modifying the class code, Introduction can dynamically add some methods or Field (variables) to the class during runtime. It is a class-level enhancement that adds an attribute or method to the original class.
  • Target (target object): The target object of the agent, that is, the object to be enhanced.
  • Weaving (implantation): It is the process of applying enhancements to target objects to create new proxy objects. Spring uses dynamic proxy implantation, while AspectJ uses compile-time implantation and class loading-time implantation.
  • Proxy (proxy): After a class is enhanced by AOP implantation, a resulting proxy class is generated.
  • Aspect (aspect): It is a combination of entry point and notification (introduction). Multiple pointcut and advice combinations are allowed.

image-20220906000217103

The underlying implementation of AOP

JDK1.3 introduces dynamic proxy technology and writes dynamic proxy programs (java.lang.reflect.Proxy and java.lang.reflect.InvocationHandler), as shown in the figure:

Generate dynamic proxy using JDK

UserDao

/**
 * DAO的接口
 *
 */
public interface UserDao {
    
    
    public void add();
    public void update();
}

UserDaoImpl

public class UserDaoImpl implements UserDao {
    
    
    @Override
    public void add() {
    
    
        System.out.println("添加用户");
    }
    @Override
    public void update() {
    
    
        System.out.println("修改用户");
    }
}

JDKProxy

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
 * JDK的动态代理机制
 * 
 * @author liuxun
 *
 */
public class JDKProxy implements InvocationHandler {
    
    
    private UserDao userDao;
    public JDKProxy(UserDao userDao) {
    
    
        super();
        this.userDao = userDao;
    }
    public UserDao createProxy() {
    
    
        UserDao proxy = (UserDao) Proxy.newProxyInstance(userDao.getClass().getClassLoader(),
                userDao.getClass().getInterfaces(), this);
        return proxy;
    }
    @Override
    // 调用目标对象的任何一个方法都相当于invoke()
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    
        if ("add".equals(method.getName())) {
    
    
            // 记录日志
            System.out.println("日志记录===================");
            Object result = method.invoke(userDao, args);
            return result;
        }
        return method.invoke(userDao, args);
    }
}

SpringTest1

import org.junit.Test;
public class SpringTest1 {
    
    
    @Test
    public void demo1() {
    
    
        UserDao userDao = new UserDaoImpl();
        userDao.add();
        userDao.update();
    }
    @Test
    public void demo2() {
    
    
        // 被代理对象
        UserDao userDao = new UserDaoImpl();
        // 创建代理对象的时候传入被代理对象
        UserDao proxy = new JDKProxy(userDao).createProxy();
        proxy.add();
        proxy.update();
    }
}

operation result

Proxy generation using CGLIB

For business classes that do not use interfaces, JDK dynamic proxies cannot be used. Cglib technology can be used. Cglib uses very low-level bytecode technology, which can create subclasses for a class to solve the problem of no-interface proxy.

CGLIB (code Genaration Library) is a powerful, high-quality, high-performance Code generation library, which can extend Java classes and implement interfaces at runtime. Hibernate supports it to realize the dynamic generation of PO (Persistent Object persistent object) bytecode, and Hibernate generates Javaassist for persistent classes. In fact, the essence of the CGLIB generation proxy mechanism is to generate a subclass of the real object.

If you want to use CGLIB technology to generate a proxy, you need to download the CGLIB jar package, but CGLIB has been integrated in Spring's core package Core, so you don't need to directly reference the CGLIB package.

image-20220906000526920

The specific code is as follows:

ProductDao

public class ProductDao {
    
    
    public void add() {
    
    
        System.out.println("添加商品...");
    }
    public void update() {
    
    
        System.out.println("修改商品...");
    }
}

CGLibProxy

import java.lang.reflect.Method;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
/**
 * 使用CGLib生成代理对象
 * 
 */
public class CGLibProxy implements MethodInterceptor {
    
    
    private ProductDao productDao;
    public CGLibProxy(ProductDao productDao) {
    
    
        super();
        this.productDao = productDao;
    }
    public ProductDao createProxy() {
    
    
        // 使用CGLIB生成代理
        // 1.创建核心类
        Enhancer enhancer = new Enhancer();
        // 2.为其设置父类
        enhancer.setSuperclass(productDao.getClass());
        // 3.设置回调
        enhancer.setCallback(this);
        // 4.创建代理
        return (ProductDao) enhancer.create();
    }
    @Override
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
    
    
        if ("add".equals(method.getName())) {
    
    
            System.out.println("日志记录=================");
            // Object obj=method.invoke(productDao, args);
            Object obj = methodProxy.invokeSuper(proxy, args);// 代理方法中执行代理对象父类中的方法
            return obj;
        }
        return methodProxy.invokeSuper(proxy, args);
    }
}

SpringTest2

import org.junit.Test;
public class SpringTest2 {
    
    
    @Test
    public void demo1() {
    
    
        ProductDao productDao = new ProductDao();
        productDao.add();
        productDao.update();
    }
    @Test
    public void demo2() {
    
    
        ProductDao productDao = new ProductDao();
        ProductDao proxy=new CGLibProxy(productDao).createProxy();
        proxy.add();
        proxy.update();
    }
}

operation result

The parameters of the intercept interception method implemented when CGLIB generates a proxy are as follows:

  • @param obj Proxy object generated by CGlib according to the specified parent class
  • @param method intercepted method
  • @param args The parameter array of the intercept method
  • The proxy object of @param proxy method, which is used to execute the method of the parent class

Note: The latest version of Spring has introduced the CGLib development class into spring-core-3.2.0.RELEASE.ja

Summary of agent knowledge

During runtime, Spring generates dynamic proxy objects and does not require a special compiler.

The bottom layer of Spring AOP is to perform horizontal implantation for target beans through JDK dynamic proxy or CGLib dynamic proxy technology. The selection method is as follows:

  • If the target object implements several interfaces, spring uses JDK's java.lang.reflect.Proxy class proxy.
  • If the target object does not implement any interface, spring uses the CGLIB library to generate a subclass of the target object.

Proxies should be created for interfaces first in the program to facilitate decoupling of the program for maintenance.

Methods marked as final cannot be proxied because they cannot be overridden for the following reasons:

  • JDK dynamic proxy is to generate subclasses for interfaces, and methods in interfaces cannot be modified with final.
  • CGLib generates subclasses for the target class, so classes or methods cannot be modified with final.

Spring only supports method join points and does not provide property joins.

AOP in Spring

Spring traditional AOP (enhanced type)

AOP is not defined by spring, but by the AOP Alliance.

The AOP Alliance defines org.aopalliance.aop.Interface.Advice for Advice

Spring can be divided into 5 categories according to the connection point position of Advice in the target class method:

  • The pre-advice org.springframework.aop.MethodBeforeAdvice is enhanced before the execution of the target method.
  • The after advice org.springframework.aop.AfterReturningAdvice implements the enhancement after the target method has successfully executed. (After is executed before and after the method is executed)
  • Surround advises org.aopalliance.intercept.MethodInterceptor to implement enhancements before and after target method execution.
  • Exception Throw Advice org.springframework.aop.ThrowsAdvice implements enhancements after a method throws an exception.
  • The introduction informs org.springframework.aop.IntroductionInterceptor to add some new methods or properties in the target class.

Spring AOP aspect types

  • Advisor: For traditional aspects in Spring, Advisor is composed of one cut point and one notification, while Aspect is composed of multiple cut points and multiple notifications. Advisor represents a general aspect, and Advice itself is an aspect that intercepts all methods of the target class (an aspect without a cut point intercepts all methods)
  • PointcutAdvisor: Represents an aspect with a cut point, which can specify which methods of the target class to intercept.
  • IntroductionAdvisor: Represents the introduction aspect, which is used for the introduction notification (not used in spring).

AOP development in Spring

Advisor aspect case—advance notification

Enhancements for all methods: facets without cut points, the development steps are as follows:

Step 1: Introduce spring aop related jar packages

  • spring-aop-3.2.0.RELEASE.jar
  • com.springsource.org.aopalliance-1.0.0.jar (from the AOP Alliance in the dependency package)

Step 2: Write the proxy object (interface and implementation class)

Step 3: Write enhanced code (write enhanced class to implement enhanced type interface)

Step 4: Generate Proxy (Configure Generate Proxy)

<bean id="helloAdvice" class="cn.test.springaop.HelloServiceBeforeAdvice"></bean>
<bean id="target" class="cn.test.springaop.HelloService" />
<bean id="helloService" class="org.springframework.aop.framework.ProxyFactoryBean" >
    <property name="proxyInterfaces" value="cn.test.springaop.IHelloService" />
    <!--如果不是针对接口代理,可以设置  <property name="proxyTargetClass" value="true"></property> ,将使用CGLib-->
    <property name="interceptorNames" value="helloAdvice"></property>
    <property name="target" ref="target"></property>
</bean>

The proxy generated by spring is based on the ProxyFactoryBean class, and the bottom layer automatically chooses whether to use the dynamic proxy of JDK or the proxy of CGLIB.

  • The commonly used configurable properties of ProxyFactoryBean are as follows:

  • target: the target object of the proxy

  • proxyInterfaces: The interface to be implemented by the proxy. The value is the full path of the interface. If the proxy implements multiple interfaces, you can use the following methods to assign values:

  • proxyTargetClass: Whether to proxy the class instead of the interface, when set to true, use the CGLib proxy.

  • interceptorNames: Advice names that need to be woven into the target.

  • singleton: Returns whether the proxy is a single instance, the default is a singleton.

  • optimize: When set to true, CGLib is forced to be used.

The sample code is as follows:

CustomerDao

public interface CustomerDao {
    
    
    public void add();
    public void update();
    public void delete();
    public void find();
}

CustomerDaoImpl

public class CustomerDaoImpl implements CustomerDao {
    
    
    public void add() {
    
    
        System.out.println("添加客户");
    }
    public void update() {
    
    
        System.out.println("修改客户");
    }
    public void delete() {
    
    
        System.out.println("删除客户");
    }
    public void find() {
    
    
        System.out.println("查询客户");
    }
}

Define the enhancement type MyBeforeAdvice

/**
 * 前置增强
 */
public class MyBeforeAdvice implements MethodBeforeAdvice {
    
    
    /**
     * method:执行的方法
     * args:参数
     * target:目标对象
     */
    public void before(Method method, Object[] args, Object target) throws Throwable {
    
    
        System.out.println("前置增强...");
    }
}

Configure enhanced applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<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">
    <!-- 不带有切点的切面 -->
    <!-- 定义目标对象 -->
    <bean id="customerDao" class="spring3.aop.demo3.CustomerDaoImpl" />
    <!-- 定义增强 -->
    <bean id="beforeAdvice" class="spring3.aop.demo3.MyBeforeAdvice" />
    <!-- spring支持配置生成代理 -->
    <bean id="customerDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!-- 设置目标对象 -->
        <property name="target" ref="customerDao" />
        <!-- 设置实现的接口,value中写接口的全路径 -->
        <property name="proxyInterfaces" value="spring3.aop.demo3.CustomerDao" />
        <!-- 需要使用value: 需要实现增强代码类的名称 -->
        <property name="interceptorNames" value="beforeAdvice" />
        <!-- 强制使用CGLIB代理 -->
        <property name="optimize" value="true" />
    </bean>
</beans>

SpringTest3

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class SpringTest3 {
    
    
    @Autowired
    // @Qualifier("customerDao") // 需要注入真实的对象,必须注入代理对象
    @Qualifier("customerDaoProxy")
    private CustomerDao customerDao;
    @Test
    public void demo1() {
    
    
        customerDao.add();
        customerDao.update();
        customerDao.delete();
        customerDao.find();
    }
}

Break point debugging to view the type of CustomerDao

image-20220906002541767

It is found that although the interface is implemented, the dynamic proxy object generated by CGLIB is because the optimize attribute is specified as true when defining the properties of ProxyFactoryBean in XML, that is, the dynamic proxy object is forced to be generated using CGLIB. If not set, Spring will automatically choose the way to generate dynamic proxy objects. Remove this attribute, and the breakpoint debugging view is as follows:

image-20220906002554748

Because proxy objects are generated for interfaces, Spring will give priority to using JDK to generate dynamic proxy objects.

Note: Due to the JDK version, the dynamic agent of JDK will report an error when using JDK1.5. When using JDK1.8, although JDK dynamic proxy can be used, @RunWith and @ContextConfiguration will throw an illegal parameter exception, so it is best to choose JDK1.7

PointcutAdvisor pointcut and aspect case—surrounding notification

For an Advisor without a cut point, its Advice enhancement itself is an aspect that enhances all methods of the target class by default. Therefore, in the above case, the interceptorNames attribute configured with the pointcut aspect name is the Advice configured by itself. The so-called aspect with a cut point is to enhance certain methods specified in the target class. It is precisely because using ordinary Advice as an aspect will intercept all methods of the target class, which is not flexible enough, so in actual development, an aspect with a cut point is often used.

Commonly used PointcutAdvisor implementation classes are as follows:

  • DefaultPointcutAdvisor is the most commonly used facet type, which can define a facet through any combination of Pointcut and Advice.
  • RegexpMethodPointcutAdvisor Constructs a regular expression to define a pointcut aspect.

The sample code is as follows:

OrderDao (proxy target class)

/**
 * 目标对象
 *
 */
public class OrderDao {
    
    
    public void add() {
    
    
        System.out.println("添加订单");
    }
    public void update() {
    
    
        System.out.println("修改订单");
    }
    public void delete() {
    
    
        System.out.println("删除订单");
    }
    public void find() {
    
    
        System.out.println("查询订单");
    }
}

MyAroundAdvice (Advice notification enhancement class)

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
/**
 * 增强的类
 * 使用的是环绕增强
 * 注意:对于增强类型的接口可以使用AOP联盟的也可以使用Spring扩展后自带的
 */
public class MyAroundAdvice implements MethodInterceptor {
    
    
    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
    
    
        System.out.println("环绕前增强...");
        Object result=methodInvocation.proceed(); // 指定目标对象的方法
        System.out.println("环绕后增强...");
        return result;
    }
}

applicationContext.xml (for configuration)

<?xml version="1.0" encoding="UTF-8"?>
<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">
    <!-- 带有切点的切面 -->
    <!-- 定义目标对象 -->
    <bean id="orderDao" class="spring3.aop.demo4.OrderDao" />
    <!-- 定义增强 -->
    <bean id="aroundAdvice" class="spring3.aop.demo4.MyAroundAdvice" />
    <!-- 定义切点切面 -->
    <bean id="myPointcutAdvisor"
        class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
        <!-- 定义表达式,规定哪些方法执行拦截 -->
        <!-- . 任意字符 * 任意个 .* 任意个任意字符 -->
        <!-- 对所有方法进行拦截 -->
        <!-- <property name="pattern" value=".*"/> -->
        <!-- 拦截指定类中以add开头的所有方法 -->
        <!-- <property name="pattern" value="spring3\.aop\.demo4\.OrderDao\.add.*"/> -->
        <!-- 拦截方法名包含add的方法 -->
        <!-- <property name="pattern" value=".*add.*"/> -->
        <!-- 多种格式的拦截:拦截方法名中包含add或find的方法 -->
        <property name="patterns" value=".*add.*,.*find.*" />
        <!-- 应用增强 -->
        <property name="advice" ref="aroundAdvice" />
    </bean>
    <!-- 定义生成代理对象 -->
    <bean id="orderDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!-- 配置目标 -->
        <property name="target" ref="orderDao" />
        <!-- 针对类代理 -->
        <property name="proxyTargetClass" value="true" />
        <!-- 在目标应用上增强 -->
        <property name="interceptorNames" value="myPointcutAdvisor" />
    </bean>
</beans>

The result of the operation is as follows:

automatic proxy

In the previous case, each proxy is woven into the aspect proxy through ProxyFactoryBean. In actual development, there are a lot of beans. If each proxy is configured with ProxyFactoryBean, it will cause a very large amount of development and maintenance.

Solution: automatic proxy (based on post-processing beans, enhancements done during bean creation, generated beans are proxies)

There are several ways to implement automatic proxy:

  • BeanAutoProxyCreator creates a proxy based on the Bean name.
  • DefaultAdvisorAutoProxyCreator creates proxies based on information contained in the Advisor itself.
  • AnnotionAwareAspectJAutoProxyCreator performs automatic proxying based on AspectJ annotations in beans.

BeanNameAutoProxyCreator: Generate a proxy by name.

The sample code is as follows:

applicationContext2.xml automatic proxy configuration

<?xml version="1.0" encoding="UTF-8"?>
<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">
    <!-- 定义目标对象 -->
    <bean id="customerDao" class="spring3.aop.demo3.CustomerDaoImpl" />
    <bean id="orderDao" class="spring3.aop.demo4.OrderDao" />
    <!-- 定义增强 -->
    <bean id="beforeAdvice" class="spring3.aop.demo3.MyBeforeAdvice" />
    <bean id="aroundAdvice" class="spring3.aop.demo4.MyAroundAdvice" />
    <!-- 自动代理:按照Bean名称的代理 基于后处理Bean,后处理Bean不需要配置ID -->
    <bean
        class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
        <!-- 对bean名称以Dao结尾的进行自动代理 -->
        <property name="beanNames" value="*Dao" />
        <!-- 定义通知类型 也可以指定多个 -->
        <property name="interceptorNames" value="beforeAdvice" />
    </bean>
</beans>

SpringTest5 new test class

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import spring3.aop.demo3.CustomerDao;
import spring3.aop.demo4.OrderDao;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext2.xml")
public class SpringTest5 {
    
    
    @Autowired
    @Qualifier("orderDao")
    private OrderDao OrderDao;
    @Autowired
    @Qualifier("customerDao")
    private CustomerDao CustomerDao;
    @Test
    public void demo1() {
    
    
        OrderDao.add();
        OrderDao.delete();
        CustomerDao.update();
    }
}

DefaultAdvisorAutoProxyCreator: Generate a proxy based on the information defined in the aspect

applicationContext3.xml configures automatic proxy based on aspect information

<?xml version="1.0" encoding="UTF-8"?>
<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">
    <!-- 定义目标对象 -->
    <bean id="customerDao" class="spring3.aop.demo3.CustomerDaoImpl" />
    <bean id="orderDao" class="spring3.aop.demo4.OrderDao" />
    <!-- 定义增强 -->
    <bean id="beforeAdvice" class="spring3.aop.demo3.MyBeforeAdvice" />
    <bean id="aroundAdvice" class="spring3.aop.demo4.MyAroundAdvice" />
    <!-- 定义一个带有切点的切面 -->
    <bean id="mypointcutAdvisor"
        class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
        <property name="pattern" value=".*add.*" />
        <property name="advice" ref="aroundAdvice" />
    </bean>
    <!-- 根据切面信息自动生成代理 -->
    <bean
        class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" />
</beans>

SpringTest6 test class

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import spring3.aop.demo3.CustomerDao;
import spring3.aop.demo4.OrderDao;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext3.xml")
public class SpringTest6 {
    
    
    @Autowired
    @Qualifier("orderDao")
    private OrderDao orderDao;
    @Autowired
    @Qualifier("customerDao")
    private CustomerDao customerDao;
    @Test
    public void demo1() {
    
    
        orderDao.add();
        orderDao.update();
        orderDao.delete();
        customerDao.add();
    }
}

Note: Distinguish between ProxyFactoryBean-based proxy and automatic proxy

ProxyFactoryBean must first have a proxied object, and the proxied object is passed into the proxy class to generate a proxy, while the automatic proxy is based on the post-processing Bean. During the bean generation process, a proxy object is generated, and the proxy object is returned. is the proxy object.

AOP(*****) of AspectJ in Spring

AspectJ is an aspect-oriented framework that extends the Java language. AspectJ defines the AOP grammar so it has a special compiler used to comply with the Class file of the Java byte encoding specification.

AspectJ is an AOP framework based on the Java language.

After Spring 2.0, support for AspectJ pointcut expressions has been added.

@AspectJ is a new feature of AspectJ1.5. Through JDK5 annotation technology, it allows to define aspects directly in the Bean class.

In the new version of the Spring framework, it is recommended to use the AspectJ method to develop AOP.

To use AspectJ, you need to import Spring AOP and AspectJ-related jar packages.

  • spring-aop-3.2.0.RELEASE.jar
  • com.springsource.org.aopalliance-1.0.0.jar
  • spring-aspects-3.2.0.RELEASE.jar
  • com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar

Enable @AspectJ through configuration: that is, introduce AOP constraints in the configuration file (also search for xsd-config.xml) and paste it: then enable AspectJ automatic proxy

image-20220906005155247

<?xml version="1.0" encoding="UTF-8"?>
<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
    http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!-- 开启AspectJ自动代理-->    
    <aop:aspectj-autoproxy />
</beans>

AspectJ expressions

Define the pointcut through the value attribute in the advice

Through the execution function, you can define the method of point-cutting.

Syntax: execution(<access modifier>?<return type><method name>(parameter)<exception>) where? Indicates that it can be omitted

For example:

  • Match all methods execution(public * *(...)) of all classes ... any number of parameters of any type
  • Match all methods of all classes under the specified package execution(* aspectj.test.dao.*(…)) excluding subpackages
  • execution(* aspectj.test.dao… (…)) … represents all classes under the current package and its descendants
  • Match all methods of the specified class execution(* aspectj.test.service.UserService.*(…))
  • Matches all class methods that implement a specific interface execution(* aspectj.test.dao.GenericDao+.*(…))
  • Matches all save methods execution(* save*(…))

AspectJ annotation-based implementation

Advice types for AspectJ, @AspectJ provides different advice types

  • @Before pre-advice, equivalent to BeforeAdvice
  • @AfterReturning post notification, equivalent to AfterReturningAdvice
  • @Around around advice, equivalent to MethodInterceptor
  • @AfterThrowing throws notifications, equivalent to ThrowAdvice
  • @After final final notification, regardless of whether there is an exception, the notification will be executed
  • @DeclareParents introduction notification, equivalent to IntroductionInterceptor (rarely used)

@Before pre-notification, you can pass in the JointPoint object in the corresponding method to obtain the point-cutting information.

@AfterReturning In addition to passing the JointPoint object in the method, the post-notification can also define the return value of the method as a parameter through the returning attribute. The parameter type is generally Object

@Around surround notification, the return value of the around method is the return value of the execution of the target proxy method, and the parameter is ProceedingJoinPoint, which can call the interception target method to execute. Pay special attention: if the proceed method of ProceedingJoinPoint is not called, the target method will be intercepted.

@AfterThrowing Throwing notification By setting the throwing attribute, you can set the exception object parameter Throwable

Name the pointcut by @Pointcut

  • Defining pointcuts in each notification will result in a large workload and is not easy to maintain. For repeated pointcuts, you can use @Pointcut to define them.
  • Pointcut method: private void method without parameters, the method name is the pointcut name.
  • When advising multiple pointcuts, you can use || to connect

The new project first imports several necessary packages in Spring, bean, context, core, expression, log related commons.loggoing, log4j, then imports Spring AOP packages (aop, aop alliance packages (in dependent packages)) and finally imports Aspect packages and dependent packages.

Create a new configuration file, introduce AOP constraints, configure automatic proxy, and finally code to achieve.

The sample code is as follows:

Write the enhanced class UserDao

public class UserDao {
    
    
    public void add(){
    
    
        System.out.println("添加用户");
    }
    public int update(){
    
    
        System.out.println("修改用户");
        return 1;
    }
    public void delete(){
    
    
        System.out.println("删除用户");
        int d = 1/ 0;
    }
    public void find(){
    
    
        System.out.println("查询用户");
    }
}

MyAspect annotation implements aspect of AspectJ

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
/**
 * 
 * 切面类:就是切点和增强的组合
 *
 */
@Aspect
public class MyAspect {
    
    
    @Before("execution(* aop.aspectj.demo1.UserDao.add(..))")
    public void before(JoinPoint joinPoint) {
    
    
        System.out.println("前置增强..." + joinPoint);
    }
    @AfterReturning(value = "execution(* aop.aspectj.demo1.UserDao.update(..))", returning = "returnVal")
    public void afterReturning(Object returnVal) {
    
    
        System.out.println("后置增强...方法的返回值" + returnVal);
    }
    @AfterThrowing(value = "MyAspect.myPointcut1()", throwing = "e")
    public void afterThrowing(Throwable e) {
    
    
        System.out.println("警告:出现异常了!!! " + e.getMessage());
    }
    @After("MyAspect.myPointcut1()||MyAspect.myPointcut2()")
    public void after(){
    
    
        System.out.println("最终通知...");
    }
    @Pointcut("execution(* aop.aspectj.demo1.UserDao.delete(..))")
    public void myPointcut1() {
    
    
    }
    @Pointcut("execution(* aop.aspectj.demo1.UserDao.find(..))")
    public void myPointcut2() {
    
    
    }
}

applicationContext.xml introduces AOP constraints and configures automatic proxy

<?xml version="1.0" encoding="UTF-8"?>
<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 http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!-- 自动生成代理 底层就是AnnotationAwareAspectJAutoProxyCreator -->
    <aop:aspectj-autoproxy />
    <bean id="userDao" class="aop.aspectj.demo1.UserDao" />
    <bean id="myAspect" class="aop.aspectj.demo1.MyAspect" />
</beans>

Write a test class SpringTest1

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class SpringTest1 {
    
    
    @Autowired
    @Qualifier("userDao")
    private UserDao userDao;
    @Test
    public void demo1() {
    
    
        userDao.add();
        userDao.update();
        userDao.find();
        userDao.delete();
    }
}

AspectJ implementation based on XML

ProductDao

public class ProductDao {
    
    
    public int add() {
    
    
        System.out.println("添加商品...");
        int d = 10 / 0;
        return 100;
    }
    public void update() {
    
    
        System.out.println("修改商品...");
    }
    public void delete() {
    
    
        System.out.println("删除商品...");
    }
    public void find() {
    
    
        System.out.println("查询商品...");
    }
}

MyAspectXML aspect class

import org.aspectj.lang.ProceedingJoinPoint;
/**
 * 切面类
 */
public class MyAspectXML {
    
    
    public void before() {
    
    
        System.out.println("前置通知...");
    }
    public void afterReturning(Object returnVal) {
    
    
        System.out.println("后置通知...返回值:" + returnVal);
    }
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
    
    
        System.out.println("环绕前增强...");
        Object result = proceedingJoinPoint.proceed();
        System.out.println("环绕后增强...");
        return result;
    }
    public void afterThrowing(Throwable e) {
    
    
        System.out.println("异常通知..." + e.getMessage());
    }
    public void after() {
    
    
        System.out.println("最终通知...");
    }
}

applicationContext2.xml configuration aspect class

<?xml version="1.0" encoding="UTF-8"?>
<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 http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!-- 定义被增强的类 -->
    <bean id="productDao" class="aop.aspectj.demo2.ProductDao" />
    <!-- 定义切面 -->
    <bean id="myAspectXML" class="aop.aspectj.demo2.MyAspectXML" />
    <!-- 定义AOP配置 -->
    <aop:config>
        <!-- 定义切点 -->
        <aop:pointcut expression="execution(* aop.aspectj.demo2.ProductDao.add(..))" id="myPointcut"/>
        <aop:pointcut expression="execution(* aop.aspectj.demo2.ProductDao.delete(..))" id="myPointcut2"/>
        <!-- 配置Aspect增强类 -->
        <aop:aspect ref="myAspectXML">
        <!-- 前置通知 -->
        <aop:before method="before" pointcut="execution(* aop.aspectj.demo2.ProductDao.update(..))" />
        <!-- 后置通知 -->
        <aop:after-returning method="afterReturning" pointcut-ref="myPointcut" returning="returnVal"/>
        <!-- 环绕通知 -->
        <aop:around method="around" pointcut-ref="myPointcut2"/>
        <!-- 异常通知 -->
        <aop:after-throwing method="afterThrowing" pointcut-ref="myPointcut" throwing="e"/>
        <!-- 最终通知 -->
        <aop:after method="after" pointcut-ref="myPointcut"/>
        </aop:aspect>
    </aop:config>
</beans>

SpringTest2 test class

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext2.xml")
public class SpringTest2 {
    
    
    @Autowired
    @Qualifier("productDao")
    private ProductDao productDao;
    @Test
    public void demo1() {
    
    
        productDao.update();
        productDao.delete();
        productDao.find();
        productDao.add();
    }
}

What is the difference between Spring AOP and AspectJ AOP?

Spring AOP is a runtime enhancement, while AspectJ is a compile-time enhancement. Spring AOP is based on proxying (Proxying), while AspectJ is based on bytecode manipulation (Bytecode Manipulation).

Spring AOP has integrated AspectJ, and AspectJ should be regarded as the most complete AOP framework in the Java ecosystem. AspectJ is more powerful than Spring AOP, but Spring AOP is relatively simpler,

If we have fewer aspects, then the performance difference between the two is not big. However, when there are too many aspects, it is best to choose AspectJ, which is much faster than Spring AOP.

Guess you like

Origin blog.csdn.net/m0_61820867/article/details/126927401