In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-17 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Internet Technology >
Share
Shulou(Shulou.com)06/02 Report--
Today, I will talk to you about how to achieve Spring circular dependency in Springboot, many people may not know much about it. In order to make you understand better, the editor has summarized the following content for you. I hope you can get something according to this article.
Spring cyclic dependency flowchart
Causes of Spring circular dependency
BeanPostProcessor with proxy feature is used
Typical transaction annotations @ Transactional, asynchronous annotations @ Async, etc.
Source code analysis reveals protected Object doCreateBean (...) {. Boolean earlySingletonExposure = (mbd.isSingleton () & & this.allowCircularReferences & & isSingletonCurrentlyInCreation (beanName)); if (earlySingletonExposure) {addSingletonFactory (beanName, ()-> getEarlyBeanReference (beanName, mbd, bean)) }. / / populateBean this sentence is particularly critical, it needs to assign a value to the property of A, so here we will de-instantiate the Bean ~ / / and B we can see from above that it is a normal Bean (there is no need to create a proxy object). After the instantiation is complete, continue to assign a value to his property A. At this point, it will get the early reference of A / / that is, when assigning a value to the attribute an of B, it will execute the getEarlyBeanReference () method in the Bean A process put above, thus getting the early reference of A ~ / / when it executes the getEarlyBeanReference () method of A, it will execute the automatic proxy creator, but because A does not label the transaction, it will not create the proxy in the end. So B qualified attribute reference will be A's * * original object * * / it should be noted that @ Async's proxy object is not created in getEarlyBeanReference (), it is a proxy created in postProcessAfterInitialization / / from this we can also see that @ Async's proxy does not support you to circulate references by default Because it does not provide early references to the proxy object ~ (note the difference between this and the automatic proxy creator) / / conclusion: here the dependency property field B of An is assigned to an instance of B (because B does not need to create a proxy So it is the original object) / / and the dependent An in instance B here is still the ordinary instance object of Bean A (note that the original object is not a proxy object) Note: at this time, exposedObject is still the original object populateBean (beanName, mbd, instanceWrapper). / / the Bean object marked @ Async will be generated here ~ ~ reference class: AsyncAnnotationBeanPostProcessor / / so after the execution of this sentence, exposedObject will be a proxy object rather than the original object. ExposedObject = initializeBean (beanName, exposedObject, mbd) . / / here is the focus of the error report ~ if (earlySingletonExposure) {/ / it says that An is dependent on the B loop, so An is put into the secondary cache at this time. So here earlySingletonReference is a reference to the original object of A (which explains why I said: if An is not cyclically dependent, it will not be wrong and there will be no problem, because if there is no circular dependency earlySingletonReference = null will be followed directly by return) Object earlySingletonReference = getSingleton (beanName, false) If (earlySingletonReference! = null) {/ / above analyzes that exposedObject is an object proxied by @ Aysnc, while bean is the original object, so it is not equal to else logic if (exposedObject = = bean) {exposedObject = earlySingletonReference } / / allowRawInjectionDespiteWrapping marks whether the original type of this Bean is allowed to be injected into other Bean, even if it will eventually be wrapped (proxy) / / false means not allowed, if changed to true means allow, there will be no error. This is one of the solutions that we will talk about later. In addition, dependentBeanMap records the Map~~~~ else if (! this.allowRawInjectionDespiteWrapping & & hasDependentBean (beanName)) of each Bean it depends on for Bean. {/ / our Bean A depends on B. So the value here is ["b"] String [] dependentBeans = getDependentBeans (beanName) Set actualDependentBeans = new LinkedHashSet (dependentBeans.length) / / check all dependencies one by one ~ for example, here B will have a problem / / "b" it finally returns to false after removeSingletonIfCreatedForTypeCheckOnly because it is already in alreadyCreated, which means that B has been completely created ~ ~ / / and b is done. So attribute an is also assigned to complete the chat, but the a referenced in B is not equal to the main process A, so there must be a problem (indicating that it is not final) ~ ~ / / so will eventually be added to the actualDependentBeans. Indicates that A really depends on ~ ~ for (String dependentBean: dependentBeans) {if (! removeSingletonIfCreatedForTypeCheckOnly (dependentBean)) {actualDependentBeans.add (dependentBean) }} / / if there is such a real dependency Then it is wrong to report ~ ~ then the exception is the exception information seen above if (! actualDependentBeans.isEmpty ()) {throw new BeanCurrentlyInCreationException (beanName) "Bean with name'" + beanName + "'has been injected into other beans [" + StringUtils.collectionToCommaDelimitedString (actualDependentBeans) + "] in its raw version as part of a circular reference But has eventually been "+" wrapped. This means that said other beans do not use the final version of the "+" bean. This is often the result of over-eager type matching-consider using "+" 'getBeanNamesOfType' with the' allowEagerInit' flag turned off, for example. ") }}.} the problem is simplified
Object earlySingletonReference = getSingleton (beanName, false) when cyclic dependency occurs; there must be a value
Cache factory addSingletonFactory (beanName, ()-> getEarlyBeanReference (beanName, mbd, bean); SmartInstantiationAwareBeanPostProcessor will be added to the instance object
AbstractAutoProxyCreator is a subclass of SmartInstantiationAwareBeanPostProcessor, be sure to remember, must remember, subclass of SmartInstantiationAwareBeanPostProcessor is critical!
ExposedObject = initializeBean (beanName, exposedObject, mbd); do BeanPostProcessor post processing, note that it is BeanPostProcessor!
Spring's circular dependency is easily solved by its three-level cache, but the post-processing in these two places brings the problem of circular dependency.
Compare AbstractAdvisorAutoProxyCreator and AsyncAnnotationBeanPostProcessor
Because the subclasses of SmartInstantiationAwareBeanPostProcessor will perform post-processing in both places, there will be the same object reference before and after, and there will be no circular dependency problems, and asynchronous annotations will not work, as for why? Look at the above analysis for yourself and look at it carefully!
How to solve the circular dependency?
Change the loading order
@ Lazy comment
AllowRawInjectionDespiteWrapping is set to true (taking advantage of the judgment statement)
Do not use the relevant BeanPostProcessor design notes, , this is not very realistic.
@ Lazy
@ Lazy generally means lazy loading, which only works on BeanDefinition.setLazyInit (). And here it adds a capability: deferred processing (proxy processing)
/ / @ since 4.0appeared quite late, it supports that @ Lazy is the most fully functional AutowireCandidateResolver public class ContextAnnotationAutowireCandidateResolver extends QualifierAnnotationAutowireCandidateResolver {/ / this is the only thing the class itself does, here the lazy proxy is refined / / returned to indicate delayed initialization The implementation process is to check whether @ Lazy=true annotation @ Override @ Nullable public Object getLazyResolutionProxyIfNecessary (DependencyDescriptor descriptor, @ Nullable String beanName) is used at @ Autowired annotation {/ / return a proxy if isLazy=true, otherwise return null / / equivalent to @ Lazy annotation A proxy is returned (of course, the value value of the @ Lazy annotation cannot be false) return (isLazy (descriptor)? BuildLazyResolutionProxy (descriptor, beanName): null) } / / this is relatively simple @ Lazy annotations on the line (the default value of the value attribute is true) / / @ Lazy supports annotations on attributes and method input parameters ~ ~ here it parses protected boolean isLazy (DependencyDescriptor descriptor) {for (Annotation ann: descriptor.getAnnotations ()) {Lazy lazy = AnnotationUtils.getAnnotation (ann, Lazy.class) If (lazy! = null & & lazy.value ()) {return true;}} MethodParameter methodParam = descriptor.getMethodParameter () If (methodParam! = null) {Method method = methodParam.getMethod (); if (method = = null | | void.class = = method.getReturnType ()) {Lazy lazy = AnnotationUtils.getAnnotation (methodParam.getAnnotatedElement (), Lazy.class) If (lazy! = null & & lazy.value ()) {return true;}} return false } / / the core content is the soul of this class ~ ~ protected Object buildLazyResolutionProxy (final DependencyDescriptor descriptor, final @ Nullable String beanName) {Assert.state (getBeanFactory () instanceof DefaultListableBeanFactory, "BeanFactory needs to bea DefaultListableBeanFactory") / / implementation-oriented class programming is impolitely used here, using the DefaultListableBeanFactory.doResolveDependency () method ~ ~ final DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) getBeanFactory () / / TargetSource is the core reason for lazy loading. This interface is highlighted in the AOP chapter. / / there are many famous implementations such as HotSwappableTargetSource, SingletonTargetSource, LazyInitTargetSource, / / SimpleBeanTargetSource, ThreadLocalTargetSource, PrototypeTargetSource, etc. / / here because you only need to use it. So use anonymous inner class to implement ~ ~ the most important thing here is to look at the getTarget method. It executes ~ ~ when used (that is, when the proxy object is actually used) TargetSource ts = new TargetSource () {@ Override public Class getTargetClass () {return descriptor.getDependencyType () } @ Override public boolean isStatic () {return false } / / getTarget is called when a proxy method is called, so executing each proxy method executes this method, which is why doResolveDependency / / I personally think it is efficient There are some problems, so it is recommended to use @ Lazy~~~ / / as little as possible here, but the efficiency should be good, compared with http, serialization and deserialization. It's not worth mentioning, so it doesn't matter @ Override public Object getTarget () {Object target = beanFactory.doResolveDependency (descriptor, beanName, null, null). If (target = = null) {Class type = getTargetClass () / / A friendly handling of null values injected by many values (do not use null) if (Map.class = = type) {return Collections.emptyMap () } else if (List.class = = type) {return Collections.emptyList () } else if (Set.class = = type | | Collection.class = = type) {return Collections.emptySet () } throw new NoSuchBeanDefinitionException (descriptor.getResolvableType (), "Optional dependency not present for lazy injection point") } return target;} @ Override public void releaseTarget (Object target) {}} / / use ProxyFactory to generate a proxy for ts / / thus it can be seen that the target object of the final generated proxy object is actually TargetSource, while the target of TargetSource is the object of our business ProxyFactory pf = new ProxyFactory (); pf.setTargetSource (ts) Class dependencyType = descriptor.getDependencyType (); / / if the injected statement is written like private AInterface a;, then the excuse value is true / / put this interface type into it (otherwise the proxy does not belong to this type, won't you directly report an error when reflecting set? ) if (dependencyType.isInterface ()) {pf.addInterface (dependencyType);} return pf.getProxy (beanFactory.getBeanClassLoader ());}}
When the @ Lazy annotation completes the injection, the final injection is just a proxy object generated temporarily here. Only when the target method is actually executed will it get the real bean instance in the container to execute the target method.
Use the allowRawInjectionDespiteWrapping attribute to force a change in judgment
@ Component public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {@ Override public void postProcessBeanFactory (ConfigurableListableBeanFactory beanFactory) throws BeansException {((AbstractAutowireCapableBeanFactory) beanFactory) .setAllowRawInjectionDespiteWrapping (true);} after reading the above, do you have any further understanding of how to implement Spring circular dependency in Springboot? If you want to know more knowledge or related content, please follow the industry information channel, thank you for your support.
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.