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 value | Behavior |
---|---|
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; }