spring body of knowledge

Spring-01

1. Introduction to Spring

​ Spring is an open source framework created by [Rod Johnson](https://baike.baidu.com/item/Rod Johnson). It was created to address the complexities of enterprise application development.

​ It is currently the soul framework for JavaEE development. It can simplify JavaEE development, integrate other frameworks very easily, and perform functional enhancements without intrusion.

The core of Spring is Inversion of Control (IoC) and Aspect Orientation (AOP).

2.IOC Inversion of Control

2.1 Concept

​ Inversion of control, the control of the object was in the hands of the class before, and now it is in the hands of Spring after the inversion.

2.2 Getting Started Case

①Import dependencies

Import SpringIOC related dependencies

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.9.RELEASE</version>
        </dependency>

②Write a configuration file

Create the applicationContext.xml file in the resources directory, the file name can be arbitrarily chosen. But it is recommended to call applicationContext.

The content is as follows:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--
        classs:The full class name of the configuration class
        id:Configure a unique identifier
    -->
    <bean class="com.sangeng.dao.impl.StudentDaoImpl" id="studentDao"  >
    </bean>


</beans>

③Create a container to get objects from the container and test

    public static void main(String[] args) {

//        1. Get the StudentDaoImpl object
        //Create a Spring container, specifying the configuration file path to read
        ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        //get object from container
        StudentDao studentDao = (StudentDao) app.getBean("studentDao");
        //Invoke a method of an object for testing
        System.out.println(studentDao.getStudentById(1));
    }

2.3 Common attribute configuration of Bean

2.3.1 id

​ The unique identifier of the bean, duplicates are not allowed in the same Spring container

2.3.2 class

​ Full class name, used to create objects by reflection

2.3.3 scope

​ scope has two main values: singleton and prototype

​ If set to singleton, there will only be one bean object in a container. This object is created when the default container is created.

​ If set to prototype, there will be multiple bean objects in a container. A new object is created each time the getBean method is called to get it.

3.DI dependency injection

​ Dependency injection can be understood as an application scenario of IoC, which reverses the maintenance rights of dependencies between objects.

3.1 set method injection

Configure in the bean tag where you want to inject properties. The premise is that the class has a set method corresponding to the provided attribute.

package com.sangeng.domain;

public class Student {

    private String name;
    private int id;
    private int age;

    private Dog dog;

    public Dog getDog() {
        return dog;
    }

    public void setDog(Dog dog) {
        this.dog = dog;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", id=" + id +
                ", age=" + age +
                '}';
    }

    public Student() {

    }

    public Student(String name, int id, int age) {
        this.name = name;
        this.id = id;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

    <bean class="com.sangeng.domain.Dog" id="dog">
        <property name="name" value="noob"></property>
        <property name="age" value="6"></property>
    </bean>

    <bean class="com.sangeng.domain.Student" id="student" >
        <!--
            name property is used to specify which property to set
            value Properties are used to set the value to be set
            ref Attributes are used to set values ​​for attributes of reference types, which can be written on Spring in a container bean of id
        -->
        <property name="name" value="southeast branch"></property>
        <property name="age" value="20"></property>
        <property name="id" value="1"></property>
        <property name="dog" ref="dog"></property>
    </bean>

3.2 Constructive injection with parameters

Configure in the bean tag where you want to inject properties. The premise is that the class has a corresponding parameterized construction.

public class Student {

    private String name;
    private int id;
    private int age;

    private Dog dog;

    public Student(String name, int id, int age, Dog dog) {
        this.name = name;
        this.id = id;
        this.age = age;
        this.dog = dog;
    }
    //.....omit others
}
    <!--Injection using parameterized constructs-->
    <bean class="com.sangeng.domain.Student" id="student2" >
        <constructor-arg name="name" value="Self-hanging southeast branch"></constructor-arg>
        <constructor-arg name="age" value="20"></constructor-arg>
        <constructor-arg name="id" value="30"></constructor-arg>
        <constructor-arg name="dog" ref="dog"></constructor-arg>
    </bean>

3.3 Complex type property injection

The entity class is as follows:

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private int age;
    private String name;
    private Phone phone;
    private List<String> list;
    private List<Phone> phones;
    private Set<String> set;
    private Map<String, Phone> map;
    private int[] arr;
    private Properties properties;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Phone {
    private double price;
    private String name;
    private String password;
    private String path;

}

The configuration is as follows:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean class="com.sangeng.domain.Phone" id="phone">
        <property name="price" value="3999"></property>
        <property name="name" value="black rice"></property>
        <property name="password" value="123"></property>
        <property name="path" value="qqqq"></property>
    </bean>
    
    <bean class="com.sangeng.domain.User" id="user">
        <property name="age" value="10"></property>
        <property name="name" value="Captain"></property>
        <property name="phone" ref="phone"></property>
        <property name="list">
            <list>
                <value>Three shifts</value>
                <value>Shih Tzu</value>
            </list>
        </property>

        <property name="phones">
            <list>
                <ref bean="phone"></ref>
            </list>
        </property>

        <property name="set">
            <set>
                <value>setEle1</value>
                <value>setEle2</value>
            </set>
        </property>

        <property name="map">
            <map>
                <entry key="k1" value-ref="phone"></entry>
                <entry key="k2" value-ref="phone"></entry>
            </map>
        </property>

        <property name="arr">
            <array>
                <value>10</value>
                <value>11</value>
            </array>
        </property>

        <property name="properties">
            <props>
                <prop key="k1">v1</prop>
                <prop key="k2">v2</prop>
            </props>
        </property>
    </bean>
</beans>

4.Lombok

①Import dependencies

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.16</version>
        </dependency>

②Add comment

@Data //Generate set and get methods based on properties
@NoArgsConstructor //Generate null-argument constructs
@AllArgsConstructor //Generate full-parameter constructs
public class Phone {
    private double price;
    private String name;
    private String password;
    private String path;

}

5.SPEL

​ We can use SPEL expressions in configuration files. It is written as follows:

        <property name="age" value="#{20}"/>
        <property name="car" value="#{car}"/>

​ Note: SPEL needs to be written to the value attribute, not the ref attribute.

6. Configuration files

6.1 Read properties file

We can make Spring read the key/value in the properties file and use the value in it.

①Set the read properties

Add the following tag to the Spring configuration file: Specify the path of the file to be read.

<context:property-placeholder location="classpath:filename.properties">

Where classpath represents the class loading path.

We will also use the following notation: classpath:**.properties where * * means any file name.

Note: Is the introduction of the context namespace correct?

②Use the value in the configuration file

When we need to use it, we can use ${key} to represent the specific value. Note that it can only be used in the value attribute. E.g:

<property name="propertyName" value="${key}"/>

6.2 Introducing the Spring configuration file

​ We can import other xml configuration files through the resource attribute of the import tag in the main configuration file

<import resource="classpath:applicationContext-book.xml"/>

7. Low frequency knowledge points

7.1 Configuration of beans

7.1.1 name attribute

We can use the name attribute to name the bean. E.g:

    <bean class="com.alibaba.druid.pool.DruidDataSource" id="dataSource" name="dataSource2,dataSource3">
        <property name="driverClassName" value="${jdbc.driver}"></property>
        <property name="url" value="${jdbc.url}"></property>
        <property name="username" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>

​ You can use this name to get it when you get it

    public static void main(String[] args) {

        ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        DruidDataSource dataSource = (DruidDataSource) app.getBean("dataSource3");
        System.out.println(dataSource);

    }

7.1.2 lazy-init

​ You can control the creation time of the bean. If it is set to true, it will be created when the object is obtained for the first time.

    <bean class="com.alibaba.druid.pool.DruidDataSource" lazy-init="true"  id="dataSource" name="dataSource2,dataSource3">
        <property name="driverClassName" value="${jdbc.driver}"></property>
        <property name="url" value="${jdbc.url}"></property>
        <property name="username" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>

7.1.3 init-method

​ Can be used to set the initialization method. After setting, the container will automatically call the corresponding method for us after the object is created.

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {

    private String name;
    private int id;
    private int age;
	//initialization method
    public void init(){
        System.out.println("Initialize the student object");
    }

}

<bean class="com.sangeng.domain.Student" id="student" init-method="init"></bean>

Note: The initialization method of the configuration can only be empty.

7.1.4 destroy-method

​ It can be used to set the method to be called before destruction. After setting, the container will automatically call the corresponding method for us before destroying the object.

    <bean class="com.sangeng.domain.Student" id="student"  destroy-method="close"></bean>
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {

    private String name;
    private int id;
    private int age;

    public void init(){
        System.out.println("Initialize the student object");
    }

    public void close(){
        System.out.println("Called before the object is destroyed to release resources");
    }
}

Note: The configured method can only have empty parameters.

7.1.5 factory-bean&factory-method

When we need to let the Spring container use the factory class to create objects into the Spring container, we can use the factory-bean and factory-method properties.

7.1.5.1 Configuring the instance factory to create objects

configuration file

    <!--Create an instance factory-->
    <bean class="com.sangeng.factory.CarFactory" id="carFactory"></bean>
    <!--Create with an instance factory Car put in a container-->
    <!--factory-bean Used to specify which factory object to use-->
    <!--factory-method Used to specify which factory method to use-->
    <bean factory-bean="carFactory" factory-method="getCar" id="car"></bean>

Create container get object test

        ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        //get the car object
        Car c = (Car) app.getBean("car");
        System.out.println(c);
7.1.5.2 Configuring a static factory to create objects

configuration file

    <!--Create with static factory Car put in a container-->
    <bean class="com.sangeng.factory.CarStaticFactory" factory-method="getCar" id="car2"></bean>

Create container get object test

        ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        //get the car object
        Car c = (Car) app.getBean("car2");
        System.out.println(c);

Spring-02

1. Annotation development

To simplify configuration, Spring supports the use of annotations instead of xml configuration.

2.Spring common annotations

2.0 Annotation development preparation

​ If you want to use annotation development, you must enable component scanning, so that annotated classes will be recognized. Spring can parse the annotations.

<!--Start the component scan, specify the package path corresponding to the scan, all classes in the package and its subpackages will be scanned, and the classes containing the specified annotations will be loaded-->
<context:component-scan base-package="com.sangeng"/>

2.1 IOC related understanding

2.1.1 @Component,@Controller,@Service ,@Repository

​ The above four annotations are all added to the class.

​ They can all act like bean tags. You can put the object of the annotated class into the Spring container.

​ You can choose any one when you actually use it again. But the last 3 annotations are semantic annotations.

​ If it is a Service class, @Service is required.

​ If the Dao class requires the use of @Repository

​ If it is a Controllerl class (learned in SpringMVC) requires the use of @Controller

​ If it is another class, you can use @Component

E.g:

The configuration file is as follows:

<?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: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/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--Start the component scan, specify the package path corresponding to the scan, all classes in the package and its subpackages will be scanned, and the classes containing the specified annotations will be loaded-->
    <context:component-scan base-package="com.sangeng"></context:component-scan>

</beans>

The classes are as follows:

@Repository("userDao")
public class UserDaoImpl implements UserDao {

    public void show() {
        System.out.println("Query the database and display the queried data");
    }
}

@Data
@NoArgsConstructor
@AllArgsConstructor
@Component("phone")
public class Phone {
    private double price;
    private String name;
    private String password;
    private String path;

}

@Service("userService")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserServiceImpl implements UserService {


    private UserDao userDao;

    private int num;

    private String str;


    public void show() {
        userDao.show();
    }
}

The test class is as follows:

public class Demo {
    public static void main(String[] args) {
        //Create a container
        ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        //get object
        UserDao userDao = (UserDao) app.getBean("userDao");
        Phone phone = (Phone) app.getBean("phone");
        UserService userService = (UserService) app.getBean("userService");
        System.out.println(phone);
        System.out.println(userService);
        System.out.println(userDao);
    }
}

2.2 DI related understanding

If a bean has been put into the Spring container. Then we can use the following annotations to implement property injection and let the Spring container help us complete the assignment of properties.

2.2.1 @Value

​ Mainly used for property injection that can be directly assigned such as String and Integer. Does not rely on setter methods, supports SpEL expressions.

E.g:

@Service("userService")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserServiceImpl implements UserService {
    private UserDao userDao;
    @Value("199")
    private int num;
    @Value("Sangeng Thatched Cottage")
    private String str;
    @Value("#{19+3}")
    private Integer age;


    public void show() {
        userDao.show();
    }
}

2.2.2 @AutoWired

​ Spring will automatically inject objects of the same data type into properties annotated with this annotation.

E.g:

@Service("userService")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDao userDao;

    @Value("199")
    private int num;
    @Value("Sangeng Thatched Cottage")
    private String str;

    @Value("#{19+3}")
    private Integer age;


    public void show() {
        userDao.show();
    }
}

The required attribute represents whether this attribute is required, and the default value is true. If it is true, an exception will occur if an object of the same type cannot be found in the Spring container to complete property injection.

2.2.3 @Qualifier

If there are multiple beans of the same type in the container, using @AutoWired alone cannot meet the requirements. At this time, you can add @Qualifier to specify the name of the bean to obtain bean injection from the container.

E.g:

    @Autowired
    @Qualifier("userDao2")
    private UserDao userDao;

Note: This direct cannot be used alone. It doesn't work alone

2.3 Notes on xml configuration files

@Configuration

​ is marked on the class, indicating that the current class is a configuration class. We can completely replace the xml configuration file with annotated classes.

​ Note: If the xml configuration is replaced with a configuration class, the spring container should use: AnnotationConfigApplicationContext

E.g:

@Configuration
public class ApplicationConfig {
}

@ComponentScan

​ Can be used in place of the context:component-scan tag to configure component scanning.

​ basePackages property to specify the packages to scan.

​ Note that it should be added to the configuration class.

E.g:

@Configuration
@ComponentScan(basePackages = "com.sangeng")//Specify packages to scan
public class ApplicationConfig {
}

@Bean

​ Can be used to replace bean tags, mainly for injection of third-party classes.

​ Use: Define a method, create the corresponding object in the method and return it as the return value. Then add the @Bean annotation to the method, and the value attribute of the annotation to set the name of the bean.

E.g:

@Configuration
@ComponentScan(basePackages = "com.sangeng")
public class ApplicationConfig {

    @Bean("dataSource")
    public DruidDataSource getDataSource(){
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
        druidDataSource.setUsername("root");
        druidDataSource.setUrl("jdbc:mysql://localhost:3306/mybatis_db");
        druidDataSource.setPassword("root");
        return druidDataSource;
    }

}

Note: If there is only one object of the same type in the container, we can leave the bean name unset.

The specific writing is as follows:

@Configuration
@ComponentScan(basePackages = "com.sangeng")
public class ApplicationConfig {

    @Bean
    public DruidDataSource getDataSource(){
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
        druidDataSource.setUsername("root");
        druidDataSource.setUrl("jdbc:mysql://localhost:3306/mybatis_db");
        druidDataSource.setPassword("root");
        return druidDataSource;
    }

}

The way to get it is as follows:

    public static void main(String[] args) {
        //Create an annotation container
        AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(ApplicationConfig.class);
		//Obtained from the bytecode object of the corresponding class
        DataSource bean = app.getBean(DataSource.class);
        System.out.println(userService);
    }

@PropertySource

​ Can be used instead of context:property-placeholder to let Spring read the specified properties file. Then you can use @Value to get the read value.

​ Use: Add the @PropertySource annotation to the configuration class, and the value attribute of the annotation to set the path of the properties file.

​ Then define member variables in the configuration class. Use the @Value annotation on member variables to get the read value and assign values ​​to the corresponding member variables.

E.g:

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis_db
jdbc.username=root
jdbc.password=root

read the file and get the value

@Configuration
@ComponentScan(basePackages = "com.sangeng")
@PropertySource("jdbc.properties")
public class ApplicationConfig {

    @Value("${jdbc.driver}")
    private String driverClassName;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;


    @Bean
    public DruidDataSource getDataSource(){
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setDriverClassName(driverClassName);
        druidDataSource.setUsername(username);
        druidDataSource.setUrl(url);
        druidDataSource.setPassword(password);
        return druidDataSource;
    }

}

Note: When using @Value to get the value in the read properties file, use ${key} instead of #{key}.

3. How to choose

①SSM

​ The IOC and DI of the classes in your own project use annotations, and the classes in the third-party jar package are configured using xml when configuring component scanning.

②SpringBoot

​ Pure annotation development

Spring-03

1. AOP

1.1 Concept

​ AOP is the abbreviation of Aspect Oriented Programming, which means: Aspect Oriented Programming. It is a technology that can enhance the dynamic unity of the program without modifying the original core code.

​ SpringAOP: Batch enhancements to the methods of bean s in the Spring container, and this enhancement is not coupled with the code in the original method.

1.2 Quick Start

1.2.1 Requirements

​ It is required that all methods of all classes under the service package in the _08_SpringAOP module be output before calling: the method is called.

1.2.2 Preparations

①Add dependencies

Need to add SpringIOC related dependencies and AOP related dependencies.

        <!--SpringIOC related dependencies-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.9.RELEASE</version>
        </dependency>
        <!--AOP related dependencies-->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.13</version>
        </dependency>
②Relevant bean s should be injected into the container

Enable component scan

<context:component-scan base-package="com.sangeng"></context:component-scan>

Add @Service annotation

@Service
public class PhoneService {

    public void deleteAll(){
        System.out.println("PhoneService middle deleteAll core code");
    }
}

@Service
public class UserService {


    public void deleteAll(){
        System.out.println("UserService middle deleteAll core code");
    }
}

1.2.3 Implementing AOP

①Enable AOP annotation support

Use the aop:aspectj-autoproxy tag

<?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">
    <!--Enable component scan-->
    <context:component-scan base-package="com.sangeng"></context:component-scan>
    <!--turn on aop Annotation support-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

</beans>
②Create a facet class

Create a class, add @Component and @Aspect to the class

Use the @Pointcut annotation to specify the method to be enhanced

Use the @Before annotation to identify the method where our enhanced code is located, and specify that the enhanced code is executed before the enhanced method is executed.

@Component
@Aspect
public class MyAspect {

//    Use the attributes in the Pointcut annotation to specify which methods to enhance
    @Pointcut("execution(* com.sangeng.service.*.*(..))")
    public void pt(){}

    /*
        Use the @Before annotation to specify that the method is enhanced code and is executed before the enhanced method is executed
        @Before Write the method annotated with @Pointcut on the attribute of : method name()
    */
    @Before("pt()")
    public void methodbefore(){
        System.out.println("method was called");
    }

}

1.2.4 Testing

    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        PhoneService phoneService = applicationContext.getBean(PhoneService.class);
        UserService userService = applicationContext.getBean(UserService.class);
        phoneService.deleteAll();

    }

1.3 AOP core concepts

  • Joinpoint (join point): The so-called join point refers to those points that can be enhanced. In spring, these points refer to methods, because spring only supports connection points of type method

  • Pointcut: The so-called pointcut refers to the enhanced connection point (method)

  • Advice (notification/enhancement): The so-called advice refers to the specific enhancement code

  • Target (target object): the object being enhanced is the target object

  • Aspect (aspect): is a combination of pointcut and advice (introduction)

  • Proxy: After a class is enhanced by AOP, it produces a resulting proxy class

1.4 Determination of tangent point

1.4.1 Pointcut Expressions

​You can use pointcut expressions to indicate which methods are to be enhanced.

Writing: execution([modifier] return value type package name. class name. method name (parameter))

  • The access modifier can be omitted, in most cases it is omitted
  • Return value type, package name, class name, method name can use asterisk * to represent any
  • A dot between the package name and the class name. Represents the class under the current package, and two dots ... represent the classes under the current package and its subpackages
  • The parameter list can use two dots ... to indicate any number, any type of parameter list

E.g:

execution(* com.sangeng.service.*.*(..))   express com.sangeng.service Any class under the package, any method name, any parameter list, any return value type
   
execution(* com.sangeng.service..*.*(..))   express com.sangeng.service Any class under the package and its subpackages, any method name, any parameter list, any return value type
    
execution(* com.sangeng.service.*.*())     express com.sangeng.service Any class under the package, any method name, no parameters, and any return value type
    
execution(* com.sangeng.service.*.delete*(..))     express com.sangeng.service Any class under the package requires that the method cannot have parameters and the return value type is arbitrary,Method name required delete beginning

1.4.2 Pointcut function @annotation

We can also add annotations to the methods to be enhanced. Then use @annotation to indicate what annotated method is enhanced.

Writing: @annotation (full class name of the annotation)

E.g:

Definition annotations are as follows

@Target({ElementType.METHOD})//This annotation can be added to the method
@Retention(RetentionPolicy.RUNTIME)
public @interface InvokeLog {
}

Add annotations to methods that need to be enhanced

@Service
public class PhoneService {

    @InvokeLog  
    public void deleteAll(){
        System.out.println("PhoneService middle deleteAll core code");
    }
}

Use @annotation in the aspect class to determine the method to be enhanced

@Component
@Aspect
public class MyAspect {

//    Use the attributes in the Pointcut annotation to specify which methods to enhance
    @Pointcut("@annotation(com.sangeng.aspect.InvokeLog)")
    public void pt(){}

    /*
        Use the @Before annotation to specify that the method is enhanced code and is executed before the enhanced method is executed
        @Before Write the method annotated with @Pointcut on the attribute of : method name()
    */
    @Before("pt()")
    public void methodbefore(){
        System.out.println("method was called");
    }
}

1.5 Notification Classification

  • @Before: pre-notification, executed before the target method is executed

  • @AfterReturning: Notification after return, executed after the target method is executed, if there is an exception, it will not be executed

  • @After: Post notification, executed after the target method, whether or not an exception occurs

  • @AfterThrowing: Exception notification, executed after the target method throws an exception

  • @Around: Surround advice, execute around the target method

Understand when different notifications are executed. (The following pseudo-code is used to understand the execution timing of a single notification, and cannot be used to understand the execution order in the case of multiple notifications. If we need to configure multiple notifications, we will choose to use the Around notification, which is more clear and easy to use)

    public Object test() {
        before();//@Before advance notice
        try {
            Object ret = target method();//target method call
            afterReturing();//Notify after @AfterReturning returns
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            afterThrowing();//@AfterThrowing exception notification notification
        }finally {
            after();//@After post notification
        }
        return ret;
    }

Surround advice is very special, it can enhance the target method in all directions.

E.g:

  	@Around("pt()")
    public void around(ProceedingJoinPoint pjp){
        System.out.println("Before the target method");
        try {
            pjp.proceed();//target method execution
            System.out.println("after the target method");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            System.out.println("The target method has an exception");
        }finally {
            System.out.println("finally enhancement in");
        }
    }

1.6 Obtaining information about the enhanced method

When we actually enhance the method, we often need to obtain the relevant information of the enhanced code, such as method name, parameter, return value, exception object, etc.

We can add a parameter of type JoinPoint to all notification methods except surround notifications. This parameter encapsulates information about the enhanced method. We can get all the information except the exception object and return value through this parameter.

E.g:

    @Before("pt()")
    public void methodbefore(JoinPoint jp){
        Object[] args = jp.getArgs();//Parameters passed in when the method is called
        Object target = jp.getTarget();//proxied object
        MethodSignature signature = (MethodSignature) jp.getSignature();//Get the object encapsulated by the enhanced method signature
        System.out.println("Before method was called");
    }

Case:

Requirement: It is required to output the full class name, method name, and parameters passed in when calling all methods of all classes under the service package.

@Component
@Aspect
public class PrintLogAspect {

    //Which methods to enhance
    @Pointcut("execution(* com.sangeng.service..*.*(..))")
    public void pt(){}

    //how to enhance
    @Before("pt()")
    public void printLog(JoinPoint joinPoint){
        //Output The name of the class where the enhanced method is located The method name The parameters passed in when calling joinPoint.getSignature().getName() joinPoint.getArgs()
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        //class name
        String className = signature.getDeclaringTypeName();
        //method name
        String methodName = signature.getName();
        //Parameters passed in when calling
        Object[] args = joinPoint.getArgs();

        System.out.println(className+"=="+methodName+"======"+ Arrays.toString(args));
    }
}

​ If you need to get the exception object or return value in the enhanced method, you need to add a parameter of the corresponding type to the method parameter and configure it using the attributes of the annotation. In this way, Spring will assign the data you want to get to the corresponding method parameters.

E.g:

    @AfterReturning(value = "pt()",returning = "ret")//Use the returning attribute to specify the parameter ret that assigns the return value of the target method to the following method
    public void AfterReturning(JoinPoint jp,Object ret){
        System.out.println("AfterReturning method was called");
    }
    @AfterThrowing(value = "pt()",throwing = "t")//Use the throwing attribute to specify that the exception object that occurs is assigned to the parameter t of the following method
    public void AfterThrowing(JoinPoint jp,Throwable t){
        System.out.println("AfterReturning method was called");
    }

​ I believe you must feel that the above acquisition method is particularly troublesome and difficult to understand. You can use the following all-purpose method.

​ Add a parameter of type ProceedingJoinPoint directly to the surround notification method. This parameter encapsulates information about the enhanced method.

When the proceed() method of this parameter is called, it is equivalent to the enhanced method being executed, and the return value after the call is equivalent to the return value of the enhanced method.

E.g:

    @Around(value = "pt()")
    public Object around(ProceedingJoinPoint pjp) {
        Object[] args = pjp.getArgs();//Parameters passed in when the method is called
        Object target = pjp.getTarget();//proxied object
        MethodSignature signature = (MethodSignature) pjp.getSignature();//Get the object encapsulated by the enhanced method signature
        Object ret = null;
        try {
            ret = pjp.proceed();//ret is the return value after the target method is executed
        } catch (Throwable throwable) {
            throwable.printStackTrace();//throwable is the exception object when an exception occurs
        }
        return ret;
    }

1.7 AOP application case

1.7.1 Requirements

The existing AI core function codes are as follows:

public class AIController {
    //AI auto answer
    public String getAnswer(String question){
        //AI core code worth 1 billion
        String str = question.replace("do you", "");
        str = str.replace("?","!");
        return str;
    }

    //AI fortune-telling
    public String fortuneTelling(String name){
        //AI fortune-telling core code
        String[] strs = {"The female offender was injured by the fugitive, and the lotus plant on the dry land could not survive, not only to eat two families, but also to brush three pots.","A flower is worn on the head, and it does not bloom all year round. When one wants to bloom, the person who picks the flowers does not come.","This life is born with a temper, and when you come up, your feet jump, you have a good temper with you, and you often quarrel and make trouble with people."};
        int index = name.hashCode() % 3;

        return strs[index];
    }
}

​ Now, in order to ensure data security, the name passed in fortuneTelling is required to be encrypted when calling the method. We need to decrypt the incoming parameters before they can be used. And the return value of the method should be encrypted and returned.

​PS: In the later stage, other methods may also be used for corresponding encryption processing.

String encryption and decryption can directly use the following tool classes:

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.SecureRandom;

public class CryptUtil {
    private static final String AES = "AES";

    private static int keysizeAES = 128;

    private static String charset = "utf-8";

    public static String parseByte2HexStr(final byte buf[]) {
        final StringBuffer sb = new StringBuffer();
        for (int i = 0; i < buf.length; i++) {
            String hex = Integer.toHexString(buf[i] & 0xFF);
            if (hex.length() == 1) {
                hex = '0' + hex;
            }
            sb.append(hex.toUpperCase());
        }
        return sb.toString();
    }

    public static byte[] parseHexStr2Byte(final String hexStr) {
        if (hexStr.length() < 1)
            return null;
        final byte[] result = new byte[hexStr.length() / 2];
        for (int i = 0;i< hexStr.length()/2; i++) {
            int high = Integer.parseInt(hexStr.substring(i * 2, i * 2 + 1), 16);
            int low = Integer.parseInt(hexStr.substring(i * 2 + 1, i * 2 + 2), 16);
            result[i] = (byte) (high * 16 + low);
        }
        return result;
    }

    private static String keyGeneratorES(final String res, final String algorithm, final String key, final Integer keysize, final Boolean bEncode) {
        try {
            final KeyGenerator g = KeyGenerator.getInstance(algorithm);
            if (keysize == 0) {
                byte[] keyBytes = charset == null ? key.getBytes() : key.getBytes(charset);
                g.init(new SecureRandom(keyBytes));
            } else if (key == null) {
                g.init(keysize);
            } else {
                byte[] keyBytes = charset == null ? key.getBytes() : key.getBytes(charset);
                SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
                random.setSeed(keyBytes);
                g.init(keysize, random);
            }
            final SecretKey sk = g.generateKey();
            final SecretKeySpec sks = new SecretKeySpec(sk.getEncoded(), algorithm);
            final Cipher cipher = Cipher.getInstance(algorithm);
            if (bEncode) {
                cipher.init(Cipher.ENCRYPT_MODE, sks);
                final byte[] resBytes = charset == null? res.getBytes() : res.getBytes(charset);
                return parseByte2HexStr(cipher.doFinal(resBytes));
            } else {
                cipher.init(Cipher.DECRYPT_MODE, sks);
                return new String(cipher.doFinal(parseHexStr2Byte(res)));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static String AESencode(final String res) {
        return keyGeneratorES(res, AES, "aA11*-%", keysizeAES, true);
    }

    public static String AESdecode(final String res) {
        return keyGeneratorES(res, AES, "aA11*-%", keysizeAES, false);
    }

    public static void main(String[] args) {
        System.out.println(
                "after encryption:" + AESencode("plaintext to be encrypted")
        );
        System.out.println(
                "after decryption:" + AESdecode("730CAE52D85B372FB161B39D0A908B8CC6EF6DA2F7D4E595D35402134C3E18AB")
        );
    }
}

1.7.2 Implementation

①Import dependencies
        <!--SpringIOC related dependencies-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.9.RELEASE</version>
        </dependency>
        <!--AOP related dependencies-->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.13</version>
        </dependency>
②Enable AOP annotation support
<?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:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">


    <!--Configure component scanning-->
    <context:component-scan base-package="com.sangeng"></context:component-scan>
    <!--start up AOP Annotation support-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

③Custom annotations

package com.sangeng.aspect;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Crypt {

}

④Add annotations to the target method

Note: The target object must remember to inject into the Spring container

@Controller
public class AIController {
	//....

    //AI fortune-telling
    @Crypt
    public String fortuneTelling(String name){
        System.out.println(name);
              //AI fortune-telling core code
        String[] strs = {"The female offender was injured by the fugitive, and the lotus plant on the dry land could not survive, not only to eat two families, but also to brush three pots.","A flower is worn on the head, and it does not bloom all year round. When one wants to bloom, the person who picks the flowers does not come.","This life is born with a temper, and when you come up, your feet jump, you have a good temper with you, and you often quarrel and make trouble with people."};
        int index = name.hashCode() % 3;

        return strs[index];
    }
}

⑤Define the facet class
package com.sangeng.aspect;

import com.sangeng.util.CryptUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class CryptAspect {

    //Determine the cut point
    @Pointcut("@annotation(com.sangeng.aspect.Crypt)")
    public void pt(){

    }

    //define notification
    @Around("pt()")
    public Object crypt(ProceedingJoinPoint pjp) {
        //Get the parameters when the target method is called
        Object[] args = pjp.getArgs();
        //Decrypt the parameter and pass it into the target method to execute after decryption
        String arg = (String) args[0];
        String s = CryptUtil.AESdecode(arg);//decrypt
        args[0] = s;
        Object proceed = null;
        String ret = null;
        try {
            proceed = pjp.proceed(args);//target method call
            //After the target method is executed, the return value needs to be obtained
            ret = (String) proceed;
            //Real return after encrypting the return value
            ret = CryptUtil.AESencode(ret);
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return ret;
    }

}

1.8 xml configuration AOP

①Define the facet class

public class MyAspect {


    public void before(JoinPoint joinPoint){
        System.out.println("before");
    }

//    @AfterReturning(value = "pt()",returning = "ret")
    public void afterReturning(JoinPoint joinPoint,Object ret){
        System.out.println("afterReturning:"+ret);
    }
//    @After("pt()")
    public void after(JoinPoint joinPoint){
        System.out.println("after");
    }

//    @AfterThrowing(value = "pt()",throwing = "e")
    public void afterThrowing(JoinPoint joinPoint,Throwable e){
        String message = e.getMessage();
        System.out.println("afterThrowing:"+message);
    }

    public Object around(ProceedingJoinPoint pjp){
        //get parameters
        Object[] args = pjp.getArgs();
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Object target = pjp.getTarget();
        Object ret = null;
        try {
            ret = pjp.proceed();//Execution of the target method
            //ret is the return value of the enhanced method
            System.out.println(ret);
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            System.out.println(throwable.getMessage());
        }
//        System.out.println(pjp);
        return ret;
    }
}

②The target class and the aspect class are injected into the container

The corresponding annotations are added to the aspect class and the target class. If you inject into the container using annotations, remember to enable component scanning.

Of course, you can also inject into the container using bean tags in xml.

@Component//Inject the aspect class into the container
public class MyAspect {
	//... Omit irrelevant code
}
@Service//Inject the target class into the container
public class UserService {
	//... Omit irrelevant code
}

③Configure 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:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--Enable component scan-->
    <context:component-scan base-package="com.sangeng"></context:component-scan>

    <!--configure AOP-->
    <aop:config>
        <!--define pointcut-->
        <aop:pointcut id="pt1" expression="execution(* com.sangeng.service..*.*(..))"></aop:pointcut>
        <aop:pointcut id="pt2" expression="@annotation(com.sangeng.aspect.InvokeLog)"></aop:pointcut>
        <!--Configure Aspects-->
        <aop:aspect ref="myAspect">
            <aop:before method="before" pointcut-ref="pt1"></aop:before>
            <aop:after method="after" pointcut-ref="pt1"></aop:after>
            <aop:after-returning method="afterReturning" pointcut-ref="pt1" returning="ret"></aop:after-returning>
            <aop:after-throwing method="afterThrowing" pointcut-ref="pt2" throwing="e"></aop:after-throwing>
        </aop:aspect>
    </aop:config>
</beans>

1.9 The multi-aspect sequence problem

​ In actual projects, we may have multiple aspects configured. In this case we will most likely need to control the order of the facets.

We have Spring has its own collation by default. (sorted by class name)

The default collation often does not meet our requirements, and we need special control.

​ If AOP is configured by annotation, you can add **@Order annotation to the aspect class to control the order. The smaller the attribute in @Order, the higher the priority. **

​ If it is an AOP configured in XML, it can be controlled by adjusting the configuration order.

E.g:

The following configuration method will first use the enhancements in CryptAspect, and then use the enhancements in APrintLogAspect

@Component
@Aspect
@Order(2)
public class APrintLogAspect {
    //Omit irrelevant code
}
@Component
@Aspect
@Order(1)
public class CryptAspect {
    //Omit irrelevant code
}

1.10 AOP principle - dynamic proxy

In fact, Spring's AOP is actually done using dynamic proxies at the bottom. And two dynamic proxies are used, namely JDK dynamic proxy and Cglib dynamic proxy.

So let's learn about these two dynamic agents and understand their differences.

1.10.1 JDK dynamic proxy

​ JDK's dynamic proxy uses the java.lang.reflect.Proxy class to implement. The class that needs to be proxied (enhanced) needs to implement the interface. And JDK dynamic proxy can only enhance the methods in the interface.

public 11 void main(String[] args) {
        AIControllerImpl aiController = new AIControllerImpl();
        //Enhance getAnswer method with dynamic proxy
        //1.JDK dynamic proxy
        //get classloader
        ClassLoader cl = Demo.class.getClassLoader();
        //An array of bytecode objects of the interface implemented by the proxied class
        Class<?>[] interfaces = AIControllerImpl.class.getInterfaces();
        AIController proxy = (AIController) Proxy.newProxyInstance(cl, interfaces, new InvocationHandler() {
            //When using the method of the proxy object, invoke will be called
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //proxy is the proxy object
                //method is the Method object encapsulated by the currently called method
                //args are the arguments passed in when calling the method
                //Call the corresponding method of the proxied object
                //Determine whether the current call is the getAnswer method
                if(method.getName().equals("getAnswer")){
                    System.out.println("enhance");
                }
                Object ret = method.invoke(aiController, args);
                return ret;
            }
        });
        String answer = proxy.getAnswer("Three in a row?");
		System.out.println(answer);
    }

1.10.2 Cglib dynamic proxy

​ It is implemented using the org.springframework.cglib.proxy.Enhancer class.

public class CglibDemo {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        //Set the bytecode object of the parent class
        enhancer.setSuperclass(AIControllerImpl.class);
        enhancer.setCallback(new MethodInterceptor() {
            //Using the proxy object to execute the method will call the intercept method
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                //Determine whether the currently called method is the getAnswer method and if it is enhanced
                if ("getAnswer".equals(method.getName())){
                    System.out.println("enhanced");
                }
                //Call the corresponding method in the parent class
                Object ret = methodProxy.invokeSuper(o, objects);
                return ret;
            }
        });
        //Generate proxy object
        AIControllerImpl proxy = (AIControllerImpl) enhancer.create();
//        System.out.println(proxy.getAnswer("How are you?"));
        System.out.println(proxy.fortuneTelling("Are you OK?"));
    }
}

1.10.3 Summary

​ JDK dynamic proxy requires that the proxy (enhanced) class must implement the interface, and the generated proxy object is equivalent to the brother of the proxy object.

Cglib's dynamic proxy does not require the proxy (enhanced) class to implement the interface, and the generated proxy object is equivalent to a subclass object of the proxy object.

By default, Spring's AOP uses the dynamic proxy of JDK first. If the dynamic proxy of JDK cannot be used, the dynamic proxy of Cglib will be used.

1.11 Switch the default dynamic proxy mode

​ Sometimes we need to modify the proxy mode of AOP.

​ We can modify it in the following ways:

If we configure AOP using annotations:

Set the proxy-target-class attribute of the aop:aspectj-autoproxy tag to true, and the proxy mode will be changed to Cglib

<aop:aspectj-autoproxy proxy-target-class="true"/>

If we use xml to configure AOP:

Set the proxy-target-class attribute of the aop:config tag to true, and the proxy mode will be changed to Cglib

<aop:config proxy-target-class="true">
</aop:config>

Spring-04

1.Spring integrates Junit

①Import dependencies

<!-- junit -->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
</dependency>
<!-- spring Integrate junit dependency -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.1.9.RELEASE</version>
</dependency>

② Write a test class

add to the test class

**@RunWith(SpringJUnit4ClassRunner.class)** annotation, specifies to let the test run in the Spring environment

@ContextConfiguration annotation, specifies the configuration file or configuration class that the Spring container needs to create

@RunWith(SpringJUnit4ClassRunner.class)//Let the tests run with the Spring test environment
@ContextConfiguration(locations = "classpath:Profile 1.xml")//Set up a Spring configuration file or configuration class
//@ContextConfiguration(classes = SpringConfig.class)
public class SpringTest {}

③Inject the object for testing

Inject the object to be tested in the test class, define a test method, and use the object to be tested in it.

@RunWith(SpringJUnit4ClassRunner.class)//Let the tests run with the Spring test environment
@ContextConfiguration(locations = "classpath:Profile 1.xml")//Set up a Spring configuration file or configuration class
//@ContextConfiguration(classes = SpringConfig.class)
public class SpringTest {
    
    // Which object you want to test, inject which object
    @Autowired
    private UserService userService;
    
    //Define test methods
    @Test
    public void testUserService() {
        userService.findById(10);
    }
    
}

2.Spring integrates Mybatis

​ If we want to integrate Mybatis into Spring, we need to use an integration package mybatis-spring

​ Official documentation: http://mybatis.org/spring/zh/index.html

①Import dependencies

	<!-- spring-jdbc -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>5.1.9.RELEASE</version>
    </dependency>

    <!-- mybatis integrated into Spring the integration package -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>2.0.4</version>
    </dependency>

    <!--mybatis rely-->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.4</version>
    </dependency>
    <!--mysql drive-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.47</version>
    </dependency>

    <!-- druid data source -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.1.16</version>
    </dependency>

②Inject integration related objects into the container

    <!--read properties document-->
    <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
    <!--Create a connection pool injection container-->
    <bean class="com.alibaba.druid.pool.DruidDataSource" id="dataSource">
        <property name="url" value="${jdbc.url}"></property>
        <property name="username" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
        <property name="driverClassName" value="${jdbc.driver}"></property>
    </bean>   
<!--spring Integrate mybatis Post-control creation and acquisition SqlSessionFactory Object-->
    <bean class="org.mybatis.spring.SqlSessionFactoryBean" id="sessionFactory">
        <!--Configure connection pool-->
        <property name="dataSource" ref="dataSource"></property>
        <!--configure mybatis the path to the configuration file-->
        <property name="configLocation" value="classpath:mybatis-config.xml"></property>
    </bean>

    <!--mapper scan configuration, scanned mapper object will be injected Spring in a container-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer" id="mapperScannerConfigurer">
        <property name="basePackage" value="com.sangeng.dao"></property>
    </bean>

The mybatis configuration file mybatis-config.xml is as follows:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <typeAliases>
        <package name="com.sangeng.domain"></package>
    </typeAliases>
</configuration>

③ Get the Mapper object from the container for use

    @Autowired
    private UserDao userDao;

3.Spring declarative transactions

3.1 Transaction review

3.1.1 The concept of transactions

​ Ensure that operations on a set of databases either succeed or fail at the same time

3.1.2 Four Features

  • isolation

    Multiple transactions must be isolated from each other and cannot interfere with each other

  • atomicity

    Refers to the transaction as an indivisible whole, similar to an indivisible atom

  • consistency

    It is guaranteed that the state of this set of data before and after the transaction is consistent. Either all succeed or all fail.

  • Persistence

    It means that once the transaction is committed, the data modified by this group of operations will really change. Even a subsequent database failure should not affect it.

3.2 Implementing declarative transactions

​ If we control the transaction ourselves, we need to add transaction control related code based on the original core code. In our actual development, this kind of transaction control operation is also very common. So Spring provides a declarative transaction method for us to control the transaction.

Transaction control can be achieved by simply adding an annotation (or xml configuration). When transaction control is not required, you only need to remove the corresponding annotation.

3.2.0 Case Environment Preparation

①Data initialization

CREATE DATABASE /*!32312 IF NOT EXISTS*/`spring_db` /*!40100 DEFAULT CHARACTER SET utf8 */;
USE `spring_db`;
DROP TABLE IF EXISTS `account`;
CREATE TABLE `account` (
  `id` INT(11) NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(50) DEFAULT NULL,
  `money` DOUBLE DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
INSERT  INTO `account`(`id`,`name`,`money`) VALUES (1,'Three shifts',100),(2,'thatched cottage',100);

②Spring integrates Mybatis

③Create Service and Dao

public interface AccountService {
    /**
     * transfer
     * @param outId The id of the transfer account
     * @param inId The id of the transfer account
     * @param money transfer amount
     */
    public void transfer(Integer outId,Integer inId,Double money);
}
@Service
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccoutDao accoutDao;

    public void transfer(Integer outId, Integer inId, Double money) {
        //Increase
        accoutDao.updateMoney(inId,money);
        //reduce
        accoutDao.updateMoney(outId,-money);
    }
}
public interface AccoutDao {

    void updateMoney(@Param("id") Integer id,@Param("updateMoney") Double updateMoney);
}

AccoutDao.xml is as follows:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.sangeng.dao.AccoutDao">


    <update id="updateMoney">
        update  account set money = money + #{updateMoney} where id = #{id}
    </update>
</mapper>

3.2.1 Annotation Implementation

①Configure the transaction manager and transaction annotation driver

Add the following configuration to the spring configuration file:

    <!--inject transaction manager Spring Container, you need to configure a connection pool-->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <!--Turn on the transaction annotation driver and configure the transaction manager used-->
    <tx:annotation-driven transaction-manager="txManager"/>
②Add notes

Transaction control can be achieved by adding the @Transactional annotation to the method or class that needs transaction control.

    @Transactional
    public void transfer(Integer outId, Integer inId, Double money) {
        //Increase
        accoutDao.updateMoney(inId,money);
//        System.out.println(1/0);
        //reduce
        accoutDao.updateMoney(outId,-money);
    }

Note: If it is added to a class, all methods of this class will be subject to transaction control. If it is added to a method, that method is subject to transaction control.

Note that because the bottom layer of declarative transactions is implemented through AOP, it is best to add all AOP-related dependencies.

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

3.2.2 xml implementation

①Configure the transaction manager
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
②Configure the transaction aspect
 	<!--Define notification classes for transaction management-->
    <tx:advice transaction-manager="txManager" id="txAdvice">
        <tx:attributes>
            <tx:method name="trans*"/>
        </tx:attributes>
    </tx:advice>

    <aop:config>
        <aop:pointcut id="pt" expression="execution(* com.sangeng.service..*.*(..))"></aop:pointcut>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pt"></aop:advisor>
    </aop:config>

Note that because the bottom layer of declarative transactions is implemented through AOP, it is best to add all AOP-related dependencies.

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

3.3 Property Configuration

3.3.1 Transaction propagation behavior propagation

When the transaction method is called nested, it is necessary to control whether to open a new transaction, which can be controlled by using the transaction propagation behavior.

Test Case:

@Service
public class TestServiceImpl {
    @Autowired
    AccountService accountService;

    @Transactional
    public void test(){
        accountService.transfer(1,2,10D);
        accountService.log();
    }
}
public class AccountServiceImpl implements AccountService {
	//...omit other irrelevant code
    @Transactional
    public void log() {
        System.out.println("print log");
        int i = 1/0;
    }

}
property valueBehavior
REQUIRED (must have)The outer method has a transaction, and the inner method joins. No outer layer, new inner layer
REQUIRES_NEW (must have a new transaction)The outer method has a transaction, and the inner method is newly created. There is no outer layer, and the inner layer is also newly built
SUPPORTS (supported)The outer method has a transaction, and the inner method joins. No outer layer, no inner layer
NOT_SUPPORTED (no support)Outer methods have transactions, inner methods do not. Neither the outer layer nor the inner layer
MANDATORY (mandatory to have the outer layer)The outer method has a transaction, and the inner method joins. The outer layer does not. Error in inner layer
NEVER (never allowed)The outer method has a transaction, and the inner method reports an error. The outer layer does not. There is no inner layer

E.g:

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void transfer(Integer outId, Integer inId, Double money) {
        //Increase
        accoutDao.updateMoney(inId,money);
        //reduce
        accoutDao.updateMoney(outId,-money);
    }

3.3.2 isolation level isolation

Isolation.DEFAULT uses the database default isolation level

Isolation.READ_UNCOMMITTED

Isolation.READ_COMMITTED

Isolation.REPEATABLE_READ

Isolation.SERIALIZABLE

   @Transactional(propagation = Propagation.REQUIRES_NEW,isolation = Isolation.READ_COMMITTED)
    public void transfer(Integer outId, Integer inId, Double money) {
        //Increase
        accoutDao.updateMoney(inId,money);
        //reduce
        accoutDao.updateMoney(outId,-money);
    }

3.3.3 Read-only readOnly

​ If the operations in the transaction are all read operations and no data write operations are involved, readOnly can be set to true. This improves efficiency.

    @Transactional(readOnly = true)
    public void log() {
        System.out.println("print log");
        int i = 1/0;
    }

Tags: Spring

Posted by clarky08 on Mon, 24 Oct 2022 22:13:12 +0530