preface
As we know, IOC is at the heart of Spring. It is responsible for controlling the life cycle of objects and the relationships between objects. For example, how do we find a partner? Common circumstance is, want to see everywhere on the road which MM already beautiful figure is good, accord with our taste. I get their numbers, I make connections, I figure out how to get to know them, and… I’m going to skip the N steps and fall in love and get married. The IOC is like a dating agency here. It has a lot of information about marriageable men and women. If you need something, just tell it what kind of girlfriend you want. It will provide us with a MM, straight love marriage, perfect! How does Spring generate and manage these objects?
1. Method entrance
Org. Springframework. Beans. Factory. Support. DefaultListableBeanFactory. PreInstantiateSingletons () method is the leading role of today, all start from it.
public void preInstantiateSingletons() throws BeansException { //beanDefinitionNames are beanName List<String> beanNames = new for all BeanDefinitions after initialization in the previous section ArrayList<String>(this.beanDefinitionNames);for (String beanName : beanNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
if(! bd.isAbstract() && bd.isSingleton() && ! Bd.islazyinit ()) {bd.islazyInit ()) {//getBean is the main force, responsible for instantiating Bean and IOC dependency injection getBean(beanName); }}}Copy the code
Instantiation of beans
In the entry method getBean, the doCreateBean method is first called. The first step is to instantiate a Bean through reflection.
protected Object doCreateBean(final String beanName,
final RootBeanDefinition mbd, final Object[] args) {
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if(instanceWrapper == null) {//createBeanInstance is the process of instantiating the Bean, which is some judgment and reflection, finally calling ctor.newinstance (args); instanceWrapper = createBeanInstance(beanName, mbd, args); }}Copy the code
3. Annotation support
After the Bean is instantiated, a piece of post-processor code is entered. Look from the code, filter realized MergedBeanDefinitionPostProcessor interface classes, call its postProcessMergedBeanDefinition () method. Who are realized MergedBeanDefinitionPostProcessor interface? Let’s focus on three
AutowiredAnnotationBeanPostProcessor
CommonAnnotationBeanPostProcessor
RequiredAnnotationBeanPostProcessor
Copy the code
Remember that in Spring’s initialization and XML chapter, we said that Spring’s support for the annotation-config tag registered some special beans, including exactly these three. Now what do they secretly do? From the method names, they do the same thing, loading the annotation metadata. Inside the method, it does the same two things
ReflectionUtils.doWithLocalFields(targetClass, new ReflectionUtils.FieldCallback()
ReflectionUtils.doWithLocalMethods(targetClass, new ReflectionUtils.MethodCallback()
Copy the code
Looking at the method arguments, targetClass is the Bean’s Class object. The next step is to retrieve its fields and methods, determine if they contain annotations, and finally turn it into an InjectionMetadata object.
public static void main(String[] args) throws ClassNotFoundException { Class<? > clazz = Class.forName("com.viewscenes.netsupervisor.entity.User");
Field[] fields = clazz.getFields();
Method[] methods = clazz.getMethods();
for (int i = 0; i < fields.length; i++) {
Field field = fields[i];
if(field. IsAnnotationPresent (Autowired. Class)) {/ / convert AutowiredFieldElement object, add container}}for (int i = 0; i < methods.length; i++) {
Method method = methods[i];
if(method. IsAnnotationPresent (Autowired. Class)) {/ / convert AutowiredMethodElement object, add container}}return new InjectionMetadata(clazz, elements);
}
Copy the code
The InjectionMetadata object has two important attributes: targetClass and injectedElements, which are the focus of annotated dependency injection.
public InjectionMetadata(Class<? > targetClass, Collection<InjectedElement> elements) {this.targetClass = targetClass; InjectedElements = elements; //injectedElements = elements; } //pd is a JDK introspection object, Protected InjectedElement(Member Member, PropertyDescriptor pd) {this. Member = Member; this.isField = (member instanceof Field); this.pd = pd; }Copy the code
With that said, let’s finally look at what the source code looks like. Take Autowired for example.
ReflectionUtils.doWithLocalFields(targetClass, new ReflectionUtils.FieldCallback() {
@Override
public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
AnnotationAttributes ann = findAutowiredAnnotation(field);
if(ann ! = null) {if (Modifier.isStatic(field.getModifiers())) {
if (logger.isWarnEnabled()) {
logger.warn("Autowired annotation is not supported on static fields");
}
return; } boolean required = determineRequiredStatus(ann); currElements.add(new AutowiredFieldElement(field, required)); }}}); ReflectionUtils.doWithLocalMethods(targetClass, new ReflectionUtils.MethodCallback() {
@Override
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
if(! BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {return;
}
AnnotationAttributes ann = findAutowiredAnnotation(bridgedMethod);
if(ann ! = null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {if (Modifier.isStatic(method.getModifiers())) {
if (logger.isWarnEnabled()) {
logger.warn("Autowired annotation is not supported on static methods");
}
return;
}
if (method.getParameterTypes().length == 0) {
if (logger.isWarnEnabled()) {
logger.warn("Autowired annotation should be used on methods with parameters:"); } } boolean required = determineRequiredStatus(ann); PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz); currElements.add(new AutowiredMethodElement(method, required, pd)); }}});Copy the code
Dependency injection
After completing the instantiation of the method Bean in doCreateBean (), dependency injection follows. Bean dependency injection can be done in two ways: configuration files and annotations.
1. Annotated injection process
In section 3 above, Spring has filtered Field and Method annotations on Bean instances that contain @AutoWired, @Resource, and so on, and returned InjectionMetadata objects that contain Class objects, introspection objects, and members. Or in the case of the @autowired, this call to AutowiredAnnotationBeanPostProcessor. PostProcessPropertyValues (). InjectedElement (@autoWired, @autowired, @autowired, @autoWired, @autoWired); InjectedElement. Inject () is then called. InjectedElement has two subclasses: AutowiredFieldElement and AutowiredMethodElement.
AutowiredFieldElement
If Autowired annotates on a field, its configuration looks like this.
public class User {
@Autowired
Role role;
}
Copy the code
protected void inject(Object bean, String beanName, PropertyValues PVS) throws Throwable {// Take @autowired Role Role as an example, The field is here / / public com.viewscenes.net supervisor. The entity. The supervisor Role / / com.viewscenes.net. The entity. The User. The Role field to field = (Field) this.member; Object value; DependencyDescriptor desc = new DependencyDescriptor(field, this.required); desc.setContainingClass(bean.getClass()); Set<String> autowiredBeanNames = new LinkedHashSet<String>(1); TypeConvertertypeConverter = beanFactory.getTypeConverter(); Try {// The beanName will re-enter the populateBean method because of the Bean, To complete Role objects/injection/value = = com.viewscenes.net supervisor. The entity. The Role @ 7228 c85c value = the beanFactory. ResolveDependency (desc, beanName, autowiredBeanNames,typeConverter);
}
catch (BeansException ex) {
throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
}
if(value ! = null) {/ / set accessible, direct assignment ReflectionUtils. MakeAccessible (field); field.set(bean, value); }}Copy the code
AutowiredFieldElement
If the Autowired annotation is on the method, it would be written like this.
public class User {
@Autowired
public void setRole(Role role) {}
}
Copy the code
Its Inject method is similar to the above, but method.invoke is the last. Interested partners can go to the source code.
ReflectionUtils.makeAccessible(method);
method.invoke(bean, arguments);
Copy the code
2. Configuration file injection process
To start with a configuration file, we inject instances of ID, name, age, and Role into the User class.
<bean id="user" class="com.viewscenes.netsupervisor.entity.User">
<property name="id" value="1001"></property>
<property name="name" value="Net vehicle"></property>
<property name="age" value="24"></property>
<property name="role" ref="role"></property>
</bean>
<bean id="role" class="com.viewscenes.netsupervisor.entity.Role">
<property name="id" value="1002"></property>
<property name="name" value="Center Manager"></property>
</bean>
Copy the code
In section 4.2 of Spring’s initialization and XML chapter, parsing the bean tag, we see that after the bean’s Class object is reflected, its property property is set. That is, the parsePropertyElements() method is called. There’s a MutablePropertyValues property inside the BeanDefinition object.
List<PropertyValue> propertyValueList: propertyValueList: Name // Name in the configuration file == ID value // Value in the configuration file ==1001 PropertyValue: Name // Name ==name value in the corresponding configuration file // Value == Network motor vehicleCopy the code
This is the structure of the MutablePropertyValues property inside the BeanDefinition object. Now that you have the name and value of the property, injection is easy. Get the writeMethod object from the introspection object PropertyDescriptor, set it to be accessible, invoke. There are two objects in PropertyDescriptor readMethodRef and writeMethodRef which correspond to get set methods.
public void setValue(final Object object, Object valueToApply) throws Exception {//pd is Introspection Object PropertyDescriptor final Method writeMethod = this.pd.getWriteMethod()); writeMethod.setAccessible(true); final Object value = valueToApply; / / id, for example writeMethod = = public void com.viewscenes.net supervisor. The entity. The User. The setId (string) writeMethod.invoke(getWrappedInstance(), value); }Copy the code
Fifth, initializeBean
After Bean instantiation and IOC dependency injection, Spring has set aside extensions that let us do some initialization of the Bean.
1, the Aware
Aware is an empty interface that has nothing. But there are a lot of xxxAware inherited from it, look at the source code. If needed, our Bean can implement the following interface to get what we want.
// Call private void invokeAwareMethods(final String beanName, final Object bean) after instantiation and IOC dependency injection.if(bean instanceof Aware) {// Let our bean get its own beanName in the containerif(bean instanceof BeanNameAware) { ((BeanNameAware) bean).setBeanName(beanName); } // Get the ClassLoader objectif(bean instanceof BeanClassLoaderAware) { ((BeanClassLoaderAware) bean).setBeanClassLoader(getBeanClassLoader()); } // Get the BeanFactory objectif (bean instanceof BeanFactoryAware) {
((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
}
if (bean instanceof ResourceLoaderAware) {
((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
}
if (bean instanceof MessageSourceAware) {
((MessageSourceAware) bean).setMessageSource(this.applicationContext);
}
if(bean instanceof ApplicationContextAware) { ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext); }... Unfinished}}Copy the code
The practice is as follows:
public class AwareTest1 implements BeanNameAware,BeanClassLoaderAware,BeanFactoryAware{
public void setBeanName(String name) {
System.out.println("BeanNameAware:" + name);
}
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("BeanFactoryAware:" + beanFactory);
}
public void setBeanClassLoader(ClassLoader classLoader) {
System.out.println("BeanClassLoaderAware:"+ classLoader); }} / / output BeanNameAware: awareTest1 BeanClassLoaderAware: WebappClassLoader context: / springmvc_dubbo_producer delegate:falserepositories: /WEB-INF/classes/ ----------> Parent Classloader: java.net.URLClassLoader@2626b418 BeanFactoryAware:org.springframework.beans.factory.support.DefaultListableBean Factory@5b4686b4: defining beans ... unfinishedCopy the code
2. Initialize
There are three ways to initialize a Bean, in order: @postconstruct, afterPropertiesSet, and init-method
@PostConstruct
This annotation hidden deeper, it is in CommonAnnotationBeanPostProcessor InitDestroyAnnotationBeanPostProcessor calls to the parent. The initialization method of this annotation does not support parameters and will throw an exception directly.
if(method.getParameterTypes().length ! = 0) { throw new IllegalStateException("Lifecycle method annotation requires a no-arg method");
}
public void invoke(Object target) throws Throwable {
ReflectionUtils.makeAccessible(this.method);
this.method.invoke(target, (Object[]) null);
}
Copy the code
afterPropertiesSet
This implements the InitializingBean interface. This can’t have arguments either, because the interface method doesn’t have any arguments.
boolean isInitializingBean = (bean instanceof InitializingBean);
if(isInitializingBean && (mbd == null || ! mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
if (logger.isDebugEnabled()) {
logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
}
((InitializingBean) bean).afterPropertiesSet();
}
Copy the code
init-method
ReflectionUtils.makeAccessible(initMethod);
initMethod.invoke(bean);
Copy the code
Six, registered
RegisterDisposableBeanIfNecessary () completed the Bean cache registration work, put the Bean registered in the Map.