目录
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)特殊符号赋值
方法一:对书名号的<<>>等标记进行转义,<,和>
方法二:将property的value属性改成property下的value标签。然后使用![CDATA[属性名]] ,此时属性名就可以随便写啦
<!--属性值包含特殊符号
1 把<>进行转义 < >
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表达式${....},即可读取配置文件中的值。