1. Preface
The most common way in development is to use Aspect to realize AOP function. Next, we will simply use @ Aspect to realize AOP Aspect oriented programming. For the usage of Spring native AOP, please refer to the previous blog post AOP proxyfactory underlying implementation of spring 5 framework (5)
Before using AspectJ, let me review some important concepts as follows:
- Before: executes program logic before a connection point.
- after returning: the program logic executed after the connection point is normal. Note that if the program throws an exception, the notification will not be executed.
- after throwing: the program logic executed when the program is abnormal.
- after: the program logic when the connection point ends execution (it will be executed whether there is an exception or not)
- around: the most powerful notification function in spring, which can complete and implement the above four functions.
2. Use Aspect to realize AOP function
If you use @ Aspect annotation, you need to support the following Aspect dependencies in your project:
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.8.10</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.10</version> </dependency>
Next, we still use Calculator to demonstrate the use of AspectJ method as follows:
- New interface and its implementation
First, AspectJ provides us with the following notice annotations:
|Notice name|||
public interface Calculator { int add(int a, int b); int sub(int a, int b); double divide(int a, int b); } @Service public class CalculatorImpl implements Calculator { @Override public int add(int a, int b) { return a + b; } @Override @AdviceRequired public int sub(int a, int b) { return a - b; } @Override public double divide(int a, int b) { return a / b; } }
- Define tangent class
@Aspect @Component public class LogAspect { /** * Defines the pointcut, where execution defines the pointcut expression */ @Pointcut(value = "execution(* *..aop..*(..))") public void logPoint() { } /** * Pre notification is executed before method execution */ @Before(value = "execution(public * com.codegeek.aop.day1.CalculatorImpl.*(..))") public static void logStart(JoinPoint joinPoint) { Object[] args = joinPoint.getArgs(); // Get the source object of the execution method -- the place where the expression actually cuts in and runs System.out.println(joinPoint.getTarget().getClass()); // System.out.println(joinPoint.getStaticPart()); print detailed pointcut expression System.out.println("LogAspect-General notice method@before: " + joinPoint.getSignature().getName() + "The log is on....Method parameters:" + Arrays.asList(args)); } /** * The notification is executed when the result is returned after the method is executed * @param result */ @AfterReturning(value = "execution(public * com.codegeek.aop.day1.CalculatorImpl.*(..))", returning = "result") public static void logRun(Object result) { System.out.println("LogAspect-General notice@AfterReturning" + "The operation result is:" + result); } /** * * This notification will be executed if there is an exception in method execution */ @AfterThrowing(value = "execution(public * com.codegeek.aop.day1.Calculator.*(..))", throwing = "e") public static void logException(Exception e) { System.out.println("LogAspect-General notice@AfterThrowing Something's wrong:" + e); } /** * Post notification pointcut after execution */ @After(value = "execution(public * com.codegeek.aop.day1.Calculator.*(..))") public void logEnd() { System.out.println("LogAspect-General notice@After The log is over"); } @Around(value = "logPoint()") public Object logAround(ProceedingJoinPoint proceedingJoinPoint) { Object proceed = null; try { // @Before System.out.println("Notice before surround.....Currently executed method:" + proceedingJoinPoint.getSignature().getName()); proceed = proceedingJoinPoint.proceed(proceedingJoinPoint.getArgs()); // @AfterReturn System.out.println("Post orbit notification....."); } catch (Throwable throwable) { // @AfterThrowing System.out.println("Surround exception notification......."); // If the exception is not thrown, the exception catch will be dropped, and the normal notification exception will not be able to sense the exception object, so it is considered to be executed normally throw new RuntimeException(throwable); } finally { // @After System.out.println("Wrap end notification....."); } return proceed; } }
After we define the facet class, we need to pay attention to several important points:
- The @ Component and @ Aspect annotations are used to declare a tangent class object, which can be scanned by Spring IOC container.
- The @ Pointcut annotation is used to define the pointcuts of methods. The so-called pointcuts are used to determine whether notifications can be executed in the target methods of those target classes.
- @Before, @ Around, @ After, @ AfterReturning, @ AfterThrowing can introduce the value of pointcuts, or customize the value of pointcuts
Next we need to define the configuration class so that Spring can scan to the tangent class and successfully execute the notification.
@Configuration @ComponentScan(basePackages = {"com.codegeek.aop.day1"}) @EnableAspectJAutoProxy(proxyTargetClass = true) public class AOPConfig { }
- Test method:
It should be noted that this configuration class can be introduced on the test class as follows:
[external link image transfer failed. The source station may have anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-nijFEgSr-1592113317231)(Spring-AOP uses @ Aspect annotation. assets/image-20200614122427720.png))
@Test public void testAspect() { // Configure the aspect class IOC to generate the proxy class of the interface, otherwise it is the basic class System.out.println(); Calculator bean = applicationContext.getBean(Calculator.class); System.out.println(bean.add(1, 5)); System.out.println("-------------------"); }
Here we use the normal method to test the running results as follows:
Notify before wrapping..... Current execution method: add class com.codegeek.aop.day1.CalculatorImpl LogAspect - normal notification method @ before: add log started.... method parameter: [1, 5] Notification after orbit Wrap end notification LogAspect - general notification @ After log is over LogAspect general notice @ AfterReturning: 6 6 -------------------
Next, we test an exception as follows:
@Test public void testAspect() { // Configure the aspect class IOC to generate the proxy class of the interface, otherwise it is the basic class System.out.println(); Calculator bean = applicationContext.getBean(Calculator.class); System.out.println(bean.add(1, 5)); System.out.println("-------------------"); System.out.println(bean.divide(5, 0)); System.out.println(bean); System.out.println(bean.getClass()); }
The output after running the test method is as follows:
Notify before wrapping..... Current execution method: add class com.codegeek.aop.day1.CalculatorImpl LogAspect - normal notification method @ before: add log started.... method parameter: [1, 5] Notification after orbit Wrap end notification LogAspect - general notification @ After log is over LogAspect general notice @ AfterReturning: 6 6 ------------------- Notify before wrapping..... Current execution method: divide class com.codegeek.aop.day1.CalculatorImpl LogAspect - normal notification method @ before: divide log started.... method parameter: [5, 0] Surround exception notification Wrap end notification LogAspect - general notification @ After log is over LogAspect - general notice @ AfterThrowing has an exception: java.lang.RuntimeException: java.lang.ArithmeticException: / by zero java.lang.RuntimeException: java.lang.ArithmeticException: / by zero
We found that the exception notification has been executed, but the exception notification will not be executed when the program is running normally. At the same time, we can find that the @ AfterReturning annotation notification has not been executed, and it will only execute the notification when the program is running normally. The order of notice execution can be expressed as follows:
[normal front] try { Before surround notification Orbit execution Orbit return } catch(Exception e) { Surround exception notification } finally { Surround post notification } [normal post] [normal method return / normal exception notification]
3. Using xml to realize AOP
First, we annotate the @ Aspect annotation on the LogAspect class as follows:
[failed to save the image in the external link. The source station may have anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-Dhct8PCr-1592113317233)(Spring-AOP uses @ Aspect annotation. Assets / image-202006123137800. PNG))
Then create a new aspect class OrderAspect as follows:
@Component //@Aspect @Order(1) public class OrderAspect { // @Before(value = "execution(* *..aop..*(..))") public static void logStart(JoinPoint joinPoint) { Object[] args = joinPoint.getArgs(); // System.out.println(joinPoint.getStaticPart()); print detailed pointcut expression System.out.println("OrderAspect-General notice method@before: " + joinPoint.getSignature().getName() + "The log is on....Method parameters:" + Arrays.asList(args)); } // @Pointcut(value = "execution(* *..aop..*(..))") public void logPoint() { } // returning tells the value returned after the method is executed // @AfterReturning(value = "execution(public * com.codegeek.aop.day1.CalculatorImpl.*(..))", returning = "result") public static void logRun(Object result) { System.out.println("OrderAspect-General notice@AfterReturning" + "The operation result is:" + result); } //@AfterThrowing(value = "execution(public * com.codegeek.aop.day1.Calculator.*(..))", throwing = "e") public static void logException(Exception e) { System.out.println("OrderAspect-General notice@AfterThrowing Something's wrong:" + e); } // @After(value = "execution(public * com.codegeek.aop.day1.Calculator.*(..))") public void logEnd() { System.out.println("OrderAspect-General notice@After The log is over"); } }
Then configure the following in xml:
<?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" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <!-- false use jdk Agent otherwise use CGlib--> <aop:aspectj-autoproxy proxy-target-class="true"/> <context:component-scan base-package="com.codegeek.aop.day1"/> <aop:config> <aop:pointcut id="logPoint" expression="execution(* *..aop..*(..))"/> <aop:aspect id="myAspect" ref="orderAspect"> <aop:before method="logStart" pointcut-ref="logPoint" arg-names="joinPoint"/> <aop:after method="logEnd" pointcut-ref="logPoint" /> <aop:after-returning method="logRun" pointcut-ref="logPoint" returning="result"/> <aop:after-throwing method="logException" pointcut-ref="logPoint" throwing="e"/> </aop:aspect> </aop:config> <bean id="orderAspect" class="com.codegeek.aop.day1.OrderAspect"/> </beans>
We run the test again as follows:
@Test public void testAspect() { // Configure the aspect class IOC to generate the proxy class of the interface, otherwise it is the basic class System.out.println(); Calculator bean = applicationContext.getBean(Calculator.class); System.out.println(bean.add(1, 5)); System.out.println("-------------------"); System.out.println(bean.divide(5, 0)); System.out.println(bean);
The operation results are as follows:
OrderAspect-General notice method@before: add The log is on....Method parameters:[1, 5] OrderAspect-General notice@AfterThe log is over OrderAspect-General notice@AfterReturningThe operation result is:6 6 ------------------- OrderAspect-General notice method@before: divide The log is on....Method parameters:[5, 0] OrderAspect-General notice@AfterThe log is over OrderAspect-General notice@AfterThrowingSomething's wrong:java.lang.ArithmeticException: / by zero java.lang.ArithmeticException: / by zero
4. How to select AOP type
In the above, we demonstrated an AOP implementation based on Aspect style annotation, and also saw an AOP implementation based on XML configuration. These are affected by a variety of factors, such as program requirements, development tools, the level of familiarity of the development team with AOP, and so on. Since both Spring AOP and AspectJ use the same Aspect style, if there are additional functional requirements for AspectJ, you can migrate the existing Aspect style annotation code to Aspect.