美文网首页
Spring-IOC

Spring-IOC

作者: Zeppelin421 | 来源:发表于2021-09-08 15:49 被阅读0次

    基础

    bean.xml
    定义需要实例化对象的全限定类名以及类之间依赖关系描述

    BeanFactory
    IOC容器,通过反射技术来实例化对象并维护对象之间的依赖关系

    Spring框架IOC实现

    纯xml和xml+注解模式下IOC容器启动方式:

    JavaSE应用
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");

    ApplicationContext applicationContext = new FileSystemXmlApplicationContext("c:/bean.xml");
    JavaWeb应用
    通过监听器 ContextLoaderListener 去加载xml

    纯注解模式下IOC容器启动方式:

    JavaSE应用
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
    JavaWeb应用
    通过监听器 ContextLoaderListener 去加载xml

    TIPS:学习注解的技巧,找xml中标签和注解的一一对应关系即可

    BeanFactory和ApplicationContext

    BeanFactory是Spring框架中IOC容器的顶层接口,它只是用来定义一些基础功能,定义一些基础规范,而ApplicationContext是它的一个子接口,所以ApplicationContext是具备BeanFactory提供的全部功能。
    通常我们称BeanFactory为SpringIOC的基础容器,ApplicationContext是容器的高级接口,比BeanFactory要拥有更多的功能,例如国际化支持和资源访问等等

    启动IoC容器的方式

    • Java环境下启动IoC容器
    • ClassPathXmlApplicationContext:从类的根路径下加载配置文件(推荐使用)
    • FileSystemXmlApplicationContext:从磁盘路径上加载配置文件
    • AnnotationConfigApplicationContext:纯注解模式下启动Spring容器
    • Web环境下启动IoC容器
    • 从xml启动容器
    <web-app>
     <display-name>Archetype Created Web Application</display-name>
     <!-- 配置Spring ioc容器的配置文件 -->
     <context-param>
       <param-name>contextConfigLocation</param-name>
       <param-value>classpath:applicationContext.xml</param-value>
     </context-param>
     <!-- 使用监听器启动Spring的IOC容器 -->
     <listener>
       <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
     </listener>
    </web-app>
    
    • 从配置类启动容器
    <web-app>
     <display-name>Archetype Created Web Application</display-name>
     <!-- 告诉ContextLoaderListener我们使用注解的方式启动IOC容器 -->
     <context-param>
       <param-name>contextClass</param-name>
       <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
     </context-param>
     <!-- 配置启动类的全限定类名 -->
     <context-param>
       <param-name>contextConfigLocation</param-name>
       <param-value>com.hooda.config.SpringConfig</param-value>
     </context-param>
     <!-- 使用监听器启动Spring的IOC容器 -->
     <listener>
       <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
     </listener>
    </web-app>
    

    实例化Bean的三种方式

    • 方式一:使用无参构造函数
    <bean id="userService" class="com.hooda.service.UserServiceImpl"/>
    
    • 方式二:使用静态方法创建
    public class DefinedBeanFactory {
      public static UserService getUserService() {
        return new UserServiceImpl();
      }
    }
    <bean id="userService" class="com.hooda.factory.DefinedBeanFactory" factory-method="getUserService"/>
    
    • 方式三:使用实例化方法创建
    public class DefinedBeanFactory {
      public UserService getUserService() {
        return new UserServiceImpl();
      }
    }
    <bean id="definedBeanFactory" class="com.hooda.factory.DefinedBeanFactory"/>
    <bean id="userService" factory-bean="definedBeanFactory" factory-method="getUserService"/>
    

    Bean的属性及生命周期

    • 标签属性
      Id属性:用于给bean提供一个唯一标识。在一个标签内部,标识必须唯一
      class属性:用于指定创建Bean对象的全限定类名
      name属性:用于给Bean提供一个或多个名称,多个名称用空格分割
      factory-bean属性:用于指定创建当前Bean对象的工厂Bean的唯一标识。当指定了此属性后,class属性失效
      factory-method属性:用于指定创建当前Bean对象的工厂方法,如配合factory-bean属性使用,则class属性失效;如配合class属性使用,则方法必须是static的
      scope属性:用于指定Bean对象的作用范围,通常情况下就是singleton。当要用到多例模式时,可以配置为prototype
      init-method属性:用于指定Bean对象的初始化方法,此方法会在Bean对象装配后调用。必须是无参方法
      destory-method属性:用于指定Bean对象的销毁方法,此方法会在Bean对象销毁前执行。他只能为scope是singleton时起作用
    • 生命周期
      singleton
      单例模式的bean对象生命周期和容器一样
      prototype
      多例模式的bean对象,Spring框架只负责创建,不负责销毁
    • 构造注入(constructor-arg)
      name:用于给构造函数中指定名称的参数赋值
      index:用于给构造函数中指定索引位置的参数赋值
      value:用于指定基本类型或者String类型的数据
      ref:用于指定其他Bean类型的数据,填写的是其他Bean唯一标识
    • set注入(property)
      name:指定注入时调用的set方法名称
      value:指定注入的数据,支持基本类型或者String类型
      ref:指定引用的数据,支持其他Bean类型,写的是其他Bean唯一标识
    • DI注解实现方式
      @Autowired:Spring提供的注解,默认按照类型注入。如果出现一个类型有多个Bean值时,需要配合@Qualifier使用
      @Resource:J2EE提供的注解,默认按照name自动注入。如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到抛异常;如果指定了name,则从上下文中查找id匹配,找不到抛异常;如果指定了type,则从上下文中找到类型匹配的唯一bean进行装配,找不到或找到多个都会抛异常;
      TIPS:@Resource在JDK11中已经移除,如需使用,单独引入jar包
    • 纯注解
      @Configutation:表明当前类是一个配置类
      @ComponentScan:替代context:component-scan
      PropertySource:引入外部数学配置文件
      @Import:引入其他配置类
      @Value:对变量赋值,可以直接赋值,也可以使用${}读取资源配置文件中的信息
      @Bean:将方法返回对象加入SpringIOC容器

    Spring IOC高级特性

    lazy-init延迟加载
    ApplicationContext容器默认在启动服务时将所有singleton-bean进行实例化。设置lazy-init为true的bean将不会在ApplicationContext启动时提前被实例化,而是第一次向容器通过getBean获取时实例化
    如果一个设置了立即加载的bean1引用了一个延迟加载的bean2,那么bean1在容器启动时被实例化,而bean2由于被bean1引用,所以也会被实例化
    如果一个bean的scope属性为prototype时,即使设置了lazy-init=false,容器启动时也不会实例化bean,而是调用时实例化。
    FactoryBean&BeanFactory
    Spring中Bean有两种,一种是普通Bean,一种是工厂Bean(FactoryBean),FactoryBean可以生成某一个类型的Bean实例,也就是说我们可以借助于它自定义Bean的创建过程。

    // 可以让我们自定义Bean的创建过程(完成复杂Bean的定义)
    public interface FactoryBean<T> {
        // 返回FactoryBean创建的Bean实例,如果isSingleton=true,则该实例会放到Spring容器的单利对象池中
        @Nullable
        T getObject() throws Exception;
    
        // 返回FactoryBean创建的Bean类型
        @Nullable
        Class<?> getObjectType();
    
        // 返回作用域是否单例
        default boolean isSingleton() {
            return true;
        }
    }
    
    // 例
    public class Company {
        private name, address, scale;
    }
    public class CompanyFactoryBean implements FactoryBean<Company> {
        private String companyInfo;
        
        @Override
        public Company getObject() throws Exception {
            // 模拟创建复杂对象
            Company company = new Company();
            company.setName(companyInfo.split(",")[0]);
            ...
            return company;
        }
        @Override
        public Class<?> getObjectType() {
            return Company.class;
        }
        @Override
        public boolean isSingleton() {
            return true;
        }
    }
    // xml配置
    <bean id="companBean" class="CompanyFactoryBean">
        <property name="companyInfo" value="xx,xx,1"/>
    </bean>
    // 获取CompanyFactoryBean产生的对象
    Object companyBean = applicationContext.getBean("companyBean");
    // 获取CompanyFactoryBean对象本身
    Object companyBean = applicationContext.getBean("&companyBean");
    

    后置处理器
    Spring提供了两种后置处理bean的扩展接口,分别为BeanPostProcessor和BeanFactoryPostProcessor,两者在使用上是有所区别的。
    BeanPostProcessor是针对Bean级别的处理,在Bean对象实例化之后。

    public interface BeanPostProcessor {
        default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; }
        default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { return bean; }
    }
    

    该接口提供了两个方法,分别在Bean的初始化方法前后执行,具体初始化方法指的什么方法,类似我们在定义bean时,init-method所指定的方法
    定义一个类实现了BeanPostProcessor,默认会对整个Spring容器中所有bean进行处理。如果要对某个bean处理,可以通过beanName来判断我们将要处理的具体bean
    BeanFactoryPostProcessor是针对BeanFactory级别的处理,是对整个Bean的工厂进行处理。典型应用:PropertyPlaceHolderConfigurer

    @FunctionalInterface
    public interface BeanFactoryPostProcessor {
        void postProcessBeanFactory(ConfigurableListableBeanFactory var1) throws BeansException;
    }
    

    此接口只提供了一个方法,方法参数为ConfigurableListableBeanFactory,该参数类型定义了一些方法


    我们可以根据getBeanDefinition方法找到BeanDefinition对象,然后对定义的属性进行修改。
    TIPS:调用BeanFactoryPostProcessor方法时bean还没有实例化,此时bean刚被解析成BeanDefinition对象

    Bean生命周期关键时机点

    Bean对象构造函数调用时机&InitializingBean之afterPropertiesSet
    在AbstractApplicationContext.refresh()的finishBeanFactoryInitialization(BeanFactory)处
    BeanFactoryPostProcessor初始化和调用时机
    BeanFactoryPostProcessor初始化和调用都在AbstractApplicationContext.refresh()的invokeBeanFactoryPostProcessor(beanFactory)
    BeanPostProcessor初始化和调用时机
    BeanPostProcessor初始化在AbstractApplicationContext.refresh()的registerBeanPostProcessors(beanFactory)处
    postProcessBeforeInitialization&postProcessAfterInitialization都在AbstractApplicationContext.refresh()的finishBeanFactoryInitialization(beanFactory)处
    Spring IOC容器初始化主流程

    public void refresh() throws BeansException, IllegalStateException {
        synchronized(this.startupShutdownMonitor) {
            // 第一步 刷新前的预处理
            prepareRefresh();
            /*
              第二步 获取BeanFactory,默认实现DefaultListableBeanFactory
              加载BeanDefinition并注册到BeanDefinitionRegistry中
            */
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
            // 第三步 BeanFactory的预准备工作(进行一些设置,比如Context的类加载器等)
            prepareBeanFactory(beanFactory);
            try {
                // 第四步:BeanFactory准备工作完成后进行的后置处理工作
                postProcessBeanFactory(beanFactory);
                // 第五步:实例化并调用实现了BeanFactoryPostProcessor接口的Bean
                invokeBeanFactoryPostProcessors(beanFactory);
                // 第六步:注册BeanPostProcessor(Bean的后置处理器),在创建bean前后执行
                registerBeanPostProcessors(beanFactory);
                // 第七步:初始化MessageSource组件(做国际化功能;消息绑定;消息解析)
                initMessageSource();
                // 第八步:初始化事件派发器
                initApplicationEventMulticaster();
                // 第九步:子类重写这个方法,在容器刷新的时候可以自定义逻辑
                onRefresh();
                // 第十步:注册实现了ApplicationListener接口的监听器
                registerListener();
                /*
                  第十一步:初始化所有剩下的非懒加载单例bean
                  初始化创建非懒加载方式的单例bean(未设置属性)
                      填充属性
                      初始化方法调用(比如调用afterPropertiesSet方法,init-method方法)
                      调用BeanPostProcessor(后置处理器)对实例bean进行后置处理
                 */
                finishBeanFactoryInitialization(beanFactory);
                // 第十二步:完成context刷新,主要是调用LifecycleProcessor的onRefresh方法,并且发布事件(ContextRefreshEvent)
                finishRefresh();
            }
            ...
        }
    }
    

    BeanFactory创建流程

    获取BeanFactory子流程

    时序图
    BeanDefinition加载解析级注册子流程
    Resource定位:指对BeanDefinition的资源定位过程。找到定义JavaBean信息的xml文件并将其封装成Resource对象
    BeanDefinition载入:把用户定义好的JavaBean表示为IoC容器内部的数据结构
    step1:入口在AbstractRefreshableApplicationContext#refreshBeanFactory方法中调用了loadBeanDefinition(beanFactory)
    step2:调用多个类的loadBeanDefinition方法,链 AbstractXmlApplicationContext -> AbstractBeanDefinitionReader -> XmlBeanDefinitionReader#doLoadBeanDefinitions,在doLoadBeanDefinition方法中执行registerBeanDefinition(doc, resource)方法

    相关文章

      网友评论

          本文标题:Spring-IOC

          本文链接:https://www.haomeiwen.com/subject/nihdbltx.html