回顾:
控制反转和依赖注入都是通过Bean实现的
Bean就是注册到Spring容器中的Java类,任何一个Java类都可以是一个Bean Bean由Spring进行管理
一 、Spring IoC容器
Spring提供相应的API来管理Bean,常用的有BeanFactory和ApplicationContext两个接口。
BeanFactory接口
是Spring容器最基本的接口
实现机制采用的是Java经典的工厂模式接口常用方法如下表
方法名称<br> | 描述<br> |
---|---|
getBean(String name)<br> | 根据参数名称获取Bean<br> |
getBean(String name,Class type)<br> | 根据参数名称、参数类型获取Bean<br> |
T getBean(Class requiredType)<br> | 根据参数类型获取Bean<br> |
Object getBean(String name,Object... args)<br> | 根据参数名称获取Bean<br> |
isTypeMatch(String name,Resolvable Typetype)<br> | 判断是否有与参数名称、参数类型匹配的Bean<br> |
Class <?>getType(String name)<br> | 根据参数名称获取类型<br> |
String[] getAliases(String name)<br> | 根据实例的名字获取实例的别名数组<br> |
boolean containsBean(String name)<br> | 根据Bean的名称判断Spring容器是否含有指定的Bean<br> |
XmlBeanFactory实现了BeanFactory接口,可以通过读取xml文件中的配置生成实例,用法示例如下。
BeanFactory beanFactory=new XmlBeanFactory(new FileSystemResource(”D:/bean.xml”));
ApplicationContext接口(重点)
ApplicationContext接口丰富了BeanFactory接口的内容
ApplicationContext接口可以为单例的Bean实行预初始化,并根据<property>元素执行setter方法。单例的Bean可以直接使用,提升了程序获取Bean实例的性能
接口实现类
类名称<br> | 描述<br> |
---|---|
<br>ClassPathXmlApplicationContext<br> | 从类路径加载配置文件,实例化ApplicationContext接口<br> |
<br>FileSystemXmlApplicationContext<br> | 从文件系统加载配置文件,实例化ApplicationContext接口<br> |
AnnotationConfigApplicationContext<br> | 从注解中加载配置文件,实例化ApplicationContext接口<br> |
<br>WebApplicationContext<br> | 在Web应用中使用,从相对于Web根目录的路径中加载配置文件,实例化ApplicationContext接口<br> |
<br>ConfigurableWebApplicationContext<br> | 扩展了WebApplicationContext类,它可以通过读取XML 配置文件的方式实例化WebApplicationContext类<br> |
二 、Bean的配置
Spring容器支持XML和properties两种格式的配置文件
实际开发常用XML进行配置
XML配置的根元素是<beans>,包含是<bean>子元素
每个<bean>元素定义一个Bean,将Bean注册到Spring容器
<bean>元素常用属性
属性<br> | 描述<br> |
---|---|
<br>id<br> | id属性是<bean>元素的唯一标识符,Spring容器对Bean的配置和管理通过id属性完成,装 配Bean时也需要根据id值获取对象。<br> |
name<br> | name属性可以为Bean指定多个名称,每个名称之间用逗号或分号隔开。<br> |
class<br> | class属性可以指定Bean的具体实现类,其属性值为对象所属类的全路径。<br> |
<br>scope<br> | scope属性用于设定Bean实例的作用范围,其属性值有:singleton(单例)、prototype<br>(原型)、request、session和global session。<br> |
<bean>元素的子元素
元素<br> | 描述<br> |
---|---|
<constructor- arg><br> | 使用<constructor-arg>元素可以为Bean的属性指定值。<br> |
<br><property><br> | <property>元素的作用是调用Bean实例中的setter方法完成属性赋值,从而完成 依赖注入。<br> |
<br>ref<br> | ref是<property>、<constructor-arg>等元素的属性,可用于指定Bean工厂中某个Bean实例的引用;也可用于指定Bean工厂中某个Bean实例的引用。<br> |
<br>value<br> | value是<property>、<constructor-arg>等元素的属性,用于直接指定一个常量值;也可以用于直接指定一个常量值。<br> |
<br><list><br> | <list>元素是<property>等元素的子元素,用于指定Bean的属性类型为List或数组。<br> |
<set><br> | <set>元素是<property>等元素的子元素,用于指定Bean的属性类型为set。<br> |
<map><br> | <map>元素是<property>等元素的子元素,用于指定Bean的属性类型为Map。<br> |
<br><entry><br> | <entry>元素是<map>元素的子元素,用于设定一个键值对。<entry>元素的key<br>属性指定字符串类型的键。<br> |
普通的Bean通常只需定义id(或者name)和class两个属性
【示例】阅读下述配置文件,理解Bean的配置属性。
<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">
<!--使用id属性定义bean1,对应的实现类为com.bean.Bean1-->
<bean id="bean1" class="com.bean.Bean1">
</bean>
<!--使用name属性定义bean2,对应的实现类为com.bean.Bean2-->
<bean name="bean2" class="com.bean.Bean2"/>
</beans>
三 、Bean的实例化
实验环境准备
第一步:在ssm_spring项目下建spring-chap07-bean模块,目录结构如下:
第二步:为项目的模块添加依赖,依赖上一单元配置好的lib库
构造方法实例化
指Spring容器通过Bean对应类的默认无参构造方法来实例化Bean
【示例1】阅读代码,理解构造方法实例化bean的一般步骤。
- 创建类Bean1**(放com.bean包中)**
package com.bean;
public class Bean1 { public Bean1(){
System.out.println("正在实例化Bean1...");
}
}
- 创建核心配置文件spring-config.xml**(放resources文件夹下)**,并做如下配置
<?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="Index of /schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--配置Bean1类的bean-->
<bean id="bean1" class="com.bean.Bean1"/>
</beans>
- 编写测试类
import com.bean.Bean1;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestBean1 {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring- config.xml");
Bean1 bean = (Bean1) context.getBean("bean1"); System.out.println(bean);
}
}
运行结果如下图。
静态工厂实例化
要求开发者定义一个静态工厂类(指用静态方法创建实例的工厂) 该静态工厂类中定义返回某个Bean实例的静态方法
配置文件Bean的class属性指向静态工厂类,且factory-method属性为创建Bean实例的静态方法
【示例2】阅读代码,理解静态工厂实例化bean的一般步骤。
- 创建类Bean2**(放com.bean包中)**
package com.bean;
public class Bean1 { public Bean1(){
System.out.println("正在实例化Bean2...");
}
}
- 创建静态工厂类MyBean2Factory**(放com.bean包中)**
package com.bean;
public class Bean2Factory {
//静态方法创建Bean2的实例
public static Bean2 createBean(){ return new Bean2();
}
}
- 创建核心配置文件spring-config.xml**(放resources文件夹下)**,并做如下配置。该文件如果已存在 直接加入新的bean配置
<?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="Index of /schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--静态工厂方法配置Bean2。class指向静态工厂,factory-method指向创建Bean2的静态方法--
<bean id="bean2" class="com.bean.Bean2Factory" factory-method="createBean"/>
</beans>
(3)编写测试类
自己写,与前述类似!
- 实例工厂实例化
要求用户定义一个工厂类
该工厂类中定义一个能返回某个Bean实例的非静态方法配置文件中先要配置该工厂类的Bean
之后配置需要实例化的Bean:将其factory-bean属性指向实例工厂Bean,factory-method属性指向该工厂中创建实例的方法
【示例3】阅读代码,理解静态工厂实例化bean的一般步骤。
- 创建类Bean3**(放com.bean包中)**
package com.bean;
public class Bean3 { public Bean3(){
System.out.println("正在实例化Bean3...");
}
}
- 创建静态工厂类MyBean3Factory**(放com.bean包中)**
package com.bean;
public class Bean3Factory {
//静态方法创建Bean3的实例public Bean3 createBean(){
return new Bean3();
}
}
- 创建核心配置文件spring-config.xml**(放resources文件夹下)**,并做如下配置。该文件如果已存在 直接加入新的bean配置
<?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="Index of /schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--实例化工厂配置Bean3-->
<!--第一步,配置工厂的Bean-->
<bean id="factory" class="com.bean.Bean3Factory"/>
<!--第二步,配置Bean3。factory-bean指向工厂的bean,factory-method指向创建Bean3的方 法-->
<bean factory-bean="factory" factory-method="createBean"/>
</beans>
(3)编写测试类
自己写,与前述类似!
四** ****、Bean的作用域 **
Spring容器创建的Bean有使用范围,又称作用域作用域通过bean元素的scope属性设置
共5种使用范围,默认作用域是singleton,具体如下表
作用域名城<br> | 描述<br> |
---|---|
<br>singleton<br> | 单例模式。在单例模式下,Spring 容器中只会存在一个共享的Bean实例,所有对Bean的请求,只要请求的id(或name)与Bean的定义相匹配,会返回Bean的同一 个实例。<br> |
prototype<br> | 原型模式,每次从容器中请求Bean时,都会产生一个新的实例。<br> |
<br>request<br> | 每一个HTTP请求都会有自己的Bean实例,该作用域只能在基于Web的Spring ApplicationContext中使用。<br> |
<br>session<br> | 每一个HTTPsession请求都会有自己的Bean实例,该作用域只能在基于Web的<br>Spring ApplicationContext中使用。<br> |
global session<br> | 限定一个Bean的作用域为Web应用(HTTPsession)的生命周期,只有在Web应用中使用Spring时,该作用域才有效。<br> |
singleton作用域示例
该作用域的实例Bean,对任何Bean请求均创建同一个对象
【示例4】阅读代码,理解singleton的实验结果。
首先,将示例1的bean配置加上singleton作用域**(不配置scope,系统会默认singleton。此步也可以不做!)**
<bean id="bean1" class="com.bean.Bean1" scope="singleton"/>
之后,写测试案例
import com.bean.Bean1; import com.bean.Bean2;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestScope {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring- config.xml");
Bean1 bean1 = (Bean1) context.getBean("bean1"); Bean1 bean2 = (Bean1) context.getBean("bean1"); System.out.println(bean1== bean2);
}
}
运行结果如下图。
prototype作用域
该作用域的实例Bean,对任何Bean请求均创建新的对象
【示例5】阅读代码,理解singleton的实验结果。 首先,将示例1的bean配置加上prototype作用域
<bean id="bean1" class="com.bean.Bean1" scope="prototype"/>
之后,运行示例4的测试案例运行结果如下图。
五 、Bean的装配方式
Bean的装配就是Bean的依赖注入。有3种方式:基于XML的装配、基于注解的装配、自动装配。
基于XML的装配方式
指通过读取XML文件中的配置信息完成依赖注入。有两种方式:基于setter方法注入和基于构造方法注入。
基于setter方法注入(重点掌握)
前提条件
Bean类必须提供一个默认无参构造方法
Bean类必须为需要注入的属性提供对应的setter方法
配置方法:在<bean>元素内加子元素是<property>,该元素常用属性name,value,ref。
【示例6】理解setter方法注入的XML配置。首先,创建类Teacher**(放com.bean包)**
package com.bean;
public class Teacher { public String name; public String course;
public void setName(String name) { this.name = name;
}
public void setCourse(String course) { this.course = course;
}
public void print(){
System.out.println(name+"老师,教授课程:"+course);
}
}
之后,配置spring容器
<!--配置Teacher的Bean,并通过setter方法注入属性-->
<bean id="teacher_setter" class="com.bean.Teacher">
<property name="name" value="张小火"/>
<property name="course" value="java,h5+css"/>
</bean>
最后,写测试类。(略)
构造方法注入
前提条件:Bean类必须提供配置时需要的构造方法
配置方法:在<bean>元素内加子元素是<property>,该元素常用属性name,value,ref。
【示例7】理解构造方法注入的XML配置。
首先,为示例6中Teacher类添中下述两个构造方法
//必须显示定义无参构造方法,否则示例5会出错public Teacher() {
}
//此构造方法对应配置里构造方法注入
public Teacher(String name, String course) { this.name = name;
this.course = course;
}
之后,配置spring容器
<!--配置Teacher的Bean,并通过setter方法注入属性-->
<bean id="teacher_con" class="com.bean.Teacher">
<constructor-arg name="name" value="张小天"/>
<constructor-arg name="course" value="中国文学,英语"/>
</bean>
最后,写测试类。(略)
基于注解的装配方式(重点掌握)
实际工作中Bean数量较多,使用XML装配很不方便 注解装配非常方便,便于后期维护和升级
装配常用注解
注解<br> | 描述<br> |
---|---|
@Component<br> | 指定一个普通的Bean,可以作用在任何层次。<br> |
<br>@Controller<br> | 指定一个控制器组件Bean,用于将控制层的类标识为Spring中的Bean,功能 上等同于@Component。<br> |
<br>@Service<br> | 指定一个业务逻辑组件Bean,用于将业务逻辑层的类标识为Spring中的<br>Bean,功能上等同于@Component。<br> |
<br>@Repository<br> | 指定一个数据访问组件Bean,用于将数据访问层的类标识为Spring 中的<br>Bean,功能上等同于@Component。<br> |
@Scope<br> | 指定Bean实例的作用域。<br> |
@Value<br> | 指定Bean实例的注入值。<br> |
@Autowired<br> | 指定要自动装配的对象。<br> |
@Resource<br> | 指定要注入的对象。 示例: @Resource(name="bean的名称")<br> |
@Qualifier<br> | 指定要自动装配的对象名称,通常与@Autowired联合使用。<br> |
@PostConstruct<br> | 指定Bean实例完成初始化后调用的方法。<br> |
@PreDestroy<br> | 指定Bean实例销毁前调用的方法。<br> |
**建议:**虽然 @Component 可以替代@Controller 、@Service 、@Repository 注解,但实际开发时为了被标注的类本身用途更清晰,定义控制主组用@Controller注解,定义业务逻辑组件用@Service 注解,定义数据访问组件用@Repository 注解。
注解装配实现方法
第一步,在类定义前加上相关注解第二步,导入依赖包spring-aop.jar
第三步,spring的配置文件中
引入Context约束
,并
启动Bean的自动扫描功能
【示例8】阅读代码,理解注解装配的基本步骤。
首先,创建POJO类User,使用注解装配**(放com.bean.pojo包)**
package com.bean.pojo;
import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component;
@Component("user") //括号里是Bean对象名@Scope("singleton")
public class User { @Value("1")
private int id; @Value(" 张 小 小 ") private String name;
@Value("123")
private String password;
public int getId() { return id;
}
public void setId(int id) { this.id = id;
}
public String getName() { return name;
}
public void setName(String name) { this.name = name;
}
public String getPassword() { return password;
}
public void setPassword(String password) { this.password = password;
}
@Override
public String toString() { return "User{" +
"id=" + id +
", name='" + name + ''' +
", password='" + password + ''' + '}';
}
}
之后,设置spring配置文件spring-config.xml**(放resources文件夹)**,引入context约束,启动Bean 的自动扫描功能
<?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="Index of /schema/context" xsi:schemaLocation="Index of /schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd Index of /schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--启动Bean的自动扫描功能-->
<context:component-scan base-package="com.bean"/>
</beans>
最后,写测试代码
import com.bean.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestUser {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring- config.xml");
User user=context.getBean(User.class); System.out.println(user);
}
}
运行结果如下图:
【示例9】上示例8基础上,模拟基于MVC结构,User数据保存业务的实现。
- 定义Dao层:创建接口UserDao**(放com.bean.dao包)**
package com.bean.dao;
public interface UserDao {
//保存数据
public void save();
}
- 实现Dao层:创建类UserDaoImpl,实现接口UserDao**(放com.bean.dao包)**
package com.bean.dao;
import com.bean.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.stereotype.Repository;
@Repository("userDao")
public class UserDaoImpl implements UserDao{ @Override
public void save() {
ApplicationContext context = new ClassPathXmlApplicationContext("spring- config.xml");
User user=context.getBean(User.class); System.out.println(user);
System.out.println("执行了UserDaoImpl.save()");
}
}
- 定义Service层:创建接口UserService**(放com.bean.server包)**
package com.bean.service;
public interface UserService { public void save();
}
- 实现Service层:创建类UserServiceImpl**(放com.bean.server包)**
package com.bean.service;
import com.bean.dao.UserDao;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service("userService")
public class UserServiceImpl implements UserService { @Resource(name="userDao")
private UserDao userDao; @Override
public void save() { userDao.save();
System.out.println("执行了UserServiceImpl.save()");
}
}
- 定义Controller层:创建类UserController做为控制层**(放com.bean.controller包)**
package com.bean.controller;
import com.bean.service.UserService;
import org.springframework.stereotype.Controller;
import javax.annotation.Resource;
@Controller("userController") public class UserController {
@Resource(name="userService") private UserService userService; public void save(){
userService.save();
System.out.println("执行了UserController.save()");
}
}
- 设置spring配置文件,引入context,开启Bean的自动扫描**(同前!) **(7)定义测试类
import com.bean.controller.UserController; import com.bean.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestUser {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring- config.xml");
UserController user=(UserController)context.getBean("userController"); user.save();
}
}
**注意:**接口不用写注解!!
自动装配
如果不用注解注入,也可以使用Spring的<bean>元素的autowire属性完成自动装配
autowire属性取值如下
属性值<br> | 描述<br> |
---|---|
default(默认值)<br> | 由<bean>的上级元素<beans>的default-autowire属性值确定。<br> |
byName<br> | 根据<bean>元素id属性的值自动装配。<br> |
<br>byType<br> | 根据<bean>元素的数据类型(Type)自动装配,如果一个Bean的数据类型,兼 容另一个Bean中的数据类型,则自动装配。<br> |
constructor<br> | 根据构造函数参数的数据类型,进行byType模式的自动装配。<br> |
no<br> | 默认值,不使用自动装配,Bean依赖必须通过<ref>元素或ref属性定义。<br> |
【示例10】阅读代码,理解将第6单元“依赖注入的应用”案例中注入改为自动装配的spring配置方法。
<!--配置UserDao的bean-->
<bean id="userDao" class="com.introduce.dao.UserDaoImpl"/>
<!--配置UserService的bean,自动装配userDaoImpl属性-->
<bean id="userService" class="com.introduce.com.introduce.service.UserServiceImpl" autowire="byType">
六 、Bean的生命周期
- Bean在不同作用域内的生命周期
Bean的生命周期是指Bean实例被创建、初始化和销毁的过程
singleton作用域的Bean,由Spring容器管理其生命周期,控制着Bean的创建、初始化和销毁prototype作用域的Bean,Spring容器只负责创建Bean实例,不会管理其生命周期
- Bean生命周期的两个时间节点
Bean实例初始化后Bean实例销毁前
- 监控时间节点的两种方式XML配置文件方式
注解方式**(重点掌握)**
@PostConstruct:写在类的初始化方法上方。实例初始化后,会调用该方法。@PreDestory:写在类的注销方法上方。实例销毁前,会调用该方法。
【示例】阅读代码,理解@PostConstruct和@PreDestory的使用。
- 创建类Dog**(放com.bean包)**
package com.bean;
import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct; import javax.annotation.PreDestroy;
@Component
public class Dog {
@Value("牧羊犬")
private String name; @Value("2")
private int age;
public Dog() {
System.out.println("创建Dog实例...");
}
public String getName() { return name;
}
public void setName(String name) { this.name = name;
}
public int getAge() { return age;
}
public void setAge(int age) { this.age = age;
}
@Override
public String toString() {
return name+",今年"+age+"岁...";
}
@PostConstruct //该注解下的方法会在实例初始化后执行public void init(){
System.out.println("Dog实例初始化成功...");
}
@PreDestroy //该注解下的方法会在实例销毁前执行public void destroy(){
System.out.println("Dog实例即将销毁...");
}
}
- 设置spring配置文件spring-config.xml**(放resources文件夹)**
<?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="Index of /schema/context" xsi:schemaLocation="Index of /schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd Index of /schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--启动Bean的自动扫描功能-->
<context:component-scan base-package="com.bean"/>
</beans>
编写测试类(AbstractApplicationContext类的registerShutdownHook()方法销毁Spring中所有
Bean)
import com.bean.Dog;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.AbstractApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestDog {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring- config.xml");
Dog dog = context.getBean(Dog.class);
System.out.println("====================="); System.out.println(dog); System.out.println("=====================");
AbstractApplicationContext ac=(AbstractApplicationContext) context; ac.registerShutdownHook(); //销毁所有bean
}
}
运行结果如下图:
七 、课后练习
**目标:**考察知识点为BeanFactory接口、ApplicationContext接口、Bean的配置、构造方法实例化、静态工厂实例化、实例工厂实例化、singleton作用域、prototype作用域、基于XML的装配、基于注解的 装配、自动装配、Bean的生命周期)
**形式:**单独完成
**题目:**代码演示如何使用注解方式装配Bean,具体要求如下。
- 创建项目名称为spring-chap07-bean;
- 创建配置文件名称为applicationContext.xml;
- 创建包名为com.bean.XXX,controller层、service层、dao层要分别创建3个包; (4)具体业务自定。