文章目录
1. IOC概念和原理
1.1概念
(1)控制反转,把对象创建和对象之间的调用过程,交给 Spring 进行管理
(2)使用 IOC 目的:为了降低耦合度
1.2原理
IOC底层原理所用到的技术有三个,分别是:xml 解析、工厂模式、反射
接下来用画流程图的方式进行讲解:
- 如下图所示,有两个类,分别是UserService和UserDao,UserService中有一个execute()方法,UserDao中有一个add()方法。现在的需求是在UserService类中调用UserDao中的add()方法,下图所示的就是最原始的方式,通过创建对象调方法实现,即在UserService中创建UserDao类的对象userDao,然后利用userDao来调用add()方法。
这种方式的缺点是耦合度太高了,当UserDao的路径发生变化,UserService也需要跟着变化, 当UserDao中的方法发生变化,UserService也需要跟着变化,不利于项目拓展。
- 为了降低耦合度,可以利用工厂模式,在上面两个类的基础上,创建一个工厂类UserFactory,工厂类中有一个静态的getDao()方法,返回UserDao对象。通过在UserService中用工厂类UserFactory的静态方法来创建UserDao对象并调用UserDao中的方法。这种方法一定程度上降低了UserService和UserDao的耦合度。
但是同时也存在新的问题,即耦合度出现在工厂类中了,并没有把耦合度降低到最低限度,需要进一步解耦。这个时候就要加上xml解析和反射技术了,三个技术合并在一起就是IOC技术了。
- 基于上述分析,IOC的解耦过程如下:
第一步:xml配置文件,配置创建的对象
第二步:有service类和dao类,创建工厂类。<?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="dao" class="com.rqs.spring5.utils.UserDao"/> </beans>
package com.rqs.spring5.utils; /** * 伪代码 */ public class UserFactory { public static UserDao getDao() { // return new UserDao(); String classValue = class属性值;//1.利用xml解析获得class属性的值"com.rqs.spring5.utils.UserDao", Class clazz = Class.forName(classValue);//2.通过反射创建对象 return (UserDao) clazz.newInstance(); } }
2.IOC接口
-
IOC 思想基于 IOC 容器完成,IOC 容器底层就是对象工厂
-
Spring 提供 IOC 容器实现的两种方式:(两个接口)
-
BeanFactory:IOC 容器的基本实现,是 Spring 内部的接口,一般不提供给开发人员进行使用(但是也可以用)
加载配置文件时不会创建对象,在获取对象(使用)才去创建对象
-
ApplicationContext:BeanFactory 接口的子接口,提供更多更强大的功能,一般由开发人员进行使用
加载配置文件时就会创建配置文件中的对象
-
-
ApplicationContext 接口有实现类,所框出来的两个是主要的实现类,其中ClassPathXmlApplicationContext在写入门案例的时候已经使用过,这个实现类中是通过配置文件的类路径来写的参数(比如:
ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
),而FileSystemXmlApplicationContext中的参数需要写的是配置文件在系统盘符的全路径。
3.传统方法中向属性中注入值的方式
举例:向入门案例中User类的userName中注入值
(1)可以用set方法
package com.rqs.spring5;
import java.awt.print.Book;
public class User {
private String userName;
public void setUserName(String userName) {
this.userName = userName;
}
public void add() {
System.out.println("add");
}
public static void main(String[] args) {
User user = new User();
user.setUserName("abc");
}
}
(2)通过有参数的构造器
package com.rqs.spring5;
public class User {
private String userName;
public User(String userName) {
this.userName = userName;
}
public void add() {
System.out.println("add");
}
public static void main(String[] args) {
User user = new User("abc");
}
}
4.什么是Bean管理
Bean管理指的是两个操作:
(1)Spring创建对象
(2)Spring注入属性
4.1.基于xml实现Bean管理
4.1.1Spring创建对象:基于xml方式
- 在spring配置文件中,使用bean标签,标签中添加对应属性,就可以实现对象的创建
- 在bean标签中有很多属性,常用的属性有:
- id属性:对象的唯一标识
- class属性:对象的类全路径
- name:作用同id,但是id中不能加特殊符号,name中可以,比如加斜杠符号
- 创建对象的时候,默认执行无参数的构造方法
所以,当在入门案例中加上有参构造器之后执行代码,就会报错
4.1.2.Spring注入属性:基于xml方式
4.1.2.1.DI概念介绍
DI,依赖注入,其实就是注入属性,它是IOC中的一种具体实现,需要在创建对象的基础上完成
4.1.2.2.第一种xml注入方式:使用 set 方法进行注入
(1)创建类,定义属性和对应的 set 方法
User类代码:
package com.rqs.spring5;
/**
* 演示使用set方法进行属性的注入
*/
//创建类
public class User {
//创建属性
private String userName;
//创建属性对应的set方法
public void setUserName(String userName) {
this.userName = userName;
}
public void add() {
System.out.println(userName);
}
}
(2)在 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">
<!--配置对象创建-->
<bean id="user" class="com.rqs.spring5.User">
<!--使用property完成属性注入
name:类里面的属性名称
value:向属性中注入的值
-->
<property name="userName" value="不可不说的宋朝史"></property>
</bean>
</beans>
进行测试:
TestSpring5 代码:
package com.rqs.spring5.testdemo;
import com.rqs.spring5.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestSpring5 {
@Test
public void testAdd() {
//加载配置文件
ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
//获取配置创建的对象
User user = context.getBean("user", User.class);//user跟配置文件中的id要保持一致 <bean id="user" class="com.rqs.spring5.User"/>
user.add();
}
}
项目结构如下:
4.1.2.2.1 p名称空间注入(了解)
可以简化基于xml的set方法注入属性,不使用property就可以完成属性注入。
第一步:在配置文件中添加 p 名称空间
第二步:不使用property,在 bean 标签里用p:userName="不可不说的宋朝史"
的形式进行进行属性注入
4.1.2.3.第二种xml注入方式:使用有参数构造进行注入
(1)创建类,定义属性,创建属性对应有参数构造方法
User类代码:
package com.rqs.spring5;
/**
* 演示使用有参构造器方法进行属性的注入
*/
//创建类
public class User {
//创建属性
private String userName;
//创建有参构造器
public User(String userName) {
this.userName = userName;
}
public void add() {
System.out.println(userName);
}
}
(2)在 spring 配置文件中进行配置
配置时要注意在上文中提到的,创建对象的时候,默认执行无参数的构造方法,如果写了有参构造器就会报错,例如配置文件中的这段代码默认使用午餐构造器创建对象,因此在写了有参构造器的时候配置文件报错
需要在bean标签中写上constructor-arg参数,才可以利用有参构造器的方式注入
配置文件代码:
<?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="user" class="com.rqs.spring5.User">
<constructor-arg name="userName" value="rqs"></constructor-arg>
</bean>
</beans>
进行测试:
TestSpring5 代码:
package com.rqs.spring5.testdemo;
import com.rqs.spring5.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestSpring5 {
@Test
public void testAdd() {
//加载配置文件
ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
//获取配置创建的对象
User user = context.getBean("user", User.class);//user跟配置文件中的id要保持一致 <bean id="user" class="com.rqs.spring5.User"/>
user.add();
}
}
项目结构如下:
4.1.3xml注入其他类型属性
以Book类为例进行介绍:
package com.rqs.spring5;
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);
}
}
测试代码如下:
package com.rqs.spring5.testdemo;
import com.rqs.spring5.Book;
import com.rqs.spring5.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestSpring5 {
@Test
public void testBook1() {
//加载配置文件
ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
//获取配置创建的对象
Book book = context.getBean("book", Book.class);
System.out.println(book);
book.testDemo();
}
}
4.1.3.1字面量(直接量)
字面量是指在程序中通过源代码直接给出的值,例如在int a = 5;代码中,为变量 a 所分配的初始值 5 就是一个字面量。
①null值
在配置文件中的property里写上null标签
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--配置对象创建-->
<bean id="book" class="com.rqs.spring5.Book">
<!--设置一个null值-->
<property name="bname">
<null></null>
</property>
<property name="bauthor" value="元朝史"></property>
<property name="address" value="图书馆"></property>
</bean>
</beans>
测试结果:
②属性中包含特殊符号
属性中包含特殊符号,比如<<>>,会被当成标签符号,直接写会报错
可以用如下方式解决:
<?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="book" class="com.rqs.spring5.Book">
<!--设置一个null值-->
<property name="bname">
<null></null>
</property>
<!--属性中包含特殊符号,比如<<>>,直接写会报错
解决方式1:把<>进行转义,用<>代替
解决方式2:把<>写入CDATA,即在<![CDATA[ ]]>里面的中括号写上具体内容,如下所展示的代码
-->
<property name="bauthor">
<value><![CDATA[<<元朝史>>]]></value>
</property>
<property name="address" value="图书馆"></property>
</bean>
</beans>
测试结果:
4.1.3.2注入属性-外部bean
讲解:在实际操作中,会出现web层调用service层,service层调用dao层的情况,通过service层调用dao层,这个过程就叫做引入外部bean
原始方法过程举例:
(1)创建service和dao类
UserService类:其中创建UserDao对象和调用方法的过程可以用Spring配置的方式实现
package com.rqs.spring5.service;
import com.rqs.spring5.dao.UserDao;
import com.rqs.spring5.dao.UserDaoImpl;
public class UserService {
public void add() {
System.out.println("service add ...........");
//创建UserDao对象
UserDao userDao = new UserDaoImpl();
//调用方法
userDao.update();
}
}
UserDao接口:
package com.rqs.spring5.dao;
public interface UserDao {
public void update();
}
接口实现类UserDaoImpl:
package com.rqs.spring5.dao;
public class UserDaoImpl implements UserDao {
@Override
public void update() {
System.out.println("dao update .........");
}
}
用Spring配置的方式实现:以set方式注入为例
第一步:在service中创建dao类型的对象属性,生成对应的set方法
在配置文件中创建两个类的对象
第二步:在service中注入dao对象
UserService类:
package com.rqs.spring5.service;
import com.rqs.spring5.dao.UserDao;
import com.rqs.spring5.dao.UserDaoImpl;
public class UserService {
//创建UserDao类型的对象属性,生成对应的set方法
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void add() {
System.out.println("service add ...........");
userDao.update();//方法调用
}
}
UserDao接口:
package com.rqs.spring5.dao;
public interface UserDao {
public void update();
}
接口实现类UserDaoImpl:
package com.rqs.spring5.dao;
public class UserDaoImpl implements UserDao {
@Override
public void update() {
System.out.println("dao update .........");
}
}
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">
<!--配置service和dao的对象-->
<bean id="userService" class="com.rqs.spring5.service.UserService">
<!--注入userDao对象
name属性:类里面的属性名称
ref属性:创建的外部userDao对象bean标签的id值
-->
<property name="userDao" ref="userDao"></property>
</bean>
<!--创建外部userDao对象-->
<bean id="userDao" class="com.rqs.spring5.dao.UserDaoImpl"></bean>
</beans>
测试代码:
package com.rqs.spring5.testdemo;
import com.rqs.spring5.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestBean {
@Test
public void testAdd() {
//1.加载Spring配置文件
ApplicationContext context = new ClassPathXmlApplicationContext("bean2.xml");
//2.获取配置创建的对象
UserService userService = context.getBean("userService", UserService.class);
userService.add();
}
}
测试结果:
4.1.3.3注入属性-内部bean
举例解释:
(1)数据库中表与表之间有不同的关系,比如一对一,一对多。
一对多关系:部门和员工
一个部门里可以有多个员工,一个员工属于一个部门,部门是1,员工是多。
在实体类之间表示一对多的关系。
Dept类:
package com.rqs.spring5.bean;
public class Dept {
private String dname;
public void setDname(String dname) {
this.dname = dname;
}
@Override
public String toString() {
return "Dept{" +
"dname='" + dname + '\'' +
'}';
}
}
员工类:
package com.rqs.spring5.bean;
public class Emp {
private String ename;
private String gender;
public void setEname(String ename) {
this.ename = ename;
}
public void setGender(String gender) {
this.gender = gender;
}
public void add() {
System.out.println("Emp{" +
"ename='" + ename + '\'' +
", gender='" + gender + '\'' +
'}');
}
}
(2)目前两个类之间没有关系,通过在员工类中新增部门对象属性的方式可以表示二者之间的关系,将员工类写成如下形式:
改写员工类:
package com.rqs.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("Emp{" +
"ename='" + ename + '\'' +
", gender='" + gender + '\'' +
", dept=" + dept +
'}');
}
}
(3)然后通过配置文件进行相关配置
<?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-->
<bean id="emp" class="com.rqs.spring5.bean.Emp">
<!--设置普通属性-->
<property name="ename" value="张三"></property>
<property name="gender" value="女"></property>
<!--设置对象类型的属性-->
<property name="dept">
<!--创建内部的dept对象-->
<bean id="dept" class="com.rqs.spring5.bean.Dept">
<property name="dname" value="安保部"></property>
</bean>
</property>
</bean>
</beans>
测试代码:
package com.rqs.spring5.testdemo;
import com.rqs.spring5.Book;
import com.rqs.spring5.User;
import com.rqs.spring5.bean.Emp;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestSpring5 {
@Test
public void testBean() {
//加载配置文件
ApplicationContext context = new ClassPathXmlApplicationContext("bean3.xml");
//获取配置创建的对象
Emp emp = context.getBean("emp", Emp.class);
emp.add();
}
}
测试结果:
4.1.3.4注入属性-级联赋值
(1)级联赋值:第一种写法
<?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="emp" class="com.rqs.spring5.bean.Emp">
<!--设置普通属性-->
<property name="ename" value="张三"></property>
<property name="gender" value="女"></property>
<!--级联赋值-->
<property name="dept" ref="dept"></property>
</bean>
<bean id="dept" class="com.rqs.spring5.bean.Dept">
<property name="dname" value="财务部"></property>
</bean>
</beans>
(2)级联赋值:第二种写法
```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 http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--级联赋值-->
<bean id="emp" class="com.rqs.spring5.bean.Emp">
<!--设置普通属性-->
<property name="ename" value="张三"></property>
<property name="gender" value="女"></property>
<!--级联赋值-->
<property name="dept" ref="dept"></property>
<property name="dept.dname" value="技术部"></property>
</bean>
<bean id="dept" class="com.rqs.spring5.bean.Dept">
<property name="dname" value="财务部"></property>
</bean>
</beans>```
注意到这样写会报错,需要在员工类中设置dept对象类型属性的get方法,如下
package com.rqs.spring5.bean;
public class Emp {
private String ename;
private String gender;
//员工属于某一个部门,使用对象形式表示
private Dept dept;
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("Emp{" +
"ename='" + ename + '\'' +
", gender='" + gender + '\'' +
", dept=" + dept +
'}');
}
}
加了get方法之后就没有报错:
测试代码:
package com.rqs.spring5.testdemo;
import com.rqs.spring5.Book;
import com.rqs.spring5.User;
import com.rqs.spring5.bean.Emp;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestSpring5 {
@Test
public void testBean() {
//加载配置文件
ApplicationContext context = new ClassPathXmlApplicationContext("bean4.xml");
//获取配置创建的对象
Emp emp = context.getBean("emp", Emp.class);
emp.add();
}
}
测试结果:
4.1.3.5注入属性-集合
-
注入数组类型的属性
-
注入List集合类型属性
-
注入Map集合类型属性
-
注入Set集合类型属性
项目结构如下:
通过创建一个Stu类进行举例,其中包含了数组、List、Map、Set类型属性,并且生成了各属性对应的set方法,创建了一个test方法,如下所示:
Stu类:
package com.rqs.spring5.collectiontype;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class Stu {
//1.数组类型属性
private String[] course;
//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 setCourse(String[] course) {
this.course = course;
}
public void setList(List<String> list) {
this.list = list;
}
public void setMaps(Map<String, String> maps) {
this.maps = maps;
}
public void test() {
System.out.println(Arrays.toString(course));
System.out.println(list);
System.out.println(maps);
System.out.println(sets);
}
}
在Spring配置文件中进行配置:
bean.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 http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--集合类型属性的注入-->
<bean id="stu" class="com.rqs.spring5.collectiontype.Stu">
<!--数组类型属性注入-->
<property name="course">
<array>
<value>java课程</value>
<value>mysql课程</value>
<value>spring5课程</value>
</array>
</property>
<!--List类型属性注入-->
<property name="list">
<list>
<value>张三</value>
<value>小三</value>
<value>三三</value>
</list>
</property>
<!--Map类型属性注入-->
<property name="maps">
<map>
<entry key="JAVA" value="java"></entry>
<entry key="MYSQL" value="mysql"></entry>
<entry key="SPRING" value="spring"></entry>
</map>
</property>
<!--Set类型属性注入-->
<property name="sets">
<set>
<value>HTML</value>
<value>CSS</value>
<value>JS</value>
</set>
</property>
</bean>
</beans>
进行测试
TestSpring5Demo1类:
package com.rqs.spring5.testdemo;
import com.rqs.spring5.collectiontype.Stu;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestSpring5Demo1 {
@Test
public void testCollection() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
Stu stu = context.getBean("stu", Stu.class);
stu.test();
}
}
测试结果如下:
两个细节问题
1.在集合中设置对象类型的值
项目结构如下:
以上的例子中,集合中的属性都是String类型(比如List集合类型属性List,泛型指定为String),那如果集合中的属性是对象类型呢?
为了在集合中设置对象类型的属性,新建一个实体类Course并重写toString方法,如下所示:
Course类:
package com.rqs.spring5.collectiontype;
public class Course {
private String cname;
public void setCname(String cname) {
this.cname = cname;
}
@Override
public String toString() {
return "Course{" +
"cname='" + cname + '\'' +
'}';
}
}
在原先的Stu类中,新增Course类型的属性,修改test方法:
Stu类(修改版):
package com.rqs.spring5.collectiontype;
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class Stu {
//1.数组类型属性
private String[] course;
//2.list集合类型属性
private List<String> list;
//3.map集合类型属性
private Map<String, String> maps;
//4.set集合类型属性
private Set<String> sets;
// 学生所学习的多门课程
private List<Course> courseList;
public void setCourseList(List<Course> courseList) {
this.courseList = courseList;
}
public void setSets(Set<String> sets) {
this.sets = sets;
}
public void setCourse(String[] course) {
this.course = course;
}
public void setList(List<String> list) {
this.list = list;
}
public void setMaps(Map<String, String> maps) {
this.maps = maps;
}
public void test() {
System.out.println(Arrays.toString(course));
System.out.println(list);
System.out.println(maps);
System.out.println(sets);
System.out.println(courseList);
}
}
在配置文件中进行配置,新增在集合中设置对象类型值的配置:
bean.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 http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--集合类型属性的注入-->
<bean id="stu" class="com.rqs.spring5.collectiontype.Stu">
<!--数组类型属性注入-->
<property name="course">
<array>
<value>java课程</value>
<value>mysql课程</value>
<value>spring5课程</value>
</array>
</property>
<!--List类型属性注入-->
<property name="list">
<list>
<value>张三</value>
<value>小三</value>
<value>三三</value>
</list>
</property>
<!--Map类型属性注入-->
<property name="maps">
<map>
<entry key="JAVA" value="java"></entry>
<entry key="MYSQL" value="mysql"></entry>
<entry key="SPRING" value="spring"></entry>
</map>
</property>
<!--Set类型属性注入-->
<property name="sets">
<set>
<value>HTML</value>
<value>CSS</value>
<value>JS</value>
</set>
</property>
<!--注入List类型集合,其值为对象类型-->
<property name="courseList">
<list>
<!--引入的是下面所创建的多个bean对象:course1和course2-->
<ref bean="course1"></ref>
<ref bean="course2"></ref>
</list>
</property>
</bean>
<!--创建多个Course对象-->
<bean id="course1" class="com.rqs.spring5.collectiontype.Course">
<property name="cname" value="Java基础课程"></property>
</bean>
<bean id="course2" class="com.rqs.spring5.collectiontype.Course">
<property name="cname" value="Mysql课程"></property>
</bean>
</beans>
测试类不变:
TestSpring5Demo1类:
package com.rqs.spring5.testdemo;
import com.rqs.spring5.collectiontype.Stu;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestSpring5Demo1 {
@Test
public void testCollection() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
Stu stu = context.getBean("stu", Stu.class);
stu.test();
}
}
测试结果如下:
2.将集合注入部分提取出来
项目结构如下:
之前的例子中,所注入的集合属性只能在Stu这个类中进行使用,别的类无法使用,如果不同实体类中的集合属性是一样的,那就可以将集合部分提取出来作为公共部分,让所有实体类都能引入这个集合。
为了演示将集合注入部分提取出出来,新建一个book类:
Book类:
package com.rqs.spring5.collectiontype;
import java.util.List;
public class Book {
private List<String> list;
public void setList(List<String> list) {
this.list = list;
}
public void test() {
System.out.println(list);
}
}
若要把Book类中的list属性作为一个公共的部分,让其他实体类也可以使用,首先,需要在配置文件中引入util名称空间(名称空间这个词,在“4.1.2.2.1 p名称空间注入(了解)”这个章节中有出现过p名称空间),如下图所示。然后,需要使用util名称空间完成集合注入部分的提取,见配置文件代码。
bean1.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"
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">
<!--1.提取list集合类型属性的注入-->
<util:list id="bookList"><!--id设置为bookList,可供后续使用-->
<!--<ref bean=""></ref>-->
<value>一读就懂的中国史</value>
<value>明朝那些事</value>
<value></value>
</util:list>
<!--2.提取list集合类型属性的注入后,用如下方式使用-->
<bean id="book" class="com.rqs.spring5.collectiontype.Book">
<property name="list" ref="bookList"></property>
</bean>
</beans>
测试方法:
TestSpring5Demo1类:
package com.rqs.spring5.testdemo;
import com.rqs.spring5.collectiontype.Book;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestSpring5Demo1 {
@Test
public void testCollection() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
Book book = context.getBean("book", Book.class);
book.test();
}
}
测试结果如下:
4.1.4 FactoryBean
项目结构如下:
说明:
-
Spring有两种类型的bean,一种是自己创建的普通的bean,另外一种是工厂bean(或者叫FactoryBean)
-
普通bean,在配置文件中,bean定义的是什么类型,返回就必须是什么类型,比如下图中bean定义的是Book类型,就必须返回Book类型
-
工厂bean,在配置文件中定义的bean类型可以和返回类型不一样,演示如下
-
第一步:创建类,实现接口FactoryBean,让这个类作为工厂bean
-
第二步:实现接口中的方法,在实现的方法中定义返回的bean类型
MyBean类:
package com.rqs.spring5.factorybean; import com.rqs.spring5.collectiontype.Course; import org.springframework.beans.factory.FactoryBean; public class MyBean implements FactoryBean<Course>{ //定义返回bean类型 @Override public Course getObject() throws Exception { Course course = new Course(); course.setCname("中国史纲"); return course; } @Override public Class<?> getObjectType() { return null; } @Override public boolean isSingleton() { return FactoryBean.super.isSingleton(); } }
配置文件:
bean2.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" 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 id="myBean" class="com.rqs.spring5.factorybean.MyBean"> </bean> </beans>
测试类:
package com.rqs.spring5.testdemo; import com.rqs.spring5.collectiontype.Course; import com.rqs.spring5.factorybean.MyBean; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestSpring5Demo1 { @Test public void testCollection() { ApplicationContext context = new ClassPathXmlApplicationContext("bean2.xml"); // MyBean myBean = context.getBean("myBean", MyBean.class); Course course = context.getBean("myBean", Course.class); System.out.println(course); } }
测试结果:
如下图所示,虽然在配置文件中定义的是MyBean类型
但是,最后返回的结果是Course类型。
-
4.1.5 bean作用域
-
在Spring中,可以设置bean实例是单实例还是多实例
-
在Spring中,默认情况下,所创建的bean是一个单实例对象
对第2点做出验证,验证如下:
如下图所示,在Spring配置文件中,为Book类做了一个bean的配置。
所以,在如下图所示的测试文件中,在得到book1和book2对象后,可以分别输出book1和book2对象。默认情况下,如果Spring所创建的对象是单实例对象,那么输出的两个实例对象的地址是同一个地址,如果Spring所创建的对象是多实例对象,那么输出的两个实例对象的地址补是同一个地址。 如下图所示,经过验证,输出的两个实例对象的地址是同一个地址,所以在默认情况下,所创建的bean是一个单实例对象,但是可以将其设置为多实例对象。
-
设置Spring所创建对象是单实例对象还是多实例对象的方法。
在Spring配置文件的bean标签中有一个属性(scope)用于设置单实例或者多实例。
如下图所示,scope常用的属性值有两个:第一个是默认值singleton,表示是单实例对象;第二个值是 prototype,表示是多实例对象。
如下图所示,如果设置为多实例对象后,之前的输出的两个实例对象的地址现在不是同一个地址了
-
设置scope值为singleton时,在加载Spring配置文件的时候就会创建单实例对象
-
设置scope值为singleton时,不是在加载Spring配置文件的时候就会创建对象,而是在调用getBean方法时创建多实例对象
演示 bean的 生命周期:项目结构目录:
创建一个Orders实体类:
package com.rqs.spring5.bean; public class Orders { //为了便于展示bean的生命周期,写出无参数构造 public Orders() { System.out.println("第一步 执行无参数构造创建bean实例"); } private String oname; public void setOname(String oname) { this.oname = oname; System.out.println("第二步 调用set方法设置属性值"); } //需要自己创建初始化的方法 //这是一个普通的方法默认不会执行,需要通过配置才能让这个方法执行 //通过配置bean的属性init-method来配置,例如: init-method="initMethod" public void initMethod() { System.out.println("第三步 执行初始化的方法"); } //创建执行的销毁的方法 public void destroyMethod() { System.out.println("第五步 执行销毁的方法"); } }
配置bean及其id、class、init-method、destroy-method方法:
<?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: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 id="orders" class="com.rqs.spring5.bean.Orders" init-method="initMethod" destroy-method="destroyMethod"> <property name="oname" value="手机"></property> </bean> </beans>
测试方法:
package com.rqs.spring5.testdemo; import com.rqs.spring5.bean.Orders; import org.junit.Test; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestSpring5Demo1 { @Test public void testBean4() { /*ApplicationContext context = new ClassPathXmlApplicationContext("bean4.xml");*/ ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean4.xml"); Orders orders = context.getBean("orders", Orders.class); System.out.println("第四步 利用getBean()方法获取创建的Bean实例对象"); System.out.println(orders); //手动让bean实例销毁 context.close(); } }
测试结果:
4.1.6.1 bean后置处理器
bean的后置处理器,bean生命周期其实有七步,初始化前后各有一个方法:
- 通过构造器创建 bean 实例(无参数构造)
- 为 bean 的属性设置值和对其他 bean 引用(调用 set 方法)
- 把 bean 实例传递bean后置处理器的方法 postProcessBeforeInitialization
- 调用 bean 的初始化的方法(需要进行配置初始化的方法)
- 把bean实例传递bean后置处理器的方法postProcessAfterInitialization
- bean 可以使用了(对象获取到了)
- 当容器关闭时候,调用 bean 的销毁的方法(需要进行配置销毁的方法)
演示添加后置处理器效果:
项目结构:
-
创建类,实现接口 BeanPostProcessor,创建后置处理器(Spring在类实现接口BeanPostProcessor之后就会把该类当做后置处理器进行执行,后置处理器会为配置文件中的所有bean都添加后置处理器的处理,即若有两个bean:book和orders,二者都会被添加后置处理器的处理)
MyBeanPost类:package com.rqs.spring5.bean; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.lang.Nullable; import org.springframework.util.SocketUtils; 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; } }
-
配置后置处理器
bean4.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" 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 id="orders" class="com.rqs.spring5.bean.Orders" init-method="initMethod" destroy-method="destroyMethod"> <property name="oname" value="手机"></property> </bean> <!--配置后置处理器,供上面的bean使用--> <bean id="myBeanPost" class="com.rqs.spring5.bean.MyBeanPost"></bean> </beans>
-
测试方法
package com.rqs.spring5.testdemo; import com.rqs.spring5.bean.Orders; import org.junit.Test; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestSpring5Demo1 { @Test public void testBean4() { /*ApplicationContext context = new ClassPathXmlApplicationContext("bean4.xml");*/ ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean4.xml"); Orders orders = context.getBean("orders", Orders.class); System.out.println("第四步 利用getBean()方法获取创建的Bean实例对象"); System.out.println(orders); //手动让bean实例销毁 context.close(); } }
测试结果:
4.1.6.2 xml自动装配
-
什么是自动装配?
在之前的演示中,可以通过配置文件中的标签为实体类注入属性,这种方式叫做手动装配。
而自动装配中Spring可以根据指定的装配规则(属性名称或者属性类型)自动将匹配的属性值进行注入。
-
演示自动装配过程
项目结构:
Emp员工类:
package com.rqs.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); } }
Dept部门类:
package com.rqs.spring5.autowire; public class Dept { @Override public String toString() { return "Dept{}"; } }
bean5.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" 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标签中有一个属性:autowire,通过它配置自动装配 autowire中比较常用的属性值是byName和byType,分别是根据属性名称和属性类型来注入。 byName要求:注入bean的id属性的值和实体类中的属性名要一致。 --> <bean id="emp" class="com.rqs.spring5.autowire.Emp" autowire="byName"> <!--自动装配不需要写property--> <!--<property name="dept" ref="dept"></property>--> </bean> <bean id="dept" class="com.rqs.spring5.autowire.Dept"></bean> </beans>
测试类:
package com.rqs.spring5.testdemo; import com.rqs.spring5.autowire.Emp; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestSpring5Demo1 { @Test public void test4() { ApplicationContext context = new ClassPathXmlApplicationContext("bean5.xml"); Emp emp = context.getBean("emp", Emp.class); System.out.println(emp); } }
测试结果显示,没有用到property属性来注入属性,但是属性依然被注入了,自动装配成功:
上面演示的xml配置文件中是根据属性名称的规则来进行自动装配的,如果是根据属性类型来进行自动装配,则autowire的属性值要改为byType。如下图所示,如果是通过属性类型自动装配,则会通过实体类Emp的属性dept的类型Dept,在配置文件中找到Dept对象来进行自动装配。
另外,在实际情况中,一般不用xml自动装配,而是利用注解的方式进行注入。
4.1.6.3引入外部属性文件
应用场景:
在之前的例子中,在配置文件中创建了bean对象之后,如果一个类的属性特别多,在bean中需要写入很多property。这样写不是很方便,特别是当属性值发生变化的时候,需要修改配置文件,而配置文件中可能还要其它的配置,所以改起来不是很方便。
所以,可以把一些固定的属性值或者相关的值放到其他文件中,然后将其引入到配置文件中。
例如,数据库的一些属性可以写在properties文件中,然后引入到配置文件中读取properties文件的内容进行注入。
演示:
方式一:直接配置数据库信息
- 配置德鲁伊连接池
- 引入德鲁伊连接池的jar包,方法同【Spring5】002-入门案例中jar包的导入。
方式二:通过引入外部属性文件配置数据库
- 创建properties 格式的外部属性文件,写入数据库信息
- 把外部 properties 属性文件引入到 spring 配置文件中
需要先引入context名称空间
在 spring 配置文件使用标签引入外部属性文件