Spring5 IOC容器

目录

1、IOC的概念和原理

2、 IOC两个接口

3、IOC操作Bean管理

4、工厂Bean(FactoryBean)

5、Bean作用域(对象是单例 or 多例) 和 生命周期

6、引入bean的外部属性文件(比如数据库线程池的配置dataSource的properties文件)


1、IOC的概念和原理

1.1 什么是IOC

(1)IOC是一个容器,控制反转,把对象创建和对象之间的调用过程,交给Spring进行管理
(2)使用IOC目的:为了耦合度降低
(3)上一节的入门案例就是IOC实现

1.2 IOC底层原理

xml解析、工厂模式、反射

解释说明:

    我们想在一个类中调用另一个类对象的方法,原始模式是先在一个类中造好另一个类的对象,然后用那个对象去调用其方法

     这种方式我们在修改代码的时候,一个类路径变了,另一个都得跟着变。一个方法名变了,另一个也得跟着变。非常不方便。为了降低耦合度,引入第二种方式,工厂模式。

        这种方式,通过引入第三方类UserFactory工厂,降低了UserService和UserDao的耦合度,但是又出现了UserService和工厂的耦合,还需要进一步解耦。

IOC底层原理图

2、 IOC两个接口

(1)IOC思想基于IOC容器完成,IOC容器底层就是对象工厂

(2)Spring提供IOC容器实现两种方式:(两个接口)

① BeanFactory:IOC容器基本实现,是Spring内部的使用接口,不提供开发人员进行使用
说明:* 加载配置文件时候不会创建对象,在获取对象(使用)才去创建对象

② ApplicationContext:BeanFactory接口的子接口,提供更多更强大的功能,一般由开发人员进行使用
说明:* 加载配置文件时候就会把在配置文件对象进行创建

总结:

①  这两种方法都可以加载配置文件,并创建配置文件中类的对象

②  第一种方法看似节约内存,用的时候再创建。但是这个是web应用,把耗时费力的东西都在Tomcat启动时一起做了,因此启动时造好对象,效率最高

(3)ApplicationContext接口有实现类

3、IOC操作Bean管理

3.1 什么是Bean管理

    Bean管理指的是两个操作

(1)Spring创建对象

(2)Spring注入属性

3.2 Bean管理操作有两种方式

(1)基于xml配置文件方式实现

(2)基于注解方式实现

3.3 IOC操作Bean管理(基于xml方式)

(1) 基于xml方式创建对象

说明:

① 在spring配置文件中,使用bean标签,标签里面添加对应属性,就可以实现对象创建
② 在bean标签有很多属性,介绍常用的属性
* id属性:唯一标识(就是给对象取个别名),后面ApplicationContext对象就通过这个id来识别这个对象
* class属性:类全路径(包类路径)
③ 创建对象时候,默认也是执行无参数构造方法完成对象创建(如果类没有无参构造器,就报错)

3.4  基于xml方式注入属性

     DI:依赖注入,就是注入属性

3.4.1 第一种注入方式:使用set方法进行注入

Step1:创建类,定义属性和对应的set方法

public class Book { 
  //创建属性 
  private String bname; 
  private String bauthor; 
  private String address;
  
  //创建属性对应的set方法 
  public void setBname(String bname) { 
    this.bname = bname; 
  } 
  public void setBauthor(String bauthor) { 
    this.bauthor = bauthor; 
  } 
  public void setAddress(String address){
    this.address=address;
  }

  public void testDemo{
    System.out.println(bname+"::"+bauthor+"::"+address);
  }
 
}

Step2: 在spring配置文件配置对象创建,配置属性注入

<?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"> 

 <!--2 set方法注入属性--> 
  <bean id="book" class="com.atguigu.spring5.Book"> 
    <!--使用property完成属性注入 
        name:类里面属性名称 
        value:向属性注入的值 --> 
    <property name="bname" value="易筋经"></property> 
    <property name="bauthor" value="达摩老祖"></property> 
  </bean>
</beans>

    创建好对象以后,用property标签,给类的属性赋值

property标签中的属性

① name:根据属性名寻找类的属性

② value:将要赋予的属性值   (思考:如果value中有特殊字符怎么办)

特别注意:null值和带有特殊符号的属性注入

    一旦属性值含有特殊符号,比如属性值为书名号的<<南京>>,如果还用value属性赋值的话,由于<>是xml语言中的标签,会导致报错。这种情况,需要特殊处理。

(1)null属性赋值

在property标签中再嵌入一个null标签

<!--null值-->
<property name="address"> 
  <null/> 
</property>

(2)特殊符号赋值

方法一:对书名号的<<>>等标记进行转义,&lt,和&gt

方法二:将property的value属性改成property下的value标签。然后使用![CDATA[属性名]] ,此时属性名就可以随便写啦

<!--属性值包含特殊符号 
    1 把<>进行转义 &lt; &gt; 
    2 把带特殊符号内容写到CDATA --> 
<property name="address"> 
    <value><![CDATA[<<南京>>]]></value> 
</property>

此时,属性值即为 <<南京>>

Step3:编写测试方法,看下set注入是否成功

public class TestSpring5{
  @Test
  public void testBook1(){
    //1、加载spring配置文件
    ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
    //2、获取配置对象的信息
    Book book = context.getBean("book",Book.class); //book 就是配置文件中的id
  
    System.out.println(book);
    book.testDemo();  //测试testDemo方法,看属性注入是否成功
  }
}

 

扩展: p名称空间注入(改进set注入属性方法)

     如果遇到多个属性,就会写很多property标签,很多很麻烦。为了进一步简化程序员的工作量,使代码更简洁,就有了p名称空间注入。

     p名称空间注入,可以直接在bean标签内部,以属性的方式给对象赋值

Step1: 在beans标签中添加 属性xmlns:p="http://www.springframework.org/schema/p"  (xmlns指名称空间)

Step2:用p:属性名  给类中的属性赋值 

<?xml version="1.0" encoding="UTF-8"?> 
 <!--1 加入p命名空间--> 
<beans xmlns="http://www.springframework.org/schema/beans" 
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> 

 <!--2 set方法注入属性--> 
<!--用p:属性值 给类的属性赋值-->
  <bean id="book" class="com.atguigu.spring5.Book" p:bname="易筋经" p:bauthor="达摩老祖"> 
  </bean>
</beans>

 

3.4.2 使用有参构造器进行属性注入

Step1 编写一个测试类,比如Orders类

 //使用有参构造器注入属性 
public class Orders { 
  //创建属性
  private String oname; 
  private String address; 
  
  //有参构造器
  public Orders(String oname,String address) { 
    this.oname = oname; 
    this.address = address;
  } 

  public void ordersTest{
    System.out.println(oname+"::"+address);
  }
}

Step2 在spring配置文件中进行配置

<?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"> 

 <!--2 使用有参构造器方法注入属性--> 
  <bean id="orders" class="com.atguigu.spring5.Orders"> 
    <constructor-arg name="oname" value="电脑"></constructor-arg> 
    <constructor-arg index="1" value="China"></constructor-arg> 
  </bean>
</beans>

说明:

        创建好对象以后,用constructor-arg标签,进行有参构造器配置(类里面一定要写好有参构造器,否则会报错)

constructor-arg标签中的属性

① name(建议使用):根据属性名寻找类的属性

② index(不推荐):根据属性的索引(从0开始),寻找类的属性

③ value:将要赋予的属性值

Step3 编写测试文件,看测试是否通过

public class TestSpring5{
  @Test
  public void testOrders(){
    //1、加载spring配置文件
    ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
    //2、获取配置对象的信息
    Orders orders = context.getBean("orders",Orders.class); //orders 就是配置文件中的id
  
    System.out.println(orders);
    orders.ordersTest();  //测试testDemo方法,看属性注入是否成功
  }
}

3.5 将外部bean作为属性值 注入类的属性(两个兄弟关系的bean,通过ref引入,手动注入

要求:在UserService类中 注入 UserDao类属性

Step1:建立两个包,dao 和 service

(1)service包下,写一个UserService类

package com.atguigu.spring5.service


public class UserService{
  //创建UserDao类型属性
  private UserDao userDao;

  //生成该属性的set方法
  public void setUserDao(UserDao userDao){
    this.userDao = userDao;
  }
  
  public void add(){
    System.out.println("service add........");
    userDao.update();//调用userDao里面的方法
  }
}

(2)dao包下,写一个接口,和一个接口的实现类

写一个接口UserDao

package com.atguigu.spring5.dao;

public interface UserDao{
  public void update();
}

(3)实现类UserDaoImpl中,实现该Userdao接口

pacakage com.atguigu.spring5.dao;

public class UserDaoImpl implements UserDao{
  @Override
  public void update(){
    System.out.println("dao update......");
  }
}

Step2 再建一个bean2.xml来创建两个类的对象。

注意:接口不能创建对象,需要使用接口的实现类来创建对象

重点:注入外部bean 不是使用value属性了(需要属性值是字面量),而是ref属性(因为属性值不是字面量了),ref属性注入相关的外部bean

<!--1 service和dao对象创建--> 
<bean id="userService" class="com.atguigu.spring5.service.UserService"> 
    <!--注入userDao对象 name属性:类里面属性名称 ref属性:创建userDao对象bean标签id值 --> 
    <property name="userDao" ref="userDaoImpl"></property> 
</bean> 
    <!--只能给实现类创建对象,接口不能创建对象 -->
<bean id="userDaoImpl" class="com.atguigu.spring5.dao.UserDaoImpl"></bean>

Step3 测试代码

注意:现在的配置文件是bean2.xml不是原来的bean1了

public class TestBean{
  @Test
  public void testOrders(){
    //1、加载spring配置文件
    ApplicationContext context = new ClassPathXmlApplicationContext("bean2.xml");
    //2、获取配置对象的信息
    UserService userService = context.getBean("userService",UserService.class); //orders 就是配置文件中的id
  
    userService.add();
  }
}

3.4.1 xml给类 自动装配类对象属性 (一般不用xml版了,但需要了解)

    通过bean标签的属性autowire,可自动装配该组件的类对象属性。

autowire属性常用两个值:

① byName:根据属性名称注入 ,注入值bean的id值类属性名称一样 (就是需要把类属性名称作为配置文件的id值)

② byType:根据属性类型注入,但注意如果有多个相同类的对象,会报错(后面有演示)

自动装配演示:

(1)新建一个autowire文件夹,包com.atguigu.spring5.autowire  下面写两个类Emp(员工) 和 dept(部门)(dept作为Emp的属性)

package com.atguigu.spring5.autowire;

public class Emp{
  private Dept dept;
  
  public void setDept(Dept dept){
    this.dept=dept;
  }
  
  @Override
  public String toString(){
    return "Emp{"+"dept="+dept+'}';
  }

  public void test(){
    System.out.println(dept);
  }
}

(2)再建立一个配置文件 bean5.xml

① 先演示手动装配,就是上一章的方法

<bean id="emp" class="com.atguigu.spring5.autowire.Emp"> 
    <property name="dept" ref="dept"></property> 
</bean>
 
<bean id="dept" class="com.atguigu.spring5.autowire.Dept"></bean>

注意:byType的时候,如果有多个相同类的对象,比如

<bean id="dept" class="com.atguigu.spring5.autowire.Dept"></bean>
<bean id="dept1" class="com.atguigu.spring5.autowire.Dept"></bean>

此时,就会报错,因为不知道注入哪个对象给Emp。

② 自动装配配置

<bean id="emp" class="com.atguigu.spring5.autowire.Emp" autowire="byName"> </bean>
 
<bean id="dept" class="com.atguigu.spring5.autowire.Dept"></bean>

此时,即可根据属性名(id值),将该类属性自动装配给emp对象。

(3)测试

public class TestSpringDemo1{
  @Test
  public void test4(){
    //1、加载spring配置文件
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean5.xml");
    //2、获取配置对象的信息
    Emp emp = context.getBean("emp",Emp.class); 
 
    System.out.println(emp);
  }
}

 

3.6 内部bean注入属性(bean内部嵌套bean)

(1)一对多关系:部门和员工

            一个部门有多个员工,一个员工属于一个部门:部门是一,员工是多

(2)在实体类之间表示一对多关系,员工表示所属部门,使用对象类型属性进行表示

要求:一个员工属于某个部门。部门也是一个类。

step1:

(1)新建一个文件夹(包),命名为bean,bean文件下写一个类Dept(部门)

package com.atguigu.spring5.bean;

//创建一个部门类,并生成set方法
public class Dept{
  private String dname;
  public void setDname(){
    this.dname=dname;
  }
  @Override
  public String toString(){
    return "Dept{"+"dname='"+dname+'\''+'}';
  }
}

(2)bean文件下再写一个类 Emp(员工)

package com.atguigu.spring5.bean;

public class Emp{
  private String ename;
  private String gender;
  //员工表示所属部门,使用对象类型属性进行表示
  private Dept dept;
  
  
  public void setDept(Dept dept){
    this.dept=dept;
  }
  public void setEname(String ename){
    this.ename=ename;
  }
  public void setGender(String gender){
    this.gender=gender;
  }

  public void add(){
    System.out.println(ename+"::"+gender+"::"+dept);
  }
}

step2:在spring的配置文件中进行配置

可以使用外部bean的ref方式注入属性(级联赋值),但是内部bean也有自己独有的方法(在bean内部创建对象)

<!--内部bean--> 
<bean id="emp" class="com.atguigu.spring5.bean.Emp"> 
    <!--设置两个普通属性 --> 
    <property name="ename" value="lucy"></property> 
    <property name="gender" value="女"></property> 

    <!--设置对象类型属性,嵌套对象-->
    <property name="dept">
        <bean id="dept" class="com.atguigu.spring5.bean.Dept">
            <property name="dname" value="安保部"></property>
        </bean>
    </property>
</bean> 

说明:实际工作中,ref写法更爽

step3:编写测试类

public class TestBean{
  @Test
  public void testBean2(){
    //1、加载spring配置文件
    ApplicationContext context = new ClassPathXmlApplicationContext("bean3.xml");
    //2、获取配置对象的信息
    Emp emp = context.getBean("emp",Emp.class); 
  
    emp.add();
  }
}

 扩展:级联赋值(这个方法赋值,可以改默认值)

首先在Emp中,要有dept对象的get方法

package com.atguigu.spring5.bean;

public class Emp{
  private String ename;
  private String gender;
  //员工表示所属部门,使用对象类型属性进行表示
  private Dept dept;
  //生成dept的get方法
  public Dept getDept(){
    return dept;
  }
  public void setDept(Dept dept){
    this.dept=dept;
  }
  public void setEname(String ename){
    this.ename=ename;
  }
  public void setGender(String gender){
    this.gender=gender;
  }

  public void add(){
    System.out.println(ename+"::"+gender+"::"+dept);
  }
}

修改配置文件

<!--内部bean--> 
<bean id="emp" class="com.atguigu.spring5.bean.Emp"> 
    <!--设置两个普通属性 --> 
    <property name="ename" value="lucy"></property> 
    <property name="gender" value="女"></property> 

    <!--级联赋值-->
    <property name="dept" ref="dept"></property>
    <property name="dept.dname" value="技术部"></property>
</bean> 
<bean id="dept" class="com.atguigu.spring5.bean.Dept">
    <property name="dname" value="财务部"></property>
</bean>

3.7 xml注入集合属性

3.7.1 注入集合(数组)类型属性

新建一个文件夹(包) com.atguigu.spring5.collectiontype,包下写Stu类

package com.atguigu.spring5.collectiontype;

public class Stu{
  //1 数组类型属性  代表课程
  private String[] courses;
  //2 list集合类型属性  代表学生的名字 和 艺名
  private List<String> list; 
  //3 map集合类型属性   
  private Map<String,String> maps; 
  //4 set集合类型属性 
  private Set<String> sets; 
  
  public void setSets(Set<String> sets) { 
    this.sets = sets; 
  } 
  public void setCourses(String[] courses) { 
    this.courses = courses; 
  } 
  public void setList(List<String> list) { 
    this.list = list; 
  } 
  public void setMaps(Map<String, String> maps) { 
    this.maps = maps; 
  }

  publi void test(){
  //数组一般不直接输出,用工具类Arrays.toString方法输出
    System.out.println(Arrays.toString(courses));
    System.out.println(list);
    System.out.println(maps);
    System.out.println(sets);
  }
}

Step2 在spring配置文件进行配置

value属性只能有一个值,所以没法写数组的对象。因此需要用value标签,这样可以给多个元素赋值。但是多个value标签外面需要用一些其他标签:

① 数组和list集合外部用一个array标签或者list标签

②Set集合,就用set标签

③map集合,用map+entry标签

具体案例如下:

<!--集合类型注入--> 
<bean id="stu" class="com.atguigu.spring5.collectiontype.Stu"> 
    <!--数组类型属性注入 --> 
    <property name="courses">
        <array>
            <value>java课程</value>
            <value>数据库课程</value>
        </array>
    </property> 

    <!--list类型属性注入-->
    <property name="list">
        <list>
            <value>张三</value>
            <value>小张</value>
        </list>
    </property>

    <!--set类型属性注入-->
    <property name="sets">
        <set>
            <value>MySQL</value>
            <value>Redis</value>
        </set>
    </property>

    <!--map类型属性注入-->
    <property name="maps">
        <map>
            <entry key="JAVA" value="JAVA"></value>
            <entry key="PHP" value="php"></value>
        </map>
    </property>
</bean> 

Step3 写测试类,测试输出集合

public class TestSpring5Demo1{
  @Test
  public void testCollection(){
    //1、加载spring配置文件
    ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
    //2、获取配置对象的信息
    Stu stu = context.getBean("stu",Stu.class); 
  
    stu.test();
  }
}

3.7.2 如果集合元素属性是对象(非字面量)时候的属性注入

将上例的课程courses 从 String类型升级成对象。在collectiontype包下再写一个Course类

package com.atguigu.spring5.collectiontype;

//课程类
public class Course{
  private String cname;  //课程名称
  public void setCname(String cname){
    this.cname = cname;
  }
  //如果没得toString方法,就打印地址
  @Override
  public String toString(){
    return "Course{"+"cname'"+cname+'\''+'}';
  }
  
}

在上例的Stu类中,将String类型的Courses数组改成 一个Course类的LIst集合

package com.atguigu.spring5.collectiontype;

public class Stu{
  //1 将课程改成对象集合 学生学习多门课程
  private List<Course> courseList;
  //2 list集合类型属性  代表学生的名字 和 艺名
  private List<String> list; 
  //3 map集合类型属性   
  private Map<String,String> maps; 
  //4 set集合类型属性 
  private Set<String> sets; 
  
  public void setSets(Set<String> sets) { 
    this.sets = sets; 
  } 
  //set方法,也一起改了
  public void setCourseList(List<Course> courseList) { 
    this.courseList = =courseList; 
  } 
  public void setList(List<String> list) { 
    this.list = list; 
  } 
  public void setMaps(Map<String, String> maps) { 
    this.maps = maps; 
  }

  publi void test(){
    System.out.println(courseList);
    System.out.println(list);
    System.out.println(maps);
    System.out.println(sets);
  }
}

Step2 在spring配置文件中进行配置

这里只演示对象配置

先造Stu对象同时也造多个Course类对象(代表Stu要学习的多个课程)

<!--注入List集合,值是对象--> 
<bean id="stu" class="com.atguigu.spring5.collectiontype.Stu"> 
    <property name="courseList">
        <list>
            <ref bean="course1"></bean>
            <ref bean="course2"></bean>
        </list>
    </property> 
</bean> 


<!--创建多个course对象--> 
<bean id="course1" class="com.atguigu.spring5.collectiontype.Course"> 
    <property name="cname" value="Spring5框架"></property> 
</bean> 
<bean id="course2" class="com.atguigu.spring5.collectiontype.Course"> 
    <property name="cname" value="MyBatis框架"></property> 
</bean>

扩展:抽取任意个集合元素,一起注入进集合

演示:将多个String数组的元素 注入 book类中的list集合

(1)在com.atguigu.spring5.collectiontype包中,再写一个book类(新建的元素将要一次性注入该类的list集合)

package com.atguigu.spring5.collectiontype;

public class Book{
  private List<String> list;
  public void setList(List<String> list){
    this.list = list;
  }
  public void test(){
    System.out.println(list);
  }
}

(2)专门再建立一个配置文件bean2.xml。直接在配置文件中,就可以写好要注入的元素

在spring配置文件中引入名称空间util,需要修改两处地方(见红色地方)

<?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:p="http://www.springframework.org/schema/p"

             xmlns:util="http://www.springframework.org/schema/util"

             xsi:schemaLocation="http://www.springframework.org/schema/beans  http://www.springframework.org/schema/beans/spring-beans.xsd

                   http://www.springframework.org/schema/util  http://www.springframework.org/schema/util/spring-util.xsd">

    <bean>......</bean>

    ............

</beans>

(3)使用util标签,提取要注入进list的集合

给util:list 的id随便取一个名字

<!--id名字随便取-->
<util:list id="bookList">
    <value>易筋经</value> 
    <value>九阴真经</value>
    <value>九阳神功</value> 
</util:list>

将上面的四个String类型元素,注入Book类对象book的list对象属性

<!--1 提取list集合类型属性注入--> 
<util:list id="bookList">
    <value>易筋经</value> 
    <value>九阴真经</value>
    <value>九阳神功</value> 
</util:list>

<!--2 提取list集合类型属性注入使用--> 
<bean id="book" class="com.atguigu.spring5.collectiontype.Book"> 
    <property name="list" ref="bookList"></property> 
</bean>

(4)编写测试类,测试是否成功

public class TestSpring5Demo1{
  @Test
  public void testCollection2(){
    //1、加载spring配置文件
    ApplicationContext context = new ClassPathXmlApplicationContext("bean2.xml");
    //2、获取配置对象的信息
    Book book = context.getBean("book",Book.class); 
  
    book.test();
  }
}

4、工厂Bean(FactoryBean)

4.1 简介

Spring有两种类型的bean,一种是普通bean,另一种是工厂bean(FactoryBean)

普通bean:在配置文件中定义bean类型就是返回类型

工厂bean:在配置文件中定义bean类型可以返回类型不一样(定义的是工厂类的子类,实现类,再由工厂类造自定义bean的对象)

4.2 创建工厂bean的方法

(1) 创建类,让这个类作为工厂bean,实现接口 FactoryBean
(2)实现接口里面的方法,在实现的方法中定义返回的bean类型

        接着前面,再新建一个factorybean文件夹(com.atguigu.spring5.factorybean) 再下面写MyBean,实现FactoryBean接口,该接口底层会帮我们造好course对象,我们还可以用setCname方法为对象注入属性

package com.atguigu.spring5.factorybean;

public class MyBean implements FactoryBean<Course>{
  //重点:定义返回的bean
  @Override
  public Course getObject() throws Exception{
    Course course = new Course();
    course.setCname("abc");
    return course;
  }

  //后面用idea自动生成,不用去管
  @Override
  public Class<?> getObjectType(){
    return null;
  }
  @Override
  public boolean isSingleton(){
    return false;
  }
}

(3)再建一个配置文件,bean3.xml,在里面创建工厂实现类Mybean,由工厂实现类再来间接造我们真正想要的bean,即Course类

<bean id="myBean" class="com.atguigu.spring5.factorybean.MyBean"> </bean>

(4)测试代码

public class TestSpring5Demo1{
  @Test 
  public void test3() { 
    ApplicationContext context = new ClassPathXmlApplicationContext("bean3.xml"); 
    Course course = context.getBean("myBean", Course.class); 
    System.out.println(course); 
  }
  
}

5、Bean作用域(对象是单例 or 多例) 和 生命周期

5.1 Bean作用域(对象是单例 or 多例)

(1)在Spring里面,可以自行设置创建bean实例是单实例还是多实例,默认情况下,bean是单实例对象

    按默认xml配置,建立的book1,和book2 的地址相同

public class TestSpring5Demo1{
  @Test
  public void testCollection2(){
    //1、加载spring配置文件
    ApplicationContext context = new ClassPathXmlApplicationContext("bean2.xml");
    //2、获取配置对象的信息
    Book book1 = context.getBean("book",Book.class); 
    Book book2 = context.getBean("book",Book.class); 
  
//两个对象地址相同
    System.out.println(book1);
    System.out.println(book1);
   
  }
}

 

(2)将Bean组件设置成多实例对象

       在spring配置文件bean标签里面使用scope属性来设置单实例还是多实例

scope常用属性值说明:

① singleton:默认值,表示是单实例对象

② prototype:表示是多实例对象

<!--将book类写成多实例--> 
<bean id="book" class="com.atguigu.spring5.collectiontype.Book" scope="prototype"> 
    <property name="list" ref="bookList"></property> 
</bean>

总结:关于singleton和prototype区别

(1) singleton单实例,prototype多实例

(2) 设置scope值是singleton时候,加载spring配置文件时候就会创建单实例对象

设置scope值是prototype时候,不是在加载spring配置文件时候创建 对象,在调用getBean方法时候创建多实例对象

说明:加载对象的时候,spring根本不知道你想造多少个对象,所以根本没法在加载配置文件的时候造对象

5.2 Bean组件生命周期

    生命周期指从对象创建到对象销毁的过程

5.2.1 一般情况下bean的生命周期

(1)通过构造器创建bean实例(无参数构造)

(2)为bean的属性设置值和对其他bean引用(调用set方法)

(3)调用bean的初始化的方法(需要进行配置初始化的方法)

说明:初始化是因为web相关资源需要

(4)bean可以使用了(对象获取到了)

(5)当容器关闭时候,调用bean的销毁的方法(需要进行配置销毁的方法)

演示:Bean生命周期

专门为Bean创建一个文件夹  com.atguigu.spring5包,在包下建立一个Orders类来演示Bean的生命周期

package com.atguigu.spring5.bean;

public class Orders{
  //无参构造器 
  public Orders(){
    System.out.println("第一步 执行无参构造创建bean实例");
  }
  private String oname;
  public void setOname(String oname){
    this.oname=oname;
    System.out.println("第二步 调用set方法设置属性值");
  }
  //创建执行的初始化方法(配合xml配置)
  public void initMethod(){
    System.out.println("第三步 执行初始化方法");
  }
  //创建执行销毁的方法
  public void destroyMethod(){
    System.out.println("第五步 执行销毁的方法");
  }
}

配置文件

  需要专门使用init-method 和 destry-method 属性来配置好初始化方法,和销毁方法

<bean id="orders" class="com.atguigu.spring5.bean.Orders" init-method="initMethod" destroy-method="destroyMethod"> 
    <property name="oname" value="手机"></property> 
</bean>
public class TestSpringDemo1{
  @Test
  public void testBean(){
    //1、加载spring配置文件
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean4.xml");
    //2、获取配置对象的信息
    Orders orders = context.getBean("orders",Orders.class); 
 
    System.out.println(第四步,获取创建bean实例对象);
    System.out.println(orders);
   
    context.close();//间接调用destroyMethod方法,让bean实例销毁
  }
}

以上代码注意,close方法必须要用 ClassPathXmlApplicationContext类对象才能调,如果用接口ApplicationContext声明的ClassPathXmlApplicationContext对象,对象会被自动提升成ClassPathXmlApplicationContext类,从而调用close方法

以上输出

5.2.2 加入后置处理器以后,bean的生命周期

    后置处理器(即BeanPostProcessor接口实现类),可以让开发期间,在初始化方法前后,加入一些程序员自己的逻辑代码

此时,生命周期有七步:

(1)通过构造器创建bean实例(无参数构造)
(2)为bean的属性设置值和对其他bean引用(调用set方法)

(3)把bean实例传递bean后置处理器的方法postProcessBeforeInitialization

(4)调用bean的初始化的方法(需要进行配置初始化的方法)

(5)把bean实例传递bean后置处理器的方法 postProcessAfterInitialization

(6)bean可以使用了(对象获取到了)
(7)当容器关闭时候,调用bean的销毁的方法(需要进行配置销毁的方法)

演示后置处理器效果

在Bean包下再写一个类MyBeanPost实现接口BeanPostProcessor,在这个实现类里面写好postProcessBeforeInitialization 和 postProcessAfterInitialization 方法,Spring会在第三,第五步调用这两个方法

package com.atguigu.spring5.bean;

public class MyBeanPost implements BeanPostProcessor{
  @Override 
  public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { 
    System.out.println("在初始化之前执行的方法"); 
    return bean; 
  } 
  @Override 
  public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { 
    System.out.println("在初始化之后执行的方法"); 
    return bean; 
  }
}

单独在Spring配置文件中配置后置处理器(MyBeanPost实现类)

<bean id="orders" class="com.atguigu.spring5.bean.Orders" init-method="initMethod" destroy-method="destroyMethod"> 
    <property name="oname" value="手机"></property> 
</bean>

<!--配置后置处理器--> 
<bean id="myBeanPost" class="com.atguigu.spring5.bean.MyBeanPost"></bean>

其实后置处理器本身也是一个Bean

再次执行测试代码后,会输出

6、引入bean的外部属性文件(比如数据库线程池的配置dataSource的properties文件)

   演示引入德鲁伊数据库连接池

(1)导入德鲁伊连接池依赖的jar包,复制进入项目的lib目录

(2)再配置一个配置文件bean6.xml

① 直接给连接池dateSource注入属性,不用连接池的外部属性文件

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> 
    <property name="driverClassName" value="com.mysql.jdbc.Driver"></property> 
    <property name="url" value="jdbc:mysql://localhost:3306/userDb"></property> 
    <property name="username" value="root"></property> 
    <property name="password" value="root"></property> 
</bean>

② 引入外部属性文件给dataSource注入属性

step1 创建一个jdbc.properties属性文件,写好属性值

prop.url=jdbc:mysql://localhost:3306/userDb
prop.userName=root
prop.password=root
prop.driverClass=com.mysql.jdbc.Driver

说明:属性名可以随便写,之所以写成prop.url的形式,只是为了避免以后项目大了,涉及多个数据库,名字冲突

Step2 写spring配置文件

再新引入一个context名称空间(之前已经引入了p空间,util空间)

<?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:p="http://www.springframework.org/schema/p"

             xmlns:util="http://www.springframework.org/schema/util"

             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/util  http://www.springframework.org/schema/util/spring-util.xsd

                                        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!--引入外部属性文件  location代表类路径下的jdbc.properties文件-->

    <context:property-placeholder location="classpath:jdbc.properties"/>

    <!--配置连接池-->

    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">

        <property name="driverClassName" value="${prop.driverClass}"></property>

        <property name="url" value="${prop.url}"></property>

        <property name="username" value="${prop.userName}"></property> 

        <property name="password" value="${prop.password}"></property>

    </bean>
</beans>

通过EL表达式${....},即可读取配置文件中的值。

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值