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.
Implementation principle:
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 classType 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 notCan 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 ), oneGenerally, 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 ( stayActions 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, securityThe 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.
-
<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; } }
@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
Define cache facets and assign priority:
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.