AOP in Spring and AOP configuration based on XML and annotations

AOP in Spring and AOP configuration based on XML and annotations

Improve the account case
  • E:\JAVAworkspace\spring_account
Analyze the problems in the case
  • Each database operation should only obtain one connection connection and put these operations into one transaction to avoid dirty reading, non repeatable reading, phantom reading and other problems.

    • ConnectionUtils class: the tool class of Connection, which is used to obtain a Connection from the data source and implement the binding with the thread. Use the ThreadLocal object to bind the Connection to the current thread, so that there is only one object in a thread that can control transactions
    private ThreadLocal<Connection> tl = new ThreadLocal<>();
    private DataSource dataSource;
    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }
    
    /**
         * Gets the connection on the current thread
         * @return
         */
    public Connection getThreadConnection(){
        //1. First get from ThreadLocacl
        Connection conn = tl.get();
        try{
            //2. Judge whether there is a connection on the current thread
            if (conn == null){
                //3. Obtain a connection from the data source and store it in ThreadLocal
                conn = dataSource.getConnection();
                tl.set(conn);
            }
            //4. Return the connection on the current thread
            return conn;
        }catch (Exception e){
            throw new RuntimeException(e);
        }
    }
    
    /**
         * Unbind the connection from the thread
         */
    public void removeConnection(){
        tl.remove();
    }
    
    • TransactionManager class: a tool class related to transaction management. It includes: starting a transaction, committing a transaction, rolling back a transaction, and releasing a connection
    private ConnectionUtils connectionUtils;
    public void setConnectionUtils(ConnectionUtils connectionUtils) {
        this.connectionUtils = connectionUtils;
    }
    
    /**
         * Open transaction
         */
    public void beginTransaction(){
        try {
            connectionUtils.getThreadConnection().setAutoCommit(false);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    /**
         * Commit transaction
         */
    public void commit(){
        try {
            connectionUtils.getThreadConnection().commit();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    /**
         * Rollback transaction
         */
    public void rollback(){
        try {
            connectionUtils.getThreadConnection().rollback();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    /**
         * Release connection
         */
    public void release(){
        try {
            connectionUtils.getThreadConnection().close();//Return to connection pool
            connectionUtils.removeConnection();//Unbind the connection from the thread
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    
  • Define a ConnectionUtil object in AccountDaoImpl to prevent QueryRunner from bean from the configuration file Instead of taking connections in the data source injection of XML, ConnectionUtil is injected into the configuration file to ensure that all operations are in one transaction.

Review the previous technology: dynamic agent

  • Features: bytecode can be created with use and loaded with use

  • Function: enhance the method without modifying the source code

  • Classification:

    • Interface based dynamic agent

      • Class involved: Proxy

      • Provider: JDK official

      • Create: use the newProxyInstance method in the Proxy class

      • Requirements for creating proxy objects: the proxy object implements at least one interface. If it does not, it cannot be used

      • Parameters of newProxyInstance method:

        ClassLoader: class loader. The bytecode used to load the proxy object uses the same class loader and fixed writing method as the proxy object.

        Class []: bytecode array. Used to make proxy objects and proxied objects have the same method. Fixed writing.

        InvocationHandler: used to provide enhanced code. It allows us to write how to proxy. We usually write an implementation class of the interface. Usually, it is an anonymous internal class, but it is not necessary. Who writes the implementation classes of this interface.

      final Producer producer = new Producer();
      //Generate proxy object
      IProducer proxyProducer = (IProducer) Proxy.newProxyInstance(producer.getClass().getClassLoader(),
                      producer.getClass().getInterfaces(), new InvocationHandler() {
                          /**
                           * Function: any interface method that executes the proxy object will pass through this method
                           * Parameter meaning of method
                           * @param proxy Reference to proxy object
                           * @param method Currently executed method
                           * @param args Parameters required for the current execution method
                           * @return   Has the same return value as the proxy object method
                           * @throws Throwable
                           */
                          @Override
                          public Object invoke(Object proxy, Method method, Object[] 							args) throws Throwable {
                              Object returnValue = null;
                              //Provide enhanced code
                              //1. Get the parameters of method execution
                              Float money = (Float) args[0];
                              //2. Judge whether the current method is sales
                              if ("saleProduct" == method.getName()){
                                  returnValue = method.invoke(producer,money*0.8f);
                              }
                              return returnValue;
                          }
                      });
      proxyProducer.saleProduct(10000f);
      
    • Subclass based dynamic agent

      • Class involved: Enhancer

      • Provider: third party cglib Library

      • create: use the create method in the Enhancer class

      • Requirement: the proxied class cannot be the final class (final modified)

      • Parameters of create method:

        Class: bytecode. Bytecode used to specify the proxy object.

        Callback: used to provide enhanced code. We usually write the implementation class of the sub interface of the interface: MethondInterceptor (method interception)

      final Producer producer = new Producer();
      //Generate proxy object
      Producer cglibProducer = (Producer) Enhancer.create(producer.getClass(), new MethodInterceptor() {
                  /**
                   * Any method that specifies the proxied object passes through the method
                   * @param o
                   * @param method
                   * @param objects
                   *  The above three slave parameters are the same as those of the invoke method in the interface based dynamic proxy
                   * @param methodProxy Proxy object for the current execution method
                   * @return
                   * @throws Throwable
                   */
                  @Override
                  public Object intercept(Object o, Method method, Object[] objects, 					MethodProxy methodProxy) throws Throwable {
                      Object returnValue = null;
                      //Provide enhanced code
                      //1. Get the parameters of method execution
                      Float money = (Float) objects[0];
                      //2. Judge whether the current method is sales
                      if ("saleProduct" == method.getName()){
                          returnValue = method.invoke(producer,money*0.8f);
                      }
                      return returnValue;
                  }
              });
      cglibProducer.saleProduct(12000f);
      
Concept of AOP
  • AOP: Aspect Oriented Programming. It is a technology to realize the unified maintenance of program functions through precompile and runtime dynamic agent. AOP can isolate each part of business logic, reduce the coupling between each part of business logic, improve the reusability of program and improve the development efficiency.
  • Function: during the running of the program, the existing methods are enhanced without modifying the source code.
  • Advantages: reduce repeated code, improve development efficiency and convenient maintenance.
  • Implementation method: dynamic agent based on interface.
AOP related terms in spring
  • Joinpoint s: refers to the intercepted points. These points refer to methods, because spring only supports connection points of method type.

  • Pointcut: it refers to the definition of which joinpoints we want to intercept. (enhanced connection point)

  • Advice (notification / enhancement): refers to the things to be done after intercepting JoinPoint. The types of notifications: pre notification, post notification, exception notification, final notification, and surround notification.

  • Introduction: it is a special notice. Without modifying the class code, introduction can dynamically add some methods or fields for the class at run time.

  • Target: the target object of the agent (accountService)

  • Weaving: refers to the process of applying enhancements to the target object to create a new proxy object. spring uses dynamic proxy weaving.

  • Proxy: after a class is enhanced by AOP weaving, a resulting proxy class is generated.

  • Aspect: a combination of pointcuts and notifications (Introductions).

XML based AOP configuration in spring
  • Step 1: leave the notification bean to Spring to manage

  • Step 2: use the aop:config tag to indicate the start of AOP configuration

  • Step 3: use the aop:aspect tag to indicate the configuration aspect

    • id attribute: provides a unique identification for the cut plane
    • ref attribute: Specifies the id of the notification class bean
  • Step 4: use the tag inside the aop:aspect tag to configure the notification type

    • aop:before: indicates that pre notification is configured

      • Method: used to specify which method in the Logger class is pre notification

      • Pointcut attribute: used to specify the pointcut expression. The meaning of the expression refers to which methods in the business layer are enhanced.

        How to write the pointcut expression:

        Keyword: execution (expression)

        Expression: access modifier return value package name Package name Package name... Class name Method name (parameter list)

        Standard expression: public void com ssm. service. impl. AccountService. saveAccount()

        The access modifier can be omitted; The return value can use wildcards to represent any return value;

        The package name can use wildcards to represent any package. However, if there are several levels of packages, you need to write several *;

        Package name can use Represents the current package and its sub packages;

        Both class name and method name can be configured with *;

        Parameter list:

        Data types can be written directly: the basic type writes the name directly (int); the reference type writes the package name and class name (java.lang.String);

        Wildcards can be used to represent any type, but there must be parameters;

        Can use Indicates whether there are parameters or not. Parameters can be of any type

        All pass configuration method: * *.. ** (..)

        The usual writing method of pointcut expression in actual development: cut to all methods under the business layer implementation class:

        * com.ssm.service.impl.*.*(..)

    <?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">
        <!--to configure spring of ioc,hold service Object configuration-->
        <bean id="accountService" class="com.ssm.service.impl.AccountServiceImpl"></bean>
        <!--spring Medium based xml of AOP Configuration steps-->
        <!--to configure Logger class-->
        <bean id="logger" class="com.ssm.utils.Logger"></bean>
        <!--to configure Aop-->
        <aop:config>
            <!--Configure section-->
            <aop:aspect id="logAdvice" ref="logger">
                <!--Configure the type of notification and establish the association between notification methods and pointcut methods-->
                <aop:before method="printLog"
                        pointcut="execution(* com.ssm.service.impl.*.*(..))"></aop:before>
            </aop:aspect>
        </aop:config>
    </beans>
    
  • Notification type:

    <!--to configure Aop-->
    <aop:config>
        <aop:pointcut id="pt1" expression="execution(* com.ssm.service.impl.*.*(..))"></aop:pointcut>
        <!--Configure section-->
        <aop:aspect id="logAdvice" ref="logger">
            <!--Configure pre notification-->
            <aop:before method="beforePrintLog" pointcut-ref="pt1"></aop:before>
            <!--Configure post notification-->
            <aop:after-returning method="afterReturningPrintLog"
                                 pointcut-ref="pt1"></aop:after-returning>
            <!--Configure exception notification-->
            <aop:after-throwing method="afterThrowingPrintLog"
                                pointcut-ref="pt1"></aop:after-throwing>
            <!--Configure final notification-->
            <aop:after method="afterPrintLog" pointcut-ref="pt1"></aop:after>
            <!--Configure pointcut expression:
                        The label is written in aop:aspect The inside of the label can only be used in the current section,
                        He can also write in aop:aspect Outside the label, all sections are available
                -->
            <!--<aop:pointcut id="pt1" expression="execution(* com.ssm.service.impl.*.*(..))"></aop:pointcut>-->
        </aop:aspect>
    </aop:config>
    

    Surround notification:

    <!--Configure surround notifications-->
    <aop:around method="aroundPrintLog" pointcut-ref="pt1"></aop:around>
    
    • Problem: when we configure the wrap notification, the pointcut method is not executed, but the notification method is executed.
    • Analysis: compared with the surround notification code in the dynamic agent, the surround notification of the dynamic agent has a clear entry point method, but our code does not.
    • Solution: the spring framework provides us with an interface: ProceedJoinPoint. The interface has a method procedure (), which is equivalent to explicitly calling the pointcut method. The interface can be used as the method parameter surrounding the notification. During program execution, the spring framework will provide us with the implementation class of the interface for us to use.
    public Object aroundPrintLog(ProceedingJoinPoint pjp){
        Object rtValue;
        try{
            Object[] args = pjp.getArgs();//Get the parameters required for method execution
            System.out.println("Logger Class aroundPrintLog Method starts logging----Front");
            rtValue = pjp.proceed(args);//Explicitly invoke business layer methods (pointcut methods)
            System.out.println("Logger Class aroundPrintLog Method starts logging----Post");
            return rtValue;
        }catch (Throwable t){
            System.out.println("Logger Class aroundPrintLog Method starts logging----abnormal");
            throw new RuntimeException(t);
        }finally {
            System.out.println("Logger Class aroundPrintLog Method starts logging----final");
        }
    }
    

    Spring surround notification: it is a way for us to manually control when the enhanced method is executed in the code provided by the spring framework.

Annotation based AOP configuration in spring
  • bean.xml

    <!--to configure spring Packages to scan when creating containers-->
    <context:component-scan base-package="com.ssm"></context:component-scan>
    <!--to configure spring Open annotation AOP Support of-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    
  • Logger.java

    @Component("logger")
    @Aspect//Indicates that the current class is a faceted class
    
    @Pointcut("execution(* com.ssm.service.impl.*.*(..))")
    private void pt1(){}
    
    @Before("pt1()")
    @AfterReturning("pt1()")
    @AfterThrowing("pt1()")
    @After("pt1()")
    @Around("pt1()")
    
  • Configuration without XML (pure annotation)

    @Configuration
    @ComponentScan(basePackages = "com.ssm")//Configure the packages to be scanned when spring creates the container
    @Import(Logger.class)//Introduce other configuration classes
    @EnableAspectJAutoProxy//Configure spring to enable annotation AOP support
    public class SpringConfiguration {
    }
    

Keywords: Spring SSM

Added by kaszu on Wed, 29 Dec 2021 10:24:02 +0200