天天看点

Spring框架:bean的作用域,bean的生命周期,引用外部属性文件,自动装配(自动注入)

bean的作用域

spring中可以在< bean>元素的scope属性里面设置bean的作用域,以决定这个bean是单例的还是多例的,scope有四种选择

singleton 默认的,在容器中就仅存的一个bean实例,在初始化容器的时候就会直接创建这个bean管理的类所对应的对象,下次再调用的时候就直接返回这个bean的单例对象
prototype 多例的,初始化容器的时候不会直接创建这个bean的对象,而是在调用的时候才反射创建,而且是调用一次反射创建一次
request 每一次http请求的时候都会创建一个新的bean,这个作用域仅适合于WebApplicationContext环境
session 同一个session共享一个bean,不同的http session使用不同的bean,这个作用域仅适合于WebApplicationContext环境

例如​

​<bean id="Myfactory" class="factorybean.Myfactory" scope="singleton"></bean>​

bean的生命周期

spring IOC容器对bean的生命周期进行管理的五个过程:

  1. 创建bean实例,通过构造器或者工厂的方法
  2. 依赖注入:给bean的属性设置值和对其他bean的引用
  3. 初始化:调用bean的初始化方法
  4. 使用bean:使用
  5. 销毁:当关闭容器的时候调用bean的销毁的方法

    但是bean的初始化的方法和销毁的方法没有,想设置的时候就在配置bean的时候通过bean标签的属性init-method和destroy-method属性为bean指定初始化和销毁的方法

//Car类
package factorybean;
public class Car
{
  private String brand;
  private Double price;
  public String getBrand()
  {
    return brand;
  }
  public void setBrand(String brand)
  {
    System.out.println("2:依赖注入");
    this.brand = brand;
  }
  public Double getPrice()
  {
    return price;
  }
  public void setPrice(Double price)
  {
    this.price = price;
  }
  @Override
  public String toString()
  {
    return "4:使用:Car [brand=" + brand + ", price=" + price + "]";
  }
  public Car()
  {
    System.out.println("1:创建对象");
  }
  public void init()
  {
    System.out.println("3:初始化");
  }
  public void destroy()
  {
    System.out.println("5:销毁");
  }
}
//配置文档
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd">

  <bean id="car" class="factorybean.Car" scope="singleton" init-method="init" destroy-method="destroy">
    <property name="brand" value="法拉利"></property>
  </bean>
</beans>
//测试类
package factorybean;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test
{
  public static void main(String[] args)
  {
    ClassPathXmlApplicationContext ac=new ClassPathXmlApplicationContext("factory-bean.xml");
    Object bean = ac.getBean("car");
    System.out.println(bean);
    ac.close();
  }
}
//结果
1:创建对象
2:依赖注入
3:初始化
4:使用:Car [brand=法拉利, price=null]
3月 30, 2020 10:11:36 下午 org.springframework.context.support.ClassPathXmlApplicationContext doClose
信息: Closing org.springframework.context.support.ClassPathXmlApplicationContext@3fb4f649: startup date [Mon Mar 30 22:11:36 CST 2020]; root of context hierarchy
5:销毁      

生命周期的后置处理器

主要是程序员觉得bean只有五个周期,有点少,所以就在bean里面增加两个周期

后置处理器就是bean初始化方法的前后对bean进行额外的处理,

而且后置处理器是IOC容器的所有bean实例都进行处理,而不是某一个bean

最典型的应用就是检查bean属性的正确性或者根据特定的标准更改bean属性

如何使用:

bean后置处理器需要实现接口使用,实现这个接口​​

​org.springframework.beans.factory.config.BeanPostProcessor​

​,在初始化方法被调用前后,spring分别将每一个bean实例传递给里面的两个抽象方法进行整理,代码如下:

//后置处理器的类
package factorybean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class AfterHandler implements BeanPostProcessor
{
  @Override
  public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException
  {
    System.out.println("初始化前:这里对bean进行了一大堆处理");
    return bean;
  }
  @Override
  public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException
  {
    System.out.println("初始化后:这里对bean进行了一大堆处理");
    return bean;
  }
}
//将这个后置处理器给容器管理,在配置文件里面配置好
<bean class="factorybean.AfterHandler"></bean>
//执行的结果
1:创建对象
2:依赖注入
初始化前:这里对bean进行了一大堆处理
3:初始化
初始化后:这里对bean进行了一大堆处理
4:使用:Car [brand=法拉利, price=null]
3月 30, 2020 10:48:25 下午 org.springframework.context.support.ClassPathXmlApplicationContext doClose
信息: Closing org.springframework.context.support.ClassPathXmlApplicationContext@3fb4f649: startup date [Mon Mar 30 22:48:25 CST 2020]; root of context hierarchy
5:销毁      

引用外部属性文件

当bean的配置信息越来越多的时候,查找和修改一些bean的配置信息就会变得困难很多,这时就可以像数据库连接的资源文件一样,将一部分信息提取到bean的配置文件的外部,以properties的格式的文件属性保存起来,同时在bean的配置文件中加载这个资源文件properties,就可以使用提取出来的信息,下面就是一个例子:

配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:context="http://www.springframework.org/schema/context"
  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">

  <!-- 这个加载德鲁伊连接池的方法是直接在里面写死了加载的信息,
     我们之前一般都是会将加载的信息写在一个资源文件里面
  <bean id="datasource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>
    <property name="url" value="jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8"></property>
    <property name="username" value="root"></property>
    <property name="password" value="1234"></property>
  </bean>
   -->
   
   <!--这个是其中的一种方法加载资源文件
  <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="location" value="Druid.properties"></property>
  </bean>
  -->
  
  <!-- 使用context命名空间进行加载资源文件,这个标签就是专门用来加载资源文件的 -->
  <context:property-placeholder location="Druid.properties" />
  
  <!-- 这里的${}就是一个格式写法,就是在已经读取到的资源文件里面,
  使用key的名称找到对应的值,花括号里面就是填写key的值 -->
  <bean id="datasource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="${jdbc.driver}"></property>
    <property name="url" value="${jdbc.url}"></property>
    <property name="username" value="${jdbc.username}"></property>
    <property name="password" value="${jdbc.password}"></property>
  </bean>
</beans>      

资源文件Druid.properties

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8
jdbc.username=root
jdbc.password=1234      
package datasource;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.alibaba.druid.pool.DruidDataSource;

public class Test
{
  public static void main(String[] args) throws Exception
  {
    ClassPathXmlApplicationContext ac=new ClassPathXmlApplicationContext("datasource.xml");
    DruidDataSource source = ac.getBean("datasource",DruidDataSource.class);
    System.out.println(source.getConnection());
    ac.close();
  }
}      

结果

4月 01, 2020 4:52:53 下午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@4d591d15: startup date [Wed Apr 01 16:52:53 CST 2020]; root of context hierarchy
4月 01, 2020 4:52:53 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [datasource.xml]
4月 01, 2020 4:52:53 下午 org.springframework.context.support.PropertySourcesPlaceholderConfigurer loadProperties
信息: Loading properties file from class path resource [Druid.properties]
4月 01, 2020 4:52:54 下午 com.alibaba.druid.pool.DruidDataSource info
信息: {dataSource-1} inited
Wed Apr 01 16:52:54 CST 2020 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
com.mysql.cj.jdbc.ConnectionImpl@76494737
4月 01, 2020 4:52:55 下午 org.springframework.context.support.ClassPathXmlApplicationContext doClose
信息: Closing org.springframework.context.support.ClassPathXmlApplicationContext@4d591d15: startup date [Wed Apr 01 16:52:53 CST 2020]; root of context hierarchy
4月 01, 2020 4:52:55 下午 com.alibaba.druid.pool.DruidDataSource info
信息: {dataSource-1}      

自动装配(自动注入)

之前我们都是通过手动将spring管理的类进行注入,就是通过value或者ref的方式进行明确指定属性的值

现在的自动装配就是根据注入的规则,不需要明确指定,spring自动将属性的值注入到bean中,但是需要确认一点,就是自动装配是相对于引用类型来说的,就是不是字面量,字面量没有自动装配

使用方法:

就是在bean的属性里面的autowire进行设置,比如通过属性名字进行装配:

<bean id="s1" class="jane.Student" autowire="byName">
    <property name="tid" value="1"></property>
    <property name="tname" value="st1"></property>
  </bean>      
  1. 根据类型装配,autowire的值就是byTtpe,在这个bean里面的非字面量的数据类型,如果在spring容器里面找到相对应的类型的bean对象,就使用容器里面的bean对象自动给这个非字面量的数据类型进行赋值

    这个方法需要注意的是:

    ​​

    ​在spring容器如果找到多个这个类型的bean,就会报错,所以得确保要自动装配的数据类型得bean在spring里面是唯一的​

    ​​

    ​根据类型自动装配是兼容性的,就是如果要赋值的属性是一个接口的,那么spring在里面找到这个接口的实现类的bean,那么也会给这个接口的属性赋值,就是通过子类创建父类的对象是同一个道理​

  2. 根据名称自动装配,autowire的值是byname,这个就是在spring里面找和这个要自动装配的属性的名称一样的bean的id,id和属性的名称一样,就进行自动装配