Spring-工厂高级特性

作者: zhl 分类: Spring 发布时间: 2023-10-31 13:17

一、对象的生命周期

什么是对象的生命周期?

指的是一个对象的创建、存活、消亡的一个完整过程

为什么要学习对象的生命周期?

由Spring负责对象的创建、存活、销毁,了解生命周期,有利于我们使用好Spring为我们创建的对象

生命周期的3个阶段

创建阶段

  • Spring工厂何时创建对象
  • scope="singleton"
    • Spring工厂创建的同时,完成对象的创建
    • 注意:设置scope="singleton" 这种情况下 也需要在获取对象的同时创建对象, 通过标签<bean lazy-init="true"/>
  • scope="prototype"
    • Spring工厂在获取对象的同时,创建对象
    • ctx.getBean("");

初始化阶段

  • Spring工厂在创建完对象后,调用对象的初始化方法,完成对应的初始化操作
    1. 初始化方法提供:程序员根据需求,提供初始化方法,最终完成初始化操作
    2. 初始化方法调用:Spring工厂进行调用
    3. 方式一:initializingBean接口
      • afterPropertesSet()
        
        /**
      • 初始化方法:程序员根据需求,实现的方法,完成初始化操作
      • @throws Exception
        */
        @Override
        public void afterPropertiesSet() throws Exception {
        System.out.println("Product afterPropertiesSet()...");
        }
    4. 方式二:对象中提供一个普通的方法
      避免Spring框架的侵入 (implements initializingBean)更灵活
      //方法名任意,为了让Spring调用,在Spring配置文件中配置属性 init-method="" 自定义的初始化方法
      public void myInit(){
      }
      // <bean id="product" class="xxx.Product" init-method="myInit"/>
  • 细节分析
    1. 如果一个对象既实现了InitializingBean 同时又提供了 普通的初始化方法。执行顺序是什么?
      • 先执行 InitializingBean 再执行普通初始化方法
    2. 注入一定发生在初始化操作的前面
    3. 什么叫做初始化操作?
      • 资源的初始化:数据库、IO、网络....(比较占用资源时间空间)
      • 实际上我们需要做的初始化操作很少,就像Servlet中的 init 方法,我们几乎没有使用init方法

销毁阶段

Spring销毁对象前,会调用对象的销毁方法,完成销毁操作

  1. Spring什么时候销毁所创建的对象?
    • ctx.close();
  2. 销毁方法:程序员根据自己的需求,定义销毁方法,完成销毁操作
    • 调用者:Spring工厂完成调用
  1. 方式一:DisposableBean接口
    public void destroy throw Exception(){
    }
  2. 方式二:定义一个普通的销毁方法
    public void myDestroy throw Exception(){
    }
    <bean id="" class="" init-method="" destroy-method="myDestroy">
    • 细节分析
      1. 销毁方法的操作只适用于scope="singleton"(初始化方法都适用)
      2. 什么叫做销毁操作?
      3. 主要指的是 资源的释放操作 io.close() connection.close()
    • 实际上我们需要做的销毁操作很少,就像Servlet中的 destroy 方法,我们几乎没有使用destroy方法

二、配置文件参数化

把Spring配置文件中经常修改的字符串信息,转移到一个更小的配置文件中

  1. Spring的配置文件中存在需要经常修改的字符串信息吗?
    存在 以数据库连接相关的参数为代表
  2. 经常变化的字符串,在Spring的配置文件中,直接修改
    不利于项目维护(修改)
  3. 转移到一个小的配置文件(properties)

配置文件参数化:利于Spring配置文件的维护(修改)

配置文件参数化的步骤

  1. 提供一个小的配置文件(.properties)

    jdbc.driver=com.mysql.cj.jdbc.Driver
    jdbc.url=jdbc:mysql://localhost:3306/mybatis
    jdbc.username=root
    jdbc.password=15737979065
  2. Spring配置文件和properties小配置文件进行整合

    <!--在Spring配置文件中添加此标签-->
    <context:property-placeholder location="classpath:/db.properties"/>
  3. 在Spring配置文件中通过 ${key}的形式获取 value值

    <bean id="conn" class="com.mysite.factorybean.ConnectionFactoryBean">
    <property name="driver" value="${jdbc.driver}"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
    </bean>

三、自定义类型转换器

类型转换器

作用:Spring通过类型转换器把配置文件中字符串类型的数据,转换成了对象中成员变量对应类型的数据,进而完成了注入。

自定义类型转换器

产生原因:当Spring内部没有提供特定类型转化器时,而程序员在应用过程中还需要使用,那么就需要我们自己定义类型转换器

  1. 类 implements Converter接口
    public class MyDateConverter implements Converter<String,Date> {
    /**
     * 将字符串类型转换成 日期类型
     *      String -> Date
     *          SimpleDateFormat sdf = new SimpleDateFormat()
     *          sdf.parse(String) -> Date
     * @param source 代表的是配置文件中 日期字符串 <value>2020-10-22</value>
     * @return 当把转换好的 Date作为convert方法的返回值后,Spring自动为birthday属性进行自动注入
     */
    @Override
    public Date convert(String source) {
        Date date = null;
        try {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            date = sdf.parse(source);
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }
        return date;
    }
    }
  2. 在Spring的配置文件中进行配置

    • MyDateConverter 对象创建出来

      <!--Spring 创建MyDateConverter 类的对象-->
      <bean id="myDateConverter" class="xxx.MyDateConverter">
    • 类型转换器的注册
      目的:告诉Spring框架,我们所创建的MyDateConverter是一个类型转换器

      <!--通过ConversionServiceFactoryBean类 用于注册类型转换器-->
      <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
      <property name="converters">
      <set>
          <ref bean="myDateConverter"/>
      </set>
      </property>
      </bean>

细节:

  1. MyDateFormat中的日期格式,通过依赖注入的方式,由配置文件完成赋值。
    定义成员变量private String pattern; 提供get set方法。在Spring配置文件中的MyDateFormat类中进行属性的配置
  2. 在创建ConversionServiceFactoryBean的对象时,id值是固定的,不能改变,一定是 conversionService
  3. Spring框架内置了日期类型的转换器
    日期格式:2020/10/22(支持的格式)

四、后置处理Bean

BeanPostProcessor作用:对Spring工厂所创建的对象,进行再加工(AOP底层大量使用)

后置处理bean的原理分析

实现BeanPostProcessor规定接口中的方法:

  • Object postProcessorBeforeInitiallization(Object bean,String beanName)
    作用:Spring创建完对象,注入后,初始化前,可以运行Before方法进行加工

  • Object postProcessorAfterInitiallization(Object bean,String beanName)
    作用:Spring对象的初始化操作后,可以运行After方法进行加工

实战中,很少做初始化的操作,很少处理Spring的初始化操作,没有必要区分Befor After。只需要实现其中的一个After方法即可

注意:
postProcessorBeforeInitallization
return bean对象

BeanPostProcessor的开发步骤

  1. 类 实现BeanPostProcessor接口
    public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        Category category = (Category) bean;
        category.setName("lisi");
        return bean;
    }
    }
  2. Spring的配置文件中进行配置
    <bean id="myBeanPostProcessor" class="xxx.MyBeanPostProcessor"/>
  3. BeanPostProcessor细节
    BeanPostProcessor会对Spring工厂中所有创建的对象进行加工
    使用 instanceof 来判断 bean 的类型,对bean 进行加工,否则会报ClassCastException
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    System.out.println("postProcessAfterInitialization()... bean = "+bean.getClass());
    //如果 bean 传入的 Category类型强转
    if (bean instanceof Category) {
        Category category = (Category) bean;
        category.setName("lisi");
    }
    //如果 bean 传入的 Person类型强转
    if (bean instanceof Person) {
        Person person = (Person) bean;
        person.setName("lisi");
    }
    return bean;
    }

    可以在实现BeanPostProcessor接口后 不做加工,但是必须要做判断,因为BeanPostProcessor 会扫描Spring工厂(Spring配置文件)中所有对象

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注