转载

手写Spring---IOC容器(1)

反转:依赖对象的获得被反转,变为由自己创建,反转为从IOC容器中获取。   
复制代码

带来的好处:

1.代码更为简洁,不需要再去new需要的对象   
    2.面向接口编程,使用类和具体类解耦,易扩展,替换实现者    
    3.方便进行AOP增强(没有IOC就无法AOP)          
复制代码

IOC容器做什么工作?

负责创建,管理类实例,向使用者提供实例
复制代码

IOC容器就是工厂模式的实例,IOC容器也被称为Bean工厂

IOC设计实现

IOC容器的工作:创建和管理Bean,它是一个工厂,负责对外提供Bean实例
复制代码

 Bean:组件,类的对象

Q1:它应该具备什么行为(对外接口)?

A:对外提供Bean实例,getBean()方法
复制代码

Q2:这个getBean()方法是否需要参数?需要几个,又为什么类型?

A:简单工厂模式中,当工厂能创建很多类产品,如果需要某类产品,需要告诉工厂
复制代码

Q3:这个getBean()方法的返回值的类型?

A:各种类型的bean只能为Object<br><br>
复制代码

此时我们可以编出BeanFactory接口

public interface BeanFactory { Object getBean(String name) throws Exception;}
复制代码

Bean工厂如何知道如何创建Bean?

就是一个定义注册,我们可以给它定义一个定义注册接口,让bean定义传入bean工厂,告知工厂创建何种类型的Bean
复制代码

Bean定义注册接口

public interface BeanDefinitionRegistry {}
复制代码

Q1:bean定义注册接口中应定义些什么方法?

注册,获取bean定义
复制代码

Q2:注册的bean定义信息如何区分?

每个Bean要有一个独立的名称
复制代码

此时我们可以编出BeanDefinitionRegistry接口

public interface BeanDefinitionRegistry {
    /**
    * 注册Bean
    * @param beanName
    * @param beanDefinition
    * @throws Exception
    */
    void registerBeanDefinition(String beanName,BeanDefinition beanDefinition) throws Exception;

    /**
    * 获取Bean
    * @param beanName
    * @return
    */
    BeanDefinition getBeanDefinition(String beanName);

    /**
    * 判断Bean是否已经被注册
    * @param beanName
    * @return
    */
    Boolean containsBeanDefinition(String beanName);
}
复制代码

bean定义

Q1:bean定义的用途是什么?

告诉bean工厂该如何创建某类bean
复制代码

Q2:获得类的实例的方式有哪些?

1.new 构造方法
    2.工厂方法(静态,成员方法)
复制代码

Q3:bean工厂帮我们创建bean时需要获取哪些信息?

1.new 构造方法(需要知道类名)
    2.静态工厂方法(需要知道工厂类名,工厂方法名)
    3.成员工厂方法(需要知道工厂类名--->工厂bean名,工厂方法名)
复制代码

Q4:每次从bean工厂获取bean实例时是否都要创建新的实例

否,因为有需要单例的情况
复制代码

Q5:bean定义是给bean工厂创建bean用的,那bean定义接口应向bean工厂提供哪些方法?

1.获取bean的类名:getBeanClass()
    2.获取工厂方法名:getFactoryMethodName()
    3.获取工厂bean名:getFactoryBeanName()
    4.是否是单例:getScope(){isSingleton();  isPrototype();}
复制代码

Q6:类对象交给IOC容器来管理,类对象的生命周期中还可能有什么生命周期阶段事情要做吗?

创建后可能需要的初始化
    销毁时有可能出现的某些销毁逻辑(比如释放资源)
    在bean定义提供让用户定制的初始化和销毁方法即可(getInitMethodName(),getDestroyMethodName())
复制代码

此时可写出bean定义接口代码:

public interface BeanDefinition {
        String SCOPE_SINGLETON = "singleton";
        String SCOPE_PROTOTYPE = "prototype";

        Class<?> getBeanClass();
        String getScope();
        boolean isSingleton();
        boolean isPrototype();
        String getFactoryBeanName();
        String getFactoryMethodName();
        String getInitMethodName();
        String getDestoryMethodName();
    
        //tips:java8开始就可以直接写接口默认方法了
        default boolean validate(){
            //class没指定,工厂bean或工厂method不指定皆为不合法情况
            if (this.getBeanClass()==null){
                if(StringUtils.isBlank(getFactoryBeanName())||StringUtils.isBlank(getFactoryMethodName())){
                    return false;
                }
            }
    
            //class和工厂bean同时存在
            if (this.getBeanClass()!=null && StringUtils.isNotBlank(getFactoryBeanName())){
                return false;
            }
            return true;
        }
    }       
复制代码

接口有了,现在我们来编写一个通用的bean定义(这里使用lombok插件)

import lombok.Data;
    import org.apache.commons.lang.StringUtils;

    @Data
    public class GeneralBeanDefinition implements BeanDefinition{
        private Class<?> beanClass;
        private String scope = BeanDefinition.SCOPE_SINGLETON;
        private String factoryBeanName;
        private String factoryMethodName;
        private String initMethodName;
        private String destroyMethodName;
    
        public void setScope(String scope) {
            if (StringUtils.isNotBlank(scope)){
                this.scope = scope;
            }
        }
    
        @Override
        public Class<?> getBeanClass() {
            return this.beanClass;
        }
    
        @Override
        public String getScope() {
            return this.scope;
        }
    
        @Override
        public boolean isSingleton() {
            return BeanDefinition.SCOPE_SINGLETON.equals(this.scope);
        }
    
        @Override
        public boolean isPrototype() {
            return BeanDefinition.SCOPE_PROTOTYPE.equals(this.scope);
        }
    
        @Override
        public String getFactoryBeanName() {
            return factoryBeanName;
        }
    
        @Override
        public String getFactoryMethodName() {
            return factoryMethodName;
        }
    
        @Override
        public String getInitMethodName() {
            return this.initMethodName;
        }
    
        @Override
        public String getDestoryMethodName() {
            return this.destroyMethodName;
        }
    }
复制代码

接下来该实现一个最基础的DefaultBeanFactory让它初步能工作起来

1.实现定义信息注册

Q1:bean定义信息如何存放?

A:Map
复制代码

Q2:bean定义是否可以重名,重名时如何解决?

A:直接设计为不能重名
复制代码

2.实现bean工厂

Q1:创建的bean使用什么存放,方便下次获取?

Map
复制代码

Q2:在getBean方法中需要做什么事情

创建bean实例,进行初始化
复制代码

知道这些之后,此时我们简单完成一个DefaultBeanFactory

public class DefaultBeanFactory implements BeanFactory,BeanDefinitionRegistry, Closeable {

    //common-logging包和log4j-api包配合即可
    private final Log logger = LogFactory.getLog(getClass());

    //考虑并发情况,256个前不需要进行扩容
    private Map<String,BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

    private Map<String,Object> beanMap = new ConcurrentHashMap<>(256);

    @Override
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws Exception {
        //参数检查
        Objects.requireNonNull(beanName,"注册bean需要输入beanName");
        Objects.requireNonNull(beanDefinition,"注册bean需要输入beanDefinition");

        //检验给入的bean是否合法
        if (!beanDefinition.validate()){
            throw new Exception("名字为["+beanName+"]的bean定义不合法,"+beanDefinition);
        }

        if (this.containsBeanDefinition(beanName)){
            throw new Exception("名字为["+beanName+"]的bean定义已经存在,"+this.getBeanDefinition(beanName));
        }

        this.beanDefinitionMap.put(beanName,beanDefinition);
    }

    @Override
    public BeanDefinition getBeanDefinition(String beanName) {
        return this.beanDefinitionMap.get(beanName);
    }

    @Override
    public Boolean containsBeanDefinition(String beanName) {
        return this.beanDefinitionMap.containsKey(beanName);
    }

    @Override
    public Object getBean(String name) throws Exception {
        return this.doGetBean(name);
    }

    //不需要判断scope,因为只有单例bean才需要放入map中
    //使用protected保证只有DefaultBeanFactory的子类可以调用该方法
    protected Object doGetBean(String beanName) throws Exception{
        Objects.requireNonNull(beanName,"beanName不能为空");
        Object instance = beanMap.get(beanName);

        if (instance != null){
            return instance;
        }
        BeanDefinition beanDefinition = this.getBeanDefinition(beanName);
        Objects.requireNonNull(beanDefinition,"beanDefinition不能为空");

        Class<?> type = beanDefinition.getBeanClass();

        //因为总共就只有3种方式,也不需要扩充或者是修改代码了,所以就不需要考虑使用策略模式了
        if (type != null){
            if (StringUtils.isBlank(beanDefinition.getFactoryMethodName())){
                instance = this.createInstanceByConstructor(beanDefinition);
            } else {
                instance = this.createInstanceByStaticFactoryMethod(beanDefinition);
            }
        }else {
            instance = this.createInstanceByFactoryBean(beanDefinition);
        }

        this.doInit(beanDefinition,instance);

        if (beanDefinition.isSingleton()){
            beanMap.put(beanName,instance);
        }

        return instance;
    }

    //构造方法来创建对象
    private Object createInstanceByConstructor(BeanDefinition beanDefinition) throws IllegalAccessException, InstantiationException {
        try{
            return beanDefinition.getBeanClass().newInstance();
        } catch (SecurityException e){
            logger.error("创建bean的实例异常,beanDefinition"+beanDefinition,e);
            throw e;
        }
    }

    //静态工厂方法(暂时不考虑带参数)
    private Object createInstanceByStaticFactoryMethod(BeanDefinition beanDefinition) throws Exception{
        Class<?> type = beanDefinition.getBeanClass();
        Method method = type.getMethod(beanDefinition.getFactoryMethodName(),null);
        return method.invoke(type,null);
    }

    //工厂bean方法来创建对象(暂时不考虑带参数)
    private Object createInstanceByFactoryBean(BeanDefinition beanDefinition) throws Exception{
        Object factoryBean = this.doGetBean(beanDefinition.getFactoryBeanName());
        Method method = factoryBean.getClass().getMethod(beanDefinition.getFactoryMethodName(),null);
        return method.invoke(factoryBean,null);
    }

    //初始化方法
    private void doInit(BeanDefinition beanDefinition, Object instance) throws Exception{
        if (StringUtils.isNotBlank(beanDefinition.getInitMethodName())){
            Method method = instance.getClass().getMethod(beanDefinition.getInitMethodName(),null);
            method.invoke(instance,null);
        }
    }
    @Override
    public void close() throws IOException {
        //执行单例实例的销毁方法
        //遍历map把bean都取出来然后调用每个bean的销毁方法
        for (Map.Entry<String,BeanDefinition> entry : this.beanDefinitionMap.entrySet()){
            String beanName = entry.getKey();
            BeanDefinition beanDefinition = entry.getValue();

            if (beanDefinition.isSingleton() && StringUtils.isNotBlank(beanDefinition.getDestoryMethodName())){
                Object instance = this.beanMap.get(beanName);
                try {
                    Method method = instance.getClass().getMethod(beanDefinition.getDestoryMethodName(),null);
                    method.invoke(instance,null);
                }catch (NoSuchMethodException|SecurityException|IllegalAccessException|IllegalArgumentException|InvocationTargetException e){
                    logger.error("执行bean["+beanName+"] "+beanDefinition+"的销毁方法异常",e);
                }
            }
        }
    }
}
复制代码

tips:此时单例的线程安全还无法保证!!!

扩展DefaultBeanFactory

Thinking:对于单例bean我们是否可以提前实例化,这有什么好处?

A:可以提前实例化,空间换时间的方法,启动慢使用快并线程安全
复制代码

如果要实现提前实例化单例bean的功能,代码如下

public class PreBuildBeanFactory extends DefaultBeanFactory{

    private Log logger = LogFactory.getLog(getClass());

    private List<String> beanNames = new ArrayList<>();

    @Override
    public void registerBeanDefinition(String beanName,BeanDefinition beanDefinition) throws Exception{
        super.registerBeanDefinition(beanName,beanDefinition);
        synchronized (beanNames){
            beanNames.add(beanName);
        }
    }

    //使用synchronized解决线程安全问题
    public void preInstantiateSingletons() throws Exception{
        synchronized (beanNames){
            for (String name : beanNames){
                BeanDefinition beanDefinition = this.getBeanDefinition(name);
                if (beanDefinition.isSingleton()){
                    this.doGetBean(name);
                    if (logger.isDebugEnabled()){
                        logger.debug("preInstantiate:name="+name+" "+beanDefinition);
                    }
                }
            }
        }
    }
}
复制代码

此时我们已经初步完成了一个IOC容器,可以来个简单的测试

编写一个bean1和一个bean1的工厂,还有一个test方法

Bean1.java

public class Bean1 {

    public void doSomething(){
        System.out.println(System.currentTimeMillis()+" "+this);
    }

    public void init(){
        System.out.println("bean1的init已执行");
    }

    public void destroy(){
        System.out.println("bean1的destroy已执行");
    }
}
复制代码

Bean1Factory.java

public class Bean1Factory {

    public static Bean1 getBean1(){
        return new Bean1();
    }

    public Bean1 getOtherBean1(){
        return new Bean1();
    }
}
复制代码

测试用例DefaultBeanFactoryTest

public class DefaultBeanFactoryTest {

    static DefaultBeanFactory defaultBeanFactory = new DefaultBeanFactory();

    @Test
    public void testRegist() throws Exception{
        GeneralBeanDefinition generalBeanDefinition = new GeneralBeanDefinition();
        generalBeanDefinition.setBeanClass(Bean1.class);
        generalBeanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON);
        generalBeanDefinition.setInitMethodName("init");
        generalBeanDefinition.setDestroyMethodName("destroy");

        defaultBeanFactory.registerBeanDefinition("bean1",generalBeanDefinition);
    }

    @Test
    public void testRegistStaticFactoryMethod() throws Exception{
        GeneralBeanDefinition generalBeanDefinition = new GeneralBeanDefinition();
        generalBeanDefinition.setBeanClass(Bean1Factory.class);
        generalBeanDefinition.setFactoryMethodName("getBean1");
        defaultBeanFactory.registerBeanDefinition("staticBean1",generalBeanDefinition);
    }

    @Test
    public void testRegistFactoryMethod() throws Exception{
        GeneralBeanDefinition generalBeanDefinition = new GeneralBeanDefinition();
        generalBeanDefinition.setBeanClass(Bean1Factory.class);
        String factoryBeanName = "factory";
        defaultBeanFactory.registerBeanDefinition(factoryBeanName,generalBeanDefinition);

        generalBeanDefinition = new GeneralBeanDefinition();
        generalBeanDefinition.setFactoryBeanName(factoryBeanName);
        generalBeanDefinition.setFactoryMethodName("getOtherBean1");
        generalBeanDefinition.setScope(BeanDefinition.SCOPE_PROTOTYPE);
        defaultBeanFactory.registerBeanDefinition("factoryBean",generalBeanDefinition);
    }

    @AfterClass
    public static void testGetBean() throws Exception{
        System.out.println("构造方法方式···");
        for (int i = 0;i<3;i++){
            Bean1 bean1 = (Bean1) defaultBeanFactory.getBean("bean1");
            bean1.doSomething();
        }

        System.out.println("静态工厂方法方式···");
        for (int i = 0;i<3;i++){
            Bean1 bean1 = (Bean1) defaultBeanFactory.getBean("staticBean1");
            bean1.doSomething();
        }

        System.out.println("工厂方法方式···");
        for (int i = 0;i<3;i++){
            Bean1 bean1 = (Bean1) defaultBeanFactory.getBean("factoryBean");
            bean1.doSomething();
        }

        defaultBeanFactory.close();
    }
}
复制代码

此测试用例的运行结果为(注意工厂方法时我们设置了多例,所以bean应该每个都不同):

构造方法方式···
bean1的init已执行
1555370012087 MySpring.Bean1@5e8c92f4
1555370012088 MySpring.Bean1@5e8c92f4
1555370012088 MySpring.Bean1@5e8c92f4
静态工厂方法方式···
1555370012088 MySpring.Bean1@50134894
1555370012088 MySpring.Bean1@50134894
1555370012088 MySpring.Bean1@50134894
工厂方法方式···
1555370012089 MySpring.Bean1@2957fcb0
1555370012089 MySpring.Bean1@1376c05c
1555370012090 MySpring.Bean1@51521cc1
bean1的destroy已执行复制代码
原文  https://juejin.im/post/5cb1c9c4e51d456e770bdc9c
正文到此结束
Loading...