preface

Article content output source: Pull hook education Java high salary training camp;

Summary of the spring

Spring advantage

1. Low coupling and high cohesion

2. AOP programming support

Declarative thing support

4. Support testing

5, easy to inherit other excellent frameworks

The core structure

Spring’s core container is the bottom core, Context, and Bean. Provides services to the superarchitecture.

AOP is the use of dynamic proxy implementation of section-oriented programming, used to extract the same code in different businesses, reduce code duplication and reduce the program see the degree of coupling.

JDBC encapsulates database operations, and makes it easy to integrate ORM frameworks and provide declarative management of database transactions.

WEB containers mainly encapsulate servlets and can easily integrate the SpringMVC framework to implement the traditional MVC architecture. Develop web application projects.

TEST is convenient for us to carry out unit TEST on each module.

core idea

IOC

-Chuck: No, it’s not an IOC inversion of control. That is to say, when we find an object, we do not need to create a new object, but tell the IOC controller, I want this object, then the IOC controller will assign this object to you, you just use it. Just like when we look for a wife, we tell the matchmaking agency what kind of partner we want. The matchmaking agency has a lot of resources and will find the one you want according to your conditions. And then arrange for you to meet. You’ll be in charge of dating whoever he finds you, and nothing else, the dating agency will clean up your mess.

DI

DI: Dependancy injection (DI) describes the same thing as IOC. It means that the IOC container infuses the class with dependent objects that it needs. You can see dependency injection from the perspective of the IOC container. IIOC control flipping, on the other hand, gives control over object creation to the IOC container from a class or Bean perspective.

AOP

AOP Aspect Oridented Programming. In order to separate the same business between different businesses into a slice. Then this part of the same business code is injected into the original business logic through the way of dynamic proxy, so as to achieve the enhancement of the original business logic. Reduced code duplication and coupling between codes.

Handwritten IOC, AOP

Design patterns

The factory pattern

The factory pattern is to create the desired objects through the factory instead of creating them yourself. This reduces coupling between codes. For example, a clothing factory can produce clothes, pants, shoes, socks, hats and so on. We want to use clothes, we don’t have to make them ourselves, but we tell the factory what you want, and the factory will produce what you want. This is the factory model. The main task is to hand over the creation of instances of objects to the factory class and manage them.

Factories are divided into simple factory pattern, factory method pattern and abstract factory pattern.

Simple Factory pattern: The simplest of the factory patterns, instances are created directly from factories. In the example above, if you want clothes, the clothing factory will make a dress for you.

Factory method pattern: Is the factory itself is not to create the entity object, but rather through the corresponding downstream factory to create and then in return, it was a bit of general factory factory, factory always manages all child factories, factory production only the specified goods, every word when inform headquarters want what things, headquarters will notify the corresponding sub factory production, to get the product after the return to the customer.

Abstract factory: An abstract factory class that declares what objects can be implemented, but leaves the implementation to the concrete factory. An abstract factory is like a foreskin company. It tells you that it can make clothes, and therefore food. For example, if you want clothes, it will give you the contact information of a clothing factory, through which you can get clothes. Want latiao, then he will give you a food company to push the contact information, let the food company do for you.

The difference between the factory method pattern and the abstract factory pattern is that the factory method pattern mainly produces a certain class of goods. And abstract factory, I don’t care what you achieve, as long as you say you can do this product, I can speak for you.

The singleton pattern

The singleton pattern indicates that the object will be created only once. So the following features are needed:

1. Private constructor

Static member variables

3. The common GET method allows other objects to get unique instances.

Singleton mode includes hungry lazy double check, thread-safe lazy, static internal flow, etc. In fact, are different ways to achieve it, the same to meet the above characteristics.

The hungry type

Private static Singleton sing=new Singleton(); private static Singleton sing=new Singleton(); Private Singleton(){} public static Singleton getInstance(){return Singleton(); }} Advantage: This method completes the initialization at class load time, obtains the object but is fast. Avoid multithreading but synchronization problems. Disadvantages: Slow class loading. Lazy loading is not achieved, and if the instance is not used from start to finish, it will cause a waste of memory.Copy the code

LanHanShi

public class Singleton{ private static Singleton sing; private Singleton(){ } private static Singleton getInstance(){ if(sing==null){ sing= new Singleton(); } return sing; }} Advantages: Save resources compared with hungry people. Disadvantages: the first load is slow, and multithreading can be problematic.Copy the code

Thread-safe slob style

public class Singleton{ private Singleton(){ } private Static Singleton sing; public static synchronized Singleton getInstance(){ if(sing==null){ sing=new Singleton(); } return sing; }} Advantages: Multithreading can be used well disadvantages: each time the getInstance method is substituted for the synchronization operation, causing unnecessary synchronization overhead. After all, you don't need synchronization most of the time.Copy the code

Double check lock

public class Singleton(){ private Singleton(){} private volalite static Singleton sing; public Singleton getInstance(){ if(sing==null){ synchronized(Singleton.calss){ if(sing==null){ sing= new Singleton(); } } } return sing; }} Advantages: thread-safe, avoids redundant thread synchronization, saves resources Disadvantages: the first instance is slow, and may fail under high concurrencyCopy the code

Static inner class

public calss Singleton(){ private Singleton(){} private static class SingletonInner(){ private static final Singleton sing=new Singleton(); } public Singleton getInstance(){ return SingletonInner.sing; }} sInstance is not initialized when the Singleton class is first loaded. The virtual machine loads the SingletonHolder and initializes the sInstance only when the getInstance method is first called. Advantages: High resource utilization, thread safety, avoiding redundant synchronization.Copy the code

The proxy pattern

The proxy model is what we want to do with the proxy object, like when we want to grab the ticket home, we don’t want to watch the ticket, so we get the proxy to do it for us. The proxy mode includes static proxy and dynamic proxy.

Static proxy: A concrete proxy class that knows at compile time what this proxy can do. Just like the agent to grab tickets, it can only do the agent to grab tickets, but not on your behalf to grab money ha ha.

Dynamic proxies: Dynamic proxies, on the other hand, are not implemented at compile time. Instead, it is generated dynamically during program execution based on the JVM’s reflection mechanism. The better known are JDK dynamic proxies and Cglib dynamic proxies.

JDK dynamic proxy: Primarily implements the InvocationHandle interface and implements its invoke method.

Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = null; // Pre-enhanced.... Result = method.invoke(obj,args); // Post-enhanced... return result; }})Copy the code

Cglib dynamic proxies: You need to introduce its dependencies in much the same way as JDK dynamic proxies. Implement the MethodInterceptor interface and intercept method.

public Object getCglibProxy(Object obj) { return Enhancer.create(obj.getClass(), new MethodInterceptor() { @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { Object result = null; // Pre-enhanced... result = method.invoke(obj,objects); // Post-enhanced... return result; }}); }Copy the code

Spring IOC application

BeanFactory vs. ApplicationContext

BeanFactory is spring’s top-level interface that provides some basic functionality and specifications, while ApplicationContext is an interface that integrates beanFactory’s interface and adds some functionality. Much of spring’s functionality is implemented through the applicationContext interface.

How to start IOC

Java environment:

ClassPathXmlApplicationContext: from the root of class loading configuration file (relative path).

FileSystemXmlApplicationContext: from a disk loading configuration file path (absolute path).

Start the spring container AnnotationConfigApplicationContext: pure annotation mode.

Web environment:

Configure the web.xml startup. Specify the configuration file to load for startup.

<context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param> <! - use the listener to start the Spring IOC container - > < listener > < listener - class > org. Springframework. Web. Context. The ContextLoaderListener </listener-class> </listener>Copy the code

Configure the web.xml startup. Specifies the configuration classes to load at startup.

<! ContextloaderListener: <context-param> <param-name>contextClass</param-name> <param-value>org.springframework.web.context.support.AnnotationConfigWebAppli cationContext </param-value> </context-param> <! <context-param> <param-name>contextConfigLocation</param-name> <param-value>com.lagou.edu.SpringConfig</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener </listener-class> </listener>Copy the code

Three ways to instantiate beans

1. Use the constructor without arguments. Instantiation is instantiated by default by calling the object’s no-argument constructor through reflection.

2. Use static injection.

<! - using static methods to create objects of configuration Settings - > < bean id = "userService" class = "com. Lagou. Factory. The BeanFactory" factory-method="getTransferService"></bean>Copy the code

3. Use example object injection.

<! - using instance methods to create objects of configuration mode - > < bean id = "the beanFactory" class = "com. Lagou. Factory. Instancemethod. The beanFactory" > < / bean > < bean id="transferService" factory-bean="beanFactory" factorymethod="getTransferService"></bean>Copy the code

The scope of the Bean is already full life

A Bean can be created with scope to select scope, and the schema is singleton. You can choose a multi-example pattern.

Singleton: Objects are created when the container is created, during project initiation initialization. Objects are destroyed only when the container is destroyed, that is, the same life cycle as an IOC container.

Prototype: When you need to use this object, a new object instance is created. The IOC container is only responsible for creating objects, not destroying them. Objects live when they are in constant use, and when they are not, they wait for the JVM garbage collection to destroy them.

There are three ways of dependency injection

1. Setter injection

Constructor injection

Annotation injection: @autowired

Difference between Autowired and Resource annotations

Autowired is a Spring provided annotation, @Resource is a JavaEE provided annotation.

Autowired uses type-by-type injection. When a class has more than one Bean, the @qualifier is used to specify a unique Bean. By default, @resource installs byName automatic injection.

3, @resource can execute name and type. It can be injected by type or name.

Lazy loading

Lazy loading of Bean object creation. For singleton’s singleton beans, the default is immediately loaded.

Single Bean: XML: configure lazy-init=trueTo enable lazy loading, the default value isfalseAnnotation:@lazyWhole beans: Added the following to the beans TAB:default-lazy-init 
Copy the code

Lazy loading, where the Bean is created when it is ready to be used. This can be used in applicationContext –beanFactory –singletonObjects to hold singleton beans. The storage structure used is currentHashMap.

FactoryBean and the BeanFactory

The BeanFactory interface is the top-level interface of a container and defines the basic behavior of the container. A factory responsible for creating and managing beans. There are two kinds of Beanfactory: ordinary beans and factory beans.

FactoryBean can generate a Bean instance for us, and we can customize the Bean creation process with factoryBean.

Rear processor

Spring provides interfaces to two types of back-end processors. BeanPostProcessor and spring BeanFactoryPostProcessor.

After the BeanFactory is initialized, you can implement the BeanFactoryPostProcessor to do some post-processing.

After instantiating a Bean object but not going through the entire loading process, you can use BeanPostProcessor for post-processing.

BeanPostProcessor is bean-specific and can take the current Bean and process it.

public interface BeanPostProcessor { @Nullable default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } @Nullable default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { return bean; }}Copy the code

This interface provides two methods that are executed before and after the Bean’s initialization method. It is similar to defining a BeanPostProcessor class with the method specified in init-method when defining a Bean. By default, all beans in the entire Spring container are processed. The method parameters are Object and String. The first parameter is the instance of each bean, and the second parameter is the value of the name or ID attribute of each bean. So we can use the second parameter to determine the specific bean we are going to process.

Spring BeanFactoryPostProcessor is a typical application: for the beanFactory PropertyPlaceholderConfifigurer. Used to replace references to external files in configuration files, such as applicationContext.xml referencing jdbc.properties to get the actual database configuration information.

@FunctionalInterface public interface BeanFactoryPostProcessor { void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException; } / / need to implement it @ Override public void postProcessBeanFactory (ConfigurableListableBeanFactory the beanFactory) throws BeansException { System.out.println(beanFactory); BeanDefinition beanDefinition = beanFactory.getBeanDefinition("lagouBean"); String beanClassName = beanDefinition.getBeanClassName(); }Copy the code

Can get to the beanfactory, thus through the beanfactory. GetBeanDefinition () get to BeanDefinition object, to get to the bean bean properties.

Note: When the BeanFactoryPostProcessor method is called, the bean is not instantiated yet, and the bean is just resolved into a BeanDefifinition object

Spring IOC source in depth analysis

  • Benefits: Improved training of code architecture thinking, in-depth understanding of the framework

  • The principle of

    • Principle of focus: Focus on the main line
    • Macro principles: Focus on the source code structure and business processes from God’s perspective (downplay the details of writing specific lines of code)
  • Read source code methods and skills

    • Breakpoints (look at the call stack)

    • Lead Lead (Find Left)

    • Experience (doXXX in the Spring framework, where specific processing is done)

  • Spring source code construction

    • Download source (Github)

    • Install Gradle 5.6.3 (like Maven) Idea 2019.1 Jdk 11.0.5

    • Import (takes some time)

    • Compile project (order: core-OXm-context-beans-aspects – AOP)

      • Project – > tasks – > compileTestJava

The IOC container initializes the body process

  • PrepareRefresh () pretreatment before the flush. It mainly sets the startup time, initializes the placeholders in the configuration file, and verifies whether the configuration information is correct.
  • To obtain the Beanfactory.
  • Beanfactory preparation, configure some properties of the Beanfactory. Like the context’s classloader
  • Post-processing after BeanFactory preparation is complete. Is a hook function.
  • Register the BeanPostProcessor(the Bean’s post-processor) to execute before and after the Bean is created
  • Initialize the MessageSource components, and adding information allpication. SingletonObjects.
  • Initialize the event dispatcher and add it to singletonObjects
  • OnRefresh (). Subclasses override this method to customize the logic when the container is refreshed.
  • Register application listeners. ApplicationListener Interface listener
  • Initialize all beans that are not set for delayed loading
  • The context is refreshed and the event is published.

Refresh AbstractApplicationContext in main () method:

Public void refresh() throws BeansException, IllegalStateException {// Add object lock, Avoid to refresh and close calls at the same time synchronized (enclosing startupShutdownMonitor) {/ * * * is the first step: Preprocessing before the flush * Preprocessing of the flush * Sets the system boot time * verifies that the environment information contains the necessary properties */ prepareRefresh(); /** * get BeanFactory; The default implementation is DefaultListableBeanFactory * load BeanDefinition and register to BeanDefitionRegistry * / ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); /** * prepareBeanFactory(BeanFactory) */ prepareBeanFactory(BeanFactory); /** * prepareBeanFactory(BeanFactory); */ postProcessBeanFactory(BeanFactory); */ postProcessBeanFactory(BeanFactory); / * * * step 5: instantiate and call for spring BeanFactoryPostProcessor interface beans * / invokeBeanFactoryPostProcessors (the beanFactory); / * * * step 6: register BeanPostProcessor post processor (Bean), around creating Bean of * / registerBeanPostProcessors (the beanFactory); /** * Step 7: Initialize MessageSource component (do internationalization; Message binding, message parsing); */ initMessageSource(); / * * * step 8: distributing device initialization events * / initApplicationEventMulticaster (); /** * step 9: Subclass override this method to customize the logic */ onRefresh() when the container is refreshed; /** * Step 10: Register application listeners. Listeners are registered * bean */ registerListeners() that implement the ApplicationListener interface. /** * step 11: * Initializes all remaining non-lazy-loaded singleton beans * Initializes creating non-lazy-loaded singleton bean instances (with no properties set) * populates properties * Initializes method calls (such as calling afterPropertiesSet method, init-method method) * Call BeanPostProcessor (post processor) to the rear of the bean instance * / finishBeanFactoryInitialization (the beanFactory); /** * step 12: * Refresh the context. The main thing is to call the LifecycleProcessor's onRefresh() method and publish events (ContextRefreshedEvent) */ finishRefresh(); } catch (BeansException ex) { ... } finally { resetCommonCaches(); }}}Copy the code

The process for creating a BeanFactory

Through the overall process, the beanFactory is obtained in the second step of the Refresh () method. The obtained subprocess occurs in the obtainFreshBeanFactory () method.

AbstractRefreshableApplicationContextprotected ConfigurableListableBeanFactory obtainFreshBeanFactory() { / / refresh the Beanfactory, call AbstractRefreshableApplicationContext refreshBeanFactory (); Return getBeanFactory(); }Copy the code
//AbstractRefreshableApplicationContext.java protected final void refreshBeanFactory() throws BeansException { // Check whether Bean exists in BeanFactory. Beanfactory if (hasBeanFactory()) {destroyBeans(); closeBeanFactory(); } the try {/ / create a DefaultListableBeanFactory DefaultListableBeanFactory the beanFactory = createBeanFactory (); / / to serialize the beanFactory id the beanFactory. SetSerializationId (getId ()); // Customize some attributes of the Bean factory customizeBeanFactory(beanFactory); // Load the beantions loadBeanDefinitions(beanFactory); synchronized (this.beanFactoryMonitor) { this.beanFactory = beanFactory; } } catch (IOException ex) { throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); }}Copy the code

BeanDefifinitionLoad the resolution registration process

Resource localization: refers to the process for the defifinition of BeanDefifinition. In plain English, this means finding the XML text that defines the Javabean information

And encapsulate it as a Resource object.

The user-defined Javabeans are represented as the data structures in the IoC container, and the numbers in the container

The structures are defifinition

process

  • Check whether beans exist in the BeanFactory. Destroy the Bean as it exists and close the BeanFactory

  • Create a DefaultListableBeanFactory

  • Serialize the ID of the beanFactory

  • Customize some properties of the beanFactory

  • Load beanDefinitions for the application

    • Read the XML information and save the XML information in the Document object.

    • Parse the Document object, encapsulate the BeanDefinition object, and register it

      • Gets the number of existing BeanDefinition objects

      • Registered BeanDefinition

      • Returns the number of newly registered BeanDefinitions

The so-called registration is to package the Bean information defined in the encapsulated XML as a Bean defifinition object and put it into a CurrentHashMap. The BeanFactory is used to organize these BeanDefifinition in a CurrentHashMap structure.

BeanCreate a process

Bean creation process occurred in AbstractApplicationContext. Refresh () method finishBeanFactoryInitialization (the beanFactory).

The following operations are performed:

* Initializes all remaining non-lazy-loaded singleton beans * Initializes creating non-lazy-loaded singleton bean instances (with no properties set) * populates properties * Initializes method calls (such as calling afterPropertiesSet method, init-method method) * Call BeanPostProcessor (post-processor) to postposition the instance beanCopy the code

The life cycle of a springBean

Call the Bean’s constructor or factory method to instantiate the Bean, depending on the configuration

2. Use dependency injection to complete configuration injection of all property values of the Bean

3. If the Bean implements the BeanNameAware interface, Spring calls the Bean’s setBeanName() to pass in the id of the current Bean

@Override
	public void setBeanName(String name) {
		System.out.println(name);
	}
Copy the code

4. If the Bean implements the BeanFactoryAware interface, call setBeanFactory() to pass in a reference to the current factory instance

@Override
	public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
		System.out.println(beanFactory);
	}
Copy the code

5. If the Bean implements the ApplicationContextAware interface, pass in a reference to the current applicationContext instance by calling setApplicationContext.

@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		System.out.println(applicationContext);
	}
Copy the code

If the BeanPostProcessor is associated with the Bean, Spring will call the pre-initialization method of the modified interface. PostProcessBeforeInitialization () is the method of pre-processing, Spring AOP is using it.

7. If the Bean implements the InitializingBean interface, the afterPropertiesSet method needs to be implemented

@Override
	public void afterPropertiesSet() throws Exception {
		System.out.println("afterPropertiesSet");
	}
Copy the code

8. If the init method is specified in the configuration file via the init-method property. Call this method.

9, if BeanPostProcessor associated with Bean, Spring the initialization method of the interface will be called postProcessAfterInitialization (). The Bean can then be used by the application system.

If scope=”singleton” is specified in the Bean tag, the Bean method singletonObjects will be added to the cache pool

11. If the Bean implements the DisposableBean interface, Spring will call the deStory () method to dispose of the spring Bean.

Lazy loading

During the initialization phase, all the Bean information in the XML is parsed into a BeanDefinition object and stored in a HashMap for initialization. And then refresh the finishBeanFactoryInitialization ().

Then go to the preInstantiateSingletons() method.

That is, only non-abstract, singleton, non-lazy-loaded beans are instantiated during the Bean instantiation phase. And put the Bean into the cache pool, singletonObjects, and then fetch it directly from singletonObjects when fetched.

A Bean with lazy loading is set when called. It is initialized on getBean() and in the final method cache.

Circular dependencies

Cyclic dependency is A dependency on B when object A is instantiated and on A when object B is instantiated. So how does Spring solve this problem? It can only solve the problem of circular dependencies when injected by setter or annotation, but not by constructor injection. Because instantiating an object through a constructor is done directly, there is no way to handle it during instantiation.

Spring addresses loop dependencies:

Using the three-level cache:

SingletonObjects: earlySingletonObjects: singletonFactoriesCopy the code

Process:

Trigger all lazy loaded Bean instantiations first.

2. If it is a getBean, it will check whether it exists in the level 1 cache pool and determine if it is being created. The aBean has not yet been created, so it is false, so null is returned.

If null is returned, it is not created. Call createBean() to create the Bean

CreateBean actually calls doCreateBean() to create the Bean

5. In the doCreateBean() method, the Bean instance is first created through the constructor, but the Bean has not yet set its properties.

6. Determine if pre-exposure is required, and place the Bean in level 3 cache if necessary.

7. Start adding attributes to the Abean object. Call the populateBean() method.

8. Call applyPropertyValues() in the populateBean() method to handle property dependencies.

9. Through a series of calls, resolveReference() takes an instance of a bBean from the BeanFactory and fills the aBean with properties.

10, when obtaining the bBean found, found the bBean, found that the bBean is not created, start to create. Same steps as aBean creation. It is only when it is time to populate the properties of the bBean that the dependency is discovered. So get the aBean from beanfactory.getBean ().

11, in the time to get aBean from the cache to find, level cache no, but found that the aBean is creating, so take from the level cache, cash back no, then go to the level cache to get, do some processing, and then add the aBean to the level cache, and delete the aBean in the level cache. At this point, the level 2 cache earlySingletonObjects contains only aBean objects. The third-level cache singletonFactories only has BBeans.

After obtaining the aBean, return the Bean directly

13. Then we return to the place where we get the aBean when we populate the property for the bBean.

14. This completes the property injection of the bBean, and the rest of the operation is done until the bBean is instantiated.

15. After the bBean is instantiated, it is added to the level 1 cache pool. And fill the secondary and tertiary cache to delete.

16. At this point, it’s back to the aBean to populate the bBean, getting the bBean from beanFactory.getBean(). So the next step is to complete the aBean instantiation process.

[img-6RR43IKF-1591430719245] [Zlf.quellanan.xyz /image-20200…]

17. Like BBeans, the instantiated abeans are added to the level 1 cache and removed from the level 2 and 3 caches.

The problem of cyclic dependencies is solved here.

The general flow of classes and methods:

  • DefaultListableBeanFactory# preInstantiateSingletons / / entrance
    • / / create BeanA AbstractBeanFactory# getBean ()
    • AbstractBeanFactory#doGetBean(
      • DefaultSingletonBeanRegistry# getSingleton () / / determine whether to create, not to continue
      • DefaultSingletonBeanRegistry# getSingleton () call createBean () to create
        • AbstractAutowireCapableBeanFactory#createBean()
        • AbstractAutowireCapableBeanFactory#doCreateBean()
          • DefaultSingletonBeanRegistry# addSingletonFactory () / / join the l3 cache
          • AbstractAutowireCapableBeanFactory# populateBean () / / filling properties
            • AbstractAutowireCapableBeanFactory#applyPropertyValues
              • BeanDefinitionValueResolver#resolveValueIfNecessary
              • Found BeanA depend on the BeanB BeanDefinitionValueResolver# resolveReference / /
                • Get BeanB AbstractBeanFactory# getBean () / /
                • AbstractBeanFactory#doGetBean(
                • . Repeat BeanA’s steps
                • DefaultSingletonBeanRegistry# getSingleton () / / get BeanA from l3 cache, to join the second level cache
                • Bean B is instantiated.
                • DefaultSingletonBeanRegistry# addSingleton / / added to the level of the cache.
            • Bean A populates the properties
          • Complete instantiation
          • Add Bean A to the level-1 cache and remove it from the level-2 and level-3 caches.

Sequence diagram:

Spring AOP applications

The concept of AOP

Join points: When a method starts, when it ends, when it runs properly, when a method fails, etc., these special points are called join points. Every method in a project has join points, and join points are candidate points.

Pointcuts: Specify which specific methods AOP ideas want to influence.

Advice to enhance

1. Crosscutting logic

2, azimuth points, add crosscutting logic to some connection points, then these connection points are azimuth points, describe the specific cutting time.

Aspect aspects are pointcuts plus enhancements

Aspect Aspect = pointcut + enhancement

= pointcut (locking method) + azimuth point (special timing in locking method) + crosscutting logic

The main purpose of these definitions is to lock down where to insert crosscutting logic code

Spring implements the idea of AOP primarily through dynamic proxies.

Dynamic proxies include JDK dynamic proxies and Cglib dynamic proxies.

  • JDK dynamic proxies require a proxy object implementation interface
  • Cglib dynamic proxies do not implement proxy interfaces

By default, Spring selects JDK dynamic proxies or Cglib dynamic proxies based on whether the propped object implements an interface. You can also configure to enforce which proxy Spring uses.

AOP proxy approach

There are three kinds of

  • Pure annotations
  • XML + annotation
  • Pure XML

XML

Configuration:

<beans xmlns=" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation=" http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd "> <! <aop:config> < AOP :aspect id="logAspect" ref="logUtils"> <! Pointcuts lock the method we are interested in, using AspectJ syntax expressions --> <! --<aop:pointcut id="pt1" expression="execution(* *.. *. * (..) )"/>--> <aop:pointcut id="pt1" expression="execution(* com.lagou.edu.service.impl.TransferServiceImpl.*(..) ) "/ > <! Pointcut --> <! Aop :before method="beforeMethod" pointcut-ref="pt1"/> <! Aop :after, execute anyway --> <! <aop:around method="arroundMethod" pointcut-ref="pt1"/> </aop:aspect> </aop:config>Copy the code

Code:

Public class LogUtils {/** * Run before the service logic starts */ public void beforeMethod(JoinPoint JoinPoint) {Object[] args = joinPoint.getArgs(); for (int i = 0; i < args.length; i++) { Object arg = args[i]; System.out.println(arg); } system.out. println(" execute....... before business logic starts execution ") ); Public void afterMethod() {system.out.println (" Execute at the end of the service logic, execute....... regardless of whether the service logic is abnormal." ); Public void exceptionMethod() {system.out.println (".......") ); } public void successMethod(Object retVal) {system.out.println (".......") ); Public Object arroundMethod(ProceedingJoinPoint ProceedingJoinPoint) throws Throwable { System.out.println(" Surround beforeMethod in notification...." ); Object result = null; Try {/ / control / / result being performed on the original business logic = proceedingJoinPoint. Proceed (proceedingJoinPoint. GetArgs ()); }catch(Exception e) {system.out.println (" ExceptionMethod in surround notification...." ); }finally {system.out. println(" After method...." ); } return result; }}Copy the code

XML + annotation

Add annotation driver to XML

<! Cglib --> <aop:aspectj-autoproxy/>Copy the code

Code:

@Component @Aspect public class LogUtils { @Pointcut("execution(* com.lagou.edu.service.impl.TransferServiceImpl.*(..) )") public void pt1(){} /** * Before the service logic starts */ @before ("pt1()") public void beforeMethod(JoinPoint JoinPoint){Object[] args = joinPoint.getArgs(); for (int i = 0; i < args.length; i++) { Object arg = args[i]; System.out.println(arg); } system.out. println(" execute....... before business logic starts execution ") ); } /** * execute After("pt1()") public void afterMethod() {system.out.println (" execute After(), execute......." ); } @afterthrowing ("pt1()") public void exceptionMethod() {system.out.println (".......") ); } @afterreturning (value = "pT1 ()", RETURNING = "retVal") public void successMethod(Object retVal) { System.out.println(" run....... if the business logic is normal" ); @around (" pT1 ()") public Object arroundMethod(ProceedingJoinPoint ProceedingJoinPoint) throws Throwable {system.out. println(" Surround beforeMethod in notification...." ); Object result = null; Try {/ / control / / result being performed on the original business logic = proceedingJoinPoint. Proceed (proceedingJoinPoint. GetArgs ()); }catch(Exception e) {system.out.println (" ExceptionMethod in surround notification...." ); }finally {system.out. println(" After method...." ); } return result; }}Copy the code

Pure annotations

Replace the configuration of the enabled function in the configuration file.

So add a configuration in config:

@EnableAspectJAutoProxy 
Copy the code

web.xml

<web-app> <display-name>Archetype Created Web Application</display-name> <! <context-param> <param-name>contextClass</param-name> <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value> </context-param> <! <context-param> <param-name>contextConfigLocation</param-name> <! --<param-value>classpath:applicationContext.xml</param-value>--> <param-value>com.lagou.edu.config.SpringConfig</param-value> </context-param> <! Start Spring's IOC container with a listener --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> </web-app>Copy the code

Spring declarative transaction support

The transaction

A transaction is generally a database transaction and represents a set of logical processes in which all units of logic either succeed or fail. Ensure data consistency and security.

Characteristics of transactions

Four properties of things: ACID, consistency, atomicity, isolation and persistence

  • Atomicity: Indicates that the operation of a transaction is an atomic, indivisible, smallest unit. All or nothing.
  • Consistency: indicates that the state of the database is consistent before and after the transaction occurs. For example, a and account minus 100, B and account plus 100 ensure that the sum of data is unchanged.
  • Isolation: Indicates that the execution of transactions is isolated from each other, and the execution of one transaction cannot affect another.
  • Durability: Transactions that change database data are permanent and non-returnable, meaning that data in the database is modified once a transaction is committed.

The isolation level of the transaction

Transactions have four isolation levels: read uncommitted, read committed, repeatable read, and serialization

  • Read uncommitted: A transaction can read data that has not been committed by another transaction. Dirty reads are generated. Lowest isolation level
  • Read Committed: a transaction can only read data after another transaction has committed. There are non-repeatable read problems, isolation level 2. A non-repeatable read occurs when a transaction is committed between two queries, resulting in inconsistent read results.
  • Repeatable read: a transaction reads the same data throughout its execution. Solve the problem of non-repeatable reads above. Isolation level 3. But there will be a phantom read, phantom read is two before and after the data obtained by the number of inconsistent. During this time, other transactions perform deletes to retrieve the added data.
  • Serialization: Highest isolation level, thread-safe, one transaction locks the table while reading, other transactions wait.

The default isolation level of mysql is repeatability, but the magic read problem solved by mysql is also mainly using the lock mechanism, but instead of locking tables, it locks rows.

Propagation behavior of transactions

Transactions are usually controlled at the service layer. If service layer method A calls another service layer method B, method A and method B book

Transaction control has been added, so when A calls B, there is some negotiation of the transaction, which is called transaction propagation row

For.

A calls B, and we observe from B’s point of view to define the propagation behavior of the transaction

PROPAGATION_REQUIRED If there is no transaction, create a new one. If there is already one, join it. This is the most common choice
PROPAGATION_SUPPORTS Current transactions are supported, and non-transactionally executed if there are none
PROPAGATION_MANDATORY Use the current transaction and throw an exception if there is no transaction currently.
PROPAGATION_REQUIRES_NEW Create a new transaction and suspend the current transaction if one exists.
PROPAGATION_NOT_SUPPORTED Performs the operation nontransactionally, suspending the current transaction if one exists.
PROPAGATION_NEVER Executes nontransactionally, throwing an exception if a transaction currently exists
PROPAGATION_NESTED If a transaction currently exists, it is executed within a nested transaction. If there are no transactions currently, an operation similar to PROPAGATION_REQUIRED is performed.

code

Declarative transactions, add annotations

@EnableTransactionManagement 
@Transactional
Copy the code

Spring AOP source depth analysis

Hand-written EXPLANATION of AOP and IOC code

Based on XML schema

Environment set up

  • Create a Maven project and import the webApp directory into the SRC /main directory.

  • The database creates a bank table and initializes the data

    DROP TABLE IF EXISTS `account`;
    CREATE TABLE `account` (
      `cardNo` varchar(20) NOT NULL,
      `money` int DEFAULT NULL,
      `name` varchar(20) DEFAULT NULL
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    
    -- ----------------------------
    -- Records of account
    -- ----------------------------
    INSERT INTO `account` VALUES ('6029621011000'.'1000'.'Li Dalei');
    INSERT INTO `account` VALUES ('6029621011001'.'1000'.'Han Meimei');
    Copy the code

The code

Pojo entity class

public class Account { private String cardNo; private String name; private int money; //getter/setter method} public class Result {private String status; private String message; / / getter/setter methods}Copy the code

DAO layer

Interface:

public interface AccountDao {

    Account queryAccountByCardNo(String cardNo) throws Exception;

    int updateAccountByCardNo(Account account) throws Exception;
}
Copy the code

Implementation class: is a transfer a query operation. Here connectionUtils is a successor to direct new injected via set or annotation.

public class AccountDaoImpl implements AccountDao { private ConnectionUtils connectionUtils=new ConnectionUtils() ; @Override public Account queryAccountByCardNo(String cardNo) throws Exception { Connection con = connectionUtils.getCurrentThreadConn(); String sql = "select * from account where cardNo=?" ; PreparedStatement preparedStatement = con.prepareStatement(sql); preparedStatement.setString(1,cardNo); ResultSet resultSet = preparedStatement.executeQuery(); Account account = new Account(); while(resultSet.next()) { account.setCardNo(resultSet.getString("cardNo")); account.setName(resultSet.getString("name")); account.setMoney(resultSet.getInt("money")); } resultSet.close(); preparedStatement.close(); return account; } @override public int updateAccountByCardNo(Account Account) throws Exception {obtain a connection from the connection pool. From the current thread for the binding of the connection link connection con = connectionUtils. GetCurrentThreadConn (); String sql = "update account set money=? where cardNo=?" ; PreparedStatement preparedStatement = con.prepareStatement(sql); preparedStatement.setInt(1,account.getMoney()); preparedStatement.setString(2,account.getCardNo()); int i = preparedStatement.executeUpdate(); preparedStatement.close(); return i; }}Copy the code

The service layer

Here the same call DAO directly new.

public interface TransferService { void transfer(String fromCardNo, String toCardNo, int money) throws Exception; } public class TransferServiceImpl implements TransferService{ private AccountDao accountDao=new AccountDaoImpl(); @Override public void transfer(String fromCardNo, String toCardNo, int money) throws Exception { Account from = accountDao.queryAccountByCardNo(fromCardNo); Account to = accountDao.queryAccountByCardNo(toCardNo); from.setMoney(from.getMoney()-money); to.setMoney(to.getMoney()+money); accountDao.updateAccountByCardNo(to); int c = 1/0; accountDao.updateAccountByCardNo(from); }}Copy the code

The servlet layer

There is no controller here, because if we were to write ioc and Aop by hand we wouldn’t have anything to introduce into Spring, and spring’s Controller is really just an encapsulated servlet. So we’ll just use a servlet instead. The code is pretty simple just write whatever code we want to execute in the dopost or doget method.

@WebServlet(name="transferServlet",urlPatterns = "/transferServlet") public class TransferServlet extends HttpServlet { private TransferService transferService; @Override public void init() throws ServletException { transferService = new TransferServiceImpl(); } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doPost(req,resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// Set the character encoding of the request body req.setCharacterEncoding("UTF-8"); String fromCardNo = req.getParameter("fromCardNo"); String toCardNo = req.getParameter("toCardNo"); String moneyStr = req.getParameter("money"); int money = Integer.parseInt(moneyStr); Result result = new Result(); Try {/ / 2. Calls to the service layer method transferService. Transfer (fromCardNo toCardNo, money); result.setStatus("200"); } catch (Exception e) { e.printStackTrace(); result.setStatus("201"); result.setMessage(e.toString()); } // response resp.setContentType(" Application /json; charset=utf-8"); resp.getWriter().print(JsonUtils.object2Json(result)); }}Copy the code

There is also another database connection class

public class ConnectionUtils { private ThreadLocal<Connection> threadLocal = new ThreadLocal<>(); Public Connection getCurrentThreadConn() throws SQLException { Connection Connection = threadlocal.get (); Connection = druidutils.getInstance ().getConnection(); if(connection == null) {// Take the connection from the pool and bind it to the thread. // Bind to the current thread threadlocal.set (connection); } return connection; }}Copy the code

XML writing and code tuning

This is the basic code, or business code. Here we do not say more, we mainly analyze, we mainly consider the following questions:

1. The DAO layer calls the ConnectionUtils connection tool using new. How to achieve IOC function to achieve dependency injection?

2, the same service layer call DAO implementation IOC

3, transfer a user deduct money and a user add money, how to ensure the consistency of things?

Based on these three questions, let’s start with an XML-based approach.

Create a beans.xml

As follows:

<? The XML version = "1.0" encoding = "utf-8"? > <! <beans> <beans> <beans! --id identifies the object, Class is a class of the fully qualified class name - > < bean id = "accountDao" class = "cn. Quellanan. Dao. Impl. AccountDaoImpl" > < property name = "ConnectionUtils" ref="connectionUtils"/> </bean> <bean id="transferService" class="cn.quellanan.service.impl.TransferServiceImpl"> <! <property name="AccountDao" ref=" AccountDao" ></property> </bean> <! - configuration of new three beans - > < Bean id = "connectionUtils" class = "cn. Quellanan. Utils. ConnectionUtils" > < / Bean > <! - the transaction manager - > < bean id = "transactionManager" class = "cn. Quellanan. Utils. TransactionManager" > < property name = "ConnectionUtils"  ref="connectionUtils"/> </bean> <! - a proxy object factory - > < bean id = "proxyFactory" class = "cn. Quellanan. Factory. ProxyFactory" > < property name = "TransactionManager" ref="transactionManager"/> </bean> </beans>Copy the code

You can see that this is somewhat similar to the spring configuration. If you want to inject an object, add a Bean tag to the class. The properties that the object needs to inject are the property tag.

BeanFactory

Now that we have this configuration file, how do we initialize it?

So let’s create a BeanFactory to parse and initialize them

Public class BeanFactory {/** * Task 1: Read the parsed XML, instantiate the object using reflection and store the standby (map collection) * Task 2: */ private static Map<String, Object> Map = new HashMap<>(); private static Map<String, Object> Map = new HashMap<>(); // Store objects static {// load XML InputStream resourceAsStream = BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml"); SAXReader = new SAXReader(); try { Document document = saxReader.read(resourceAsStream); Element rootElement = document.getRootElement(); List<Element> beanList = rootElement.selectNodes("//bean"); for (int i = 0; i < beanList.size(); i++) { Element element = beanList.get(i); // Process each bean element and get the element's ID and class attribute String id = element.attributeValue("id"); // accountDao String clazz = element.attributeValue("class"); / / com.lagou.edu.dao.impl.JdbcAccountDaoImpl / / by reflection technology instantiation object Class <? > aClass = Class. Class.forname (clazz); Object o = aClass.newInstance(); Put (id,o); put(id,o); } // Maintain object dependencies after instantiation, check which objects need to be passed in, according to its configuration, List<Element> propertyList = rootElement. SelectNodes ("//property"); For (int I = 0; i < propertyList.size(); i++) { Element element = propertyList.get(i); //<property name="AccountDao" ref="accountDao"></property> String name = element.attributeValue("name"); String ref = element.attributeValue("ref"); // Find the bean Element parent = element.getparent (); String parentId = paren. attributeValue("id"); // Call the reflection function of the parent object. Object parentObject = map.get(parentId); "Set" + name Method[] methods = parentobject.getClass ().getmethods (); for (int j = 0; j < methods.length; j++) { Method method = methods[j]; If (method.getName().equalsignoRecase ("set" + name)) {setAccountDao(AccountDao AccountDao) method.invoke(parentObject,map.get(ref)); Map. put(parentId,parentObject); } } catch (DocumentException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); }} public static Object getBean(String ID) {return map.get(id); }}Copy the code

We then remove the set method created by new and add an attribute. Such as this format:

private ConnectionUtils connectionUtils;

public void setConnectionUtils(ConnectionUtils connectionUtils) {
	this.connectionUtils = connectionUtils;
}

Copy the code

Consistency of things

Create a proxy object, encapsulate the transaction logic with a dynamic proxy, and then cut into the business logic, which is our AOP implementation of Spring.

Let’s start with a thing controlled class.

public class TransactionManager { @Autowired private ConnectionUtils connectionUtils; public void setConnectionUtils(ConnectionUtils connectionUtils) { this.connectionUtils = connectionUtils; Public void beginTransaction() throws SQLException { connectionUtils.getCurrentThreadConn().setAutoCommit(false); System.out.println(" open transaction....." ); } / / affairs public void commit () throws SQLException {connectionUtils. GetCurrentThreadConn (), commit (); System.out.println(" Commit transaction.....") ); } / / rollback transaction public void the rollback () throws SQLException {connectionUtils. GetCurrentThreadConn (). The rollback (); System.out.println(" Rollback transaction....." ); }}Copy the code

There are three main methods: open things, commit things successfully, and roll things back when they fail.

We then create a proxy class to handle the proxy objects, and we need to proxy the TransferServiceImpl class.

ProxyFactory

public class ProxyFactory { @Autowired private TransactionManager transactionManager; Public Object getJdkProxy(Object obj) {public Object getJdkProxy(Object obj) Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = null; Try {/ / open transaction (close the transaction automatically submit) transactionManager. BeginTransaction (); result = method.invoke(obj,args); / / submit transactionManager.com MIT (); }catch (Exception e) { e.printStackTrace(); / / rollback transaction transactionManager. The rollback (); // Throw an exception so that the upper servlet can catch throw e; } return result; }}); } public Object getCglibProxy(Object obj) {return Enhancer.create(obj.getClass(), new MethodInterceptor() { @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { Object result = null; Try {/ / open transaction (close the transaction automatically submit) transactionManager. BeginTransaction (); result = method.invoke(obj,objects); / / to commit the transaction transactionManager.com MIT (); }catch (Exception e) { e.printStackTrace(); / / rollback transaction transactionManager. The rollback (); // Throw an exception so that the upper servlet can catch throw e; } return result; }}); }}Copy the code

There are two implementations, JDK dynamic proxy and Cglib dynamic proxy. The logic is the same. At this point the whole roughness is done. The functions of IOC and AOP can be implemented as long as the TransferServiceImpl object in the map is obtained in the servlet.

Annotation-based approach

Instead of using XML configuration, we use annotations, so we need to implement custom annotations. Here we implement a few custom annotations modeled after Spring.

Custom annotations

Create an Annotation package and create a custom annotation.

Autowired

@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
    boolean required() default true;
}
Copy the code

Component

You can set value. The default value is “”.

@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Component { String value()  default ""; }Copy the code

Service

As with the Component annotation, the main difference is the identity

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Service {
    String value() default "";
}
Copy the code

Transactional

Transaction annotations, where type, which sets an enumeration type, specifies which dynamic proxy to use.

@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented public @interface Transactional { ProxyType type() default ProxyType.DEFAULT; } public enum ProxyType {//cglib cglib JDK, //cglib cglib, // DEFAULT DEFAULT}Copy the code

Code changes

After we have created our custom annotations, we need to change the places where we use XML injection.

AccountDaoImpl

@Service(value = "accountDao") public class AccountDaoImpl implements AccountDao { @Autowired private ConnectionUtils connectionUtils; . }Copy the code

You can see the addition of a custom Autowired annotation and a Service annotation that sets the value.

TransferServiceImpl

Add annotations @service, @Transactional, @Autowired as well

@Service @Transactional public class TransferServiceImpl implements TransferService{ @Autowired private AccountDao accountDao; . }Copy the code

Note here that we are calling the AccountDao interface at the Service layer, and instantiation should instantiate AccountDaoImpl. So how do you control this? This can be specified by the @service value above in the AccountDaoImpl class.

The core agent

Now there is a problem. We have added these annotations to these classes, but we haven’t empowered them yet. So they are not useful now, so let’s give them supreme power now.

Core idea: scan all classes under the project specified package and find annotations on the class. We will process the corresponding class, and then save the processed objects in the map, which is the corresponding Spring singletonObjects level cache pool.

So we create a BeanFactoryByAnno to do just that.

Process:

  • The customized path provided gets the package pathname
  • Save these package pathnames in the SET union
  • Iterate through the set to see if it contains @service, @Component annotations. If it does, create a Bean and store it in the Map
  • After completing the previous step, iterate through the collection again to determine if it contains the Autowired annotation and assign a value to the attribute
  • After completing the previous step, iterate through the collection again to determine whether the Transactional annotation is included, which indicates that a proxy object needs to be created to implement it.
public static void searchClass(String basePack) throws Exception { Set<String> classPaths = new HashSet<>(); / / convert the package name to path first, first of all get the project classpath String classpath = BeanFactoryByAnno. Class. The getResource ("/"). GetPath (); // Then convert our package name basPach to the pathname basePack = basepack.replace (".", file.separator); String searchPath = classpath + basePack; String searchPath = classpath + basePack; doPath(new File(searchPath),classPaths); // At this point we have the absolute path of all classes under the specified package. We now use these absolute paths and Java's reflection mechanism to get their class object for (String s: ClassPaths) {// Convert the absolute path to the full class name s = s.replace(classpath.replace("/","\\").replaceFirst("\\\\",""),"").replace("\\",".").replace(".class",""); // Handle @service annotation, instantiate object createBeans(s); } // Do the same with the @autoWired annotation, injecting the instantiated Bean for this property. for (String s : ClassPaths) {// Convert the absolute path to the full class name s = s.replace(classpath.replace("/","\\").replaceFirst("\\\\",""),"").replace("\\",".").replace(".class",""); // Instantiate the object populateBean(s); } // If you include the @transactional annotation, you need to delegate this class for (String s: ClassPaths) {// Convert the absolute path to the full class name s = s.replace(classpath.replace("/","\\").replaceFirst("\\\\",""),"").replace("\\",".").replace(".class",""); // ProxyBean(s); }}Copy the code

This method takes all the classes and writes their absolute paths to the classPaths

/** * this method will get all the classes, @param file */ private static void doPath(file file,Set<String> classPaths) {if (file.isdirectory ()) {// folder // folder we recurse file [] files = file.listfiles (); for (File f1 : files) { doPath(f1,classPaths); If (file.getName().endswith (".class")) {// If (file.getname ().endswith (".class")) {// If (file.getname ().endswith (".class"))) {// If (file.getname ().endswith (".class"))) {// If (file.getname ().endswith (".class"))) { classPaths.add(file.getPath()); }}}Copy the code

Create a Bean

private static void createBeans(String classPath) throws Exception{ Class<? > aClass = Class.forName(classPath); If (aClass. IsAnnotationPresent (Service. Class) | | aClass. IsAnnotationPresent (Component. Class)) {/ / instantiate the Object after the Object o = aClass.newInstance(); String key = getMapKey(aClass); Put (key,o); }}Copy the code

Create an instance of an annotation containing a Service or Component and add it to the map collection. So I’m going to wrap key separately as a method. Take into account that these two annotations can set the value of value. If a value is set then the value is used as the key, if no value is set then the name of the current class is used as the key. For maximum compatibility, convert the key to all uppercase.

private static String getMapKey(Class<? > aClass){ String key = aClass.getSimpleName(); String value=""; if (aClass.isAnnotationPresent(Service.class)) { value = aClass.getAnnotation(Service.class).value(); }else if(aClass.isAnnotationPresent(Component.class)){ value=aClass.getAnnotation(Component.class).value(); } if(! value.isEmpty()){ key=value; } key=key.toUpperCase(); return key; }Copy the code

After you have created all the annotated beans, you need to populate the properties for the beans that contain the Autowired annotation. That is, linking beans to beans.

private static void populateBean(String classPath) throws Exception{ Class<? > aClass = Class.forName(classPath); Field[] declaredFields = aclass.getDeclaredFields (); for (Field field : DeclaredFields) {// If the property is annotated, For this attribute to fill the Bean the if (field. IsAnnotationPresent (Autowired. Class)) {/ / get to fill attribute class Object obj = map. Get (getMapKey (aClass)); // The property can be set to field.setaccessible (true); String name = field.getType().getSimplename ().toupperCase (); Object value = getMapValue(name); // Assign a value to the attribute. if(value! =null && obj! =null){ field.set(obj,value); }}}}Copy the code

Iterate through the class and get the attributes of the class, and if the attributes contain Autowired annotations, we process them. We start by getting an instance of the current class from the Map via the class. The class to inject is then retrieved by the type of the attribute. Finally, field. Set () assigns a value to the current object’s properties.

I’m taking the value out of the map by key, and I’m also encapsulating it. The main thing is that if the Autowired annotation gets the interface, specifically by subclass, I’ve added an IMPL suffix to get the value by convention.

private static Object getMapValue(String key){ key=key.toUpperCase(); Object value = map.get(key); If (value == null) {value=map.get(key+ IMPL); } return value; }Copy the code

We then iterate again to deal with the Transactional annotation that contains the Transactional annotation, which states that the class needs to use a proxy object.

private static void ProxyBean(String classPath)throws Exception{ Class<? > aClass = Class.forName(classPath); / / if the containing transaction annotations if (aClass isAnnotationPresent (Transactional. Class)) {/ / obtain the actual object Bean String mapKey = getMapKey (aClass); Object mapValue = getMapValue(mapKey); ProxyFactory = (ProxyFactory) getMapValue("ProxyFactory"); ProxyType type = aclass.getannotation (Transactional.class).type(); / / determine whether to designate an agent switch (type) {case JDK: useJdkProxy (proxyFactory, mapKey, mapValue); break; case CGLIB:usecGLIBProxy(proxyFactory,mapKey,mapValue); break; case DEFAULT:useDefaultProxy(aClass,proxyFactory,mapKey,mapValue); default:break; }}}Copy the code

The key and instance vaule of the current class are obtained by the class name. The proxy object is then retrieved from the Map to determine the type specified by the annotation.

The JDK dynamic proxy is used if it is JDK, and the CGLIB dynamic proxy is used if it is CGLIB. If it’s the default, then judge. I’ve separated out all the individual methods here.

If it is the default, it determines whether the current class implements the interface, excluding the implementation of the business interface. If you don’t use cglib dynamic proxies, if you do use JDK dynamic proxies.

private static void useDefaultProxy(Class<? > aClass,ProxyFactory ProxyFactory,String mapKey,Object mapValue){ >[] interfaces = aClass.getInterfaces(); / / contains in addition to its implementing an interface interface interface if (interfaces. The length > 1) {useJdkProxy (proxyFactory mapKey, mapValue); }else { usecGLIBProxy(proxyFactory, mapKey, mapValue); }}Copy the code

JDK dynamic proxy, which gets the proxy object from the current object and saves it in map instead of the original object. Additional.

private static void useJdkProxy(ProxyFactory proxyFactory,String mapKey,Object mapValue){
        Object jdkProxy = proxyFactory.getJdkProxy(mapValue);
        map.put(mapKey,jdkProxy);
    }
    
private static void usecGLIBProxy(ProxyFactory proxyFactory,String mapKey,Object mapValue){
        Object cglibProxy = proxyFactory.getCglibProxy(mapValue);
        map.put(mapKey,cglibProxy);
    }
    
Copy the code

Finally, a method interface is thrown that gets the instance by key

public static Object getBean(String key) {
        return getMapValue(key);
    }
Copy the code

Increase to monitor

We add a listener to initialize the core code when the project starts.

Create a listener class to implement ServletContextListener

public class ContextListener implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent servletContextEvent) { String packageName = "cn.quellanan"; try { BeanFactoryByAnno.searchClass(packageName); } catch (Exception e) { e.printStackTrace(); Throw new RuntimeException(" Initialize exception... ") ); } } @Override public void contextDestroyed(ServletContextEvent servletContextEvent) { } }Copy the code

Finally, the modification is added in the total servlet, mainly to obtain the transferService instance.

private TransferService transferService;

    @Override
    public void init() throws ServletException {
        transferService = (TransferService) BeanFactoryByAnno.getBean("transferService");
    }
Copy the code

test

Initial database

Transfer 100

The correct interface

The console log

Look at the database again, there is no change

Remove exceptions. Restart project

Transfer 100, indicating success

Look at the console log:

Look at the database:

This indicates that the whole process is ok, and the operation of submitting successful things and rolling back failed things is realized.

One day

If you think it’s useful, keep it.