【Spring】揭开Spring朦胧的面纱

使用Spring的IOC容器获取一个Bean的实质,其实可以简化成使用反射的一个过程。

首先

这是一个Bean的定义。

Student bean

package cn.edu.au.selection.service;

public class Student {

    private String name;

    private int age;

    public Student() {
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

然后

启动Spring的时候,也就是Spring初始化的时候。有经验的人都知道ApplicationContext的初始化过程主要是在AbstractApplicationContext类中refresh()方法中进行。而且该方法中最重要的两个子方法分别是ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();finishBeanFactoryInitialization(beanFactory);

第一个方法

第一个方法的主要作用就是创建可供上下文使用的基本IOC容器,也就是DefaultListableBeanFactory。由该IOC容器对我们所注册的BeanDefinition以及Bean进行托管。在该IOC容器的初始化过程中,主要做了三件事:

 1. `BeanDefinition`的`Resource`的定位。也就是说我们配置`<bean></bean>`标签的Spring`*.xml`文件的定位。或是`FileSystemResource`,或是`ClassPathContextResource`。这些Resource其实就是对Xml文件的一个封装。
 2. 当定位到`Resource`之后,便可以解析 `BeanDefinition`了。该`BeanDefinition`其实是对Bean定义的一个抽象。包含你定义的Bean的一些元数据、`ClassLoader`等等。该过程又可能分成两个步骤,第一步,进行Xml文件解析,该步只是将文件当成普通的Xml文件进行解析。而第二步就是Spring按照`<bean></bean>`标签的定义对`BeanDefinition`进行解析生成`BeanDefinition`对象。这个对象由`AbstractBeanDefinition`指向,放在IOC容器中统一托管。
 3. 第三步就是`BeanDefination`在IoC容器中的注册,也就是正式托管的过程。这个过程其实就是将`BeanDefination`放入`DefaultListableBeanFactory`的一个`ConcurrentHashMap`中,叫做`beanDefinitionMap`。供随后创建Bean的实体做准备。

注意:在第二个步骤中,不仅仅将*.xml文件中定义的Bean的BeanDefinition解析出来,而且将有<context:component-scan/>标签的,有@Component等注解的那些Bean的BeanDefinition也解析出来。当然还有Spring内部自己使用的一些Bean的BeanDefinition

第二个方法

第二个方法finishBeanFactoryInitialization(beanFactory);的主要作用就是初始化所有剩余的非延迟加载(non-lazy-init)的单例的Bean。注意,在这个方法的调用栈中就包含了我们会使用的ApplicationContext.getBean()的调用。也就是说,其实所有Bean的创建都是在getBean()方法中进行的。方法调用中有一个比较关键的方法就是AbstractBeanFactory.doGetBean()方法。这个方法的逻辑路线是这样的。

  1. 首先当初始化上下文的时候,非延迟加载的单例Bean先会通过Object sharedInstance = getSingleton(beanName);方法查找。当使用getBean方法的时候,首先也是在getSingleton(beanName)方法中查找有没有之前创建好的实例。这一步如果没有找到,当然只能找到单例,prototype类型的肯定找不到,还有lazy-init的也会找不到。
  2. 然后进行一个判断。首先从IOC容器中获取BeanDefination,从其中判断待获取的Bean是单例还是原型(prototype)模式。如果是原型模式,每次创建一个Bean的实例即可。如果是单例模式,当第一次创建完实例之后需要保存到IOC容器中,第二次获取的时候就无需再次创建了。同样的,该单例Bean也是保存在DefaultListableBeanFactory的另一个ConcurrentHashMap中,叫做singletonObjects
  3. 在创建Bean实例,调用AbstractAutowiredCapableBeanFactory.doCreateBean()方法时有两个关键的方法,分别是createBeanInstance()populateBean()。其中第一个方法就是使用反射,调用类的构造方法来创建一个该类的实例对象。而第二个方法就是对该实例对象进行初始化,也就是实例中属性的初始化。有些@Autowired或者@Value的注解的属性的初始化都会在这里进行。并且有@Autowired注解的属性还会触发递归调用创建Bean的方法。说白了,就是依赖关系处理的过程。
  4. 在完成依赖关系处理之后,在该方法中接着会执行一个这个方法initializeBean(),这个方法中又有三个重要的方法,分别是applyBeanPostProcessorsBeforeInitialization()invokeInitMethods()applyBeanPostProcessorsAfterInitialization()。其中含有@PostConstruct注解的方法会在第一个方法中执行。第二个方法中一般执行BeanDefinition中指定的init-method方法或者afterPropertitiesSet()实现方法。

创建Bean实例的简化过程

至此,Spring之IOC容器的初始化过程以及依赖注入都说完了。
那么创建Bean实例的过程可以简化成以下代码:

package cn.edu.au.selection.service;

import com.alibaba.fastjson.JSON;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Reflection {

    private static Logger logger = LoggerFactory.getLogger(Reflection.class);

    public static void main(String[] args){
        ClassLoader classLoader = ClassLoader.getSystemClassLoader();
        Class<?> clazz;
        String className = "cn.edu.au.selection.service.Student";
        Object object = null;
        String name = "Ethan Hunt";
        int age = 18;
        try {
            clazz = classLoader.loadClass(className);
            Constructor<?>[] ctors = clazz.getDeclaredConstructors();
            for (Constructor<?> constructor : ctors){
                constructor.setAccessible(true);
                object = constructor.newInstance();
            }
            Field field = clazz.getDeclaredField("name");
            field.setAccessible(true);
            field.set(object, name);
            Method method = clazz.getDeclaredMethod("setAge", int.class);
            method.setAccessible(true);
            method.invoke(object, age);

        } catch (ClassNotFoundException e) {
            logger.error("class {} cannot be found!", className);
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            logger.error("被调用的方法{}的内部抛出了异常而没有被捕获!", "Student()/setAge()");
            e.printStackTrace();
        } catch (InstantiationException e) {
            logger.error("{}类无法被实例化!", className);
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            logger.error("方法{}或属性{}无法被访问!", "setAge()", "name");
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            logger.error("没有这样的属性{}!", "name");
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            logger.error("没有这样的方法{}!", "setAge()");
            e.printStackTrace();
        }
        logger.info(JSON.toJSONString(object));
    }
}

输出结果为:

{
    "age": 18,
    "name": "Ethan Hunt"
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值