Spring learning precipitation
Basic concepts
From the definition of Spring official website: end to end support for reactive & servlet based apps on the JVM
Understanding: provide an end-to-end support for the applications based on reactive and servlet running on the JVM (it should encapsulate the basic logic of some applications based on reactive and servlet, and the development based on the spring framework does not need to be aware of these things)
Reactive && Servlet
Reactive
- Definition: Reactive programming is a new style of programming
1)react to events responds to events immediately. The event driven nature enables the reactions to be loaded and implemented immediately, and achieves scalability by avoiding contention for shared resources.
2)react to failure is an immediate response to failure. At any level, you can build an elastic system capable of failure recovery.
3)react to users responds immediately to users, regardless of any load, worth the same response time.
Features: asynchronous or concurrent, event driven, PUSH mechanism for message notification, non blocking IO
For more information: https://www.jdon.com/reactive.html
http://www.reactive-streams.org/
Servlet
- Definition: Java Servlet is a program running on the Web server or application server. It is the middle layer between the request from the Web browser or other HTTP clients and the database or application program on the HTTP server.
Features: this programming mode is blocked IO, one request and one thread
Spring annotation summary
Assembly bean
Configuration class Config
@Configuration: mark this class as a configuration class, which creates objects with java syntax
@Componentscan (basepackages = {xxxbean. Class, xxxbean. Class}): mark on the Configuration class. Scan the class with @ Component mark in the package of xxxBean.class. If not configured, the class in the same package with Config class will be scanned by default
Create bean s for the classes marked with @ Component
@Bean, marked in the method of Config class, tells Spring that this method will return an object
@Profile: indicates that this configuration is adopted in this environment
@Conditional: used on the @ bean annotation method, when the given condition is true, the bean will be created
@Propertysource (file pathname): declare a property source
Bean
@Component: mark in a normal class, indicating that Spring wants to create a Bean for this class;
@Component("id") = @ Named("id"), define an id for the bean
@Autowired = @Inject, inject bean
@Scope (configurablebeanfactory. Scope \ \ prototype): annotation is defined as a prototype bean on the bean, and a new bean instance will be created each time it is injected or obtained through the Spring application context
Test class
@Run with (springjunit4classrunner. Class): mark the test class and automatically create the Spring application context at the beginning of the test
@ContextConfiguration(classes = xxx.class)
Tell the test class which Config class to load the configuration in
@ActiveProfiles("dev"): specifies which profile to activate when running the test
Source learning
Dependency injection and inversion of control (spring container)
The overall architecture of spring is shown in the figure, based on the core container(IOC container), on which the prosperity of AOP, DATA and WEB can be seen. The three core jar packages of spring container are bean, context and core. Bean is the cornerstone of spring. Context maintains the application context. If bean is an actor, then context is the stage and core is the prop.
Application context
ApplicationConext: from the inheritance relationship of the class diagram, we can see that the basic class ApplicationConext inherits five abilities: resource, message, event, environment and factory. ApplicationConext only contains simple read-only attributes.
ConfigurableApplicationContext: it inherits the life cycle control ability, and at the same time inherits ApplicationConext, expanding the environment, event and other write attributes of context.
AbstractApplicationContext: most of the capabilities are implemented in this class definition. This class inherits the class loading capability interface DefaultResourceLoader and the ConfigurableApplicationContext of the read-write context. The process initiated by the ioc container parses this class in detail.
BeanFactory
spring's world is full of bean s, several important interfaces and classes
Alias registry: alias registry
BeanFactory: Factory
BeanDefinitionRegistry:
DefaultListableBeanFactory converges all the upper level capabilities, including the core BeanDefinitionRegistry and BeanFactory, that is, the bean definition and the bean production factory.
Bean creation process
A simple demo:
- Test start class Application
package com.alipay; import org.springframework.context.ApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext; /** - Unit test for simple App. */ public class AppTest { public static void main(String[] args){ String XMLPath = "springdemo/src/spring-config.xml"; ApplicationContext applicationContext = new FileSystemXmlApplicationContext(XMLPath); ILogin login = (ILogin) applicationContext.getBean("loginService"); login.loginCheck("boy", "123"); } }
- spring profile
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="loginService" class="com.alipay.LoginImpl"> </bean> </beans>
After the above steps, we successfully created the object described in the configuration file through spring's IOC container, and no longer need to use the form of new to create the object
IOC container startup process
Overall flow chart:
Class inheritance diagram:
- Create context object
Here, the construction method of FileSystemXmlApplicationContext is called, and the path (relative path) of the xml file is passed in. The construction method of xml is called as follows.
public FileSystemXmlApplicationContext(String configLocation) throws BeansException { this(new String[] {configLocation}, true, null); //This (parameter a, parameter b, parameter c) can call another constructor } public FileSystemXmlApplicationContext( String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException { super(parent); setConfigLocations(configLocations); if (refresh) { refresh(); // Core method } }
- Core method refresh interpretation
@Override public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. prepareRefresh(); // Tell the subclass to refresh the internal bean factory. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. registerBeanPostProcessors(beanFactory); // Initialize message source for this context. initMessageSource(); // Initialize event multicaster for this context. initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. onRefresh(); // Check for listener beans and register them. registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } } }
The refresh method is used to start the IOC container. The whole operation is contained in a synchronization code block. The synchronization lock is a private final access object. The refresh method mainly includes the following steps:
2.1 initialization before container initialization (prepareRefresh())
Data structure and types used: AtomicBoolean and LinkedHashSet, why do you use these two things?
AtomicBoolean can guarantee the atomicity of operation; the LinkedHashSet set also determines the storage location of elements according to the value of element hashCode, but it also uses the linked list to maintain the order of elements, so that the elements appear to be saved in the order of insertion.
2.2 create BeanFactory(obtainFreshBeanFactory()), which is one of the two core modules of the entire IOC container startup process, mainly including two steps:
2.2.1 reset BeanFactory, destroy if it exists, and create if it does not (refreshBeanFactory())
Interpretation of destroyBeans():
1) When destroying an object, the AbstractApplicationContext first obtains a ConfigurableListableBeanFactory object through getBeanFactory() (this is an interface. The specific implementation is defaultlistablebeanfactory extensions abstractautowirecapablebeanfactory extensions abstractbeanfactory extensions factorybeanregistrysupport extensions defaultsingletonbeanregistry)
2) Call the ConfigurableListableBeanFactory.destroySingletons() method
2.1) in this method, the parent class method defaultsingletonbeanregistry. Destroysingletons() - > this method will call DefaultSingletonBeanRegistry.destroySingleton(String beanName)
The operation of this method is to remove the records corresponding to this bean name from several map s according to the bean name
public void destroySingleton(String beanName) { // Remove a registered singleton of the given name, if any. removeSingleton(beanName); // Destroy the corresponding DisposableBean instance. DisposableBean disposableBean; synchronized (this.disposableBeans) { disposableBean = (DisposableBean) this.disposableBeans.remove(beanName); } destroyBean(beanName, disposableBean); }
Removesignleton method
protected void removeSingleton(String beanName) { synchronized (this.singletonObjects) { this.singletonObjects.remove(beanName); this.singletonFactories.remove(beanName); this.earlySingletonObjects.remove(beanName); this.registeredSingletons.remove(beanName); } }
singletonObjects is a concurrenthashmap < string, Object >
Singleton factories is a HashMap < string, objectfactory <? > >
earlySingletonObjects is a HashMap < string, Object >
Registerdsingletons is a LinkedHashSet
disposableBeans is a LinkedHashMap < string, Object >
destroyBean() method
protected void destroyBean(String beanName, @Nullable DisposableBean bean) { // Trigger destruction of dependent beans first... Set<String> dependencies; synchronized (this.dependentBeanMap) { // Within full synchronization in order to guarantee a disconnected Set dependencies = this.dependentBeanMap.remove(beanName); } if (dependencies != null) { if (logger.isTraceEnabled()) { logger.trace("Retrieved dependent beans for bean '" + beanName + "': " + dependencies); } for (String dependentBeanName : dependencies) { destroySingleton(dependentBeanName); } } // Actually destroy the bean now... if (bean != null) { try { bean.destroy(); } catch (Throwable ex) { if (logger.isWarnEnabled()) { logger.warn("Destruction of bean with name '" + beanName + "' threw an exception", ex); } } } // Trigger destruction of contained beans... Set<String> containedBeans; synchronized (this.containedBeanMap) { // Within full synchronization in order to guarantee a disconnected Set containedBeans = this.containedBeanMap.remove(beanName); } if (containedBeans != null) { for (String containedBeanName : containedBeans) { destroySingleton(containedBeanName); } } // Remove destroyed bean from other beans' dependencies. synchronized (this.dependentBeanMap) { for (Iterator<Map.Entry<String, Set<String>>> it = this.dependentBeanMap.entrySet().iterator(); it.hasNext();) { Map.Entry<String, Set<String>> entry = it.next(); Set<String> dependenciesToClean = entry.getValue(); dependenciesToClean.remove(beanName); if (dependenciesToClean.isEmpty()) { it.remove(); } } } // Remove destroyed bean's prepared dependency information. this.dependenciesForBeanMap.remove(beanName); }
Overall logic, delete the bean that depends on it before deleting it.
(1) When the value corresponding to the beanName as key in the dependentbeanmap is not empty, you need to destroy the bean that depends on it first
(2) The value corresponding to the beanName as key in the containedbeanmap is not empty. Yes, you need to delete the bean it contains
(3) This bean is removed from the dependency of the bean it depends on. The iterator is used in the deletion process
First get the iterator pointing to the map. Entry set(), first remove the beanName from the set corresponding to each entry (dependenciesToClean.remove(beanName)), if the last set is empty, then remove the entire entry (it.remove())
2.2)updateManualSingletonNames()
After cleaning the map, the name of the single instance will be updated. There are many jdk8 things used here
updateManualSingletonNames(Set::clear, set -> !set.isEmpty());
The input parameter of the first function expression is a consumer interface, which represents an operation that accepts an input parameter and does not return
The input parameter of the second function expression is a Predicate assertive function interface, which takes an input parameter and returns a boolean result.
Code:
private void updateManualSingletonNames(Consumer<Set<String>> action, Predicate<Set<String>> condition) { if (hasBeanCreationStarted()) { // Cannot modify startup-time collection elements anymore (for stable iteration) synchronized (this.beanDefinitionMap) { if (condition.test(this.manualSingletonNames)) { Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames); action.accept(updatedSingletons); this.manualSingletonNames = updatedSingletons; } } } else { // Still in startup registration phase if (condition.test(this.manualSingletonNames)) { action.accept(this.manualSingletonNames); } } }
If the Bean has been created, you cannot clear the manualSingletonNames directly (see why later). First, judge whether the manualSingletonNames is empty. If not, create a new set according to the manualSingletonNames, then clear the set, and then point the manualSingletonNames to the set (why? )
Usage of jdk8:: https://blog.csdn.net/csmans/article/details/82256690
Interpretation of createBeanFactory()
DefaultListableBeanFactory beanFactory = createBeanFactory();
createBeanFactory ->
return new DefaultListableBeanFactory(getInternalParentBeanFactory());
@Nullable protected BeanFactory getInternalParentBeanFactory() { return (getParent() instanceof ConfigurableApplicationContext ? ((ConfigurableApplicationContext) getParent()).getBeanFactory() : getParent()); }
ApplicationContext inherits the BeanFactory interface
Interpretation of loadBeanDefinitions()
(1) Function: load the bean definition configured in the xml file through XmlBeanDefinitionReader
Official explanation:
The lifecycle of the bean factory is handled by the {@link #refreshBeanFactory}method; hence this method is just supposed to load and/or register bean definitions.
(2) To actually read the bean definition from the xml file:
XmlBeanDefinitionReader.doLoadBeanDefinitions(InputSource inputSource, Resource resource)
Document doc = doLoadDocument(inputSource, resource); int count = registerBeanDefinitions(doc, resource); if (logger.isDebugEnabled()) { logger.debug("Loaded " + count + " bean definitions from " + resource); } return count;
1)Document doc = doLoadDocument(inputSource, resource); generate document object based on xml file
Parsing method from xml to dom object: https://www.cnblogs.com/shenliang123/archive/2012/05/11/2495252.html
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception { return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler, getValidationModeForResource(resource), isNamespaceAware()); }
2)int count = registerBeanDefinitions(doc, resource); register beans, return the number of beans found in the file
protected void doRegisterBeanDefinitions(Element root) { // Any nested <beans> elements will cause recursion in this method. In // order to propagate and preserve <beans> default-* attributes correctly, // keep track of the current (parent) delegate, which may be null. Create // the new (child) delegate with a reference to the parent for fallback purposes, // then ultimately reset this.delegate back to its original (parent) reference. // this behavior emulates a stack of delegates without actually necessitating one. BeanDefinitionParserDelegate parent = this.delegate; this.delegate = createDelegate(getReaderContext(), root, parent); if (this.delegate.isDefaultNamespace(root)) { String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE); if (StringUtils.hasText(profileSpec)) { String[] specifiedProfiles = StringUtils.tokenizeToStringArray( profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); // We cannot use Profiles.of(...) since profile expressions are not supported // in XML config. See SPR-12458 for details. if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) { if (logger.isDebugEnabled()) { logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + getReaderContext().getResource()); } return; } } } preProcessXml(root); parseBeanDefinitions(root, this.delegate); postProcessXml(root); this.delegate = parent; }
When the DefaultBeanDefinitionDocumentReader processes the Document element, it delegates the specific resolution of the element in the Document to the BeanDefinitionParserDelegate class. The default BeanDefinitionParserDelegate will process "http://www.springframework.org/schema/beans" elements and their attributes under the namespace
Traverse all the child node elements under root to register the bean code
/** * Register the given bean definition with the given bean factory. * @param definitionHolder the bean definition including name and aliases * @param registry the bean factory to register with * @throws BeanDefinitionStoreException if registration failed */ public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { // Register bean definition under primary name. String beanName = definitionHolder.getBeanName(); registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); // Register aliases for bean name, if any. String[] aliases = definitionHolder.getAliases(); if (aliases != null) { for (String alias : aliases) { registry.registerAlias(beanName, alias); } } }
When registering beans, call the DefaultListableBeanFactory.registerBeanDefinition() method. When registering beans, add < beanName, beandefinition > and beanName to beanDefinitionMap and beanDefinitionNames
2.2.2 return the created new factory (getBeanFactory()), and finally get a ConfigurableListableBeanFactory object
When the refreshBeanFactory() is reset, the bean definition is obtained (loadBeanDefinitions()): including
Read configuration file - > parse configuration file (map the label attribute in a single xml configuration file to BeanDefinitionHolder object) - > register object (add beandefinition in beanDefinitionMap)
2.3 object instantiation
Finishbeanfactoryinitialization (beanfactory) - > call beanFactory.preInstantiateSingletons() to instantiate the object
2.3.1 when instantiating an object, first instantiate the bean that the bean depends on, and then instantiate the bean
How to create bean s
if (instanceWrapper == null) { instanceWrapper = createBeanInstance(beanName, mbd, args); }
The core method to create a bean:
return BeanUtils.instantiateClass(constructorToUse); -> ctor.newInstance(argsWithDefaultValues)
(ctor is a Constructor class that constructs objects by reflection)
2.4 object attribute initialization
How to initialize bean properties
// Initialize the bean instance. Object exposedObject = bean; try { populateBean(beanName, mbd, instanceWrapper); exposedObject = initializeBean(beanName, exposedObject, mbd); }
The pollatebean() method initializes the properties of the bean - > called
AbstractAutowireCapableBeanFactory.applyPropertyValues()
(1) new a deep copy list: list deepcopy = new ArrayList < > (original. Size());
(2) Then add the properties in List original to deepCopy
(3) Finally, the value is set into the object property through the beanwrapper method, bw.setPropertyValues(new MutablePropertyValues(deepCopy));
Facing section
Reference: https://juejin.im/entry/5b572405e51d451964625f66
Basic theory of AOP
The relationship between SpringAop and AspectJ
AspectJ can do many things that spring AOP can't do. It's a complete solution for AOP programming. Spring AOP is committed to solving the most common AOP requirements (method weaving) in enterprise development, rather than striving to become a complete AOP programming solution like AspectJ.
Because AspectJ weaves the actual code before it runs, Spring AOP is based on proxy implementation. When the container starts, it needs to generate proxy instances, and the depth of stack will also be increased in method calls, so the performance of Spring AOP is not as good as AspectJ.
(the proxy mode requires an interface and a concrete implementation class, and then defines a proxy class to wrap the implementation class, add custom logic, and use the proxy class to generate instances.)
Spring AOP can only work on beans in spring containers. It is implemented using pure Java code and can only work on bean methods.
Spring extends the concepts in AspectJ, including the annotations in the jar package provided by AspectJ, but does not rely on its implementation functions.
Spring AOP basic usage and noun explanation
-
Several basic usage methods of Spring AOP
Spring 1.2 interface based configuration: the earliest Spring AOP is completely based on several interfaces. Students who want to see the source code can start here.
Spring 2.0 schema based configuration: after spring 2.0, XML is used for configuration and namespace is used
Spring 2.0 @AspectJ configuration: it is the most convenient way to use annotation to configure. In addition, although it is called @ AspectJ here, it has nothing to do with AspectJ -
Spring AOP name explanation
2.1 let's understand the concept of Advisor. It's also relatively simple. It needs to specify an Advice internally. The Advisor decides which methods to intercept, and the work to be completed after interception is still done by the internal Advice.
2.2 Spring AOP only supports methods in beans (not as powerful as AspectJ), so we can think that Pointcut is used to match methods of all beans in Spring container
For developers, the most important thing is to define Pointcut and use appropriate Advice on each Pointcut
AOP source code analysis
Reference link:
https://juejin.im/entry/5b572405e51d451964625f66
Basic process:
When the ioc container starts to create a bean whose method needs to be injected into the aop interface, it will call the beanPostProcessor.postProcessAfterInitialization(Object bean,String beanName) interface method, which is specifically implemented as abstractautoproxycreator.createproxy (class <? > beanclass, string beanname, object [] specificinterceptors, Targetsource Targetsource)
This method creates a ProxyFactory, then calls the proxyFactory.getProxy(getProxyClassLoader()) method, and finally returns the aop proxy of this bean.
ProxyFactory details:
proxyFactory.getProxy(getProxyClassLoader()) will call the following methods of ProxyFactory in turn:
- getProxy()
public Object getProxy(@Nullable ClassLoader classLoader) { return createAopProxy().getProxy(classLoader); }
- createAopProxy(): This is the method that ProxyFactory inherits from ProxyCreatorSupport
protected final synchronized AopProxy createAopProxy() { if (!this.active) { activate(); } return getAopProxyFactory().createAopProxy(this); }
- AopProxyFactory.createAopProxy(AdvisedSupport config)
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException { if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) { Class<?> targetClass = config.getTargetClass(); if (targetClass == null) { throw new AopConfigException("TargetSource cannot determine target class: " + "Either an interface or a target is required for proxy creation."); } if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) { return new JdkDynamicAopProxy(config); } return new ObjenesisCglibAopProxy(config); } else { return new JdkDynamicAopProxy(config); } }
Create a jdk dynamic proxy class or Cglib proxy class. By default, config.isOptimize() and config.isProxyTargetClass() are fasle.
hasNoUserSuppliedProxyInterfaces(config) determines whether there is an interface to implement customization. If the target class of the proxy implements one or more customized interfaces, JDK dynamic proxy will be used. If no interface is implemented, CGLIB will be used to implement the proxy. If proxy target class = "true" is set, CGLIB will be used.
So (targetclass. Isinterface() | proxy. Isproxyclass (targetclass)) what's the use of this judgment logic? If the class to proxy itself is an interface or java.lang.reflect.proxy.isproxyclass (class <? > CL) method, the method returns true only when the specified class is dynamically generated as a proxy class using the getProxyClass method or newProxyInstance method
The following analysis returns the JdkDynamicAopProxy proxy class:
@Override public Object getProxy(ClassLoader classLoader) { if (logger.isDebugEnabled()) { logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource()); } //According to the aop configuration (this.advised), obtain all interfaces that need to be proxied Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true); //Find proxy method findDefinedEqualsAndHashCodeMethods(proxiedInterfaces); //Create a new proxy instance. This is the InvocationHandler instance. We see that this is passed here, because JdkDynamicAopProxy implements the InvocationHandler interface return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this); }
The invocationHandler(to study) has only one method. When the generated proxy class provides external services, it will be imported into this method, which is implemented by JdkDynamicAopProxy.
Difficulty: why is it configured
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>
Can automatic agent be realized?
DefaultAdvisorAutoProxyCreator is mainly used to add an instance of AbstractAutoProxyCreator to the beanPostProcessors list in AbstractBeanFactory after initializing the bean.
Then when the Object current = processor.postProcessAfterInitialization(result, beanName); method is called when other beans are initialized,
The AbstractAutoProxyCreator.postProcessAfterInitialization(Object bean, String beanName) method is called.