Spring Learning Notes - AOP and Spring Transaction Learning

This weekend, I'm going to take a look at another focus of Spring, transaction learning in AOP and Spring. I've written articles about transactions before, all about how to use them in my work. I'll add some basics today, try to explain the role in some straightforward words, and then sort out the interview questions I occasionally heard in the past few days. Okay, let's start now.

AOP

AOP(Aspect Oriented Programming) is face-to-face programming, and your Java learning partners will certainly know that Java is an OOP(Object Oriented Programming) object-oriented programming language.

Let me just say: I think XX-oriented refers to the way to solve problems. Java object-oriented refers to calling methods through objects, which improves code reuse and reduces code coupling compared to programming languages such as procedure-oriented C.

So how should we understand this face-to-face? Did he solve some problems with facets?

Face-oriented programming actually uses the dynamic proxy mode to execute a specified method before, after, and throw an exception.

The following diagram is intended to help you understand what face-to-face programming is:

So how does AOP implement face-to-face programming?

AOP uses dynamic proxy mode for adding horizontal methods. If your partner doesn't know much about proxy mode, you can read this article and get some results.

Design Mode - Proxy Pattern

If you have a small partner who knows about proxy mode and JDK dynamic proxy and CGlib dynamic proxy, we will continue.

It is not difficult to see from the above flowchart that AOP allows us to add new logic and functionality without changing the original code, which reduces code coupling and enhances the original code.

Use AOP

Mode 1:

1. We need to import AOP woven package dependencies (not imported but not used)

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.4</version>
        </dependency>

2. We need to create business classes for testing

2-1. Show me the project catalog for my tests

2-2. Create User Interface

package aopTest;

/**
 * @author Me
 * @date 2022/2/26 12:30
 */
public interface User {
    /**
     * insert
     */
    void insert();
    /**
     * To update
     */
    void update();
    /**
     * delete
     */
    void delete();
    /**
     * query
     */
    void select();
}

2-3. Create UserImpl Implementation Class

package aopTest;

/**
 * @author ME
 * @date 2022/2/26 12:30
 */
public class UserImpl implements User{
    @Override
    public void insert() {
        System.out.println("Insert an object");
    }

    @Override
    public void update() {
        System.out.println("Update an object");
    }

    @Override
    public void delete() {
        System.out.println("Delete an object");
    }

    @Override
    public void select() {
        System.out.println("Query an object");
    }
}

3. Create Face Method

package aopTest;

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

/**
 * @author ME
 * @date 2022/2/26 12:33
 */
// Preface requires MethodBeforeAdvice interface, override before method
public class BeforeLog implements MethodBeforeAdvice {
    /**
     * @param method Method of the target object to execute
     * @param objects Parameters of the target object method
     * @param o Target object
     * @throws Throwable
     */
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println("Front Face Executed" + o.getClass().getName() + "In class" + method.getName() + "Method");
    }
}
package aopTest;

import org.springframework.aop.AfterReturningAdvice;

import java.lang.reflect.Method;

/**
 * @author ME
 * @date 2022/2/26 12:40

 * AfterAdvice After the end of the method, execute before returning, get no return value
 * AfterReturningAdvice Execute after returning the method to get the return value
 */
public class AfterLog implements AfterReturningAdvice {
    /**
     * @param returnValue Return value content
     * @param method Method of the target object to execute
     * @param args Parameters of the target object method
     * @param target Target object
     * @throws Throwable
     */
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("Rear Face Executed" + target.getClass().getName() + "In class" + method.getName() + "Method");
    }
}

4. Create an applicationContext.xml configuration file

4-1. Show me the directory where the profile is located

4-2. Create a configuration file (add constraints for AOP when using aop)

<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">

<!--    register bean,Must be registered Class,Non-registrable interface-->
    <bean id="user" class="aopTest.UserImpl"/>
    <bean id="beforeLog" class="aopTest.BeforeLog"/>
    <bean id="afterLog" class="aopTest.AfterLog"/>

<!--    To configure AOP,Need to import aop Constraints are configurable-->
<!--    Spring Native API Mode Creation-->
    <aop:config>
<!--        Configure entry points, id Identifies the point of tangency, expression Case sensitive for effective range-->
<!--        The expression can be searched on the Internet, and the expression I use is src.main.java All Belts Under Path i Beginning Method(Case sensitive, I Invalid at the beginning)-->
<!--        <aop:pointcut id="pointcut" expression="execution(* i*(..))"/>-->
<!--        This expression is src.main.java.aopTest Under Path UserImpl All methods in a class-->
        <aop:pointcut id="pointcut" expression="execution(* aopTest.UserImpl.*(..))"/>

<!--        Identify in expression In the configured class mode, we configure several facets, which are executed logically based on beanId Configuration with point of tangency id-->
        <aop:advisor advice-ref="beforeLog" pointcut-ref="pointcut"/>
        <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
    </aop:config>

</beans>

5. Create a startup class file and make method calls

import aopTest.User;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author ME
 * @date 2022/2/19 22:39
 */
public class StartController {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext application = new ClassPathXmlApplicationContext("applicationContext.xml");
        // Since Spring native AOP is based on JDK dynamic proxy, type conversion to interface type is required
        // If we get a subclass object, using AOP will error, not using it will not error
        User user = (User) application.getBean("user");
        user.insert();
    }
}

Looking at the results of the execution, we find that instead of simply executing a sentence, it is as follows:

We found that facet programming was working.

Mode 2:

In addition to the above, we can also customize the specified classes. Next, we describe how to implement this AOP. We also use the User class as a test object.

1. Create Face Method

package aopTest;

/**
 * @author ME
 * @date 2022/2/26 14:24
 */
public class AopPoint {
    /**
     * Name Customization of Pre-methods
     */
    public void myBefore() {
        System.out.println("Pre-notification before method execution");
    }

    /**
     * Post-method name customization
     */
    public void myAfter() {
        System.out.println("Post-notification after method execution");
    }
}

2. Modify the configuration file

<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">

<!--    register bean,Must be registered Class,Non-registrable interface-->
    <bean id="user" class="aopTest.UserImpl"/>
    <bean id="aopPoint" class="aopTest.AopPoint"/>

<!--    To configure AOP,Need to import aop Constraints are configurable-->
<!--    Custom Face Class Creation-->
    <aop:config>
<!--        Introducing a configured facet class Bean object-->
        <aop:aspect ref="aopPoint">
<!--            Configure the point of tangency, execution Expression representation src.java.aopTest.UserImpl All methods of a class add facets-->
            <aop:pointcut id="point" expression="execution(* aopTest.UserImpl.*(..))"/>
<!--            Represents the preceding execution of a method within a tangent at a configured point of tangency Bean In Container ID by aopPoint In class myBefore Method-->
            <aop:before method="myBefore" pointcut-ref="point"/>
<!--            Represents execution after execution of a method within a tangent at a configured point of tangency Bean In Container ID by aopPoint In class myAfter Method-->
            <aop:after method="myAfter" pointcut-ref="point"/>
        </aop:aspect>
    </aop:config>

</beans>

3. Execute Startup Class

import aopTest.User;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author ME
 * @date 2022/2/19 22:39
 */
public class StartController {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext application = new ClassPathXmlApplicationContext("applicationContext.xml");
        // Since Spring native AOP is based on JDK dynamic proxy, type conversion to interface type is required
        // If we get a subclass object, using AOP will error, not using it will not error
        User user = (User) application.getBean("user");
        user.insert();
    }
}

The console output after execution is as follows:

Mode 3:

The third way is to use annotations to complete the AOP or the same example

1. Create a facet class

package aopTest;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

/**
 * @author ME
 * @date 2022/2/26 20:31
 */
// Aspect annotation labels this class as an AOP facet class
@Aspect
public class AnnoPoint {

    // Annotations are represented as method precomputes and arguments are execution expressions
    // The expression I use is src. Main. Java. All methods in the UserImpl class under the aopTest package
    @Before("execution(* aopTest.UserImpl.*(..))")
    public void before() {
        System.out.println("Pre-notification before method execution");
    }

    // Annotations are represented as method postnotifications
    @After("execution(* aopTest.UserImpl.*(..))")
    public void after() {
        System.out.println("Post-notification after method execution");
    }

    // Annotations are represented as method wrapping notifications, where pre- and post-notifications can be performed
    // In wrapping enhancements, we can pass in a parameter representing the method object we want to enter
    @Around("execution(* aopTest.UserImpl.*(..))")
    public void around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("Pre-notification around notifications");
        // Get the signature of the method
        System.out.println("The method signature of the cut-in method is:" + joinPoint.getSignature());
        // Calling this method means executing the cut-in method, the return value is the return value of the cut-in method, and the void keyword returns null
        Object proceed = joinPoint.proceed();
        System.out.println("Post notification around notification");
    }
}

2. Create an XML configuration file

<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">

<!--    register bean,Must be registered Class,Non-registrable interface-->
    <bean id="user" class="aopTest.UserImpl"/>
    <bean id="annoPoint" class="aopTest.AnnoPoint"/>

<!--    open AOP Annotation Support(Also need to import above beans Inside Label aop constraint)-->
<!--    proxy-target-class You can specify the use of(false)JDK Dynamic proxy or(true)CGlib Dynamic proxy, default is(false)JDK Dynamic Proxy-->
<!--    The difference between them is that JDK Dynamic proxy AOP Only works on interface objects, CGlib Dynamic proxy AOP Available for interface and class objects-->
<!--    Specific differences between the two types of dynamic proxies See the link to the proxy mode above-->
    <aop:aspectj-autoproxy proxy-target-class="true"/>

</beans>

3. Execute Startup Class

import aopTest.User;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author ME
 * @date 2022/2/19 22:39
 */
public class StartController {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext application = new ClassPathXmlApplicationContext("applicationContext.xml");
        // Since Spring native AOP is based on JDK dynamic proxy, type conversion to interface type is required
        // Once the CGlib dynamic proxy is turned on by setting the aop property in the XML file, class objects can be used as a point of tangency for notification
        // In the case of JDK dynamic proxy we get subclass objects and use AOP will error
        User user = (User) application.getBean("user");
        user.insert();
    }
}

The results are as follows:

We can see the difference in three ways:

The first way is to get the contents of the tangent method being executed, such as parameters, return values, class information, and so on

The second way does not get the information of the point-of-tangency method, it can only get the data through other ways and execute its own logic

The third method is the easiest to configure, but the disadvantage is that each notification method needs to write an expression, which is prone to low-level errors.

I've written an article about AOP using custom annotations before. Here's the link. If a partner needs to add logging functionality at work, you can use this method. The configured method that needs AOP enhancement only needs to add a comment on the method. It's very useful!

SpringBoot Custom AOP Annotations

Spring Transactions

Transactions This part of the content I have previously sorted out an article, because Spring is now used less, testing Spring transactions is more difficult, but also inherit Mybatis, connect to the database, and so on. And the transaction part in SpringBoot is a commentary thing, so don't repeat more, check out my article if necessary.

Spring transactions and usage in work

If your little buddies really want to know something about Spring, they can go to see videos. It's recommended that they go crazy because I just watch his videos to learn. Next, I'll add some conceptual content.

Transactions in Spring are divided into declarative and programmatic transactions.

Declarative transactions: Enhance with AOP without affecting the original logic

Programmatic Transactions: Transaction management required in code

1. In declarative transactions, we need to understand a parameter of the transaction, propagation, which is used to indicate how to use the transaction with, or without, the method.

NameMeaning
REQUIRED (default)Supports the current transaction and creates a new one if there is no current one
SUPPORTSSupports the current transaction and executes non-transactionally if no transaction exists
MANDATORYSupports the current transaction, throws an exception if no transaction exists
REQUIRES_NEWCreate a new transaction and suspend it if one exists
NOT_SUPPORTEDPerform the operation non-transactionally and suspend the current transaction if one exists
NEVERExecutes non-transactionally and throws an exception if a transaction currently exists
NESTEDSupports the current transaction, executes a nested transaction if the current transaction exists, and creates a new one if there is no current transaction

2.read-only property, only select operation is supported in this transaction.

Spring is basically finished here. There are fewer and fewer companies that need to use Spring now, but Spring is still a frequent occurrence in interview questions. IOC and AOP should focus on understanding the content, so hopefully your little buddies will learn the basics well.

Keywords: Java Spring AOP

Added by fukas on Sat, 26 Feb 2022 19:43:38 +0200