- Scope
- The Scope to test
- Source code analysis
- The life cycle
- BeanFactory
- Circular dependencies
- test
- Source code analysis
- Making LeetCode project
Scope
The official Spring documentation #Bean Scopes
The Spring Framework supports six types of Scope, four of which are available only in web-Aware ApplicationContext. The remaining two types are:
singleton
: There is only one instance (singleton) per Spring IoC container.prototype
Each injection creates a new object. The Spring IoC container does not cache Prototype beans.
public interface BeanDefinition extends AttributeAccessor.BeanMetadataElement {
/**
* Scope identifier for the standard singleton scope: {@value}.
* <p>Note that extended bean factories might support further scopes.
* @see #setScope
* @see ConfigurableBeanFactory#SCOPE_SINGLETON
*/
String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
/**
* Scope identifier for the standard prototype scope: {@value}.
* <p>Note that extended bean factories might support further scopes.
* @see #setScope
* @see ConfigurableBeanFactory#SCOPE_PROTOTYPE
*/
String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;
Copy the code
The Scope to test
SingletonBean is a Singleton Scope bean. The Scope annotation is optional. The default is Singleton.
@Component
@Scope
@Data
@Slf4j
public class SingletonBean {
private int i;
@PostConstruct
public void init(a) {
log.info("SingletonBean init ..."); }}Copy the code
A ProtoTypeBean is a Prototype Scope bean.
@Component
@Scope(scopeName = SCOPE_PROTOTYPE)
@Data
@Slf4j
public class ProtoTypeBean {
private int i;
@PostConstruct
public void init(a) {
log.info("ProtoTypeBean init ..."); }}Copy the code
In the test code, fetch each bean twice from the container and look at the log output.
@RunWith(SpringRunner.class)
@Slf4j
@SpringBootTest(classes = Application.class)
public class BeanTest {
@Autowired
private ApplicationContext context;
@Test
public void testScope(a) { context.getBean(SingletonBean.class); context.getBean(ProtoTypeBean.class); }}Copy the code
The final output is one SingletonBean and two ProtoTypeBeans.
SingletonBean init ...
ProtoTypeBean init ...
Copy the code
Source code analysis
Look for reference in the definition Scope annotations, found the Scope only AnnotationScopeMetadataResolver as instance variables.
public class AnnotationScopeMetadataResolver implements ScopeMetadataResolver {
private final ScopedProxyMode defaultProxyMode;
protected Class<? extends Annotation> scopeAnnotationType = Scope.class;
Copy the code
@Override
public ScopeMetadata resolveScopeMetadata(BeanDefinition definition) {
ScopeMetadata metadata = new ScopeMetadata();
if (definition instanceof AnnotatedBeanDefinition) {
AnnotatedBeanDefinition annDef = (AnnotatedBeanDefinition) definition;
AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(
annDef.getMetadata(), this.scopeAnnotationType);
if(attributes ! =null) {
metadata.setScopeName(attributes.getString("value"));
ScopedProxyMode proxyMode = attributes.getEnum("proxyMode");
if (proxyMode == ScopedProxyMode.DEFAULT) {
proxyMode = this.defaultProxyMode; } metadata.setScopedProxyMode(proxyMode); }}return metadata;
}
Copy the code
To see how Spring handles the Scope annotation of the SingletonBean, we can add a breakpoint here for debugging, Condition is Objects. Equals (” yano. Spring. Beans. SingletonBean annDef. GetBeanClassName ()).
Then breakpoint debugging up step by step, you will call to org. Springframework. Context. The annotation. ClassPathBeanDefinitionScanner# doScan. Specific code will not be analyzed, as long as you understand the Spring framework, you can understand the source code.
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
for (String basePackage : basePackages) {
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
for (BeanDefinition candidate : candidates) {
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
if (candidate instanceof AbstractBeanDefinition) {
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
if (candidate instanceof AnnotatedBeanDefinition) {
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
if (checkCandidate(beanName, candidate)) {
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
registerBeanDefinition(definitionHolder, this.registry); }}}return beanDefinitions;
}
Copy the code
For a detailed analysis of the Spring IoC container, see the simplest Spring IoC Container source code analysis
The life cycle
Start by understanding the life cycle of the Spring Bean through a test case. The following defines a LifeBean:
@Component
@Data
@Slf4j
public class LifeBean implements BeanNameAware.BeanClassLoaderAware.InitializingBean.DisposableBean {
private int i;
@PostConstruct
public void init(a) {
log.info("LifeBean init ...");
}
@Override
public void setBeanName(String s) {
log.info("LifeBean setBeanName {}", s);
}
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
log.info("LifeBean setBeanClassLoader {}", classLoader);
}
@Override
public void afterPropertiesSet(a) {
log.info("LifeBean afterPropertiesSet i = {}", i);
}
@Override
public void destroy(a) {
log.info("LifeBean destroy ..."); }}Copy the code
Unit test code:
@Test
public void testLife(a) {
LifeBean bean = context.getBean(LifeBean.class);
bean.setI(1);
}
Copy the code
The log output:
LifeBean setBeanName lifeBean
LifeBean setBeanClassLoader jdk.internal.loader.ClassLoaders$AppClassLoader@55f96302
LifeBean init ...
LifeBean afterPropertiesSet i = 0
LifeBean destroy ...
Copy the code
BeanFactory
The comments on the BeanFactory interface file are as follows. This contains the life cycle of the bean and the corresponding sequence.
Customizing the Nature of a Bean
Circular dependencies
Circular dependencies are circular references where two or more beans hold each other. So how does Spring address loop dependencies?
There are three cases of cyclic dependencies in Spring:
- Constructor loop dependencies
- Setter loop dependency
- Prototype scope dependency handling
The constructor loop dependency is irresolvable because a bean is first created through the constructor, but the constructors are interdependent, the Java equivalent of a multithreaded deadlock.
test
Create two beans, CircleBean1 inject CircleBean2 with field and CircleBean2 inject CircleBean1 with field
@Data
@Component
public class CircleBean1 {
private int i;
@Autowired
private CircleBean2 circleBean2;
}
Copy the code
@Data
@Component
public class CircleBean2 {
private int i;
@Autowired
private CircleBean1 circleBean1;
}
Copy the code
We can see that the following test cases work well.
@Test
public void testCircle(a) {
CircleBean1 bean1 = context.getBean(CircleBean1.class);
CircleBean2 bean2 = context.getBean(CircleBean2.class);
log.info("bean1 {}, bean2 {}", bean1.getI(), bean2.getI());
}
Copy the code
Source code analysis
The dependencies created by setter injection are accomplished by the Spring container’s pre-exposure of beans that have just completed constructor injection but have not completed other steps, such as setter injection, and can only resolve singleton scoped bean loop dependencies. By exposing a singleton factory method in advance so that other beans can reference it, the code looks like this:
/** * Return the (raw) singleton object registered under the given name. * <p>Checks already instantiated singletons and also allows for an early * reference to a currently created singleton (resolving a circular reference). *@param beanName the name of the bean to look for
* @param allowEarlyReference whether early references should be created or not
* @return the registered singleton object, or {@code null} if none found
*/
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// Quick check for existing instance without full singleton lock
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
synchronized (this.singletonObjects) {
// Consistent creation of early reference within full singleton lock
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) { ObjectFactory<? > singletonFactory =this.singletonFactories.get(beanName);
if(singletonFactory ! =null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
Copy the code
EarlySingletonObjects are defined as:
/** Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
Copy the code
Making LeetCode project
Welcome to star, Fork, merge to create the most complete LeetCode problem solving on GitHub!
Java programming ideas – the most complete mind map -GitHub download link, need small partners can be taken ~!!
YANO SPACE 2021