浅试实现mini-spring-ioc容器(下)

简介: 浅试实现mini-spring-ioc容器

先说初始化方法:

@Override
protected Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws BeansException {
    Object bean = null;
    try {
        // 前面的操作
        // 执行 Bean 的初始化方法和 BeanPostProcessor 的前置和后置处理方法  !!! 
        bean = initializeBean(beanName, bean, beanDefinition);
    } catch (Exception e) {
        throw new BeansException("Instantiation of bean failed", e);
    }
    // 注册实现了 DisposableBean 接口的 Bean 对象  !!! 这里需要保存起来哪些Bean是含有销毁时方法的,在最后需要执行
    registerDisposableBeanIfNecessary(beanName, bean, beanDefinition);
    registerSingleton(beanName, bean);
    return bean;
}
private Object initializeBean(String beanName, Object bean, BeanDefinition beanDefinition) {
    // 1. 执行 BeanPostProcessor Before 处理
    Object wrappedBean = applyBeanPostProcessorsBeforeInitialization(bean, beanName);
    // 执行 Bean 对象的初始化方法,
    try {
        // 本次的核心 !!!
        invokeInitMethods(beanName, wrappedBean, beanDefinition);
    } catch (Exception e) {
        throw new BeansException("Invocation of init method of bean[" + beanName + "] failed", e);
    }
    // 2. 执行 BeanPostProcessor After 处理
    wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    return wrappedBean;
}

初始化方法调用

private void invokeInitMethods(String beanName, Object bean, BeanDefinition beanDefinition) throws Exception {
    // 1. 实现接口 InitializingBean
    if (bean instanceof InitializingBean) {
        ((InitializingBean) bean).afterPropertiesSet();
    }
    // 2. 注解配置 init-method {判断是为了避免二次执行初始化}
    String initMethodName = beanDefinition.getInitMethodName();
    if (StrUtil.isNotEmpty(initMethodName) && !(bean instanceof InitializingBean)) {
        Method initMethod = beanDefinition.getBeanClass().getMethod(initMethodName);
        if (null == initMethod) {
            throw new BeansException("Could not find an init method named '" + initMethodName + "' on bean with name '" + beanName + "'");
        }
        initMethod.invoke(bean);
    }
}

注册含有Destroy方法的Bean

protected void registerDisposableBeanIfNecessary(String beanName, Object bean, BeanDefinition beanDefinition) {
    if (bean instanceof DisposableBean || StrUtil.isNotEmpty(beanDefinition.getDestroyMethodName())) {
        registerDisposableBean(beanName, new DisposableBeanAdapter(bean, beanName, beanDefinition));
    }
}

那现在初始化方法已经执行完了,Destroy方法什么时候执行? 这里我们用到了JVM的钩子函数,我们先定义接口

public interface ConfigurableApplicationContext extends ApplicationContext {
    void refresh() throws BeansException;
    void registerShutdownHook();
    void close();
}
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext { 
@Override
    public void registerShutdownHook() {
        Runtime.getRuntime().addShutdownHook(new Thread(this::close));
    }
    @Override
    public void close() {
        getBeanFactory().destroySingletons();
    }
}

将DisposableBean封装为DisposableBeanAdapter,未来调用

public class DisposableBeanAdapter implements DisposableBean {
        @Override
        public void destroy() throws Exception {
            // 1. 实现接口 DisposableBean
            if (bean instanceof DisposableBean) {
                ((DisposableBean) bean).destroy();
            }
            // 2. 注解配置 destroy-method {判断是为了避免二次执行销毁}
            if (StrUtil.isNotEmpty(destroyMethodName) && !(bean instanceof DisposableBean && "destroy".equals(this.destroyMethodName))) {
                Method destroyMethod = bean.getClass().getMethod(destroyMethodName);
                if (null == destroyMethod) {
                    throw new BeansException("Couldn't find a destroy method named '" + destroyMethodName + "' on bean with name '" + beanName + "'");
                }
                destroyMethod.invoke(bean);
            }
        }
}

Close() 时具体的操作

public void close() {
    getBeanFactory().destroySingletons();
}
public void destroySingletons() {
        Set<String> keySet = this.disposableBeans.keySet();
        Object[] disposableBeanNames = keySet.toArray();
        for (int i = disposableBeanNames.length - 1; i >= 0; i--) {
            Object beanName = disposableBeanNames[i];
            // 从单例工厂中删除
            DisposableBean disposableBean = disposableBeans.remove(beanName);
            try {
                // 调用的是DisposableBeanAdapter重载的Destroy,最终调用Bean自身定义的Destroy。
                disposableBean.destroy();
            } catch (Exception e) {
                throw new BeansException("Destroy method on bean with name '" + beanName + "' threw an exception", e);
            }
        }
    }

第八章:感知容器对象

image.png

需求:在Bean初始化时,能够感知到容器对象,通过容器对象进行一系列操作

定义Aware接口,表示是可感知的对象,具体由子类实现

image.png

@Override
public void refresh() throws BeansException {
    // 1. 创建 BeanFactory,并加载 BeanDefinition
    // 2. 获取 BeanFactory
    // 3. 添加 ApplicationContextAwareProcessor,让继承自 ApplicationContextAware 的 Bean 对象都能感知所属的 ApplicationContext !!! 
    beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
    // 4. 在 Bean 实例化之前,执行 BeanFactoryPostProcessor 
    // 5. BeanPostProcessor 需要提前于其他 Bean 对象实例化之前执行注册操作
    // 6. 提前实例化单例Bean对象
}

这里的第三步,因为无法在Bean初始化时感知到ApplicationContext属性,所以将ApplicationContext写入一个BeanPostProcessor,这样在执行初始化前就可以获取并分配ApplicationContext了。

AbstractAutowireCapableBeanFactory:createBean()调用

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);
        }
    }
}

总之当Bean实现Aware实现类后,当Bean被注入属性后,就会将对应的容器对象进行填充,得到一个完整的对象。

第九章:对象作用域与FactoryBean

image.png

FactoryBean 和 BeanFactory有什么区别?

BeanFactory是用来获取Bean的,FactoryBean它本身是一个Bean,但它本身也是一个工厂,可以内部通过动态代理,凝聚出一个功能更完善的Bean,未来这个获取Bean获取的就是FactoryBean内部代理的Bean,可对原本的方法做出调整,而不需要修改原先基本的代码。

为什么需要FactoryBean?

可以扩充自己的对象功能了。MyBatis 就是实现了一个 MapperFactoryBean 类,在 getObject 方法中提供 SqlSession 对执行 CRUD 方法的操作

如何使用FactoryBean?

定义一个类实现FactoryBean接口,实现内部的getObject(),然后注册到容器中即可。

AbstractBeanFactory::doGetBean

protected <T> T doGetBean(final String name, final Object[] args) {
    Object sharedInstance = getSingleton(name);
    if (null != sharedInstance) {
        // 如果是 FactoryBean,则需要调用 FactoryBean#getObject,获取FactoryBean中定义的元素
        return (T) getObjectForBeanInstance(sharedInstance, name);
    }
    BeanDefinition beanDefinition = getBeanDefinition(name);
    Object bean = createBean(name, beanDefinition, args);
    return (T) getObjectForBeanInstance(bean, name);
}
private Object getObjectForBeanInstance(Object beanInstance, String beanName) {
    // 判断是否为FactoryBean,如果不是则直接返回
    if (!(beanInstance instanceof FactoryBean)) {
        return beanInstance;
    }
    // 缓存中找
    Object object = getCachedObjectForFactoryBean(beanName);
    if (object == null) {
        FactoryBean<?> factoryBean = (FactoryBean<?>) beanInstance;
        // !!!  获取或创建FactoryBean内对象
        object = getObjectFromFactoryBean(factoryBean, beanName);
    }
    return object;
}
protected Object getObjectFromFactoryBean(FactoryBean factory, String beanName) {
    if (factory.isSingleton()) {
        Object object = this.factoryBeanObjectCache.get(beanName);
        if (object == null) {
            object = doGetObjectFromFactoryBean(factory, beanName);
            this.factoryBeanObjectCache.put(beanName, (object != null ? object : NULL_OBJECT));
        }
        return (object != NULL_OBJECT ? object : null);
    } else {
        return doGetObjectFromFactoryBean(factory, beanName);
    }
}

第十章:容器事件与事件监听器

image.png

这里将用到观察者模式,比如说我们有一个注册用户的业务,注册业务成功后,需要发送短信通知,我们最初就是将注册代码与发送短信代码写在了一起,后来自己定义观察者与被观察者,实际上Spring为了更好的扩容性和灵活性,加入了事件监听机制,我们下面来自己设计如何控制事件:

定义和实现事件

image.png

事件广播器: 可用于添加,删除监听,广播事件,通过对应的监听器进行通知

image.png

事件监听器,自定义监听器,执行自定义事件触发方法

image.png

事件如何发送?并触发对应的事件相应方法?

@Test
public void test_event() {
    ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring.xml");
    // 发送事件
    applicationContext.publishEvent(new CustomEvent(applicationContext, 1019129009086763L, "成功了!"));
    applicationContext.registerShutdownHook();
}
@Override
public void publishEvent(ApplicationEvent event) {
    // 通过事件广播器进行通知
    applicationEventMulticaster.multicastEvent(event);
}
@Override
public void multicastEvent(final ApplicationEvent event) {
    // 找到事件对应的监听器,并执行方法
    for (final ApplicationListener listener : getApplicationListeners(event)) {
        listener.onApplicationEvent(event);
    }
}
/**
 * 监听器是否对该事件感兴趣
 */
protected boolean supportsEvent(ApplicationListener<ApplicationEvent> applicationListener, ApplicationEvent event) {
    Class<? extends ApplicationListener> listenerClass = applicationListener.getClass();
    // 按照 CglibSubclassingInstantiationStrategy、SimpleInstantiationStrategy 不同的实例化类型,需要判断后获取目标 class
    Class<?> targetClass = ClassUtils.isCglibProxyClass(listenerClass) ? listenerClass.getSuperclass() : listenerClass;
    Type genericInterface = targetClass.getGenericInterfaces()[0];
    Type actualTypeArgument = ((ParameterizedType) genericInterface).getActualTypeArguments()[0];
    String className = actualTypeArgument.getTypeName();
    Class<?> eventClassName;
    try {
        eventClassName = Class.forName(className);
    } catch (ClassNotFoundException e) {
        throw new BeansException("wrong event class name: " + className);
    }
    // 判定此 eventClassName 对象所表示的类或接口与指定的 event.getClass() 参数所表示的类或接口是否相同,或是否是其超类或超接口。
    // isAssignableFrom是用来判断子类和父类的关系的,或者接口的实现类和接口的关系的,默认所有的类的终极父类都是Object。如果A.isAssignableFrom(B)结果是true,证明B可以转换成为A,也就是A可以由B转换而来。
    return eventClassName.isAssignableFrom(event.getClass());
}
目录
相关文章
|
2天前
|
XML Java 数据格式
Spring容器的本质
本文主要讨论Spring容器最核心的机制,用最少的代码讲清楚Spring容器的本质。
|
6月前
|
XML Java 数据格式
Spring5入门到实战------7、IOC容器-Bean管理XML方式(外部属性文件)
这篇文章是Spring5框架的实战教程,主要介绍了如何在Spring的IOC容器中通过XML配置方式使用外部属性文件来管理Bean,特别是数据库连接池的配置。文章详细讲解了创建属性文件、引入属性文件到Spring配置、以及如何使用属性占位符来引用属性文件中的值。
Spring5入门到实战------7、IOC容器-Bean管理XML方式(外部属性文件)
|
2月前
|
XML Java 数据格式
Spring容器Bean之XML配置方式
通过对以上内容的掌握,开发人员可以灵活地使用Spring的XML配置方式来管理应用程序的Bean,提高代码的模块化和可维护性。
73 6
|
2月前
|
安全 Java 开发者
Spring容器中的bean是线程安全的吗?
Spring容器中的bean默认为单例模式,多线程环境下若操作共享成员变量,易引发线程安全问题。Spring未对单例bean做线程安全处理,需开发者自行解决。通常,Spring bean(如Controller、Service、Dao)无状态变化,故多为线程安全。若涉及线程安全问题,可通过编码或设置bean作用域为prototype解决。
50 1
|
3月前
|
前端开发 Java Docker
使用Docker容器化部署Spring Boot应用程序
使用Docker容器化部署Spring Boot应用程序
|
3月前
|
Java Docker 微服务
利用Docker容器化部署Spring Boot应用
利用Docker容器化部署Spring Boot应用
71 0
|
4月前
|
Java 测试技术 Windows
咦!Spring容器里为什么没有我需要的Bean?
【10月更文挑战第11天】项目经理给小菜分配了一个紧急需求,小菜迅速搭建了一个SpringBoot项目并完成了开发。然而,启动测试时发现接口404,原因是控制器包不在默认扫描路径下。通过配置`@ComponentScan`的`basePackages`字段,解决了问题。总结:`@SpringBootApplication`默认只扫描当前包下的组件,需要扫描其他包时需配置`@ComponentScan`。
|
5月前
|
XML Java 开发者
经典面试---spring IOC容器的核心实现原理
作为一名拥有十年研发经验的工程师,对Spring框架尤其是其IOC(Inversion of Control,控制反转)容器的核心实现原理有着深入的理解。
210 3
|
4月前
|
XML Java 数据格式
Spring IOC容器的深度解析及实战应用
【10月更文挑战第14天】在软件工程中,随着系统规模的扩大,对象间的依赖关系变得越来越复杂,这导致了系统的高耦合度,增加了开发和维护的难度。为解决这一问题,Michael Mattson在1996年提出了IOC(Inversion of Control,控制反转)理论,旨在降低对象间的耦合度,提高系统的灵活性和可维护性。Spring框架正是基于这一理论,通过IOC容器实现了对象间的依赖注入和生命周期管理。
105 0
|
6月前
|
XML Java 数据格式
Spring5入门到实战------3、IOC容器-Bean管理XML方式(一)
这篇文章详细介绍了Spring框架中IOC容器的Bean管理,特别是基于XML配置方式的实现。文章涵盖了Bean的定义、属性注入、使用set方法和构造函数注入,以及如何注入不同类型的属性,包括null值、特殊字符和外部bean。此外,还探讨了内部bean的概念及其与外部bean的比较,并提供了相应的示例代码和测试结果。
Spring5入门到实战------3、IOC容器-Bean管理XML方式(一)