Handwritten Spring, define the tag type Aware interface, and realize the Aware container object

Posted by websesame on Wed, 26 Jan 2022 09:10:56 +0100


Author: Xiao Fu Ge
Blog: https://bugstack.cn

Precipitate, share and grow, so that you and others can gain something! 😄

1, Foreword

I can't understand the code written by my colleagues!

The big guy's code is like "Lai toad bubble frog, Zhang's ugly flower": a class implements multiple interfaces, the inherited class inherits other classes, the interface can also inherit with the interface, the abstract class that implements the interface, and then the abstract class method is implemented by the class, the class B inherited by class a implements the interface C implemented by class A, and so on.

The code that looks complex and difficult to understand can meet the needs of efficient iteration and smooth expansion again and again. You move bricks like a screw. You just complete a small function under an interface in the code written by the boss. Even after writing, you don't know how to be called and run. The whole process is as magical as looking at the Spring source code. You can't get a clue!

In fact, this is mainly because whether your code uses the design mode. Of course, the design mode is not so magical. Just like your two houses are 120 square meters, his family has three bedrooms, two living rooms, one kitchen and one bathroom, transparent north and south, and full sunlight. But your home is different. Your home has pots and pans, bathroom toilet, sofa coffee table and the 1.8 double bed. It is open in the 120 square meter house, without dynamic and static isolation, dry and wet separation, and pure freedom. So your code looks messy!

2, Target

The Spring framework that has been implemented at present can provide capabilities in Bean operation, including the definition and registration of Bean objects, as well as the execution in the process of operating Bean objects, BeanFactoryPostProcessor, BeanPostProcessor, InitializingBean, DisposableBean, and some new configuration processing in XML, so that we can make Bean objects more operable.

So, if we want to obtain the capabilities of BeanFactory, ApplicationContext and BeanClassLoader provided by the Spring framework, what should we do when we use the extension framework. Therefore, in this chapter, we hope to provide a container aware interface in the Spring framework. If anyone implements such an interface, he can obtain various capabilities in the interface input parameters.

3, Design

If I want to get some resources provided by the Spring framework, I need to first consider how to obtain them, and then you need to define the acquisition method and how to undertake them in the Spring framework. After realizing these two contents, you can expand some capabilities belonging to the Spring framework itself.

In the Bean object instantiation phase, we have operated on some additional definitions, properties, initialization and destruction. In fact, we can also do this by obtaining some Spring objects such as BeanFactory and ApplicationContext. Then we need to define a marked interface. This interface does not need to have methods. It only plays the role of marking. For specific functions, other functional interfaces that inherit this interface define specific methods. Finally, this interface can be judged and called through instanceof. The overall design structure is shown in the figure below:

  • Define the interface Aware. In the Spring framework, it is a token Aware interface. The specific subclass definition and implementation can sense the related objects in the container. That is, through this bridge, container services are provided to specific implementation classes
  • The interfaces that inherit Aware include BeanFactoryAware, BeanClassLoaderAware, BeanNameAware and ApplicationContextAware. Of course, there are other annotations in the Spring source code, but we still can't use them at present.
  • In the specific interface implementation process, you can see that some (beanFactory aware, BeanClassLoaderAware, BeanNameAware) are in the support folder of the factory, and ApplicationContextAware is in the support folder of the context, because different content acquisition needs to be provided under different packages. Therefore, in the specific implementation of AbstractApplicationContext, the ApplicationContextAwareProcessor operation of adding BeanPostProcessor content to beanFactory will be used. Finally, the corresponding calling operation will be processed when AbstractAutowireCapableBeanFactory creates a createBean. The applybeanpostprocessors beforeinitialization has been implemented in the previous chapter. If you forget, you can turn forward

4, Realize

1. Engineering structure

small-spring-step-08
└── src
    ├── main
    │   └── java
    │       └── cn.bugstack.springframework
    │           ├── beans
    │           │   ├── factory
    │           │   │   ├── factory
    │           │   │   │   ├── AutowireCapableBeanFactory.java
    │           │   │   │   ├── BeanDefinition.java
    │           │   │   │   ├── BeanFactoryPostProcessor.java
    │           │   │   │   ├── BeanPostProcessor.java
    │           │   │   │   ├── BeanReference.java
    │           │   │   │   ├── ConfigurableBeanFactory.java
    │           │   │   │   └── SingletonBeanRegistry.java
    │           │   │   ├── support
    │           │   │   │   ├── AbstractAutowireCapableBeanFactory.java
    │           │   │   │   ├── AbstractBeanDefinitionReader.java
    │           │   │   │   ├── AbstractBeanFactory.java
    │           │   │   │   ├── BeanDefinitionReader.java
    │           │   │   │   ├── BeanDefinitionRegistry.java
    │           │   │   │   ├── CglibSubclassingInstantiationStrategy.java
    │           │   │   │   ├── DefaultListableBeanFactory.java
    │           │   │   │   ├── DefaultSingletonBeanRegistry.java
    │           │   │   │   ├── DisposableBeanAdapter.java
    │           │   │   │   ├── InstantiationStrategy.java
    │           │   │   │   └── SimpleInstantiationStrategy.java  
    │           │   │   ├── support
    │           │   │   │   └── XmlBeanDefinitionReader.java
    │           │   │   ├── Aware.java
    │           │   │   ├── BeanClassLoaderAware.java
    │           │   │   ├── BeanFactory.java
    │           │   │   ├── BeanFactoryAware.java
    │           │   │   ├── BeanNameAware.java
    │           │   │   ├── ConfigurableListableBeanFactory.java
    │           │   │   ├── DisposableBean.java
    │           │   │   ├── HierarchicalBeanFactory.java
    │           │   │   ├── InitializingBean.java
    │           │   │   └── ListableBeanFactory.java
    │           │   ├── BeansException.java
    │           │   ├── PropertyValue.java
    │           │   └── PropertyValues.java 
    │           ├── context
    │           │   ├── support
    │           │   │   ├── AbstractApplicationContext.java 
    │           │   │   ├── AbstractRefreshableApplicationContext.java 
    │           │   │   ├── AbstractXmlApplicationContext.java 
    │           │   │   ├── ApplicationContextAwareProcessor.java 
    │           │   │   └── ClassPathXmlApplicationContext.java 
    │           │   ├── ApplicationContext.java 
    │           │   ├── ApplicationContextAware.java 
    │           │   └── ConfigurableApplicationContext.java
    │           ├── core.io
    │           │   ├── ClassPathResource.java 
    │           │   ├── DefaultResourceLoader.java 
    │           │   ├── FileSystemResource.java 
    │           │   ├── Resource.java 
    │           │   ├── ResourceLoader.java 
    │           │   └── UrlResource.java
    │           └── utils
    │               └── ClassUtils.java
    └── test
        └── java
            └── cn.bugstack.springframework.test
                ├── bean
                │   ├── UserDao.java
                │   └── UserService.java
                └── ApiTest.java

Project source: official account "bugstack wormhole stack", reply: Spring column, get the complete source code.

The design and implementation class relationship of Spring aware interface is shown in Figure 9-2

  • The above whole class relationship is about the definition of Aware perception and the implementation of container perception.
  • Aware has four inherited interfaces. The inheritance of other interfaces is to inherit a tag. With the existence of tags, it is more convenient for class operation and specific judgment implementation.
  • In addition, because the ApplicationContext is not under the createBean method in AbstractAutowireCapableBeanFactory, you need to register addBeanPostProcessor in the container, and then the createBean uniformly calls applybeanpoprocessorsbeforeinitialization for operation.

2. Define tag interface

cn.bugstack.springframework.beans.factory.Aware

/**
 * Marker superinterface indicating that a bean is eligible to be
 * notified by the Spring container of a particular framework object
 * through a callback-style method.  Actual method signature is
 * determined by individual subinterfaces, but should typically
 * consist of just one void-returning method that accepts a single
 * argument.
 *
 * Tag class interface, and the implementation of this interface can be perceived by the Spring container
 *
 */
public interface Aware {
}
  • In Spring, there are many design methods of tag interfaces like this. Their existence is like a label, which can facilitate the unified extraction of the implementation classes belonging to this kind of interface. instanceof is usually used together.

3. Container perception

3.1 BeanFactoryAware

cn.bugstack.springframework.beans.factory.BeanFactoryAware

public interface BeanFactoryAware extends Aware {

   void setBeanFactory(BeanFactory beanFactory) throws BeansException;

}
  • Interface to be implemented by beans that wish to be aware of their owning {@link BeanFactory}.
  • Implement this interface to sense the BeanFactory to which it belongs

3.2 BeanClassLoaderAware

cn.bugstack.springframework.beans.factory.BeanClassLoaderAware

public interface BeanClassLoaderAware extends Aware{

    void setBeanClassLoader(ClassLoader classLoader);

}
  • 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.
  • Implementing this interface can sense the ClassLoader to which it belongs

3.3 BeanNameAware

cn.bugstack.springframework.beans.factory.BeanNameAware

public interface BeanNameAware extends Aware {

    void setBeanName(String name);

}
  • Interface to be implemented by beans that want to be aware of their bean name in a bean factory.
  • Implement this interface to sense the BeanName to which it belongs

3.4 ApplicationContextAware

cn.bugstack.springframework.context.ApplicationContextAware

public interface ApplicationContextAware extends Aware {

    void setApplicationContext(ApplicationContext applicationContext) throws BeansException;

}
  • Interface to be implemented by any object that wishes to be notifiedof the {@link ApplicationContext} that it runs in.
  • Implementing this interface can sense the ApplicationContext to which it belongs

4. Packaging processor (ApplicationContextAwareProcessor)

cn.bugstack.springframework.context.support.ApplicationContextAwareProcessor

public class ApplicationContextAwareProcessor implements BeanPostProcessor {

    private final ApplicationContext applicationContext;

    public ApplicationContextAwareProcessor(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof ApplicationContextAware){
            ((ApplicationContextAware) bean).setApplicationContext(applicationContext);
        }
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

}
  • Because the ApplicationContext cannot be obtained directly when creating a Bean, you need to write the ApplicationContext into a packaged BeanPostProcessor during the refresh operation, and then use abstractautowirecapablebeanfactory Applybeanpostprocessorsbeforeinitialization method call.

5. Register BeanPostProcessor

cn.bugstack.springframework.context.support.AbstractApplicationContext

public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {

    @Override
    public void refresh() throws BeansException {
        // 1. Create BeanFactory and load BeanDefinition
        refreshBeanFactory();

        // 2. Get BeanFactory
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();

        // 3. Add ApplicationContextAwareProcessor to make Bean objects inherited from ApplicationContextAware aware aware of their ApplicationContext
        beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));

        // 4. Before Bean instantiation, execute beanfactoryprocessor (invoke factory processors registered as beans in the context.)
        invokeBeanFactoryPostProcessors(beanFactory);

        // 5. BeanPostProcessor needs to perform the registration operation before other Bean objects are instantiated
        registerBeanPostProcessors(beanFactory);

        // 6. Instantiate the singleton Bean object in advance
        beanFactory.preInstantiateSingletons();
    }
    
 	// ...   
}    
  • The refresh() method is the operation process of the whole Spring container. Compared with the previous chapter, the operation of addBeanPostProcessor is added this time.
  • Add ApplicationContextAwareProcessor so that Bean objects inherited from ApplicationContextAware can perceive the ApplicationContext to which they belong.

6. Aware call operation

cn.bugstack.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory {

    private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();

    @Override
    protected Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws BeansException {
        Object bean = null;
        try {
            bean = createBeanInstance(beanDefinition, beanName, args);
            // Populate the Bean with attributes
            applyPropertyValues(beanName, bean, beanDefinition);
            // Execute the initialization method of Bean and the pre and post processing methods of BeanPostProcessor
            bean = initializeBean(beanName, bean, beanDefinition);
        } catch (Exception e) {
            throw new BeansException("Instantiation of bean failed", e);
        }

        // Register the Bean object that implements the DisposableBean interface
        registerDisposableBeanIfNecessary(beanName, bean, beanDefinition);

        addSingleton(beanName, bean);
        return bean;
    }

    private Object initializeBean(String beanName, Object bean, BeanDefinition beanDefinition) {

        // invokeAwareMethods
        if (bean instanceof Aware) {
            if (bean instanceof BeanFactoryAware) {
                ((BeanFactoryAware) bean).setBeanFactory(this);
            }
            if (bean instanceof BeanClassLoaderAware){
                ((BeanClassLoaderAware) bean).setBeanClassLoader(getBeanClassLoader());
            }
            if (bean instanceof BeanNameAware) {
                ((BeanNameAware) bean).setBeanName(beanName);
            }
        }

        // 1. Execute BeanPostProcessor Before processing
        Object wrappedBean = applyBeanPostProcessorsBeforeInitialization(bean, beanName);

        // Executes the initialization method of the Bean object
        try {
            invokeInitMethods(beanName, wrappedBean, beanDefinition);
        } catch (Exception e) {
            throw new BeansException("Invocation of init method of bean[" + beanName + "] failed", e);
        }

        // 2. Execute BeanPostProcessor After processing
        wrappedBean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
        return wrappedBean;
    }



    @Override
    public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException {
        Object result = existingBean;
        for (BeanPostProcessor processor : getBeanPostProcessors()) {
            Object current = processor.postProcessBeforeInitialization(result, beanName);
            if (null == current) return result;
            result = current;
        }
        return result;
    }

    @Override
    public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException {
        Object result = existingBean;
        for (BeanPostProcessor processor : getBeanPostProcessors()) {
            Object current = processor.postProcessAfterInitialization(result, beanName);
            if (null == current) return result;
            result = current;
        }
        return result;
    }

}
  • Here, we remove some classes and only keep the operation of Aware aware interface this time.
  • First, in initializeBean, by judging the bean instanceof Aware, three interface methods are called, beanfactoryaware setBeanFactory(this),BeanClassLoaderAware.setBeanClassLoader(getBeanClassLoader()),BeanNameAware.setBeanName(beanName), so that you can notify the class that has implemented this interface.
  • In addition, we also added ApplicationContextAwareProcessor to BeanPostProcessor. At this time, it will also be called to the specific class implementation in this method to get an ApplicationContex attribute.

5, Testing

1. Preparation in advance

cn.bugstack.springframework.test.bean.UserDao

public class UserDao {

    private static Map<String, String> hashMap = new HashMap<>();

    public void initDataMethod(){
        System.out.println("Execution: init-method");
        hashMap.put("10001", "Little brother Fu");
        hashMap.put("10002", "Eight cups of water");
        hashMap.put("10003", "A Mao");
    }

    public void destroyDataMethod(){
        System.out.println("Execution: destroy-method");
        hashMap.clear();
    }

    public String queryUserName(String uId) {
        return hashMap.get(uId);
    }

}

cn.bugstack.springframework.test.bean.UserService

public class UserService implements BeanNameAware, BeanClassLoaderAware, ApplicationContextAware, BeanFactoryAware {

    private ApplicationContext applicationContext;
    private BeanFactory beanFactory;

    private String uId;
    private String company;
    private String location;
    private UserDao userDao;

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Override
    public void setBeanName(String name) {
        System.out.println("Bean Name is: " + name);
    }

    @Override
    public void setBeanClassLoader(ClassLoader classLoader) {
        System.out.println("ClassLoader: " + classLoader);
    }

    // ...get/set
}
  • UserDao has not changed this time. It still provides initialization methods, which are implemented in spring The init method and destroy method configuration information are provided in XML.
  • UserService newly added beannameaware, beanclassloaderaware, applicationcontextaware, beanfactory aware, and implemented corresponding interface methods in the class.

2. Configuration file

Basic configuration, no BeanFactoryPostProcessor, BeanPostProcessor, implementation class

<?xml version="1.0" encoding="UTF-8"?>
<beans>

    <bean id="userDao" class="cn.bugstack.springframework.test.bean.UserDao" init-method="initDataMethod" destroy-method="destroyDataMethod"/>

    <bean id="userService" class="cn.bugstack.springframework.test.bean.UserService">
        <property name="uId" value="10001"/>
        <property name="company" value="tencent"/>
        <property name="location" value="Shenzhen"/>
        <property name="userDao" ref="userDao"/>
    </bean>

</beans>
  • There is no additional configuration information in this chapter, which is the same as that in the previous chapter.

3. Unit test

@Test
public void test_xml() {
    // 1. Initialize BeanFactory
    ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring.xml");
    applicationContext.registerShutdownHook();      

    // 2. Get Bean object and call method
    UserService userService = applicationContext.getBean("userService", UserService.class);
    String result = userService.queryUserInfo();
    System.out.println("Test results:" + result);
    System.out.println("ApplicationContextAware: "+userService.getApplicationContext());
    System.out.println("BeanFactoryAware: "+userService.getBeanFactory());
}
  • The test method mainly adds a write about the call of the new Aware implementation. Other unnecessary calls also print the corresponding log information, which can be seen in the test results.

test result

Execution: init-method
ClassLoader: sun.misc.Launcher$AppClassLoader@14dad5dc
Bean Name is: userService
 Test result: little brother Fu,tencent,Shenzhen
ApplicationContextAware: cn.bugstack.springframework.context.support.ClassPathXmlApplicationContext@5ba23b66
BeanFactoryAware: cn.bugstack.springframework.beans.factory.support.DefaultListableBeanFactory@2ff4f00f
 Execution: destroy-method



Process finished with exit code 0
  • It can be seen from the test results that the specific implementation corresponding to the newly added sensing interface (beannameaware, beanclassloaderaware, applicationcontextaware, beanfactory aware) in this chapter can output the results as scheduled.

6, Summary

  • At present, in the implementation of Spring framework, some function points have become more and more complete, especially the life cycle of Bean objects. The overall summary is shown in Figure 9-3

  • This chapter is about the implementation of the four inherited interfaces of Aware's perception interface, beannameaware, beanclassloaderaware, applicationcontextaware and beanfactory Aware, which also extends the functions of Spring. If you have developed Spring middleware, you will use a lot of these classes. Now you not only have used them, but also know when they reached them. You can also have a clear idea to check the instantiation order of classes in the future.

  • The implementation of each chapter is to fill in the functions of various modules on the structure with the design pattern as the core. Writing code simply will not gain much. We must understand why it is designed like this, what the benefits of such design are, and how to apply so many interfaces and abstract classes. These are the core of Spring framework learning.

7, Series recommendation

Topics: Spring