1, an overview of the

The Spring framework is an open source, loosely coupled, layered, configurable, one-stop enterprise-level Java development framework with IoC (Inversion of Control) and AOP (faceted programming) at its core. In Spring, the objects that make up the backbone of an application and are managed by the Spring IoC container are called beans. Beans are objects that are instantiated, assembled, and otherwise managed by the IoC container.

IoC is an idea that, at its core, devolves control. What it really means is that the job of manually creating beans, which programmers do by hand, is handed over to Spring, and the IoC container is the one that places and manages those beans. Programmers can write the creation of objects and their dependencies on each other in configuration files, meaning that beans and their dependencies are reflected in configuration metadata used by containers to achieve loose coupling.

DI stands for dependency injection, which means that a component receives injection of resources from a container in some predefined way, such as Setter methods. IoC is also known as DI.

IoC is also known as dependency injection (DI). –Spring5.1.3.RELEASE

AOP can encapsulate the logic that has nothing to do with business but is called by business, and make unified or specific function enhancement for application business. It can realize the decoupling of application business and enhanced logic. For example, functions such as log and transaction are realized by AOP.

The core modules of the Spring framework:

2. Quick start of Spring

2.1 Creating a Project

The environment is jdK1.8, Maven 3.6.3, and IDEa2021.1

Create a Maven project

2.2 Adding project dependency Information

Add the spring dependencies to the project’s pom.xml file. The quick start project only needs to import the context package, using Spring 5.1.3.RELEASE:

<project ... >... <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> < version > 5.1.3. RELEASE < / version > < / dependency > < / dependencies > < project >Copy the code

Click the Load Maven Changes button in the upper right corner for dependency installation

2.3 Creating a Configuration File

Create the Spring configuration file under the project SRC /main/resources

Initial configuration:

<? The 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"> </beans>Copy the code

To configure the application context of IDEA, click OK

2.4 Creating entity Classes

Create an entity class named Person under SRC /main/ Java to customize the package structure (mine is top.jtszt.bean). Get started quickly just to feel the effect of the inversion of control, so you don’t need to define the information in the class

2.5 the statement bean

Add a declaration for the Person class to the Spring configuration file and use the bean tag declaration in the beans body:

<! Class is the full class name of the class to be declared. > <bean id="person" class="top.jtszt.bean.Person"></bean> </beans>Copy the code

2.6 Creating a Startup Class

Under the custom package to create a startup class QuickStartApplication. Java

public class QuickStartApplication { public static void main(String[] args) { ApplicationContext ioc = new ClassPathXmlApplicationContext("ioc.xml"); Person person = (Person) ioc.getBean("person"); System.out.println(person); }}Copy the code

Using the ClassPathXmlApplicationContext here to load the configuration file, after the completion of the loading with ApplicationContext interface to receive, then call getBean method, to need access to the bean’s id, access to the bean and print

 top.jtszt.bean.Person@523884b2
Copy the code

Now that we have a quick start with the Spring framework, we can see that instead of manually going to the new object, we declare the object in a configuration file and fetch it from the factory.

3. IoC Part

3.1 the IoC container

Org. Springframework. Context. ApplicationContext interface represents the Spring IoC container, it is responsible for instantiate, configure, and assemble the bean. As you can quickly learn, you can write class declarations in configuration files (you can also use annotation-driven development, more on that below) and then load resources into an IoC container to be managed by the container. Before reading instances of beans through the IoC container, the IoC container itself needs to be instantiated. In the web application is usually through ClassPathXmlApplicationContext under the classpath loading configuration files (XML based configuration), to generate an IoC container:

 ApplicationContext ioc = new ClassPathXmlApplicationContext("ioc.xml");
Copy the code

In fact, you can return a BeanFactory, which is the basic implementation of the IoC container and features lazy loading of all beans; The ApplicationContext is a subinterface to the BeanFactory, which simplifies AOP integration, message mechanism, event mechanism, and extension to the Web environment while maintaining compatibility with basic features. The Official Spring documentation recommends this approach.

You should use an ApplicationContext unless you have a good reason for not doing so.

In addition you can FileSystemXmlApplicationContext from the file system or a URL to load XML configuration files; XmlWebApplicationContext and XmlPortletApplicationContext for Web and entry application.

3.2 Dependency Lookup (BASED on XML)

Dependency lookup refers to the fact that controlled objects in a container use the container’s API to find resources and collaborative objects that they depend on. Finding objects in this way requires calling the relevant API. The ioc.getBean(“person”) used above is a dependency lookup.

1) Context-based dependency lookup:

(1) Get by object ID, pass in the id of the object configured in the XML file, that is, the kind of quickly get started method;

If a bean of the same type is configured in the XML file, it will throw an exception. Therefore, the bean of the same type must be unique in the container.

 Person person = ioc.getBean(Person.class);
Copy the code

③ Use the type and ID parameter to find

 Person person = ioc.getBean("person", Person.class);
Copy the code

GetBeansOfType getBeansOfType getBeansOfType getBeansOfType

 Map<String, MyInterface> beans = ioc.getBeansOfType(MyInterface.class);
Copy the code

2) Find all beans in the IoC container

If you need to find all beans in the IoC container, you can also use the getBeanDefinitionNames method, which takes the ids of all beans, and then finds the corresponding beans based on their IDS

 String[] beanNames = ioc.getBeanDefinitionNames();
Copy the code

3) Delayed lookup

For some special scenarios, you need to rely on specific beans in the container, but you need a way to handle them when they don’t exist, rather than throwing exceptions. Assume that we don’t the Person class in the IoC container, so if direct getBean NoSuchBeanDefinitionException anomalies, what we need is to let him delay find this bean, how to do?

In the ApplicationContext, there is a method called getBeanProvider, which passes in the bean’s class information and returns an ObjectProvider<> object. An exception is thrown only when the getObject method of that object is called to retrieve the bean object inside.

Further, if the bean does not exist and I do not want it to throw an exception, what do I do?

ObjectProvider also has a method called getIfAvailable that gets the bean if it exists and returns null instead of an exception if it fails to get the bean.

3.3 Dependency Injection (BASED on XML)

Now we have been able to get to the object from the IoC container, but you can find these objects are no specific attributes, and dependency injection is a process, that is to say, for these properties we don’t have to manually to preach, but the hands of the IoC container, the container object for injection by means of reflection.

Modify the Person object:

public class Person { private String name; private Integer age; private String phone; private Car car; private List<Car> CarList; private Map<String, Object> testMap; public Person(String name, Integer age, String phone) { this.name = name; this.age = age; this.phone = phone; } public void setTestMap(Map<String, Object> testMap) { this.testMap = testMap; } public void setCar(Car car) {this.car = car; } public void setCarList(List<Car> carList) {CarList = carList; } public void setnameForPerson(String name) {this.name = name; } public void setAge(Integer age) {this.age = age; } public void setPhone(String phone) {this.phone = phone; } // Override toString method}Copy the code

There are two main forms of dependency injection, constructor – based and Setter – based.

1) Setter-based dependency injection;

The name attribute is the Xx of the corresponding setXx method, not the property defined in the class, and value is the value of the property:

<bean id="person1" class="top.jtszt.bean.Person"> <! <property name="nameForPerson" value="zhangsan"/> <property name="age" value="16"/> <property name="phone" value="135000"/> </bean>Copy the code

2) Constructor-based dependency injection;

In the XML file, assign values using the constructor-arg tag, where name is the constructor parameter name and value is the value:

<bean id="person2" class="top.jtszt.bean.Person"> <! <constructor-arg name="name" value="lisi"/> <constructor-arg name="age" value="18"/> <constructor-arg name="phone" value="135111"/> </bean>Copy the code

The constructor-arg name attribute can also be omitted here, but values must be written in constructor order

3) Other cases of assigning values to bean properties

① Assign the attribute to null;

 <bean id="person3" class="top.jtszt.bean.Person">
     <property name="name">
         <null/>
     </property>
 </bean>
Copy the code

② Assign values to attributes of reference types;

Create a Car class:

 public class Car {
     private String carName;
     private Integer price;
 ​
     public void setCarName(String carName) {this.carName = carName;}
     public void setPrice(Integer price) {this.price = price;}
     //重写toString
 }
Copy the code

Method 1: Assign values using bean tags

 <bean id="person4" class="top.jtszt.bean.Person">
     <property name="car">
         <bean class="top.jtszt.bean.Car">
             <property name="carName" value="benz"/>
             <property name="price" value="8800"/>
         </bean>
     </property>
 </bean>
Copy the code

Approach 2: Reference an external bean

<bean id="car1" class="top.jtszt.bean.Car"> <property name="carName" value="bmw"/> <property name="price" value="3380"/>  </bean> <bean id="person4" class="top.jtszt.bean.Person"> <property name="car" ref="car1"/> </bean>Copy the code

③ Assign a value to the set type attribute;

1) Assign to the List attribute:

<bean id="person5" class="top.jtszt.bean.Person"> <property name="carList"> <! List = new ArrayList<>(); --> <list> <! -- Method 1: < class=" top.jtszt.bean.car "> <property name="carName" value=" BYD "/> value="6600"/> </bean> <! <ref bean="car1"/> <! </list> </property> </bean>Copy the code

2) Assign the map attribute:

<bean id="person6" class="top.jtszt.bean.Person"> <property name="testMap"> <! Map = new LinkedHashMap<>(); --> <map> <! <entry key="key01" value="zhangsan"/> <! --> <entry key="key02" value-ref="car1"/> <! -- Method 3: Write the bean directly inside the entry tag as its value --> <entry Key ="key03"> <bean class=" top.jtszt.bean.car "> <property name="carName" value="honda"/> <property name="price" value="5200"/> </bean> </entry> </map> </property> </bean>Copy the code

3.4 Annotation-driven development

With the introduction of many annotations from Spring Framework3.0, it is possible to use an annotation-driven approach instead of XML configuration, which has become more common since then.

1) Configuration classes

The annotation driver needs a Configuration class. A Configuration class can be interpreted as an XML, as long as you tag it with @Configuration, that class is a Configuration class. Whereas in XML we use bean tags to declare beans, in annotation-driven methods we simply label @beans to represent bean declarations.

@configuration public class ConfClass {// The default method name is bean id, @bean public Person() {return new Person(); @bean public Person() {return new Person(); }}Copy the code

This is equivalent to creating an XML file and registering the Person class with id Person in the IoC container.

2) Dependent lookup

After complete the configuration class, dependent lookup required AnnotationConfigApplicationContext to initialize the loading container.

 ApplicationContext context = new AnnotationConfigApplicationContext(ConfClass.class);
 Person person = (Person)context.getBean("person");
 System.out.println(person);
Copy the code

3) Dependency injection

Method one: setter-based injection

You simply assign the property in the method using the set method and return the object after the assigned property.

 @Bean
 public Person person(){
     Person person = new Person();
     person.setName("zs");
     person.setAge(11);
     return person;
 }
Copy the code

Method two: construction-based injection

 @Bean
 public Person person(){
     return new Person("zs",11,"135111");
 }
Copy the code

4) Component registration

It is not practical to write many methods to annotate many bean annotations when more and more beans need to be registered. Therefore, Spring also provides schema annotations that allow for quick registration of components. The most basic annotation is the @Component annotation, which means that the annotated class will be registered as a Bean in the IoC container. That is, we can annotate the annotation directly on the Person entity class without having to manually write the Bean annotation method to register it.

@Component public class Person { private String name; . }Copy the code

The default is to register the Person class with the IoC container, with a small hump id of the class name. If you want to specify an ID, you can declare it using @Component(“xx”).

5) Component scanning

After a component is declared, a scan of the component is required to make the IoC container aware of its existence. We can directly configure an additional @ComponentScan(“xx”) annotation on the configuration class, xx is the package path, specifying the package to be scanned and its subpackages.

 @Configuration
 @ComponentScan("top.jtszt.bean")
 public class ConfClass { }
Copy the code

Can also create AnnotationConfigApplicationContext object when the incoming packet path, it can also be scanned for components.

 ApplicationContext context = new AnnotationConfigApplicationContext("top.jtszt.bean");
Copy the code

In addition to annotation-driven component scanning, component scanning is also possible based on XML configuration

 <beans>
     <context:component-scan base-package="top.jtszt.bean"/>
 </beans>
Copy the code

6) Declare several annotations for the bean

Component: Generic annotation to mark any class as a Spring Component.

@repository: Corresponds to the persistence layer (Dao layer), which is used for database-related operations.

@service: corresponds to the Service layer, which involves complex logic and requires the Dao layer.

@Controller: Corresponding to the Spring MVC control layer, the primary user accepts the user request and calls the Service layer to return data.

7) Annotation and XML fantasy linkage

Turn on annotation configuration in XML and declare the configuration class

 <beans>
     <context:annotation-config/>
     <bean class="top.jtszt.annotation.ConfClass"/>
 </beans>
Copy the code

Import the XML file with the @importResource annotation on the configuration class

 @Configuration
 @ImportResource("classpath:applicationcontext.xml")
 public class ConfClass { }
Copy the code

3.5 Other postures for Dependency Injection (annotation-based)

1) Annotated property injection

When property injection is performed on a Component scanned by @Component, the @Value(“xx”) annotation can be used to inject information on the corresponding property, and the entity class can do so without Setter methods.

@Component public class Person { @Value("zs") private String name; @Value("12") private Integer age; @Value("132111") private String phone; // toString}Copy the code

2) Injection from external configuration files

It is also allowed in Spring to inject properties after reading information from external configuration files. You first need to annotate the class with the @propertysource (“xx”) annotation, where xx represents the location of the configuration file. The attribute is then injected using the @value (“${YY}”) annotation, where YY represents the key corresponding to the Value in the configuration file.

① Create a configuration file my.properties under the classpath

 person.name=ls
 person.age=16
 person.phone=135123
Copy the code

Annotate @propertysource (“classpath:my.properties”) on entity class

③ Mark @Value with the corresponding attribute to set the Value

@Component @PropertySource("classpath:my.properties") public class Person { @Value("${person.name}") private String name; @Value("${person.age}") private Integer age; @Value("${person.phone}") private String phone; // toString}Copy the code

3) SpEL injection

SpEL is the Spring expression language, which has been supported since Spring3.0. SpEL supports invocation of attribute values, attribute parameters, method calls, array storage, logical calculations, and other functions. The syntax of SpEL is #{xx} and xx is an expression.

The most common are references to bean properties or method calls using SpEL.

Example: Now that we have two entity classes Bird and Dog, we can use SpEL to inject the age property of Bird into the age property of Dog.

@Component public class Bird { @Value("16") private String age; . }Copy the code
@Component public class Dog { @Value("#{bird.age}") private String age; . }Copy the code

3.6 Automatic Injection

1) the @autowired

The @Autowired annotation is directly annotated on the attribute or Setter method in the bean, and the IoC container will find the bean of the corresponding type in the container and assign the value to the corresponding attribute according to the corresponding type of attribute, so as to achieve automatic injection.

If the container can’t find the corresponding type of bean, then throws NoSuchBeanDefinitionException abnormalities, also can add the required = false on the annotation, so when they can’t find the bean into the null.

Let’s say we have a Car class, and now the Person class has an attribute called Car and we want to inject it automatically:

@Component public class Person { private String name; @Autowired private Car car; // toString}Copy the code

★ Principle of Autowired injection:

The type of the property is retrieved and searched in the IoC container. If one is found, it is returned. Throw an exception if it cannot be found (the bean cannot be found); If more than one is found, the container is searched for the corresponding ID based on the attribute ID, and if there is an ID, the container is returned, and an exception is thrown (this type of bean is not unique).

2) @ the Qualifier

Injection will fail if there are multiple beans of the same type in the container (and their ids do not match). You can use the @Qualifier annotation to explicitly specify which bean to inject.

 @Autowired
 @Qualifier("bmw")
 private Car car;
Copy the code

3) @ Primary

In addition to using @qualifier, you can also annotate @primary on the injected Bean and specify the default injected Bean.

@Configuration @ComponentScan("top.jtszt.bean") public class ConfClass { @Bean @Primary public Car bmw(){ return new Car(); }}Copy the code

4) @ the Resource

@Resource comes from the JSR250 specification, which differs from @AutoWired in that: @Autowired is injected by type, and @Resource is injected directly by attribute name/Bean name, which is a combination of @Autowired and @Qualifier.

 @Resource(name="benz")
 private Car carCar;
Copy the code

5) @ Inject

Inject is from JSR330 specification and needs to import Javax. Inject dependency before using. The usage is equivalent to @AutoWired, but because it is a JSR specification, it is not restricted by the Spring framework.

3.7 Similarities and differences between DI and DL

  • Different target of action

    • Dependency injection usually targets class members
    • The target of dependent lookup can be in the method itself or in the method itself
  • Different implementations

    • Dependency injection is usually passive reception with a context
    • Dependent lookup usually actively uses context search

3.8 bean senior

1) Scope of bean

Singleton: The single-instance bean is created before the container is created and has only a single object in the container;

Prototype: a multi-instance bean will only be created when a method is called to retrieve the bean, one at a time;

Request: Each HTTP request generates a new bean that is valid only within the current HTTP request;

Session: Each HTTP request generates a new bean that is valid only for the current HTTP session;

Global-session: the global session scope, which is only meaningful in portlet-based web applications, is no longer available in Spring5. Portlets are small Java Web plug-ins that can generate snippets of semantic code, such as HTML. They are based on portlet containers and can handle HTTP requests like servlets. However, unlike servlets, each portlet has a different session.

Single-instance/multi-instance can be specified through the scope property in bean configuration.

<! <bean id="car1" class="top.jtszt.bean.Car" scope="prototype"/>Copy the code

2) Type of bean

(1) common bean

Beans like those created earlier are normal beans

 @Component
 public class Person { ... }
Copy the code

(2) the factory bean

Factory beans are created using factory methods using FactoryBeans when the bean object creation is too complex or there are some special policies. The FactoryBean interface is a factory for creating objects. If the Bean implements the FactoryBean interface, it is no longer an ordinary Bean itself and will not function in the actual business logic, but in the object created.

The FactoryBean interface has three methods:

Public interface FactoryBean<T> {// Return the created object @Nullable T getObject() throws Exception; @nullable Class<? > getObjectType(); Default Boolean isSingleton() {return true; }}Copy the code

A few things to note about factoryBeans:

  • The beans it creates are placed directly in the IoC container
  • It is loaded with the initialization timing of the IoC container, which is created before the container is generated
  • Its mechanism for producing beans is delayed production and is created only when a method is called to retrieve the beans
  • The beans produced using it are singleton by default

The difference between BeanFactory and FactoryBean

BeanFactory: Class inheritance structure, it is the implementation of Spring’s top-level container; It is the deepest container in terms of the class composition structure, and ApplicationContext combines the BeanFactory at the bottom level.

Factorybeans: Factory beans that create objects and can be used to directly create objects that have a complex initialization process.

3) Bean life cycle

  • The Bean container finds the definition of the Spring Bean in the configuration file.

  • The Bean container creates an instance of a Bean using the Java Reflection API.

  • If some attribute values are involved use the set() method to set some attribute values.

  • If the Bean implements the BeanNameAware interface, call the setBeanName() method, passing in the Bean’s name.

  • If the Bean implements the BeanClassLoaderAware interface, call the setBeanClassLoader() method, passing in an instance of the ClassLoader object.

  • Similar to the above, if other *.aware interfaces are implemented, the corresponding methods are called.

  • If you have, and load the Bean Spring container of related objects BeanPostProcessor, perform postProcessBeforeInitialization () method

  • If the Bean implements the InitializingBean interface, execute the afterPropertiesSet() method.

  • If the Bean definition in the configuration file contains the init-method attribute, the specified method is executed.

  • If you have, and load the Bean Spring container of related objects BeanPostProcessor, perform postProcessAfterInitialization () method

  • When it is time to destroy a Bean, execute the destroy() method if the Bean implements the DisposableBean interface.

  • When it is time to destroy a Bean, execute the specified method if the Bean’s definition in the configuration file contains the destroy-method attribute. reference

4) How the bean is instantiated

① Register the bean directly with the

tag or with the @bean or @Component annotations

② Use FactoryBean to instantiate beans

③ Use static factory method factory-method to instantiate beans

④ Use the factory-bean + factory-method method to instantiate the bean

5) Thread-safety of single-instance beans

By default, single instance beans are not thread-safe (as long as there are mutable member attributes), which means that if you define objects/primitive type variables/static variables with data storage capabilities at the global level of a single instance bean, then there is a thread-safe problem.

Solutions:

  • Make the Bean multi-instance by declaring its Scope=”prototype”
  • This is defined using the local variable ThreadLocal

4. AOP

4.1 summary of AOP

In OOP development, some repetitive operations can be separated into modules, which can reduce the amount of code, but still cannot fundamentally solve the code redundancy. In this case, we can decouple these repetitive operations into slices by dynamically proxying them into existing objects at run time, which is AOP, which complements OOP.

AOP, or aspect oriented programming, is essentially a business on demand enhancement of methods that have nothing to do with business logic (e.g. Log printing, permission verification, data caching, etc.) is removed as an enhancer, and enhanced by dynamic proxy. From this we can also realize that AOP also has the role of decoupling, and AOP can achieve componentization, pluggable function extension.

AOP is designed to be a crosscutting enhancement of the original business logic, with the underlying run-time dynamic proxy mechanism.

Unlike OOP, which focuses on objects, AOP’s core is facets.

4.2 the term

(1) Target object: refers to the proxy object, that is, the object to be enhanced;

② Join points: In Spring, join points refer to all methods in the target object.

③ pointcut: refers to the target object to enhance the join point, the target object in the join point may be many, but need to enhance may not be all, so the pointcut must be join point, but the join point is not necessarily the pointcut;

Notice: the code used to enhance the object (such as: log printing, recording, etc.);

⑤ Proxy object: refers to the combination of target object and notification

⑥ Aspect: refers to the combination of pointcuts and advice

4.3 Notification Type

Pre-notification Before: fired Before the target method (pointcut method) is called;

Post-notification After: fired After the target method (pointcut method) is called;

AfterReturing: fires after the target method (pointcut method) returns successfully;

Exception notification AfterThrowing: Triggered after the target method (pointcut method) has/throws an exception;

Notification Around: It takes the target object and the method to execute, so it can be accessed at any point in the program’s execution.

Proceed (); // Proceed (); // catch(e){// catch(e){finally{//Copy the code

4.4 Pointcut Expressions

AOP configuration in Spring is based on pointcut expressions to find specific ways to cut in (enhance), so we need to understand the various ways pointcut expressions can be written before implementing AOP.

1) Syntax of pointcut expressions

Execution (Access qualifier method return value type method full class name (parameter list type) [throws] Exception full class name)

2) Wildcard

If the package name is.. Represents all subpackages (recursively), if the argument is.. Said do not restrict parameters, if the package name/method called * indicates that all packages/method, and expression of support | | && operator

Such as:

1) execution (public int top. JTSZT. Impl. MyCalculator. * (int, int)), (2) the execution (int top. JTSZT.. *. * (..) )Copy the code

Which is cut into the top. JTSZT. Impl. MyCalculator with class, all the return value is an int type with two int type parameter method of the public

< span style = “max-width: 100%; clear: 100%; clear: 100%

4.5 AOP Implementation (based on XML)

Background: Target for top. JTSZT. Impl. MyCalculator, it is on the top. The JTSZT. System. The realization of the Calculator interface, including the add, sub, div, multi these four points, The section class is top.jtszt.utils.logutils, including logStart, logReturn, logException, logEnd and logAround. Now we need to use the section class to cut into the target object.

The first step is to import the required dependencies for AOP in Maven, including Spring-AOP (which is dependent on spring-Context), AopAlliance, AspectJWeaver, and Cglib. You then declare the AOP configuration in spring’s configuration file, where you need to import the AOP namespace.

① Section class injection IoC: configure beans for the section class;

② Configure pointcut expression: Aop is then configured using the < AOP :config> tag. To reuse pointcut expressions, we can first declare pointcuts using the < AOP :pointcut> tag. Its expression attribute is the pointcut expression. (Note: The cut class must be injected into the IoC container)

③ Define the aspect class: use the < AOP :aspect> tag to define, ref attribute points to the aspect class bean, then define various notification methods in the tag body;

There are five tags that can define a notification method. In the tag body, the method attribute is the notification method name, and the pointcut attribute points to the pointcut expression defined above. < AOP :before> stands for pre-notification; < AOP :after-returning> represents return notification. You can use the RETURNING attribute to define the name of the variable that will receive return values, passed in as a parameter in the cut method. < AOP :after-throwing> represents an exception notification. You can use the Throwing attribute to define the name of the variable that receives the exception information and pass it in as an argument in the cut method.

stands for post-notification; < AOP :around> stands for surround advice.

<beans> <! LogUtils2 class=" top.jtszt.utils.logutils "/> <! --> < AOP :config> <! - define breakthrough point expression - > < aop: pointcut id = "myPoint" expression = "execution (public * top. JTSZT. Impl. MyCalculator. * (int, int))" / > <! < AOP :aspect ref="logUtils2"> <! Aop :before method="logStart" pointcut ="myPoint"/> <aop:before method="logStart" pointcut ="myPoint"/> <! Aop: post-returning method="logReturn" pointcut-ref="myPoint" RETURNING ="result"/> <aop: post-returning method="logReturn" pointcut-ref="myPoint" returning="result"/> Aop :after-throwing method="logException" pointcut-ref="myPoint" throwing="exception"/> <aop:after-throwing method="logException" pointcut-ref="myPoint" throwing="exception"/> <! Aop :after method="logEnd" pointcut ="myPoint"/> <aop:after method="logEnd" pointcut ="myPoint"/> <! <aop:around method="logAround" pointcut-ref="myPoint" /> </aop:aspect> </aop:config> </beans>Copy the code

4.6 AOP Implementation (annotation-based)

1) Inject IoC into Aspect classes: annotate the Aspect classes with @Component and @aspect annotations

2) Configure the Pointcut expression: Define an empty method in the aspect class and declare the Pointcut expression using the @pointcut annotation to reuse the expression below;

 @Pointcut("execution(public int top.jtszt.impl.MyCalculator.*(int,int))")
 public void pointcutExpression(){}
Copy the code

3) Define notification method:

① @Before() : indicates that the method is started Before;

② @afterreturning () : indicates that it is cut after the method returns normally, after which the parameter name of the returned value can be declared;

③ @afterthrowing () : indicates that an exception is thrown after the method is entered, after the declaration of the exception parameter name;

④ @After() : indicates that the method is at the end of the entry (e.g. Try.. Finally as in catch);

⑤ @around () : indicates that this is a circular notification method. The circular method is executed before the other four notification methods. The return value of this method represents the return value of the actual method.

@Before("pointcutExpression()") public static void logStart(){} @AfterReturning(value="pointcutExpression()",returning =  "result") public static void logReturn(Object result){} @AfterThrowing(value="pointcutExpression()",throwing = "exception") public static void logException(Exception exception){} @After("pointcutExpression()") public static void logEnd(){}Copy the code

4) Turn on annotation AOP

If you use XML + annotations, you can configure < AOP: Aspectj-Autoproxy /> to turn on annotation AOP in XML.

If you use pure annotations, you can turn on annotation AOP with the @EnableAspectJAutoProxy annotation in the configuration class.

4.7 Notification Method Parameters

As with the native dynamic proxy, if you need to get the parameters and method name of the input method in the notification method, you need to pass in a JoinPoint parameter. Some of the more common methods are:

  • Object JoinPoint.gettarget () : Gets the unbrokered target Object
  • Object JoinPoint.getThis() : Gets the proxy Object
  • Object[] JoinPoint.getargs () : Gets the argument list of the entry method
  • Signature JoinPoint.getSignature() : Obtains the method Signature
  • String signature.getName () : Gets the method name
  • Method(MethodSignature) signature.getMethod () : Gets Method information

Note that since the return value of the surrounding advice method represents the return value of the call to the actual method, a parameter of type ProceedingJoinPoint is passed in. Call the Proceed () method with this object to get the return value of the actual method. This statement is also equivalent to calling the invoke() method in a dynamic proxy.

4.8 Execution sequence of multiple facets

If more than one aspect class makes an entry to the same method, follow the outside-in rule (in hexadecimal order of the unicode encoding of the aspect class name).

For example, the outer section class A is AspectOne, and the inner section class B is AspectTwo.

The execution sequence is as follows: A Pre-notification method →B pre-notification method → Actual method →B Return/exception notification method →B post notification method →A Return/exception notification method →A post notification method

If you want to change the Order in which the sections are executed, you can use the @order annotation to set the priority of the section class, passing in an int. The lower the value, the higher the priority. The default is the lowest priority.

In addition, the same type of notification methods in the same aspect are executed in Unicode encoding order.

4.9 Use AOP to do transaction control

Background information: the bookstore carries out book sales activities, and the members have balance information in the system. After the user buys books, the system needs to reduce the book inventory and the user balance, which is a whole (a transaction). Now you need to use AOP for transaction control to ensure consistency between the two operations.

Process: Let Spring manage the database connection pool and jdbcTemplate. DAO uses the auto-assembled jdbcTemplate for database operations. Service does the specific checkout method. Then let Spring use AOP to do transaction control over the checkout method.

1) Environmental preparation

Maven dependencies include mysql-connector-Java, spring-tx, C3P0, spring-JDBC, IOC, and AOP dependencies.

② Prepare the database table

User information table:

 CREATE TABLE t_account(
     username VARCHAR(50) PRIMARY KEY,
     balance INT
 )
Copy the code

Book Information Table:

 CREATE TABLE t_book(
     isbn VARCHAR(50) PRIMARY KEY,
     book_name VARCHAR(50),
     price INT
 )
Copy the code

Book Inventory table:

 CREATE TABLE t_book_stock(
     isbn VARCHAR(50),
     stock INT,
     CONSTRAINT fk_isbn FOREIGN KEY(isbn) REFERENCES t_book(isbn)
 )
Copy the code

Operation database:

@Repository public class BookDAO { @Autowired JdbcTemplate jdbcTemplate; Public void updateBalance(String userName, int price){String SQL = "UPDATE t_account SET balance=balance-? WHERE username=?" ; jdbcTemplate.update(sql,price,userName); Public int getPrice(String isbn){String SQL = "SELECT price FROM t_book WHERE isbn=?" ; return jdbcTemplate.queryForObject(sql, Integer.class, isbn); } public void updateStock(String isbn){String SQL = "UPDATE t_book_stock SET stock=stock-1 WHERE isbn=?" ; jdbcTemplate.update(sql,isbn); }}Copy the code

Service method (for convenience of not writing interfaces) :

@Service public class BookService { @Autowired private BookDAO bookDAO; Public void checkout(String username,String isbn){bookdao.updatestock (isbn); Bookdao.updatebalance (username, bookdao.getPrice (isbn)); }}Copy the code

XML also has operations like packet scanning that I won’t post here.

2) Configure declarative transactions (based on XML)

The above Service method does not do transaction management. If an exception occurs after the destocking method is executed, the inventory is successfully subtracted by 1, but the user balance is not deducted, which is obviously not possible. Next we’ll manage it transactally, starting with an XML-based configuration that relies on tx and AOP namespaces.

The data source needs to be configured first, and since we used the jdbcTemplate autoloassembly above, we’ll configure it here as an aside.

<beans> <! Db.properties --> <context:property-placeholder location="db.properties"/> <! - c3p0 connection pool - > < bean id = "ds" class = "boPooledDataSource com.mchange.v2.c3p0.Com" > < property name = "user" value="${jdbc.user}"/> <property name="password" value="${jdbc.password}"/> <property name="jdbcUrl" value="${jdbc.jdbcUrl}"/> <property name="driverClass" value="${jdbc.driverClass}"/> <property name="maxPoolSize" value="${jdbc.maxPoolSize}"/> <property name="minPoolSize" value="${jdbc.minPoolSize}"/> </bean> <! - configured jdbcTemplate - > < bean id = "jdbcTemplate" class = "org. Springframework. JDBC. Core. JdbcTemplate" > < property name="dataSource" ref="ds"/> </bean> </beans>Copy the code

Then configure Spring provides the transaction manager, when using JDBC/MyBatis persistence can use DataSourceTransactionManager to do the transaction manager.

 <bean id="tm" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
     <property name="dataSource" ref="ds"/>
 </bean>
Copy the code

You then need to tell Spring which methods are transactional using the advice tag in the TX namespace, where transaction-Manager points to the transaction manager, and attributes under that tag has only one tag method, The name attribute is used to match transaction methods (wildcards can be used), as well as other attributes:

  • Timeout Sets the timeout to automatically terminate a transaction and roll back the transaction.
  • Read-only Sets the transaction to read-only.
  • No-rollback -for specifies which exceptions are not rolled back. The name of the exception class is passed in. The default value is empty.
  • Rollback-for Rolls back when the method raises an exception, passing in the full class name of the exception; The default is to catch all runtime exceptions and errors;
  • Isolation Modifies the isolation level of a transaction;
  • Propagation Indicates the propagation behavior of specified transactions.

These attributes can also be configured in the @Transactional annotation.

<tx:advice id="myAdvice" transaction-manager="tm"> <! <tx:method name="*"/> <tx:method name="checkout" timeout="-1" read-only="false"/> <tx:method name="checkout" timeout="-1" Read-only ="false"/> <tx:method name="get*" read-only="true"/> </tx:attributes> </tx:advice>Copy the code

The above is just a declaration of transaction methods, but you actually need to set the pointcut for transaction management, and only when you successfully cut in will there be subsequent transaction management. That is, a transactional method must be a pointcut, but a pointcut need not be a transactional method.

<aop:config> <aop:pointcut id="txPoint" expression="execution(* top.jtszt.*.*.*(..) ) "/ > <! <aop:advisor advice-ref="myAdvice" pointcut-ref="txPoint"/> </aop:config>Copy the code

3) Configure declarative transactions (annotation-based)

First need to add @ at configuration class EnableTransactionManagement said open transaction manager, also can open up in the XML file based on declarative transaction annotations.

 @Configuration
 @EnableTransactionManagement
 @ComponentScan("top.jtszt")
 public class ConfClass {}
Copy the code
 <tx:annotation-driven transaction-manager="tm"/>
Copy the code

Then configure the data source and transaction manager in the configuration class

@ Configuration @ EnableTransactionManagement @ ComponentScan (" top. JTSZT ") public class ConfClass {/ / read the Configuration file @ Bean public Properties properties() throws IOException { Properties properties = new Properties(); properties.load(new FileReader("db.properties")); return properties; @bean public ComboPooledDataSource dataSource(Properties Properties) throws PropertyVetoException { ComboPooledDataSource ds = new ComboPooledDataSource(); ds.setUser(properties.getProperty("jdbc.user")); ds.setPassword(properties.getProperty("jdbc.password")); ds.setJdbcUrl(properties.getProperty("jdbc.jdbcUrl")); ds.setDriverClass(properties.getProperty("jdbc.driverClass")); return ds; } public jdbcTemplate jdbcTemplate jdbcTemplate(DataSource DataSource){jdbcTemplate jdbcTemplate = new JdbcTemplate(); jdbcTemplate.setDataSource(dataSource); return jdbcTemplate; } / / configuration transaction manager @ Bean public DataSourceTransactionManager DataSourceTransactionManager (DataSource DataSource) { DataSourceTransactionManager tm = new DataSourceTransactionManager(); tm.setDataSource(dataSource); return tm; }}Copy the code

You then need to tell Spring which methods are Transactional, using @Transactional, and that it is best to set the rollbackFor attribute, which has other attributes similar to

.

@Transactional(rollbackFor = {Exception.class}) public void checkout(String username,String isbn){... }Copy the code

Also, @Transactional can be set ona class to indicate that all methods are Transactional.

4) Transaction propagation behavior

The transaction method Settings have a property that sets the propagation behavior of a transaction. What is the propagation behavior of a transaction?

Transaction propagation behavior refers to the way a transaction method is run when called by another transaction method. Spring defines the propagation behavior as follows:

  • REQUIRED: run a transaction if there is one, otherwise start a new transaction and run it in its own transaction (transaction attributes inherit from the larger transaction);
  • REQUIRES_NEW: The current method must start a new transaction, run in its own transaction, and suspend if a transaction is running;
  • SUPPORTS: Methods run in a transaction if there is one running, otherwise they do not run in a transaction.
  • NOT_SUPPORTED: The current method should not run in a transaction and will be suspended if one is running;
  • MANDATORY: The current method must run inside a transaction or an exception is thrown.
  • NEVER: The current method should not run inside a transaction, otherwise an exception will be thrown;
  • NESTED: If a transaction is running, the current method should run in a NESTED transaction of that transaction, otherwise a new transaction should be started and run in its own transaction.

5) Transaction invalidation

In general, transaction failures occur in the following scenarios:

  • In SSM development, Spring and SpringMVC are in charge of two containers. In this case, if SpringMVC scans @Service, it will inject no transaction methods for @Controller, which will invalidate the transaction. So the configuration of declarative transactions must be loaded by the Spring container.
  • If the @Transactional annotation is annotated on the interface, but the implementation class uses the Cglib proxy, the transaction is invalidated. You annotate the interface, but Cglib proxies take the implementation classes directly to build the proxy object, bypassing the transaction management of the interface.
  • The transaction catches RuntimeException by default. If an Exception is thrown, the transaction is not caught by default, so it is generally declared to catch Exception explicitly.
  • If a try-catch exception is handled in a Service method, the transaction interceptor will be presented with no exception, which will cause the transaction to fail.
  • If a method in the same class calls another method with transaction control, the transaction will also be invalidated when called directly.

References:

  • The Spring Framework 5.1.3.release documentation
  • Learn Spring- Nuggets from 0
  • JavaGuide-Spring
  • Spring singleton Bean thread safety issues -CSDN
  • Spring Bean lifecycle – Blogosphere
  • Spring IOC container source analysis _Javadoop
  • Spring5 system architecture -CSDN
  • Leifeng Spring, Spring MVC, MyBatis course – Bilibili