Spring Bean Class loading phase

The last section analyzed the detailed process of merging BeanDefinitions, so the next step is to load the class corresponding to the Bean before the Spring Bean is created. So there are some details that need to be sorted out.

This class loading

The first involves some of the basics of Java, the ClassLoader, which will be more familiar to those of you who have come into contact with the framework, but Java relies on a ClassLoader to load classes.

For details, see Java ClassLoader

Java Security Security control

The second is about Java Security, which is some controls in Java security. In the early days of Spring, we did not add this part of the implementation. In the later years, we started to add some related controls. When DI, the IoC implementation, talked to us about dependency injection and dependency lookup, he didn’t focus on a Security integration with Java Security, but this is clearly described in the Java EE specification, so when we look at the current version later, You’ll find some code for the Java Security API, and we’ll see what that does later.

ConfigurableBeanFactory temporary this

The third aspect, and this aspect is a big one, which I’m going to selectively skip in this section, and I’m going to introduce you to a simple scenario, which is actually very limited, and we don’t usually use it.

Code sample

java.lang.ClassLoader org.springframework.beans.factory.config.BeanDefinition org.springframework.beans.factory.support.AbstractBeanFactory org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory org.springframework.util.ClassUtils java.lang.Class

org.geekbang.thinking.in.spring.bean.lifecycle.MergedBeanDefinitionDemo

Let’s start with the question, how does a BeanDefinition load into a Class from a meta-information form of text?

Well, let’s go back to idea. In the previous section, we know that BeanDefinition will generate RootBeanDefinition after merging. So the next thing that’s important is that we saw earlier that BeanDefinition actually has a very important piece of information init, which is a basic source for our bean, which is that it gets getBeanClassName.

org.springframework.beans.factory.config.BeanDefinition#getBeanClassName

	/**
	 * Return the current bean class name of this bean definition.
	 * <p>Note that this does not have to be the actual class name used at runtime, in
	 * case of a child definition overriding/inheriting the class name from its parent.
	 * Also, this may just be the class that a factory method is called on, or it may
	 * even be empty in case of a factory bean reference that a method is called on.
	 * Hence, do <i>not</i> consider this to be the definitive bean type at runtime but
	 * rather only use it for parsing purposes at the individual bean definition level.
	 * @see #getParentName()
	 * @see #getFactoryBeanName()
	 * @see #getFactoryMethodName()
	 */
	@Nullable
	String getBeanClassName(a);
Copy the code

As we’ve seen before, BeanDefinition is a configuration or description of meta information about a Bean, so it’s actually presented in text, it doesn’t explicitly tell you what its Class is, we know that in Java Class descriptions are expressed in terms of Class, The Class is loaded by the ClassLoader, so this process of instantiating BeanDefinition is certainly not incompatible with our traditional Java mechanism.

java.lang.ClassLoader

/** * A class loader is an object that is responsible for loading classes. The * class ClassLoader is an abstract class. Given the binary name of a class, a class loader should attempt to * locate or generate data that constitutes a definition for the class. A * typical strategy is to transform the name into a file name and then read a * "class file" of that name from a file system. * */
public abstract class ClassLoader {
Copy the code

In fact, in the Spring IoC container implementation, ClassLoader loading is a marginal operation. Usually we don’t pay much attention, and then we will do it in a method way.

Let’s take a look at the previous example, MergedBeanDefinitionDemo, and put a breakpoint at the location of the dependency lookup:

org.geekbang.thinking.in.spring.bean.lifecycle.MergedBeanDefinitionDemo org.springframework.beans.factory.support.AbstractBeanFactory#getMergedBeanDefinition(java.lang.String, org.springframework.beans.factory.config.BeanDefinition, org.springframework.beans.factory.config.BeanDefinition)

And then put a breakpoint on getMergedBeanDefinition, which we saw in the last video.

Next, the debugger goes directly to the second breakpoint and looks at the call stack: org.springframework.beans.factory.support.AbstractBeanFactory#checkMergedBeanDefinition

Click on the doGetBean method, hit a breakpoint and run to that location, so we’ll skip the previous section and focus on what our own section looks like to load the BeanDefinition’s corresponding Class.

The User RootBeanDefinition is already assembled, but at this stage of execution, we only know the meta information of the Bean, and we haven’t told you how to create a Bean.

There are a few details to skip. The dependsOn array is empty, so skip an if

The process for loading classes is nested within the process for Spring to instantiate beans

Ok, notice the comment here, Create bean instance. First mbD.issingleton () does not currently specify a scope for the User, so the bean defaults to Singleton, and so on

org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, org.springframework.beans.factory.ObjectFactory<? >)

The getSingleton method is passed with a lambda expression, especially the second argument. So let’s put a breakpoint inside getSingleton:

org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, org.springframework.beans.factory.ObjectFactory<? >) Here you can see singletonObjects, this ConcurrentHashMap that was discussed earlier

Spring – the core – 7-75 | singleton object as a dependent source: single object what are the differences and common spring Bean?

SingletonBeanRegistry#registerSingleton handles the singleton and holds the singleton. As we’ve seen before, the singleton registered in the Spring container has no lifecycle management and is an external object. And because there are multiple get and set operations, you need to add a lock to keep multiple threads safe.

If (singletonObject == null) == null); if (singletonObject == null) == null; You can also see this by running down:

Middle over less important a part of the code, run to singletonObject = singletonFactory. The getObject ();

SingletonObject is the second entry to the getSingleton method. We noticed earlier that it is a lambda expression, so we put a breakpoint in it:

CreateBean (beanName, MBD, args)

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean(java.lang.String, org.springframework.beans.factory.support.RootBeanDefinition, java.lang.Object[])

The breakpoint is typed in, and the process of creating the Bean sample begins.

AbstractBeanFactory#resolveBeanClass The key method for loading BeanClass

First of all, RootBeanDefinition has been given, and then if you look down, you’ll get a call to the resolveBeanClass method, and look at the comment

// Make sure bean class is actually resolved at this point, and
// clone the bean definition in case of a dynamically resolved Class
// which cannot be stored in the shared merged bean definition.Make sure of the beanclassHas been definitively parsed at this point, and clonedbean definitionIn case of dynamic parsingclassWhen, becausemerged beanDefinitionIs shared and therefore cannot be storedCopy the code

So this method involves the parsing of class from beanDefinition, which is very important. Go ahead and see

org.springframework.beans.factory.support.AbstractBeanFactory#resolveBeanClass So resolveBeanClass passes two arguments, resolveBeanClass(MBD, beanName), one is RootBeanDefinition, the other is beanName, This method actually takes three arguments, the last of which is variable-length and not passed.

If (mbD.hasbeanClass ()) tells you if the current RootBeanDefinition exists as a class for the bean, which would be returned if the class had been parsed.

org.springframework.beans.factory.support.AbstractBeanDefinition#hasBeanClass

Note, however, that we judge this.beanClass instanceof Class, and we can look at the definition in the current beanDefinition

Is a string, although its contents are not empty. HasBeanClass determines whether it is a Class unless it has been called previously:

org.springframework.beans.factory.support.AbstractBeanDefinition#setBeanClass

Otherwise beanClass is still a string and is not a complete Class object at all, so this judgment is false.

The important thing to see here is that beanClass is a String until the CLass that corresponds to BeanDefinition is loaded, and then it becomes a CLass, and that’s what we’ll see here.

Go down, System. GetSecurityManager (), visible returns null, involved in front of us speak JavaSecurity, Java security framework, it returns null System security manager has not been activated.

Of course both a branch into the if of judgment, to be able to see will call doResolveBeanClass method, but in front of the set of a layer of the AccessController. The doPrivileged (), there could be an access control, To understand how permission control works, go to ClassLoader:

Turn the corner and look at Java’s security controls

java.lang.ClassLoader#loadClass(java.lang.String) java.lang.ClassLoader#loadClass(java.lang.String, boolean)

Usually, when a ClassLoader tries to load a class, it calls a loadClass() method, but few people pay attention to this method. This method is not allowed to load all classes, but it has a safe limit.

LoadClass involves the selection of a ClassLoader. Let’s look at another method,

java.lang.ClassLoader#getSystemClassLoader

See the comment, there’s actually a SecurityException in there, and the comment says there’s going to be a checkPermission, it’s going to check for permissions, permissions if they’re allowed to get the SystemClassLoader, To get a ClassLoader, you basically need a secure Permission, but usually all permissions are allowed, that is, a corresponding Permission to grant, which is usually all. For example, file read permission, classLoader permission, and reflection permission are all allowed.

So in the same way, by the way, inside Thread,

java.lang.Thread#getContextClassLoader

Thread has a getContextClassLoader method, which also has a security constraint. You’ll see that the last method also checks to see if the SecurityManager is enabled. So this is a safe switch to control.

Of course normally the SecurityManager is null, so it is possible to retrieve the ContextClassLoader directly, but this does not mean that it does not have this concern. So that’s one of the controls that we’re talking about when BeanDefinition reads from the ClassLoader.

Back to the theme, AbstractBeanFactory#resolveBeanClass

And then the method that we’re going to do is the doResolveBeanClass method, which is going to take our class from BeanDefinition, and we’re going to leave the two parameters alone, and we’re going to go in.

org.springframework.beans.factory.support.AbstractBeanFactory#doResolveBeanClass

There will be two classloaders, one for our current BeanFactory and the other for dynamicLoader. We mentioned a temporary ClassLoader on a ConfigurableBeanFactory, so this is here.

The next code, the first beanClassLoader, is the current AppClassLoader, and the second dynamicLoader is actually the same, which is supplied by the JVM to our Spring container.

One more detail is that sometimes to get this beanClassLoader, we need to use a BeanClassLoaderAware, which we’ll talk about later,

package org.springframework.beans.factory;

/** 
 * Callback that allows a bean to be aware of the bean
 * {@link ClassLoader class loader}; that is, the class loader used by the
 * present bean factory to load bean classes.
 *
 * <p>This is mainly intended to be implemented by framework classes which
 * have to pick up application classes by name despite themselves potentially
 * being loaded from a shared class loader.
 *
 * <p>For a list of all bean lifecycle methods, see the
 * {@link BeanFactory BeanFactory javadocs}.
 *
 * @author Juergen Hoeller
 * @author Chris Beams
 * @since 2.0
 * @see BeanNameAware
 * @see BeanFactoryAware
 * @see InitializingBean
 */
public interface BeanClassLoaderAware extends Aware {

   /**
    * Callback that supplies the bean {@linkClassLoader class loader} to * a bean instance. * <p>Invoked <i>after</i> the population of normal bean properties but *  <i>before</i> an initialization callback such as * {@link InitializingBean InitializingBean's}
    * {@link InitializingBean#afterPropertiesSet()}
    * method or a custom init-method.
    * @param classLoader the owning class loader
    */
   void setBeanClassLoader(ClassLoader classLoader);

}
Copy the code

This interface will pass in the ClassLoader, which is the ClassLoader from the BeanFactory.

With this interface, the problem is that Spring’s ClassLoader is replaceable.

Back inside the doResolveBeanClass, and looking further down, we know that typesToMatch is actually null, so this if judgment will not be entered.

So if you go down here,

mbd.getBeanClassName();
Copy the code

The BeanClassName may be inconsistent in the Spring container, so see what happens next

Object evaluated = evaluateBeanDefinitionString(className, mbd);
Copy the code

org.springframework.beans.factory.support.AbstractBeanFactory#evaluateBeanDefinitionString

@Nullable
protected Object evaluateBeanDefinitionString(@Nullable String value, @Nullable BeanDefinition beanDefinition) {
   if (this.beanExpressionResolver == null) {
      return value;
   }

   Scope scope = null;
   if(beanDefinition ! =null) {
      String scopeName = beanDefinition.getScope();
      if(scopeName ! =null) { scope = getRegisteredScope(scopeName); }}return this.beanExpressionResolver.evaluate(value, new BeanExpressionContext(this, scope));
}
Copy the code

As you go down, there’s going to be an operation in here. If the name BeanClassName exists and it’s not empty, then we’re going to evaluate it, and it’s a long code, but it’s not really relevant to our main process, so we’re just going to return the current BeanClassName, and notice that, Sometimes BeanClassName and evaluated may not be consistent. I’m not going to talk about that, but our current example doesn’t have that.

Further down, freshResolve is also false, so I skipped another part

Look directly

mbd.resolveBeanClass(beanClassLoader);
Copy the code

So it’s back to BeanDefinition, right? ! That front so complicated is doing what?!

AbstractBeanDefinition#resolveBeanClass

org.springframework.beans.factory.support.AbstractBeanDefinition#resolveBeanClass

If I go in there, the first line gets the className,

Class<? > resolvedClass = ClassUtils.forName(className, classLoader);Copy the code

Next up is classutils.forname (className, classLoader); ClassUtils is a utility Class provided by Spring. The key to getting a Class is to call class.forname (), which uses Java’s traditional ClassLoader to perform ClassLoading.

org.springframework.util.ClassUtils#forName

java.lang.Class#forName(java.lang.String, boolean, java.lang.ClassLoader)

Now, there’s a very important change in the BeanDefinition of User right here

Notice what beanClass used to be, it used to be String, and when we get the Class, it does a swap here, it turns the old data type into a Class type, and that’s when we get the Class that we need.

A simple step:

AbstractBeanFactory#resolveBeanClass Is a string beanClass GetContextClassLoader (), class.forname (); beanClass ();

conclusion

The BeanDefinition, which I just debug with a simple example, will eventually use a traditional Java ClassLoader in ClassLoading, but it will involve some Java security details when called, In fact, this operation is already available in Java itself, but we usually don’t activate it and therefore ignore the call.

The next important step is how to instantiate a BeanDefinition from configuration resolution and merge to Class loading.