Why was IOC introduced?

class Programer {
    Computer computer = new Mac2015();
    private void work(a) { computer.help(); }}Copy the code

At this time, there is a problem that computer and programer are coupled together. This programer is not extensible (it only uses MAC2015). If the company changes a batch of computers Mac2016, it needs to create a new programmer class, which is obviously unreasonable. From a design point of view, a class itself is either a template for definition or an abstraction that is not pure abstraction if it is bound or coupled to a concrete implementation. This also violates the Don’t Call me law of design patterns. So at this time, the computer and programer need to be decoupled. The method of decoupling is very simple. The implementation of a computer is specified by the caller, not by the class itself. The class then needs to expose some interface for the outside world to implement the injection function. There are three common approaches

  • Constructor injection
  • The set injection
  • Interface injection

This is the basic idea of IOC, and for web development there are generally no callers (although there are at the container level), so it’s up to Spring to ensure dynamic injection of properties in a class. Next, what injection method is chosen and what properties need to be injected is notified to Spring? There are two common approaches

  • annotations
  • Configuration file (XML/Properties)

By the way, beans can be instantiated using either a normal constructor construct or a factory method construct

<bean id="clientService"
    class="examples.ClientService"
    factory-method="createInstance"/>
Copy the code

Class refers to the class that contains the factory method, not the class that is returned by the factory method

<! -- the factory bean, which contains a method called createInstance() -->
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
    <! -- inject any dependencies required by this locator bean -->
</bean>

<! -- the bean to be created via the factory bean -->
<bean id="clientService"
    factory-bean="serviceLocator"
    factory-method="createClientServiceInstance"/>
Copy the code

IOC container classification

  • BeanFactory

    The default lazy – load, so start quickly Commonly used an implementation class is DefaultListableBeanFactory, this class also implements the BeanDefinitionRegistry interface, This interface plays the role of Bean registration manager registerBeanDefinition#BeanDefinitionRegistry Beans are registered in BeanDefinitionRegistry as beanDefinition The BeanFactory interface defines only some query methods about beans, and the real work requires other interface definitions that its subclasses implement ~

  • ApplicationContext

    Built on top of BeanFactory, non-lazy, takes a long time to start up, and provides some additional functionality (event publishing, internationalization information support, etc.) in addition to the basic functionality of BeanFactory.

Manual and automatic

  • Configuration file-based injection is what we call manual

    <bean> <name=”propertyName” ref=”otherBean”>

  • Annotation based injection is what we call fully automated

    @Conponet/@Autowire/@Resource + <componet-scan>

  • There are also annotations + semi-automatic configuration

    <bean> + @autowire/@resource

About beans in XML

The beans tag has several unique properties that apply to all contained beans

  • default-lazy-init

    Whether all beans are lazily loaded

  • default-autowire

    All internal injection way no bean (default)/byName/byType/constrctor autodetect here byName/byType material @ the resource / @ autowire

  • default-dependency-check

    Whether to do dependency checking None (default)

  • default-init-method

    All bean initializers such as init

  • default-destroy-method

    All bean destruction methods such as destroy

Basically all properties of beans can be mapped to beans, Prototype Singleton beans live as long as the Prototype bean. The Prototype bean’s lifecycle is not maintained by the container. The container no longer has a reference to the current object and is left to its own devices

Remember a problem we solved earlier: hold a reference to an object (new/reflect), but the object is not injected. How can we use Spring to inject it?

     applicationContext.getAutowireCapableBeanFactory().
     autowireBeanProperties(this, AutowireCapableBeanFactory.AUTOWIRE_NO, true);

Copy the code

The life cycle of the Bean

Put the conclusion first, followed by a specific code demonstration (graphic and text combination is better)

public class Car implements BeanFactoryAware.BeanNameAware.InitializingBean.DisposableBean {
    private String carName;
    private BeanFactory beanFactory;
    private String beanName;

    public Car(a) {
        System.out.println("Bean constructor");
    }

    public void setCarName(String carName) {
        System.out.println("Property injection");
        this.carName = carName;
    }

    // This is the BeanFactoryAware interface method
    public void setBeanFactory(BeanFactory arg0) throws BeansException {
        System.out
                .println("BeanFactoryAware.setBeanFactory()");
        this.beanFactory = arg0;
    }

    // This is the BeanNameAware interface method
    public void setBeanName(String arg0) {
        System.out.println("BeanNameAware.setBeanName()");
        this.beanName = arg0;
    }

    // This is the InitializingBean interface method
    public void afterPropertiesSet(a) throws Exception {
        System.out
                .println("InitializingBean.afterPropertiesSet()");
    }

    // This is the DiposibleBean interface method
    public void destroy(a) throws Exception {
        System.out.println("DiposibleBean.destory()");
    }

    // The initialization method specified by the init-method property of 
      
    public void myInit(a) {
        System.out.println("Initialization method specified by the init-method property of 
      
       "
      );
    }

    // Initialization method specified by the destroy-method attribute of 
      
    public void myDestory(a) {
        System.out.println("Initialization method specified by the destroy-method property of 
      
       "
      ); }}Copy the code
public class MyBeanPostProcessor implements BeanPostProcessor {

    public MyBeanPostProcessor(a) {
        super(a); System.out.println("BeanPostProcessor constructor");
    }

    public Object postProcessAfterInitialization(Object arg0, String arg1)
            throws BeansException {
        if (arg1.equals("car")) {
            System.out
                    .println("BeanPostProcessor.postProcessAfterInitialization()");
        }
        return arg0;
    }

    public Object postProcessBeforeInitialization(Object arg0, String arg1)
            throws BeansException {
        if (arg1.equals("car")) {
            System.out
                    .println("BeanPostProcessor.postProcessBeforeInitialization()");
        }
        returnarg0; }}Copy the code
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

    public MyBeanFactoryPostProcessor(a) {
        super(a); System.out.println("BeanFactoryPostProcessor constructor");
    }

    public void postProcessBeanFactory(ConfigurableListableBeanFactory arg0)
            throws BeansException {
        System.out.println("BeanFactoryPostProcessor.postProcessBeanFactory()");
        System.out.println("I can change beanDefination now but I'm not going to change it here."); }}Copy the code
public class MyInstantiationAwareBeanPostProcessor extends
        InstantiationAwareBeanPostProcessorAdapter {

    public MyInstantiationAwareBeanPostProcessor(a) {
        super(a); System.out .println("InstantiationAwareBeanPostProcessorAdapter constructor");
    }

    // interface method before instantiating the Bean
    @Override
    public Object postProcessBeforeInstantiation(Class beanClass, String beanName) throws BeansException {
        if(beanName.equals("car")) {
            System.out
                    .println("InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation()");
        }
        return null;
    }

    // interface method, after instantiating the Bean
    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        if(beanName.equals("car")) {
            System.out
                    .println("InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation()");
        }
        return true;
    }
    
    // called when the interface method is set to a property
    @Override
    public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName)
            throws BeansException {
        if(beanName.equals("car")) {
            System.out
                    .println("InstantiationAwareBeanPostProcessor.postProcessPropertyValues()");
        }
        returnpvs; }}Copy the code
<bean id="beanPostProcessor" class="test.MyBeanPostProcessor"/>
<bean id="instantiationAwareBeanPostProcessor" class="test.MyInstantiationAwareBeanPostProcessor"/>
<bean id="beanFactoryPostProcessor" class="test.MyBeanFactoryPostProcessor"/>
<bean id="car" class="test.Car" init-method="myInit"
destroy-method="myDestory" scope="singleton" p:carName="BMW"/>
Copy the code

Here’s a look at the process

public static void main(String[] args) {
        System.out.println("Start the container");
        ApplicationContext factory = new ClassPathXmlApplicationContext("spring/applicationContext.xml");
        System.out.println("Container initialization successful");
        Car car = factory.getBean("car",Car.class);
        System.out.println("Start closing the container");
        ((ClassPathXmlApplicationContext)factory).registerShutdownHook();
    }
Copy the code

The final console output is:

Begin to start the container spring BeanFactoryPostProcessor constructor spring BeanFactoryPostProcessor. PostProcessBeanFactory () I can modify beanDefination now but I will not modify here InstantiationAwareBeanPostProcessorAdapter constructor BeanPostProcessor constructor The bean's constructor InstantiationAwareBeanPostProcessor. PostProcessBeforeInstantiation () InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation() InstantiationAwareBeanPostProcessor. PostProcessPropertyValues () attributes inject BeanNameAware. SetBeanName () BeanFactoryAware.setBeanFactory() BeanPostProcessor.postProcessBeforeInitialization() InitializingBean. AfterPropertiesSet () < bean > init - method attribute specifies the initialization method of BeanPostProcessor. PostProcessAfterInitialization () Successful container initialization begins to close the initialization method specified by the destroy-method property of container diposiblebean.destory () <bean>Copy the code

1) During the container startup stage, containers need to rely on beandefinitionReaders to parse and analyze the loaded Configuration MetaData, and group the analyzed information into corresponding Beandefinitions. Finally, these BeanDefinitions, which hold the required bean definition information, are registered with the appropriate BeanDefinitionRegistry, and container startup is complete. BeanDefinitionReader 1) for dealing with configuration files 2) for reading content from the corresponding configuration file and mapping beans to BeanDefinition 3) Then register beanDefinitions with BeanDefinitionRegistry Eg: PropertiesBeanDefinitionReader, XmlBeanDefinitionReader

XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanDefinitionRegistry);
reader.loadBeanDefinitions("classpath:xxx");
return (BeanFactory) beanDefinitionRegistry;
Copy the code

—–BeanFactoryPostProcessor Changes the BeanDefinition information registered with the container before the container instantiates the object. Here are a few typical BeanFactoryPostProcessors

  • PropertyPlaceholderConfigurer

Before instantiating the bean, replace the placeholder in the bean configuration with the actual value

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="locations" value="classpath:com/foo/jdbc.properties"/>
</bean>

<bean id="dataSource" destroy-method="close"
        class="org.apache.commons.dbcp.BasicDataSource">
    <property name="driverClassName" value="${jdbc.driverClassName}"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
</bean>
Copy the code
# jdbc.properties
jdbc.driverClassName=org.hsqldb.jdbcDriver
jdbc.url=jdbc:hsqldb:hsql://production:9002
jdbc.username=sa
jdbc.password=root
Copy the code

When the placeholder is empty the placeholder is replaced with matched values in the properties file. The PlaceholderConfiguer class implements BeanFactoryPostProcessor

  • CustomEditorConfigurer

That is, the container reads strings from xmL-formatted files, but the end application is made up of objects of various types. CustomEditorConfigurer helps us communicate information about the conversion rules that are needed to complete this conversion from a string to a concrete object (regardless of who ultimately does the conversion). We need to convert 2018/07/27 of String to date

<bean id="dateFoo" class="... DateFoo">
    <property name="date">
        <value>2018/07/27</value>
    </property>
</bean>
Copy the code

You can customize CustomEditorConfigurer to specify the string to be converted to logic for a specific type

  • A practical example of BeanFactoryPostProcessor

The previous project used JUNIT for unit testing, but each unit test had to load all the beans, and if the project was large, it would take a long time for the unit tests to start. In order to solve this problem to develop a simple spring BeanFactoryPostProcessor, principle is simple is to set all the beans to lazy load pattern, use which instantiates which unit tests

public class LazyBeanFactoryProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        DefaultListableBeanFactory fac = (DefaultListableBeanFactory) beanFactory;
        Map<String, AbstractBeanDefinition> map = (Map<String, AbstractBeanDefinition>) ReflectionTestUtils.getField(fac, "beanDefinitionMap");
        for (Map.Entry<String, AbstractBeanDefinition> entry : map.entrySet()) {
            entry.getValue().setLazyInit(true); }}}Copy the code

2) Bean instantiation phase 1. InstantiationAwareBeanPostProcessor. PostProcessBeforeInstantiation (Class beanClass, String beanName) Before the constructor of the implementation Bean, if have InstantiationAwareBeanPostProcessor will execute it postProcessBeforeInstantiation () if the Bean returned at this time is not null, So won’t continue to carry out the follow-up process of Bean, only perform postProcessAfterInitialization (), Caused the “short circuit” is a little need to be aware of when mentioned InstantiationAwareBeanPostProcessor method specified into the best, because it has several same name in different methods, easy to be confused

The container uses the policy mode to determine how to initialize the bean instance (reflecting or CGLIB default). If the configured bean has look-up or replace, you need to create a proxy class using CGLIB

3.InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation(Class beanClass,String beanName)

4.InstantiationAwareBeanPostProcessor.postProcessPropertiesValues(PropertyValues pvs,PropertyDescriptor[] pds, Object bean, String beanName)

5. The bean is instantiated with a BeanWrapper instance, which is a Wrapper for the bean. The Wrapper can be used to inject the properties of the object in a uniform way. BeanWrapper uses PropertyEditor for the type conversion of the property

BeanNameAware: Sets BeanName to the current instance. BeanClassLoaderAware: sets the ClassLoader that loads the current bean to the current instance BeanFactoryAware: Sets the BeanFactory to the current instance

7. BeanPostProcessor. Many postProcessBeforeInitialization ApplicationContext uniquely Aware interface is also injected through BeanPostPorcessor attribute

public Object postProcessBeforeInitialization(Object bean, String beanName) throws  BeansException {
 if (bean instanceof ResourceLoaderAware) {
((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
}
if (bean instanceof ApplicationEventPublisherAware) {
((ApplicationEventPublisherAware) bean).setApplicationEventPublisher 
(this.applicationContext); }
if (bean instanceof MessageSourceAware) {
((MessageSourceAware) bean).setMessageSource(this.applicationContext);
}
if (bean instanceof ApplicationContextAware) {
   ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext); }
    return bean;
}
Copy the code

8.InititalizingBean.afterPropertiesSet()

9.init-method

10.BeanPostProcessor.postProcessAfterInitialization(Object bean, String beanName)

11. The container is successfully initialized and the service logic is successfully executed

12.DisposableBean.destroy()

13.destroy-method

Circular dependencies

Spring handles cyclic dependencies between singleton beans (Set injection); in other words, it can’t handle cyclic dependencies for singleton constructor injection and prototype-type beans. Assume that A depends on B, and B depends on A. During the instantiation of A, semi-finished A will be put into the cache, and then the dependent B will be instantiated. In this case, the semi-finished A will be directly filled into B to complete the instantiation of the circular dependent Bean.

About the BeanPostProcessor

  • If (beanName== XXX) if(beanName== XXX) if(beanName== XXX)
  • A bean post-processor typically checks for callback interfaces or may wrap a bean with a proxy. Some Spring AOP infrastructure classes are implemented as bean post-processors in order to provide proxy-wrapping logic. AOP is implemented through BeanPostProcessor
  • Classes that implement the BeanPostProcessor interface are special and are treated differently by the container. All BeanPostProcessors and beans that they reference directly are instantiated on startup, As part of the special Startup Phase of the ApplicationContext, the bean that implements this interface and the beans it depends on need to be started first, and can be considered as a phase of container startup. And beans that implement this interface (or depend on them) cannot dynamically proxy them because dynamic proxies are themselves implemented by BeanPostProcessor.