Spring source line and talk about recent insights

I. Introduction

    Recently line and the Spring IOC, AOP, Transactional source code, I wrote this article to talk about what gadgets, this may be the biggest gain after reading the source code. In the hope that we can learn something inside of it;

Two, Spring IOC simple implementation

    The first step is to look at the configuration files, configuration files, simulation Spring Bean look injection time, less XML validation head of something;

<?xml version="1.0" encoding="UTF-8"?>
<beans>
    <bean id="student" class="org.handwriting.spring.entity.Student">
        <property name="name"  value="wtz" />
        <property name="age"  value="20" />
    </bean>
</beans>
View Code

    The second step is the entity class;

public class Student {

    private String name;

    private String age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }
}
View Code

   The third step is the most important step to achieve parsed from the configuration file, and then create a Student object by reflection, the Student resolution configuration attributes, assigned to the Student object is initialized, and finally loaded into the Map container?;

public class SpringIoc {

    private HashMap<String, Object> beanMap = new HashMap<String, Object>();

    public SpringIoc(String location) throws SAXException, IllegalAccessException, IOException, InstantiationException, ParserConfigurationException, NoSuchFieldException {
        loadBeans(location);
    }

    /**
     * Get bean
     *
     * @Param name the name of bean
     * @return bean
     */
    public Object getBean(String name) {
        Object bean = beanMap.get(name);
        if (bean == null) {
            throw new IllegalArgumentException(" bean is null"+name);
        }
        return bean;
    }

    /**
     * Load configuration and initialization bean properties
     *
     * @param location
     * @throws IOException
     * @throws ParserConfigurationException
     * @throws SAXException
     * @throws IllegalAccessException
     * @throws InstantiationException
     * @throws NoSuchFieldException
     */
    private void loadBeans(String location) throws IOException, ParserConfigurationException, SAXException, IllegalAccessException, InstantiationException, NoSuchFieldException {
        //加载 xml 配置文件
        InputStream inputStream = new FileInputStream(location);
        DocumentBuilderFactory documentBuilderFactory =
                DocumentBuilderFactory.newInstance();
        DocumentBuilder documentBuilder =
                documentBuilderFactory.newDocumentBuilder();
        Document document = documentBuilder.parse(inputStream);
        Element root = document.getDocumentElement();
        NodeList nodeList = root.getChildNodes();
        for (int i = 0; i < nodeList.getLength(); i++) {
            Node node = nodeList.item(i);
            if (node instanceof Element) {
                Element element = (Element) node;

                String id = element.getAttribute("id");
                ClassName String = element.getAttribute ( "class" );
                 // get the current through the reflection type 
                Class beanClass = null ;
                 the try {
                    beanClass = Class.forName(className);
                } catch (ClassNotFoundException ex) {
                    ex.printStackTrace();
                }
                Bean Object = beanClass.newInstance ();
                 // assign attribute values to the bean object 
                a NodeList propertyList = element.getElementsByTagName ( "Property" );
                 for ( int J = 0; J <propertyList.getLength (); J ++ ) {
                    Node propertyNode = propertyList.item(j);
                    Element propertyElement = (Element) propertyNode;
                    String name = propertyElement.getAttribute("name");
                    String value = propertyElement.getAttribute("value");

                    Field declaredField = bean.getClass().getDeclaredField(name);
                    declaredField.setAccessible(true);

                    if (value != null && value.length() > 0) {
                        declaredField.set(bean, value);
                    }
                }
                beanMap.put(id, bean);
            }

        }


    }

}
View Code

   Fourth test call;

public class SpringIocTest {

    public  static  void main (String [] args) throws IllegalAccessException, a ParserConfigurationException, IOException, an InstantiationException is, a SAXException, a NoSuchFieldException {
         // by ClassLoader loading information file 
        String LOCATION = 
                SpringIoc. class .getClassLoader (). the getResource ( "spring.xml" ). the getFile ();
         // complete bean object initialization 
        SpringIoc IOC = new new SpringIoc (LOCATION);
         // Get the object student 
        Student student = (Student) ioc.getBean ( "student" );
        System.out.println(student);
    }
}
View Code

  This simple steps, in fact, reflect the creation process Bean, first we look at how to get an object through BeanFactory, ApplicationContext here do not look at this guy is not a pure person;

  

  Labeled as shown above, the first step is to initialize the main profile information, generate Resource, the process is the process I wrote to read the file;

  The second step is to initialize the main BeanFactory, DefaultListableBeanFactory class BeaFactory default implementation, this is commonly used means of Spring, who always complicated to achieve a multi-functional abstraction abstract class and a default implementation, the benefits of doing so is that we only need to override the abstract method in class will be able to implement some of the new extensions;

  The third part of the complete conversion of the Resource Document, Document resolved to BeanDefinition, eventually added to the Map process vessel, the second, the equivalent of three half ago I realized Spring IOC method of loadBeans, this time the object is not really available ;

  The fourth part is the most important part of the partially completed BeanDefinition to create Entity Bean, made a specific implementation for this part of my entire life cycle of the Bean to use interface;

  To summarize, Spring IOC configuration is added to the container, the container then removed from the configuration information, creates a corresponding instance using reflection mode or CGLIB then filled with the attribute information, the final completion of the initialization;

Three, Spring Bean Lifecycle

  First, look at what the interface and implementation inheritance;  

public class LifeCycleBean implements BeanNameAware, BeanClassLoaderAware,
        BeanFactoryAware, InitializingBean, DisposableBean, BeanPostProcessor {

    private String test;

    public String getTest() {
        return test;
    }

    public void setTest(String test) {
        System.out.println ( "Properties injection" );
         the this .test = Test;
    }

    public void method(){
        System.out.println ( "method is called" );
    }

    public void setBeanClassLoader(ClassLoader classLoader) {
        System.out.println ( "BeanClassLoaderAware is called" );
    }

    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        System.out.println ( "the BeanFactoryAware is called" );
    }

    public void setBeanName(String name) {
        System.out.println ( "BeanNameAware is called" );
    }

    public void destroy() throws Exception {
        System.out.println ( "DisposableBean is called" );
    }

    public void afterPropertiesSet() throws Exception {
        System.out.println ( "InitializingBean is called" );
    }

    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println ( "the BeanPostProcessor began to be called" );
         return bean;
    }

    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println ( "the BeanPostProcessor end is called" );
         return bean;
    }

    public void initMethod(){
        System.out.println ( "the init-Method, is called" );
    }

    public void destroyMethod(){
        System.out.println ( "Method, the destroy-is called" );
    }
}
View Code

  Then look at operating results and how to call;

public class SpringIocTest {

    public  static  void main (String [] args) {
         // load the configuration file 
        a ClassPathResource ClassPathResource = new new a ClassPathResource ( "spring.xml" );
         // initialize beanFactory 
        DefaultListableBeanFactory DefaultListableBeanFactory = new new DefaultListableBeanFactory ();
         // read the profile information
        XmlBeanDefinitionReader definitionReader =
                new XmlBeanDefinitionReader(defaultListableBeanFactory);
        definitionReader.loadBeanDefinitions(classPathResource);
        // beanFactory must manually invoke BeanPostProcessor injection 
        defaultListableBeanFactory.addBeanPostProcessor ( new new LifeCycleBean ());
         // Get the bean 
        LifeCycleBean lifeCycleBean = 
                (LifeCycleBean) defaultListableBeanFactory.getBean (LifeCycleBean. Class );
         // perform a method 
        lifeCycleBean.method ();
        defaultListableBeanFactory.destroySingletons();
    }
}
View Code

  

  This is the entire Spring Bean's life cycle, understand the whole life cycle of the source code and then go to Spring IOC part I believe we will be better understood, then introduce these functions as an interface;

  BeanNameAware, BeanClassLoaderAware, BeanFactoryAware the three interfaces are executed after the Bean created to achieve Aware is actually the type of interface capabilities provided in the form of Spring Bean exposed to;

  BeanPostProcessor Bean is an important extension points before and after the instantiation provided, allowing modification of Bean in the Bean instantiation stage, Spring classic achieve AOP, transactions are postProcessAfterInitialization implemented by its extension methods;

  InitializingBean, init-method BeanPostProcessor after performing pre-processing and interfaces Aware, Bean will then detects whether the object implements this interface to InitializingBean, if so, which will be called afterPropertiesSet 方法,further adjustments Bean state of the object instance, the method init-method specified in afterPropertiesSet performed, which performs the same effect, but init-method specified method implemented on a reflective implemented, eliminating the dependence of interface inheritance;

  DisposableBean, destory-method is canceled when the container is triggered, refer to two different InitializingBean and init-method;

  Then we can talk mybatis-spring plug-in mechanism, so that we can reflect the value of the life cycle, can also appreciate the beauty of Spring;

Four, mybatis-spring plug

 We can think how to achieve this, we need to complete those steps:

 Resolving Mapper file;

 2. The subsequent Spring Mapper be analyzed into the Bean;

 3. After the conversion which is injected into the Bean Spring container;

 After the above three steps, we can entrust Mapper Spring to manage it, we look at how mybatis-spring is achieved, the whole process we use what Spring Bean life cycle, first-come, first put the plug-in to integrate;

 The first step to add a dependency in Maven;

 

  The second step is added in dependence Mybatis Spring configuration;

 

 Then look principle, the configuration process can be seen from the mybatis SqlSessionFactory to Spring container management, look at the inheritance structure SqlSessionFactoryBean,

 

 圈出了重点两个接口,FactoryBean是Spring Bean注入的一种方式,关键的重点在于InitializingBean,看到这个就想到生命周期,这个动作发生于Bean创建完成以后,我们看一下他做那些事;

 

 在Spring实例化Bean的时候会调用FactoryBeangetObject()方法。所以Mybatis与Spring的集成的入口就是org.mybatis.spring.SqlSessionFactoryBean#getObject()方法,是通过XMLConfigBuilderXMLMapperBuilderXMLStatementBuilder三个建造者来完成了对Mybatis XML文件的解析。

 现在已经将Spring与Mybatis整合到一起了,接下来可以看下如何将Mapper动态加载到Spring容器中,配置中MapperScannerConfigurer实现这一过程,接下来我们一起看一下该类的继承结构;

 重点是BeanDefinitionRegistryPostProcessor,会执行BeanDefinitionRegistryPostProcessorpostProcessBeanDefinitionRegistry方法来完成Bean的装载;

 

  我们可以看到通过包扫描,将会扫描出所有的Mapper类,然后注册Bean定义到Spring容器。Mapper是一个接口,不能直接实例化,在ClassPathMapperScanner中,它将所有Mapper对象的BeanDefinition给改了,将所有Mapper的接口对象指向MapperFactoryBean工厂Bean,所以在Spring中Mybatis所有的Mapper接口对应的类是MapperFactoryBean,源码如下:

  

  至此我们完成Mapper注入到Spring容器中,至于Mybatis后面的东西暂不去讨论了。整个过程中出现的一个从未出现过的接口BeanDefinitionRegistryPostProcessor,接下来我们一起来探究下这个类;

五、BeanDefinitionRegistryPostProcessor

  这里通过一个例子看下这个接口作用;

public class BeanDefinitionRegistryPostProcessorDemo implements BeanDefinitionRegistryPostProcessor {

    private static final String beanName = "student";


    /**
     * 加载 teacher 类
     *
     * @param registry
     * @throws BeansException
     */
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {

        //设置 bean 相关属性
        GenericBeanDefinition definition = new GenericBeanDefinition();
        definition.setBeanClass(Student.class);
        definition.setScope("singleton");
        // bean 属性赋值
        MutablePropertyValues mutablePropertyValues = new MutablePropertyValues();
        mutablePropertyValues.add("name", "wtz");
        mutablePropertyValues.add("age", 20);
        definition.setPropertyValues(mutablePropertyValues);
        //注入 bean
        registry.registerBeanDefinition("student", definition);

    }

    /**
     * 获取 Bean 对象并修改其属性
     * 
     * @param beanFactory
     * @throws BeansException
     */
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

        BeanDefinition definition = beanFactory.getBeanDefinition("teacher");
        MutablePropertyValues mutablePropertyValues = null;
        mutablePropertyValues = definition.getPropertyValues();
        if (definition != null && mutablePropertyValues != null) {
            PropertyValue propertyValue=
                    mutablePropertyValues.getPropertyValue("name");
            System.out.println("获取到的值"+propertyValue.getValue());
            propertyValue.setConvertedValue("修改属性为go");
        }
    }
}
View Code

  接下来看下实体和配置;

/**
 * student
 *
 * @author wtz
 */
public class Student {

    private String name;

    private Integer age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "student name  " + name + " age " + age;
    }
}

/**
 * teacher
 *
 * @author wtz
 */
public class Teacher {

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "teacher name " + name;
    }
}

    <bean id="teacher" class="org.handwriting.springdemo.Teacher">
        <property name="name" value="www" />
    </bean>

    <bean id="beanDefinitionRegistryPostProcessorDemo"
            class="org.handwriting.springdemo.BeanDefinitionRegistryPostProcessorDemo" />
View Code  

 我们来看一下运行结果;

 BeanDefinitionRegistryPostProcessor接口的方法postProcessBeanFactory是由BeanFactoryPostProcessor继承得来的,所以我们就对这两个接口进行总结一下;

 BeanFactoryPostProcessor该接口可以获取BeanFactory对象,通过操作BeanFactory修改BeanDefinition中的属性,但不支持实例化该Bean;

 BeanDefinitionRegistryPostProcessor是BeanFactoryPostProcessor的子类除了继承父类功能以外,我们可以获取到BeanDefinitionRegistry,通过该对象向容器中注入Bean;

 这两个接口是实现自动扫描的关键,比如ConfigurationClassPostProcessor这里我就不展开讨论了,这里我们还需要知道是什么时候加载的BeanDefinitionRegistryPostProcessor,上面给大家提到一个不纯粹的人;

 这个家伙有个refresh方法,该方法中有个invokeBeanFactoryPostProcessors方法,该方法就是自动扫描的关键,也就是这个时候执行的该接口的实现,源码展开说明,送大家一张图结束本文;

六、总结

  文章有些散乱,总体还是讲明白了三件事情:

  1.Spring IOC到底是在做一件什么事情;

  2.Spring Bean的生命周期都经历什么;

  3.插件是怎么加入到Spring容器中的;

  希望大家看过以后,能做到有需求写一个插件注入到Spring容器中的时候能不虚,很快将这个功能实现,另外还小小提了一下自动扫描,有兴趣自己去了解下;Spring加载过程中其实还有很多细节问题,这里都没有提及,希望大家努力去看源码,真的能学习到很多东西!

七、结束

  欢迎大家加群438836709!欢迎大家关注我!

  

Guess you like

Origin www.cnblogs.com/wtzbk/p/12078238.html