Simple Spring-Aop steps

Keywords: Java Spring AOP

1. What is Spring Aop?

AOP(Aspect Oriented Programming Face-to-Face Programming) is a design idea. It is a kind of improvement of oop. It enhances the business function of the target object by dynamic proxy during compile or run time, that is, expands the function of the target object. There are two ways to extend the function if using traditional methods:
Option 1: Implement functionality based on inheritance with the following design: **

Suppose you have an announcement (notification) business interface and implementation:

How to extend objects without changing source code

pubic interface NoticeService{
 int deleteById(Integer...ids);
}

 

public class NoticeServiceImpl implements NoticeService{
 public int deleteById(Integer...ids){
 System.out.println(Arrays.toString(ids));
 return 0 ;
 } }

Requirements: Expand the functionality of the NoticeServiceImpl class in a practical way based on OCP (Open and Close Principle - Open to Extensions, Close to Modifications), such as outputting the following system times before and after the deleteById method

 public int deleteById(Integer...ids){
 System.out.println("Start:"+System.currentTimeMillis());
 Int rows=super.deleteById(ids);
 System.out.println("After:"+System.currentTimeMillis());
 return rows;
 } }

The key design is as follows:

public class CglibLogNoticeService extends NoticeServiceImpl{
 public int deleteById(Integer...ids){
 System.out.println("Start:"+System.currentTimeMillis());
 Int rows=super.deleteById(ids);
 System.out.println("After:"+System.currentTimeMillis());
 return rows;
 } }

The test classes are as follows:

public class NoticeServiceTests{
 public static void main(String[] args){
 NoticeService ns=new CglibLogNoticeService();
 ns.deleteById(10,20,30);
 } }

Conclusion: Functional extension based on inheritance is simple and easy to understand, but not flexible (only one class can inherit in java, not another), and the coupling is too high.

Option 2: Implement functional extensions based on combinations with the following code:

public class JdkLogNoticeService implements NoticeService{
 private NoticeService noticeService;//has a 
 public JdkLogNoticeService(NoticeService noticeService){
 this.noticeService=noticeService;
}
 public int deleteById(Integer...ids){
 System.out.println("Start:"+System.currentTimeMillis());
 int rows=this.noticeService.deleteById(ids);
 System.out.println("After:"+System.currentTimeMillis());
 return rows;
 } }

The test classes are as follows:

public class NoticeServiceTests{
 public static void main(String[] args){
 NoticeService ns=
 new JdkLogNoticeService(new NoticeServiceImpl());
 ns.deleteById(10,20);
 } }

CONCLUSION: Functional expansion based on combinatorial approach is more flexible, less coupling and more stable, but it is difficult to understand.

Hard.
           
In short, whether inherited, Or combination is based on OCP Method implements object function extension , Both have their own advantages and disadvantages , and
And we all have to write our own subclasses or siblings, can we simplify such template code? aop achieves object and two ways of optimizing.
        
        
In real-world projects, we usually understand object-oriented
Is a static process ( For example, how many modules are in a system, what objects are in a module, and what attributes are in an object ) , Face
Tangent is understood as a dynamic process (dynamically weaving in some extended functions or controlling object execution while the object is running). Figure 1
-1 As shown
        

Implementation principle:

AOP can create subclass or sibling type objects for target types at system startup , Such objects we usually call
It is a dynamic proxy object . As shown in the diagram :
        

There are two ways to create a proxy object for a target type (XxxServiceImpl):

  • First way : With the help of JDK Official API Create sibling type objects for target object types , But the target object class
    Type requires the corresponding interface to be implemented.
  • Second way : With the help of CGLIB Library creates its subclass type object for the target object type , But the target object type is not
    Can use final Modification .
    Note: jdk proxy is used by default in spring, and CGLIB proxy is used instead if the target object does not implement the interface; spring boot uses CGLIB proxy by default.

    Terminology Analysis

    • section (aspect): Transverse Surface Object , It is generally a specific class object.
    • breakthrough point (pointcut): Defines where to start extending business logic ( Which ways to start expanding your business when it runs ), one
      Generally, it is defined by an expression , Multiple entry points can be defined in one slice.
    • notice (Advice): Specific method objects for internal encapsulation to extend business logic , There can be multiple notifications in a slice ( stay
      Actions performed at a specific location of the facet ( Extended functionality ) .
    • Connection Point (joinpoint): An object encapsulating information about a target method being executed during program execution , Yes?
      Get specific target method information from this object , It even calls the target method. The connection point and entry point definitions are shown in the figure.
      As shown:

       

      Description: We can simply understand one airport security checkpoint as a connection point, multiple security checkpoints as a starting point, security
      The process of checking is considered a notification. In short, the concept is obscure and difficult to understand. If you do more examples, it will become clear. First, you can understand it in vernacular.
Steps to get started:
                        1. Add a spring-boot-starter-aop dependency
                        
 <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-aop</artifactId>
 </dependency>

Description: Based on this dependency spring, the AspectJ framework can be integrated to quickly complete the basic implementation of AOP. AspectJ is a facet-oriented framework that defines some syntax for AOP and has a special byte code generator to generate class files that conform to the java specification.

Business Face Object Design

Step 1: Create a comment type to apply to the definition of the entry point expression with the following key codes:

package com.cy.pj.common.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RequiredLog {
 String operation();
}

Step 2: Create face-to-face objects for log business enhancements with the following key codes:

@Aspect
@Component
public class SysLogAspect {
 private static final Logger log= 
LoggerFactory.getLogger(SysLogAspect.class);
 /**
 * @Pointcut Annotations are used to define entry points
 * @annotation(Annotation) as a starting point expression, followed by the method described by this annotation as a starting point
 * Point method
 */
 @Pointcut("@annotation(com.cy.pj.common.annotation.RequiredLog)")
 public void doLog(){}//This method is only responsible for hosting the definition of the entry point
 /**
 * @Around Annotations describe ways to expand your business before and after the entry point is executed.
 * @param jp Connection point object that encapsulates the target method information to be executed.
 * The target method can be invoked through the connection point object
 * @return Result of execution of target method
 * @throws Throwable
 */
 @Around("doLog()")
 public Object doAround(ProceedingJoinPoint jp)throws Throwable{
     long t1=System.currentTimeMillis();
 try {
 //Execute the target method (one of the tangent methods)
 Object result = jp.proceed();
 long t2=System.currentTimeMillis();
 log.info("opertime:{}",t2-t1);
 return result;//Result of execution of target business method
 }catch(Throwable e){
 e.printStackTrace();
 long t2=System.currentTimeMillis();
log.info("exception:{}",e.getMessage());
 throw e;
 }
 }
Step 3: By annotating RequiredLog Comments describe announcement business (@Service) Related method, in this case the method is
Log entry point methods, such as:
@RequiredLog(operation="Announcement Query")
@Override
public List<SysNotice> findNotices(SysNotice notice) {
 
 //log.debug("start: {}",System.currentTimeMillis());
List<SysNotice> list=sysNoticeDao.selectNotices(notice);
 //log.debug("end: {}",System.currentTimeMillis());
 return list; }

Step 4: Test the notification business method, detect the log output, and understand how it works, as shown in the diagram

Modify the face object after success:

@Aspect
@Component
public class SysLogAspect {
 private static final Logger log= 
LoggerFactory.getLogger(SysLogAspect.class);
 /**
 * @Pointcut Annotations are used to define entry points
 * @annotation(Annotation) as a starting point expression, followed by the method described by this annotation as a starting point
 * Point method
 */
 @Pointcut("@annotation(com.cy.pj.common.annotation.RequiredLog)")
 public void doLog(){}//This method is only responsible for hosting the definition of the entry point
 /**
 * @Around Annotations describe ways to expand your business before and after the entry point is executed.
 * @param jp Connection point object that encapsulates the target method information to be executed.
 * The target method can be invoked through the connection point object.
 * @return Result of execution of target method
 * @throws Throwable
 */
 @Around("doLog()")
 public Object doAround(ProceedingJoinPoint jp)throws Throwable{
 long t1=System.currentTimeMillis();
 log.info("Start:{}",t1);
 try {
 //Execute the target method (one of the tangent methods)
 Object result = jp.proceed();
 long t2=System.currentTimeMillis();
 log.info("After:{}",t2);
 doLogInfo(jp,t2-t1,null);
 return result;//Result of execution of target business method
 }catch(Throwable e){
 e.printStackTrace();
 long t2=System.currentTimeMillis();
 doLogInfo(jp,t2-t1,e);
 throw e;
 }
 }
 //Logging user behavior log
13
 private void doLogInfo(ProceedingJoinPoint jp,
 long time,Throwable e)
 throws Exception {
 //1. Get the user behavior log
 //1.1 Get the login user name (you can give a fixed value first when you are not logged in)
 String username="cgb";
 //1.2 Get ip address
 String ip= "202.106.0.20";
 //1.3 Get the value of the value attribute in the operation name - @RequiredLog comment
 //1.3.1 Get the target object type
 Class<?> targetCls=jp.getTarget().getClass();
 //1.3.2 Target Acquisition Method
 MethodSignature ms=
 (MethodSignature) jp.getSignature();//Method Signature
 Method targetMethod=targetCls.getMethod(
 ms.getName(),ms.getParameterTypes());
 //1.3.3 RequiredLog annotation on acquisition method
 RequiredLog annotation =
 targetMethod.getAnnotation(RequiredLog.class);
 //1.3.4 Get the operation name defined in the comment
 String operation=annotation.operation();
 //1.4 Get method declaration (class full name + method name)
 String classMethodName=
 targetCls.getName()+"."+targetMethod.getName();
 //1.5 Getting method actual parameter information
 Object[]args=jp.getArgs();
 String params=new ObjectMapper().writeValueAsString(args);
 //2. Encapsulate user behavior logs
 SysLog sysLog=new SysLog();
 sysLog.setUsername(username);
 sysLog.setIp(ip);
 sysLog.setOperation(operation);
 sysLog.setMethod(classMethodName);
 sysLog.setParams(params);
 sysLog.setTime(time);
 if(e!=null) {
 sysLog.setStatus(0);
 sysLog.setError(e.getMessage());
 }
 //3. Print logs
 String userLog=new ObjectMapper()
 .writeValueAsString(sysLog);
 log.info("user.oper {}",userLog);
 } }

Spring AOP Technology Advancement

* Notification type

  • @Around (Highest priority notifications allow flexibility in business development before and after the target method is implemented.)
  • @Before (called before target method execution)
  • @AfterReturning (execution of target method at normal end)
  • @AfterThrowing (execution at end of target method exception)
  • @After (the target method executes when it ends, both normal and exception ends)

Section Execution Order

The priority of facets needs help @Order Note describing that the smaller the number, the higher the priority, the default priority comparison
Low. For example:
@Order(1)
@Aspect
@Component
public class SysLogAspect {
...
}

Define cache facets and assign priority:

@Order(2)
@Aspect
@Component
public class SysCacheAspect {
...
}

 

Description: When multiple facets act on the same target object method, they form a chain of facets, similar to filtering
The execute analysis of the interceptor chain and the interceptor chain is shown in the figure:

 

Note: The cache provided in spring is @Cacheable(cacheNames = "lk") when integrated with third-party paging frameworks. If this annotation is placed in the service layer when querying the results, the first time is normal and the second time there is no data, it should be due to the execution order of the interceptor...

*The underlying layer of paging intercepts the original sql statement before sql accesses the data, while the cache puts the returned result into the cache after the first access to the database, and fetches it from the cache the next time it accesses the same method. If the cache is placed in the service layer, the user sends a request to invoke the method to perform paging first, so the data is not placed at this timeWhen you go to the cache, the cache is empty, which results in no results for the second query on the page.



        
    

Posted by Karve on Tue, 05 Oct 2021 09:40:00 -0700