spring--第二部分:Spring IoC高级应用与源码分析(一)

Spring IoC基础

   

BeanFactory与ApplicationContext区别

1.BeanFactory是Spring框架中IoC容器的顶层接口,它只是用来定义一些基础功能,定义一些基础规范
ApplicationContext是它的⼀个⼦接⼝,所以ApplicationContext是具备BeanFactory提供的全部功能
的。
 
2.通常,我们称BeanFactory为SpringIOC的基础容器,ApplicationContext是容器的高级接口,⽐
BeanFactory要拥有更多的功能,比如说国际化支持和资源访问(xml,java配置类)等等

 启动 IoC 容器的方式

Java环境下启动IoC容器
  1.ClassPathXmlApplicationContext:从类的根路径下加载配置文件(推荐使用)
  2.FileSystemXmlApplicationContext:从磁盘路径上加载配置文件
  3.AnnotationConfifigApplicationContext:纯注解模式下启动Spring容器
 
 
Web环境下启动IoC容器
  1.从xml启动容器
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<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</listenerclass>
 </listener>
</web-app>
  2.从配置类启动容器
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<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.AnnotationConfigWebAppli
cationContext</param-value>
 </context-param> 
 <!--配置启动类的全限定类名-->
 <context-param>
 <param-name>contextConfigLocation</param-name>
 <param-value>com.lagou.edu.SpringConfig</param-value>
 </context-param>
 <!--使⽤监听器启动Spring的IOC容器-->
 <listener>
 <listenerclass>org.springframework.web.context.ContextLoaderListener</listenerclass>
 </listener>
</web-app>

纯xml模式

  1)xml文件头(固定约束)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://www.springframework.org/schema/beans
 https://www.springframework.org/schema/beans/spring-beans.xsd">

  2)pom文件引入 spring-ioc,spirng-web ,spring-aop依赖

<!-- 使用Spirng方式启动 -->

    <!--引入Spring IoC容器功能-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.1.12.RELEASE</version>
    </dependency>

    <!--引入spring web功能-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-web</artifactId>
        <version>5.1.12.RELEASE</version>
    </dependency>


      <!--纯xml配置spring声明式事务-->
      <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-aop</artifactId>
          <version>5.1.12.RELEASE</version>
      </dependency>
      <dependency>
          <groupId>org.aspectj</groupId>
          <artifactId>aspectjweaver</artifactId>
          <version>1.8.13</version>
      </dependency>

      <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-jdbc</artifactId>
          <version>5.1.12.RELEASE</version>
      </dependency>
      <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-tx</artifactId>
          <version>5.1.12.RELEASE</version>
      </dependency>

  3.1)通过读取classpath下的xml文件来启动容器(xml模式SE应用下推荐)

   3.2)通过监听器启动容器(xml模式javaWeb应用)

  4)删除BeanForty

 // 首先从BeanFactory获取到proxyFactory代理工厂的实例化对象
    private ProxyFactory proxyFactory = (ProxyFactory) BeanFactory.getBean("proxyFactory");
    private TransferService transferService = (TransferService) proxyFactory.getJdkProxy(BeanFactory.getBean("transferService")) ;

  在servlet中 ,存在手写 工厂bean,我们可以删除工厂bean,而从 web容器中获取 bean 对象,不需要在从自定义的beanForty中获取,所以删除Beanforty

  那么,如果用Spring的Xml模式的方式,从 web容器中获取 bean 对象

  private TransferService transferService = null;
    @Override
    public void init() throws ServletException {
//        WebApplicationContext webApplicationContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
//        transferService = (TransferService) webApplicationContext.getBean("transferService");


        WebApplicationContext webApplicationContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
        ProxyFactory proxyFactory = (ProxyFactory)webApplicationContext.getBean("proxyFactory");
        transferService = (TransferService) proxyFactory.getJdkProxy(webApplicationContext.getBean("transferService")) ;
    }

其中一些细节部分:实例化Bean的三种⽅式、Bean生命周期、DI 依赖注⼊的xml配置

  1、实例化Bean的三种⽅式:
   <!--Spring ioc 实例化Bean的三种方式-->
    <!--方式一:使用无参构造器(推荐)-->
    <bean id="connectionUtils" class="com.lagou.edu.utils.ConnectionUtils"></bean>

    <!--另外两种方式是为了我们自己new的对象加入到SpringIOC容器管理-->
    <!--方式二:静态方法-->
    <bean id="connectionUtils" class="com.lagou.edu.factory.CreateBeanFactory" factory-method="getInstanceStatic"/>
    
    <!--方式三:实例化方法-->
    <bean id="createBeanFactory" class="com.lagou.edu.factory.CreateBeanFactory"></bean>
    <bean id="connectionUtils" factory-bean="createBeanFactory" factory-method="getInstance"/>
      
  ⽅式⼀:使⽤⽆参构造函数
  在默认情况下,它会通过反射调⽤⽆参构造函数来创建对象。如果类中没有⽆参构造函数,将创建
  失败。     
<!--配置service对象-->
<bean id="userService" class="com.lagou.service.impl.TransferServiceImpl">
</bean>  
 
  ⽅式⼆:使⽤静态⽅法创建
  在实际开发中,我们使⽤的对象有些时候并不是直接通过构造函数就可以创建出来的,它可能在创
  建的过程 中会做很多额外的操作。此时会提供⼀个创建对象的⽅法,恰好这个⽅法是static修饰的
  ⽅法,即是此种情 况。
  例如,我们在做Jdbc操作时,会⽤到java.sql.Connection接⼝的实现类,如果是mysql数据库,那
  么⽤的就 是JDBC4Connection,但是我们不会去写 JDBC4Connection connection = new
  JDBC4Connection() ,因 为我们要注册驱动,还要提供URL和凭证信息,
  ⽤ DriverManager.getConnection ⽅法来获取连接。
  那么在实际开发中,尤其早期的项⽬没有使⽤Spring框架来管理对象的创建,但是在设计时使⽤了
  ⼯⼚模式 解耦,那么当接⼊spring之后,⼯⼚类创建对象就具有和上述例⼦相同特征,即可采⽤
  此种⽅式配置。
<!--另外两种方式是为了我们自己new的对象加入到SpringIOC容器管理-->
    <!--方式二:静态方法-->
    <bean id="connectionUtils" class="com.lagou.edu.factory.CreateBeanFactory" factory-method="getInstanceStatic"/>
public class CreateBeanFactory {

    public static ConnectionUtils getInstanceStatic() {
        return new ConnectionUtils();
    }
}
 
  ⽅式三:使⽤实例化⽅法创建
  此种⽅式和上⾯静态⽅法创建其实类似,区别是⽤于获取对象的⽅法不再是static修饰的了,⽽是
  类中的⼀ 个普通⽅法。此种⽅式⽐静态⽅法创建的使⽤⼏率要⾼⼀些。
  在早期开发的项⽬中,⼯⼚类中的⽅法有可能是静态的,也有可能是⾮静态⽅法,当是⾮静态⽅法
  时,即可 采⽤下⾯的配置⽅式:
 <!--方式三:实例化方法-->
    <bean id="createBeanFactory" class="com.lagou.edu.factory.CreateBeanFactory"></bean>
    <bean id="connectionUtils" factory-bean="createBeanFactory" factory-method="getInstance"/>
public class CreateBeanFactory {

    public ConnectionUtils getInstance() {
        return new ConnectionUtils();
    }
} 
  2、Bean的作用范围及⽣命周期
    
  1、作⽤范围的改变
  在spring框架管理Bean对象的创建时,Bean对象默认都是单例的,但是它⽀持配置的⽅式改
  变作⽤范围。作⽤范围官⽅提供的说明如下图:
 <!--scope:定义bean的作用范围

                singleton:单例,IOC容器中只有一个该类对象,默认为singleton
                prototype:原型(多例),每次使用该类的对象(getBean),都返回给你一个新的对象,Spring只创建对象,不管理对象
    -->
    <bean id="accountDao" class="com.lagou.edu.dao.impl.JdbcTemplateDaoImpl" scope="singleton" init-method="init" destroy-method="destory">

 

   2、生命周期

单例模式:singleton
对象出⽣:当创建容器时,对象就被创建了。
对象活着:只要容器在,对象⼀直活着。
对象死亡:当销毁容器时,对象就被销毁了。
⼀句话总结:单例模式的bean对象⽣命周期与容器相同。
多例模式:prototype
对象出⽣:当使⽤对象时,创建新的对象实例。
对象活着:只要对象在使⽤中,就⼀直活着。
对象死亡:当对象⻓时间不⽤时,被java的垃圾回收器回收了。
⼀句话总结:多例模式的bean对象,spring框架只负责创建,不负责销毁。
 
  3、基本方法
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对象销毁前执⾏。它只
能为scopesingleton时起作⽤。
 
  3、DI 依赖注⼊的xml配置
依赖注⼊分类
  按照注⼊的⽅式分类
    构造函数注⼊:顾名思义,就是利⽤带参构造函数实现对类成员的数据赋值。
    set⽅法注⼊:它是通过类成员的set⽅法实现数据的注⼊。(使⽤最多的)
 
  按照注⼊的数据类型分类
    基本类型和String
    注⼊的数据类型是基本类型或者是字符串类型的数据。
    其他Bean类型
 
  复杂类型(集合类型)
    注⼊的数据类型是Aarry,List,Set,Map,Properties中的⼀种类型。
 
1、构造函数注⼊
 <bean id="accountDao" class="com.lagou.edu.dao.impl.JdbcAccountDaoImpl" scope="singleton" init-method="init" destroy-method="destory">
        <constructor-arg name="connectionUtils" ref="connectionUtils"/>
        <constructor-arg  name="name" value="zhangsan"/>
        <constructor-arg  name="sex" value="1"/>
        <constructor-arg name="money" value="100.3"/</bean>
public class JdbcAccountDaoImpl implements AccountDao {
    private ConnectionUtils connectionUtils;
    private String name;
    private int sex;
    private float money;
    public JdbcAccountDaoImpl(ConnectionUtils connectionUtils, String name, int sex, float money) {
        this.connectionUtils = connectionUtils;
        this.name = name;
        this.sex = sex;
        this.money = money;
    }
}

2.set注入

     <bean id="accountDao" class="com.lagou.edu.dao.impl.JdbcAccountDaoImpl" scope="singleton" init-method="init" destroy-method="destory">
<!-- set注入使用property标签,如果注入的是另外一个bean那么使用ref属性,如果注入的是普通值那么使用的是value属性 -->
        <property name="ConnectionUtils" ref="connectionUtils"/>
        <property name="name" value="zhangsan"/>
        <property name="sex" value="1"/>
        <property name="money" value="100.3"/>
public class JdbcAccountDaoImpl implements AccountDao {

    private ConnectionUtils connectionUtils;
    private String name;
    private int sex;
    private float money; 

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

    public void setSex(int sex) {
        this.sex = sex;
    }

    public void setMoney(float money) {
        this.money = money;
    }
}

3.复杂类型注入

                <!--set注入注入复杂数据类型-->
                <property name="myArray">
                    <array>
                        <value>array1</value>
                        <value>array2</value>
                        <value>array3</value>
                    </array>
                </property>
                <property name="myMap">
                    <map>
                        <entry key="key1" value="value1"/>
                        <entry key="key2" value="value2"/>
                    </map>
                </property>
                <property name="mySet">
                    <set>
                        <value>set1</value>
                        <value>set2</value>
                    </set>
                </property>
                <property name="myProperties">
                    <props>
                        <prop key="prop1">value1</prop>
                        <prop key="prop2">value2</prop>
                    </props>
                </property>

xml与注解相结合模式

1、⾃⼰开发的bean定义使⽤注解

 

 2、维护Bean之间的依赖关系,一般用@Autowired(推荐使⽤)@Resource

@Autowired(推荐使⽤)@Resource之间区别?

@Autowired
@Autowired为Spring提供的注解,需要导⼊包org.springframework.beans.factory.annotation.Autowired。
@Autowired采取的策略为按照类型注⼊
public class TransferServiceImpl {
 @Autowired
 private AccountDao accountDao; }
如上代码所示,这样装配回去spring容器中找到类型为AccountDao的类,然后将其注⼊进来。这
样会产⽣⼀个问题,当⼀个类型有多个bean值的时候,会造成⽆法选择具体注⼊哪⼀个的情况,
这个时候我们需要配合着@Qualififier使⽤。
@Qualififier告诉Spring具体去装配哪个对象
public class TransferServiceImpl {
 @Autowired
 @Qualifier(name="jdbcAccountDaoImpl") 
 private AccountDao accountDao; }
这个时候我们就可以通过类型和名称定位到我们想注⼊的对象。
---------------------------------------------------------------------------------------------------
@Resource
@Resource 注解由 J2EE 提供,需要导⼊包 javax.annotation.Resource。
@Resource 默认按照 ByName ⾃动注⼊
public class TransferService {
 @Resource 
 private AccountDao accountDao;
 @Resource(name="studentDao") 
 private StudentDao studentDao;
 @Resource(type="TeacherDao") 
 private TeacherDao teacherDao;
 @Resource(name="manDao",type="ManDao") 
 private ManDao manDao;
}
如果同时指定了 name 和 type,则从Spring上下⽂中找到唯⼀匹配的bean进⾏装配,找不
到则抛出异常。
如果指定了 name,则从上下⽂中查找名称(id)匹配的bean进⾏装配,找不到则抛出异
常。
如果指定了 type,则从上下⽂中找到类似匹配的唯⼀bean进⾏装配,找不到或是找到多个,
都会抛出异常。
如果既没有指定name,⼜没有指定type,则⾃动按照byName⽅式进⾏装配;
 
注意:
@Resource 在 Jdk 11中已经移除,如果要使⽤,需要单独引⼊jar包
<dependency>
 <groupId>javax.annotation</groupId>
 <artifactId>javax.annotation-api</artifactId>
 <version>1.3.2</version>
</dependency>

特别注意:

   1、开启注解扫描,base-package指定扫描的包路径

   2、引入外部资源文件

<beans  xmlns="http://www.springframework.org/schema/beans"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:aop="http://www.springframework.org/schema/aop"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd
">
    <!--第三方jar中的bean定义在xml中-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <!--开启注解扫描,base-package指定扫描的包路径-->
    <context:component-scan base-package="com.lagou.edu"/>

    <!--引入外部资源文件-->
    <context:property-placeholder location="classpath:jdbc.properties"/>

</beans>

纯注解模式

改造xm+注解模式,将xml中遗留的内容全部以注解的形式迁移出去,最终删除xml,从Java配置类启动
对应注解
  @Confifiguration 注解,表名当前类是⼀个配置类
  @ComponentScan 注解,替代 context:component-scan
  @PropertySource,引⼊外部属性配置⽂件
  @Import 引⼊其他配置类
  @Value 对变量赋值,可以直接赋值,也可以使⽤ ${} 读取资源配置⽂件中的信息
  @Bean 将⽅法返回对象加⼊ SpringIOC 容器
 
基于当前项目,进行改造
  1、创建项目启动类SpringConfig
@Configuration  /*@Configuration 注解,表名当前类是⼀个配置类*/
@ComponentScan({"com.lagou.edu"})  /* context:component-scan*/
@PropertySource({"classpath:jdbc.properties"})  /*引⼊外部属性配置⽂件*/
public class SpringConfig {
    /*将配置文件中值加入容器*/
    @Value("${jdbc.driver}")
    private String driverClassName;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;

    @Bean   /*注解方式将第三方对象加入容器*/
    public DataSource creatDataSource(){
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setDriverClassName(driverClassName);
        druidDataSource.setUrl(url);
        druidDataSource.setUsername(username);
        druidDataSource.setPassword(password);
        return  druidDataSource;
    }


}

  2、配置web.xml文件

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<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.lagou.edu.SpringConfig</param-value>
    </context-param>
    <!--使用监听器启动Spring的IOC容器-->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

</web-app>

  3、测试

Spring IOC高级特性

lazy-Init 延迟加载

Bean的延迟加载(延迟创建)
ApplicationContext 容器的默认⾏为是在启动服务器时将所有 singleton bean 提前进⾏实例化。提前
实例化意味着作为初始化过程的⼀部分,ApplicationContext 实例会创建并配置所有的singleton
bean。
<bean id="lazyResult" class="com.lagou.edu.pojo.Result"/>
public class Result {

    private String status;
    private String message;
}

测试:

<bean id="lazyResult" class="com.lagou.edu.pojo.Result" lazy-init="true"/> 设置懒加载

如果不想让⼀个singleton bean 在 ApplicationContext实现初始化时被提前实例化,那么可以将bean
设置为延迟实例化。
 
如果⼀个设置了⽴即加载的 bean1,引⽤了⼀个延迟加载的 bean2 ,那么 bean1 在容器启动时被实例
化,⽽ bean2 由于被 bean1 引⽤,所以也被实例化,这种情况也符合延时加载的 bean 在第⼀次调⽤
时才被实例化的规则
 
也可以在容器层次中通过在 元素上使⽤ "default-lazy-init" 属性来控制延时初始化。如下⾯配置:
<beans default-lazy-init="true">
 <!-- no beans will be eagerly pre-instantiated... -->
</beans>
如果⼀个 bean 的 scope 属性为 scope="pototype" 时,即使设置了 lazy-init="false",容器启动时也不
会实例化bean,⽽是调⽤ getBean ⽅法实例化的。

FactoryBean 和 BeanFactory

BeanFactory接⼝是容器的顶级接⼝,定义了容器的⼀些基础⾏为,负责⽣产和管理Bean的⼀个⼯⼚,
具体使⽤它下⾯的⼦接⼝类型,⽐如ApplicationContext;此处我们重点分析FactoryBean
 
Spring中Bean有两种,⼀种是普通Bean,⼀种是⼯⼚Bean(FactoryBean),FactoryBean可以⽣成
某⼀个类型的Bean实例(返回给我们),也就是说我们可以借助于它⾃定义Bean的创建过程。
 
Bean创建的三种⽅式中的静态⽅法和实例化⽅法和FactoryBean作⽤类似,FactoryBean使⽤较多,尤
其在Spring框架⼀些组件中会使⽤,还有其他框架和Spring框架整合时使⽤
// 可以让我们⾃定义Bean的创建过程(完成复杂Bean的定义)
public interface FactoryBean<T> {
 @Nullable
 // 返回FactoryBean创建的Bean实例,如果isSingleton返回true,则该实例会放到Spring容器
的单例对象缓存池中Map
 T getObject() throws Exception;
 @Nullable
 // 返回FactoryBean创建的Bean类型
 Class<?> getObjectType();
 // 返回作⽤域是否单例
 default boolean isSingleton() {
 return true;
 } }

实例:

  1、company 公司类

public class Company {
    private String name;
    private String address;
    private int scale;
}

  2、CompanyFactoryBean   公司工厂类 用于创建工厂

public class CompanyFactoryBean implements FactoryBean<Company> {

    private String companyInfo; // 公司名称,地址,规模

    public void setCompanyInfo(String companyInfo) {
        this.companyInfo = companyInfo;
    }


    @Override
    public Company getObject() throws Exception {
        // 模拟创建复杂对象Company
        Company company = new Company();
        String[] strings = companyInfo.split(",");
        company.setName(strings[0]);
        company.setAddress(strings[1]);
        company.setScale(Integer.parseInt(strings[2]));
        return company;
    }

    @Override
    public Class<?> getObjectType() {
        return Company.class;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }
}

  3、beans.xml  添加bean信息

 <bean id="companyBean" class="com.lagou.edu.factory.CompanyFactoryBean">
        <property name="companyInfo" value="拉勾,中关村,500"/>
    </bean>

  4、测试

后置处理器

Spring提供了两种后处理bean的扩展接⼝,分别为 BeanPostProcessor 和
BeanFactoryPostProcessor,两者在使⽤上是有所区别的。
 
⼯⼚初始化(BeanFactory)—> Bean对象
 
在BeanFactory初始化之后可以使⽤BeanFactoryPostProcessor进⾏后置处理做⼀些事情
 
在Bean对象实例化(并不是Bean的整个⽣命周期完成)之后可以使⽤BeanPostProcessor进⾏后置处理做⼀些事情
 
注意:对象不⼀定是springbean,⽽springbean⼀定是个对象

SpringBean的⽣命周期

  

BeanPostProcessor

BeanPostProcessor是针对Bean级别的处理,可以针对某个具体的Bean
该接⼝提供了两个⽅法,分别在Bean的初始化⽅法前初始化⽅法后执⾏
具体这个初始化⽅法指的是什么⽅法,类似我们在定义bean时,定义了init-method所指定的⽅法
 
定义⼀个类实现了BeanPostProcessor接口,默认是会对整个Spring容器中所有的bean进⾏处理。如果要对
具体的某个bean处理,可以通过⽅法参数判断,两个类型参数分别为Object和String,
第⼀个参数是每个bean的实例,
第⼆个参数是每个bean的name或者id属性的值。
所以我们可以通过第⼆个参数,来判断我们将要处理的具体的bean。
 
注意:处理是发⽣在Spring容器的实例化和依赖注⼊之后

 实例:

  1、配置bean,  init-method是指向初始化方法

<bean id="lazyResult" class="com.lagou.edu.pojo.Result" init-method="initMethod" lazy-init="true"/>

  2、Result实现类

public class Result implements BeanNameAware, BeanFactoryAware, ApplicationContextAware, InitializingBean, DisposableBean {

    private String status;
    private String message;

    public String getStatus() {
        return status;
    }

    public void setStatus(String status) {
        this.status = status;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    @Override
    public String toString() {
        return "Result{" +
                "status='" + status + '\'' +
                ", message='" + message + '\'' +
                '}';
    }

    /**
     * bean 实现了 BeanNameAware ,则sprig调用Bean的setBeanName()方法,传入当前的Bean的Id值
    */
    @Override
    public void setBeanName(String name) {
        System.out.println("注册我成为bean时定义的id:" + name);
    }

    /**
     * bean 实现了 BeanFactoryAware ,则sprig调用setBeanFactory()方法,传入当前的工厂的实例的引用
     */
    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        System.out.println("管理我的beanfactory为:" + beanFactory);
    }
    /**
     * bean 实现了 ApplicationContextAware ,则sprig调用setApplicationContext()方法,传入当前ApplicationContext的实例的引用
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println("高级容器接口ApplicationContext:" + applicationContext);
    }
    /**
     * bean 实现了 InitializingBean ,则sprig调用afterPropertiesSet()方法,传入当前ApplicationContext的实例的引用
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("afterPropertiesSet......");
    }
    

    /**postCoustrcut 和 initMethod 都定制的初始化方法,postCoustrcut是注解的方式,initMethod是XML配置方式
     * 通常我们会是在Spring框架中使用到@PostConstruct注解 该注解的方法在整个Bean初始化中的执行顺序:
     * Constructor(构造方法) -> @Autowired(依赖注入) -> @PostConstruct(注释的方法)
     * 所以 postCoustrcut是注解的方式 会在 initMethod是XML配置方式 之前实例化
     */
    @PostConstruct
    public void postCoustrcut() {
        System.out.println("postCoustrcut");
    }
    
    /**
     * 配置文件中通过 Init-method 调用实例化方法
     */
    public void initMethod() {
        System.out.println("init-method....");
    }

    /**
     * 注解方式销毁对象
     */
    @PreDestroy
    public void PreDestroy(){
        System.out.println("PreDestroy...");
    }

    /**
     * bean 实现了 DisposableBean ,则sprig调用destroy方法,进行Bean销毁
     */
    @Override
    public void destroy() throws Exception {
        System.out.println("destroy.....");
    }
}

  3、拦截实例化之后的对象(实例化了并且属性注入了)

/**
 *
 * 拦截实例化之后的对象(实例化了并且属性注入了)
 */
@Component //加入扫描
public class MyBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if("lazyResult".equalsIgnoreCase(beanName)) {
            System.out.println("MyBeanPostProcessor  before方法拦截处理lazyResult");
        }

        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if("lazyResult".equalsIgnoreCase(beanName)) {
            System.out.println("MyBeanPostProcessor  after方法拦截处理lazyResult");
        }
        return bean;
    }
}

  4、测试

BeanFactoryPostProcessor

BeanFactory级别的处理,是针对整个Bean的⼯⼚进⾏处理,典型应
⽤:PropertyPlaceholderConfifigurer
此接⼝只提供了⼀个⽅法,⽅法参数为ConfifigurableListableBeanFactory,该参数类型定义了⼀些⽅法
其中有个⽅法名为getBeanDefifinition的⽅法,我们可以根据此⽅法,找到我们定义bean 的
BeanDefifinition对象。然后我们可以对定义的属性进⾏修改,以下是BeanDefifinition中的⽅法

 

⽅法名字类似我们bean标签的属性,setBeanClassName对应bean标签中的class属性,所以当我们拿
到BeanDefifinition对象时,我们可以⼿动修改bean标签中所定义的属性值。
 
BeanDefifinition对象:我们在 XML 中定义的 bean标签,Spring 解析 bean 标签成为⼀个 JavaBean,
这个JavaBean 就是 BeanDefifinition
 
注意:调⽤ BeanFactoryPostProcessor ⽅法时,这时候bean还没有实例化,此时 bean 刚被解析成
BeanDefifinition对象

 

 
 

 

 

posted @ 2021-03-08 21:33  trueAndFalse  阅读(96)  评论(0编辑  收藏  举报