One, foreword

Here I have to sigh at the perfection of Spring code and excellent, from before to see the source code dazed to now basic understanding of Spring part of the source code, more and more found Spring developers thoughtful!

So what’s the purpose of learning source code? As I particularly like a sentence, there is no art, art is still available! There is art without tao, stop in art! As a project management framework used by the vast majority of Java developers in China, Spring is an ecology. What is ecology? For example, now SpringBoot, SpringCloud, what are they? Is an integral part of the Spring ecosystem! They use the various extension points provided in the Spring ecosystem, step by step encapsulation, the achievements of Spring fast start, automatic configuration and other eye-catching features! As Spring users, we should know about Spring’s implementation and various extension points so that we can really dive into the Spring ecosystem! In depth, then to the ecological components such as the SpringBoot framework, will be naturally!

Two, the opening question

I believe that most developers use Spring naturally! So the following code you must be familiar with!

/** * Global configuration class **@author huangfu
 */
@Configuration
public class ExpandRunConfig {
	@Bean
	public TestService testService(a) {
		return new TestServiceImpl();
	}

	@Bean
	public UserService userService(a) {
		testService();
		return newUserServiceImpl(); }}Copy the code

As you can see, Spring manages two classes called TestService,UserService, but there is also a reference to TestService () in UserService ()! So the question is, how many times do you think TestService will be instantiated?

I’m sure a lot of you will say it once, right, right, but why? I was deeply self-doubt about the problem here! I even doubted my Java foundation for a while, seeing that there was another method called, but why wasn’t it instantiated twice?

I asked a lot of colleagues, friends, all they know is that it is ok to write like this! But the specific reason does not know! Why is that? We look down with that question in mind!

Is the configuration class you see a real configuration class?

Let’s pull this configuration class out of the bean container and see what’s different!

public static void main(String[] args) {
    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(ExpandRunConfig.class);
    ExpandRunConfig bean = ac.getBean(ExpandRunConfig.class);
    System.out.println(bean);

}
Copy the code

Let’s debug and see what we got!

Sure enough, he is not he, he is (tainted) proxy, and the proxy is cglib, so here you can guess a problem, call another Bean method in a Bean method, he must do it through the proxy, thus completing many calls only to instantiate once!

Here we go, we’re done. That’s it! So there are two questions:

  1. When was the proxy added to the configuration class?
  2. How does the proxy logic accomplish multiple calls to the function that returns the same instance?

Below we are with two questions, to chase the Spring source code, to see how in the end!

Four, agent ICONS

This diagram I put out, if you have not understood, it must be very confused, it doesn’t matter, the source code will be explained, and after the source code, we will probably write a, to help you understand!

Five, the source code detailed explanation

Guess what, readers of my previous posts should know! For example, if you want to create a bean instance with definitionMap (bgDefinitionMap), then you need to replace the branch (bgdefinitionMap) with the branch (bgdefinitionMap). If you want to instantiate the branch (bgdefinitionMap) with the branch (bgDefinitionMap), then you must replace the branch (bgdefinitionMap) with the branch (bgdefinitionMap). So our entrance should be here:

So where is this code enhanced?

/** * Prepare to configure classes to service Bean requests at run time * by replacing them with CGLIB enhanced subclasses. * /
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {... Ignore the corresponding logic................// The bytecode enhanced configuration class appears to use cglibenhanceConfigurationClasses(beanFactory); . Ignore the corresponding logic................ }Copy the code

Call the configuration class enhanceConfigurationClasses enhanced logic

/** * Use BeanDefinitions for BeanFactory; /** * Use BeanDefinitions for BeanFactory; Then, any candidate will pass the {@linkConfigurationClassEnhancer}. * candidate status be determined by the attribute BeanDefinition metadata. *@see ConfigurationClassEnhancer
 */
public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
    // Finally we need to do the enhanced Bean definitions
    Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();
    for (String beanName : beanFactory.getBeanDefinitionNames()) {
        BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
        // What is the Full class
        if(ConfigurationClassUtils.isFullConfigurationClass(beanDef)) { ..... Ignore logs....... is displayed//// This is available only if it is in Full modeconfigBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef); }}if (configBeanDefs.isEmpty()) {
        // There is nothing to enhance -> return immediately
        return;
    }
    // Configure the class enhancer
    / / ConfigurationClassEnhancer is at the heart of the configuration class do enhance operation classes
    / / initialization initialize two BeanFactoryAwareMethodInterceptor and BeanMethodInterceptor chlib interceptor class
    The methods in this class will produce the final proxy class
    // This method has one
    ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();
    // Enhance () for each Full mode configuration class one by one
    for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) {
        AbstractBeanDefinition beanDef = entry.getValue();
        // If @Configuration is proxied, always proxied the target class
        beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
        try {
            // Set an enhanced subclass of the user-specified bean class
            //CGLIB implements the proxy by generating subclass objects for the parent class, so the "parent" type is specified hereClass<? > configClass = beanDef.resolveBeanClass(this.beanClassLoader);
            if(configClass ! =null) {
                // do the enhancement, return enhancedClass is an enhanced subclass
                This will build a CGlib enhancer that will eventually return the class object completed by the proxy!Class<? > enhancedClass = enhancer.enhance(configClass,this.beanClassLoader);
                // If the proxy is not equal, then the actual type is set
                if(configClass ! = enhancedClass) { ..... Ignore logs..... is displayed// When instantiating an instance of the configuration class, the enhanced subclass is instantiated
                    // Replace beanClass in config class.beanDef.setBeanClass(enhancedClass); }}}catch(Throwable ex) {... Ignoring exception Handling...... }}}Copy the code

This class is crucial. It does a few things:

  1. Filter configuration class, only add@ConfigurationThe configuration class will be enhanced!
  2. useenhancer.enhanceBuild an enhancer that returns enhanced proxy class objects!
  3. Replace configuration class original beanClass with proxy class!

So what we’re most concerned about is “enhance”, which is the logic of enhancer.enhance

publicClass<? > enhance(Class<? > configClass,@Nullable ClassLoader classLoader) {
		// If the interface has been implemented and the proof has been proxied, return
		if(EnhancedConfiguration. Class. IsAssignableFrom (configClass)) {... Ignore log printing...return configClass;
		}
		// Not represented. We create an Enhancer by calling the newEnhancer() method
		// Then use the enhancer to generate the proxy Class bytecode Class object
		// Create a new CGLIB Enhancer instance and configure it accordingly
        //createClass sets a set of callbacks (cglib's method interceptors)Class<? > enhancedClass = createClass(newEnhancer(configClass, classLoader));if(logger.istraceEnabled ()) {... Ignore log printing... }return enhancedClass;
	}
Copy the code

This is a transition method, the newEnhancer method that really builds an agent enhancer, we seem to be close to our answer!

/** * create a new CGLIB {@linkEnhancer} instance. * /
private Enhancer newEnhancer(Class<? > configSuperClass,@Nullable ClassLoader classLoader) {
    Enhancer enhancer = new Enhancer();
    // Target type: this will be used as the parent type to generate a bytecode subclass
    enhancer.setSuperclass(configSuperClass);
    // The proxy class implements the EnhancedConfiguration interface, which inherits the BeanFactoryAware interface
    // This step is necessary to make the configuration class enforce the EnhancedConfiguration (BeanFactoryAware) so that you can easily obtain the beanFactory
    enhancer.setInterfaces(newClass<? >[] {EnhancedConfiguration.class});/ / set the generated proxy class doesn't implement org. Springframework. Additional. Proxy. The Factory interface
    enhancer.setUseFactory(false);
    // Set the generation policy for the proxy class name: Spring defines a generation policy where you have "BySpringCGLIB" in the name
    enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
    enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));
    Filters have a set of callback classes, which are the actual methods that intercept instances
    enhancer.setCallbackFilter(CALLBACK_FILTER);
    enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());
    return enhancer;
}
Copy the code

If you’re familiar with CGlib, you’ll be familiar with these lines of code, which do a few things!

  1. Set the class that needs a proxy
  2. Set the interface that the generated proxy class needs to implementEnhancedConfigurationNote that this is an important operation to ensure that the final class returns from the beanFactory. Why? becauseEnhancedConfigurationisBeanFactoryAwareSpring will call back to him and set a beanFactory for him. If you don’t understand it, you might as well write it down and this first, wait for the end of the look in the taste!
  3. Set the filter. The filter is actually a set of callback methods. This callback method is the actual logic that will be executed after the final method is intercepted.
  4. Return the final enhancer!

As mentioned earlier, what we need to focus on is the set of interceptors. Let’s go inside the interceptor and find the corresponding callback instance!

CALLBACK_FILTER: the constant corresponds to a filter. Let’s see how it works:

private static final ConditionalCallbackFilter CALLBACK_FILTER = new ConditionalCallbackFilter(CALLBACKS);
Copy the code

CALLBACKS: CALLBACKS: CALLBACKS: CALLBACKS: CALLBACKS: CALLBACKS: CALLBACKS

// The callback to use. Note that these callbacks must be stateless.
private static final Callback[] CALLBACKS = new Callback[] {
    / / this is really able to Bean method call returns for many times is the actual interception of a Bean instance method, the interceptor is fully capable of, why many times call returns only
    // An instance of the problem
    new BeanMethodInterceptor(),
    // Intercept BeanFactoryAware and assign the setBeanFactory inside
    BeanFactoryAware = BeanFactoryAware = BeanFactoryAware = BeanFactoryAware = BeanFactoryAware = BeanFactoryAware = BeanFactoryAware = BeanFactoryAware = BeanFactoryAware = BeanFactoryAware = BeanFactoryAware = BeanFactoryAware = BeanFactoryAware
    new BeanFactoryAwareMethodInterceptor(),
    Cglib is an exception thrown by cglib because it does not handle eques in objects
    // Methods are allowed to handle instances of methods that are not intercepted
    // This instance does not implement anything. Empty means no processing!
    NoOp.INSTANCE
};
Copy the code

Specific inside each interceptor is exactly what, the comment said very clearly, we start from the second! Why not start with the first one? The first is more troublesome, we from shallow to deep, step by step!

BeanFactoryAwareMethodInterceptor

/** * block any {@linkBeanyaware # setBeanFactory (BeanFactory)}@code @Configuration} class instance to record {@linkThe BeanFactory}. *@see EnhancedConfiguration
	 */
private static class BeanFactoryAwareMethodInterceptor implements MethodInterceptor.ConditionalCallback {

    @Override
    @Nullable
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        // Find the field named '$$beanFactory' in this class (proxy class)
        Field field = ReflectionUtils.findField(obj.getClass(), BEAN_FACTORY_FIELD);
        // If it is not found, an error is reported. If the field is found, it is assigned a valueAssert.state(field ! =null."Unable to find generated BeanFactory field");
        field.set(obj, args[0]);

        // Does the actual (non-Cglib) superclass implement BeanFactoryAware?
        // If so, call its setBeanFactory () method. If not, quit.
        // If the user class (i.e., your own class) implements the interface itself, then don't worry, it will assign a value to you
        if (BeanFactoryAware.class.isAssignableFrom(ClassUtils.getUserClass(obj.getClass().getSuperclass()))) {
            return proxy.invokeSuper(obj, args);
        }
        return null;
    }

    /** * The setBeanFactory(XXX) method matched successfully *@paramCandidateMethod Currently executed method *@return* /
    @Override
    public boolean isMatch(Method candidateMethod) {
        // Check whether the method is setBeanFactory
        returnisSetBeanFactory(candidateMethod); }... Ignore unnecessary logic......... }Copy the code

If you have noticed that the resulting proxy configuration class has a $$beanFactory property, this is where the property is assigned! Put the picture out again, and look at the last attribute!

The main functions of this interceptor are:

  1. interceptsetBeanFactoryMethods for$$beanFactoryAssignment!

Ok, this interceptor introduction finished, the function you also remember, so, we analyze the next interceptor, this is the key!

BeanMethodInterceptor

/** * {@link Bean @Bean} method to check the existence of the bean object in the supplied BeanFactory. *@throwsThrowable serves as a super implementation of the pooling agent method for all exceptions that might be thrown when invoked, i.e., the actual {@code @Bean} method * when the method is matched, it goes to the intercepting method. This is important logic to solve the problem that the bean method is created only once */
@Override
@Nullable
public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs, MethodProxy cglibMethodProxy) throws Throwable {
    // Get the Bean factory by reflection. This is the value of the $$beanFactory property
    // The value of the last interceptor that was injected
    ConfigurableBeanFactory beanFactory = getBeanFactory(enhancedConfigInstance);
    // Get the name of the Bean
    String beanName = BeanAnnotationHelper.determineBeanNameFor(beanMethod);

    // Determines whether this bean is a scoped proxy
    // Whether the method is labeled with @scoped annotation
    if (BeanAnnotationHelper.isScopedProxy(beanMethod)) {
        String scopedBeanName = ScopedProxyCreator.getTargetBeanName(beanName);
        if(beanFactory.isCurrentlyInCreation(scopedBeanName)) { beanName = scopedBeanName; }}... Ignore irrelevant code...// Check whether the given method corresponds to the factory method of the currently invoked container.
    // Compare the method name with the argument list to determine if it is the same method
    // What is the meaning of this sentence
    // In the whole method, I think this judgment is the core, why is it the core, because only if this judgment returns false it will really follow the enhanced logic
    // When will it be false?
    // Spring first gets the currently used method and then the currently called method, and returns false if the two methods are inconsistent
    // Under what circumstances are hu inconsistent?
    // When another method is called inside the bean method, the current method is inconsistent with the calling method, resulting in the return of false and then to execute the enhanced logic
    if (isCurrentlyInvokedFactoryMethod(beanMethod)) {
        // This is a minor detail: if your @bean returns type BeanFactoryPostProcessor
        // Please use the static method, otherwise the log will be printed ~~~~
        // Because if the method is non-static, part of the post-processing failure will not handle you and may have an image on your program
        // Of course, there may be no impact, so the official is only a suggestion
        if(logger.isInfoEnabled() && BeanFactoryPostProcessor.class.isAssignableFrom(beanMethod.getReturnType())) { ...... Ignore logs....... is displayed }// This means: the current method is the intercepted method, so there is nothing to say
        // This is equivalent to executing super(XXX) in the proxy class;
        // However, however, this is still the proxy class
        // The method that actually calls itself will eventually be called again to the resolveBeanReference method below
        // The design here is wonderful.
        // A basic understanding of cglib is necessary to understand this method.
        // The first thing to understand is that CGLIb is based on subclass integration to enhance the target method
        // So it is easy to call the original method of the parent class to execute the implementation without the enhancement
        // When the current method is called and the called method is a method, the cglib superclass is directly called
        BeanFactory (); // beanFactory ();
        return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);
    }
    // The instantiated method that is called in the method will be handed over here
    // This step is the real way to do it. When it is found that the method needs a proxy, the original method of the superclass is not called
    // Instead, I call the logic I need to proxy to return an object, thus completing the proxy for the object
    return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);
}
Copy the code

At first glance, is not a lot, nothing we bit by bit analysis:

  1. First let’s look at the judgmentif (isCurrentlyInvokedFactoryMethod(beanMethod))This judgment is important! He is fromThreadLocalWe’ve mentioned factory methods many times before. What is a factory method? The method that you wrote at sign Bean, we call it the factory method, and we use the above methodThe opening questionIn that code as an example!
    • When creating aUserServiceImplThe current method object is stored firstUserServiceImplMethod object, which is placed toThreadLocalGo inside!
    • Then it finds a proxy object, goes into the proxy logic, inside the proxy logic, goes to the judgment logic, finds the method and the interceptThreadLocalThe methods inside are consistent, and then let go and start calling the realuserService()Method, when I execute this method, the method is called internallytestService();Methods!
    • foundtestService()It’s a proxy object again, so you go to the proxy logic again, and then you go to this judgment, and you find out what the current interception istestServiceThe methods inside ThreadLocal areuserService“At this point the judgment has failed, so go to another branch!
    • Instead of executing this method, the other branch will go directly to the beanFactory to fetch the bean and return it directly!
  2. return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);This is when the intercepting method is a factory method when the direct release, execute the parent class logic, why is the parent class! Cglib is implemented on the basis of inheritance, and its parent class is the original unproxied method, equivalent to the callsuper.userService()To call the original logic!
  3. resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);And this is also the code logic that we’re going to look at in a minute, which is that when it doesn’t work, when it finds out that there’s another factory method called inside of a factory method, it goes in here! Let’s take a look at the logic!

ResolveBeanReference method logic

private Object resolveBeanReference(Method beanMethod, Object[] beanMethodArgs, ConfigurableBeanFactory beanFactory, String beanName) {... Ignore unnecessary code...// Get the instance from the container via getBean
        $$beanFactory = '$$beanFactory'; $$beanFactory = '$$beanFactory'Object beanInstance = (useArgs ? beanFactory.getBean(beanName, beanMethodArgs) : beanFactory.getBean(beanName)); . Ignore unnecessary code...returnbeanInstance; }}Copy the code

The main logic here is to get the bean object corresponding to this method from the beanFactory and return it directly! Instead of calling the corresponding method to create! This is why multiple calls will always return the same instance!

Six, summarized

The whole process is more around, readers can follow the article debugging source code, I believe that after deep thinking, you must have a harvest!

The whole process is divided into two parts:

  1. Enhanced configuration class
    • Detection of added@ConfigurationAnnotated configuration class!
    • Create a proxy object (BeanMethodInterceptor, BeanFactoryAwareMethodInterceptor) as an enhancer of callback methods.
    • Return the class object after the proxy!
    • Set the beanClass into the configuration class!
  2. Create a bean
    • Discovered that the bean was created with the configuration class attached (that is, the @bean method added)!
    • Call back to the method of the enhanced configuration class and document that method!
    • Determine whether the interception method is consistent with the recording method
      • If consistent, go to the original creation logic!
      • If not, get it from the bean factory!
    • Returns the created bean

Call it a day!


If there is an error in the understanding of the article, you are welcome to talk to the bosses. Welcome to follow the author’s public account, progress together, learn together!