Developing Spring AOP with annotations

Posted by Sulman on Thu, 27 Jan 2022 11:10:15 +0100

Concept and understanding of AOP: Understanding aspect oriented programming (AOP) by drawing

AOP terminology and XML configuration quick start

(1) Create target interface and implementation class

package com.ssm.aop.service;

import com.ssm.aop.game.Role;

public interface RoleService {
    public void printRole(Role role);
}
package com.ssm.aop.service.Impl;

import com.ssm.aop.game.Role;
import com.ssm.aop.service.RoleService;
import org.springframework.stereotype.Component;

@Component
public class RoleServiceImpl implements RoleService {

    @Override
    public void printRole(Role role) {
        System.out.println("{id: "+role.getId()+", "
        +"role_name:"+role.getRoleName()+", "
        +"note:"+role.getNote()+"}");
    }
}

(2) Create a facet class (a method to enhance the target class)

package com.ssm.aop.aspect;

import org.aspectj.lang.annotation.*;

@Aspect
public class RoleAspect {
    @Before("execution(* com.ssm.aop.service.Impl.RoleServiceImpl.printRole(..))")
    public void before()
    {
        System.out.println("before...");
    }
    @After("execution(* com.ssm.aop.service.Impl.RoleServiceImpl.printRole(..))")
    public void after()
    {
        System.out.println("after...");
    }
    @AfterReturning("execution(* com.ssm.aop.service.Impl.RoleServiceImpl.printRole(..))")
    public void afterReturning()
    {
        System.out.println("afterReturning...");
    }
    @AfterThrowing("execution(* com.ssm.aop.service.Impl.RoleServiceImpl.printRole(..))")
    public void afterThrowing()
    {
        System.out.println("afterThrowing...");
    }

}

Adding the @ Aspect annotation above the class indicates that the class is a faceted class

@Before: the method of adding the annotation indicates that the method is called before the method of the proxy object, that is, the pre notification.

@After: the method of adding the annotation indicates that the method is called after the method of the proxy object, that is, post notification.

@AfterReturning: the method of adding the annotation indicates that the method is called after the method of the proxy is returned normally.

@AfterThrowing: the method of adding the annotation indicates that the method is invoked after throwing an exception in the method of the proxy.

The configuration item in the parentheses of the annotation on the method (defining the regular expression of execution to match the corresponding tangent point) is used to define the tangent point:

Syntax of expression:

execution([modifier] return value type package name. Class name. Method name (parameter))

  • Access modifiers can be omitted
  • The return value type, package name, class name and method name can be represented by *
  • A point between the package name and the class name Represents the class under the current package, two points Represents the classes under the current package and its sub packages
  • The parameter list can use two points Represents any number and any type of parameter list.

Take the following expression as an example:

execution(* com.ssm.aop.service.Impl.RoleServiceImpl.printRole(..))

execution: triggered when the method is executed

*: represents a method of any return type

com.ssm.aop.service.Impl.RoleServiceImpl: represents the fully qualified name of the class

printRole: the name of the intercepted method

(..): Arbitrary parameters

In the above facet class, the definition of the Pointcut is repetitive code, which is troublesome. You can avoid this trouble by introducing @ Pointcut to define a Pointcut

Define @ Pointcut annotation above a method without any logic, and the configuration item is the expression of tangent point. When in use, just configure the method name without logic in the configuration item.

The improved code is as follows

package com.ssm.aop.aspect;

import org.aspectj.lang.annotation.*;

@Aspect
public class RoleAspect {

    @Pointcut("execution(* com.ssm.aop.service.Impl.RoleServiceImpl.printRole(..))")
    public void print()
    {
        
    }
    @Before("print()")
    public void before()
    {
        System.out.println("before...");
    }
    @After("print()")
    public void after()
    {
        System.out.println("after...");
    }
    @AfterReturning("print()")
    public void afterReturning()
    {
        System.out.println("afterReturning...");
    }
    @AfterThrowing("print()")
    public void afterThrowing()
    {
        System.out.println("afterThrowing...");
    }

}

(3) Disposition

To make the above annotation effective, you must first start AspectJ automatic proxy, so that Spring can generate dynamic proxy objects.

The configuration code is as follows:

package com.ssm.aop.config;

import com.ssm.aop.aspect.RoleAspect;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@EnableAspectJAutoProxy
@ComponentScan("com.ssm.aop")
public class AopConfig {
    @Bean
    public RoleAspect getRoleAspect()
    {
        return new RoleAspect();
    }
}

@EnableAspectJAutoProxy

 @Bean
    public RoleAspect getRoleAspect()
    {
        return new RoleAspect();
    }

This part of the code represents the automatic agent that starts the AspectJ framework

Another way to enable automatic proxy is through XML 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"
       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
">
    <aop:aspectj-autoproxy/>
    <bean id="roleAspect" class="com.ssm.aop.aspect.RoleAspect"/>
    <bean id="roleService" class="com.ssm.aop.service.Impl.RoleServiceImpl"/>

</beans>

You can see that the RoleAspect aspect class and RoleServiceImpl target class are added to the Ioc container as beans in XML, which is equivalent to @ Bean and @ Component in the annotation

< AOP: AspectJ AutoProxy > has the same function as the annotation @ EnableAspectJautoProxy, enabling the automatic proxy function.

(4) Testing

The test code is as follows:

import com.ssm.aop.config.AopConfig;
import com.ssm.aop.game.Role;
import com.ssm.aop.service.RoleService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Main {
    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(AopConfig.class);//Configure using config class
       // ApplicationContext ctx=new ClassPathXmlApplicationContext("application-config.xml");// Using XML configuration files
        RoleService roleService = (RoleService) ctx.getBean(RoleService.class);
        Role role = new Role();
        role.setId(1L);
        role.setRoleName("role_name_1");
        role.setNote("note_1");
        roleService.printRole(role);
        System.out.println("####################");
        //Test exception notification
        role = null;
        roleService.printRole(role);
    }
}

The operation results are shown in the figure below;

An empty object was deliberately created in the test code to trigger the exception notification.

In the absence of errors, you can see first calling the pre notification, then calling the printRole method, calling the notification returned successfully, and finally calling the post notification.

When an exception is thrown, the first notification is invoked and then an exception occurs when the pringRole method is invoked. Then the exception notification is sent to output afterThrowing and finally the post notification is invoked.

In other words, the post notification will be called whether the call to pringRole is successful or not.

Topics: Java Spring Back-end