• 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:

  1. singleton: There is only one instance (singleton) per Spring IoC container.
  2. prototypeEach 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