This is the 10th day of my participation in Gwen Challenge


The previous article talked about a lot of primitive things, it is cumbersome to use, through the Spring framework to provide annotations can be very convenient to achieve the aspect function.

AOP is implemented using annotations in Spring, and implemented in the @AspectJ way. You first determine the method you want to cut into, which is the join point

@Service public class UserServiceMethod { public void add(String name) { System.out.println("UserServiceMethod add name is:" + name); }}Copy the code

Development section

With join points, you also need facets to describe the weaving of the process by describing the AOP’s other information

@Aspect @Component public class LogAgent { @Before("execution(* com.niu.dao.UserServiceMethod.add(..) )") public void beforeAdd(JoinPoint joinPoint) { MethodSignature signature = (MethodSignature) joinPoint.getSignature();  Method method = signature.getMethod(); System.out.println("before method rule intercept: "+ method.getName()); } @After("execution(* com.niu.dao.UserServiceMethod.add(..) )") public void afterAdd(JoinPoint joinPoint) { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); System.out.println("after method rule intercept: "+ method.getName()); } @AfterReturning("execution(* com.niu.dao.UserServiceMethod.add(..) )") public void afterReturnAdd(JoinPoint joinPoint) { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); System.out.println("afterReturn method rule interceptor: "+ method.getName()); } @AfterThrowing("execution(* com.niu.dao.UserServiceMethod.add(..) )") public void afterThrowsAdd(JoinPoint joinPoint) { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); System.out.println("afterThrows method rule interception: "+ method.getName()); }}Copy the code

The annotation of execution (* com. Niu. Dao. UserServiceMethod. Add (..) ) is a regular match,

Execution refers to intercepting the regular matching method within it while it is being executed

* A method that represents any return type

Com. Niu. Dao. UserServiceMethod specify the target the fully qualified name of the object

Add Specifies the method of the target object

(..) Matches any parameter

AssertJ’s pointer to SpringAop pointcuts:

  • Args () qualifies join point method parameters
  • @args() via annotations on join point method arguments
  • Execution () is used to match execution methods at join points
  • This () restricts the join point to match the AOP proxy bean reference to the specified type
  • Target Target object
  • @target Limits the configuration of the target object
  • Within limits the join point to match the specified type
  • @within() qualifies the join point with a matching annotation type
  • @annotation() qualifies join points with specified annotations

Define the point of tangency

In the section definition above, you can see @before, @after, etc., and a re is defined to define when to enable AOP. After all, AOP is not required for all functions. In the above code, each annotation has a re, which is quite redundant. To solve this problem, Spring defines the concept of pointcuts, which describe to Spring which methods of which classes need to start AOP programming. With the idea of pointcuts in mind, we can make code simpler:

@Aspect @Component public class LogAgent2 { @Pointcut("execution(* com.niu.dao.UserServiceMethod.add(..) )") public void pointCut() { System.out.println("pointCut"); } @Before("pointCut()") public void beforeAdd(JoinPoint joinPoint) { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); System.out.println("before method rule intercept: "+ method.getName()); } @After("pointCut()") public void afterAdd(JoinPoint joinPoint) { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); System.out.println("after method rule intercept: "+ method.getName()); } @AfterReturning("pointCut()") public void afterReturnAdd(JoinPoint joinPoint) { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); System.out.println("after Return method rule intercept: "+ method.getName()); }}Copy the code

Testing AOP, the output is exactly the same

@SpringBootApplication public class App { public static void main(String[] args) throws Exception { ConfigurableApplicationContext context = SpringApplication.run(App.class, args); System.out.println("Start app success."); UserServiceMethod userServiceMethod = context.getBean(UserServiceMethod.class); userServiceMethod.add(); Before method rule interception: add UserServiceMethod add after method rule interception: add after Return method rule interception: addCopy the code

The introduction of

If you want to check whether the user information is empty, do not print if the input parameter is empty, but the original interface does not have this verification, what can be done? Spring enhancements should be introduced at this point to introduce a new interface to the interface

Firstly, the verification interface is provided:

public interface UserValidator { boolean validate(String name); } public class UserValidatorImpl implements UserValidator { @Override public boolean validate(String name) { System.out.println(" import check "); return StringUtils.isEmpty(name); }}Copy the code

Introducing new interfaces:

@Aspect
@Component
public class MyAspect {

    @DeclareParents(value = "com.niu.dao.UserService+",
            defaultImpl = UserValidatorImpl.class)
    public UserValidator userValidator;
}
Copy the code

Here @declareParents is used to introduce a new class to enhance the service, which must have a value and defaultImpl configuration:

Value: indicates the target object to be enhanced. Set it to UserService defaultImpl: Indicates the class to be enhanced. Set it to UserValidatorImpl

public static void main(String[] args) throws Exception { ConfigurableApplicationContext context = SpringApplication.run(App.class, args); System.out.println("Start app success."); UserValidator userService = (UserValidator)context.getBean(UserService.class); if(userService.validate("steven")){ System.out.println("Valid name"); }} Output: Valid nameCopy the code

After the interface is forcibly typed, you can use the new method to prove that the UserValidator method already exists.

Notification fetch parameter

In most of the above notifications, there are no notification delivery parameters, which can be obtained by using Joinpoint

@Before("pointCut() && args(name)") public void beforeAdd(JoinPoint joinPoint, String name) { System.out.println(Arrays.toString(joinPoint.getArgs()));; System.out.println(name);; } Output: [Steven] StevenCopy the code

The regular pointCut() &&args (name) pointCut() refers to the pointCut rule originally defined, and the convention passes in the join point (the target object method) as the parameter name. All parameters can be retrieved from the join point getArgs. Join point parameters can also obtain the information of the target object, so as to complete the required section task, the following is the form of no parameter:

@Before("pointCut()") public void beforeAdd(JoinPoint joinPoint) { System.out.println(Arrays.toString(joinPoint.getArgs()));; } output: [Steven]Copy the code

weave

Weaving is a process of generating dynamic proxy objects and weaving the target object method of the slice into a convention. Generally, it adopts the mode of interface plus implementation class, which is also recommended by Spring. As mentioned in the previous question, there are many implementation methods of dynamic proxy, such as JDK and CGLIB. In Spring, classes that implement AOP use JDK dynamic proxies if they have interfaces, and CGLib if they don’t

With breakpoint verification, I have tried both ways, using CGlib regardless of whether the implementation class has an interface or not

Multiple aspects

Spring supports multiple facets

Three facets are provided

@Aspect @Component public class LogAgent { @Before("execution(* com.niu.dao.UserServiceMethod.add(..) )") public void beforeAdd(JoinPoint JoinPoint) {system.out.println ("LogAgent Intercepts "); } } @Aspect @Component public class LogAgent2 { @Pointcut("execution(* com.niu.dao.UsrTestService.add(..) )") public void pointCut() { System.out.println("pointCut"); } @before ("pointCut()") public void beforeAdd(JoinPoint JoinPoint) {system.out.println ("LogAgent2 Intercept "); } } @Aspect @Component public class LogAgent3 { @Pointcut("execution(* com.niu.dao.UsrTestService.add(..) )") public void pointCut() { System.out.println("pointCut"); } @before ("pointCut()") public void beforeAdd(JoinPoint JoinPoint) {system.out.println ("LogAgent3 Intercepts "); }} Output: LogAgent3 intercept LogAgent Intercept LogAgent2 interceptCopy the code

The Order of each output interception is not fixed. If you need to fix the Order of execution, you can add an Order annotation to the class. For example:

@Aspect @Component @Order(1) public class LogAgent2 { @Pointcut("execution(* com.niu.dao.UsrTestService.add(..) )") public void pointCut() { System.out.println("pointCut"); }} Output: LogAgent2 intercepts LogAgent3 Intercepts LogAgent interceptsCopy the code

It can be sorted according to the need, if combined with the printing of after method, you can see that this is a typical sequence of responsibility chain model, and you can study it yourself

LogAgent2 Interception LogAgent3 Interception LogAgent interception UserServiceMethod Add name is: Steven LogAgent1 :after Method rule Interception: Add LogAgent3:after Method rule intercept: add LogAgent2:after method rule intercept: addCopy the code

conclusion

AOP article is written about this, the function is not difficult, important idea, common development can also consider this decoupling way. Some information was found on the efficiency of these two implementations: In JDK 1.6 and 1.7, JDK dynamic proxies were slower than CGLib dynamic proxies, but not by a factor of 10. In JDK1.8, JDK dynamic proxies were already faster than CGLib dynamic proxies.