目录
[二、基于 XML 管理 Bean](#二、基于 XML 管理 Bean)
[2、配置 Bean](#2、配置 Bean)
[3、获取 Bean](#3、获取 Bean)
[4、Spring 管理数据源](#4、Spring 管理数据源)
[三、基于注解管理 Bean](#三、基于注解管理 Bean)
[4、获取 Bean](#4、获取 Bean)
[1、Bean 的作用域](#1、Bean 的作用域)
[2、Bean 的生命周期](#2、Bean 的生命周期)
[3、Bean 的后置处理器](#3、Bean 的后置处理器)
一、含义
把自己创建资源、向环境索取资源变成环境将资源准备好,我们享受资源注入。
二、基于 XML 管理 Bean
1、准备
创建 Maven 工程,pom.xml 添加依赖。
XML
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.1</version>
</dependency>
</dependencies>
com.example.pojo 包下,创建实体类 Teacher、Clazz、Student。
java
public class Teacher { //Alt+Insert,一键生成构造器、Get、Set、toString 方法...
private Integer tid;
private String tname;
public Teacher() {
}
public Teacher(Integer tid, String tname) {
this.tid = tid;
this.tname = tname;
}
public void setTid(Integer tid) {
this.tid = tid;
}
public void setTname(String tname) {
this.tname = tname;
}
public Integer getTid() {
return tid;
}
public String getTname() {
return tname;
}
@Override
public String toString() {
return "Teacher{" +
"tid=" + tid +
", tname='" + tname + '\'' +
'}';
}
}
public class Clazz {
private Integer cid;
private String cname;
private List<Student> students;
public Clazz() {
}
public Clazz(Integer cid, String cname) {
this.cid = cid;
this.cname = cname;
}
public Integer getCid() {
return cid;
}
public String getCname() {
return cname;
}
public List<Student> getStudents() {
return students;
}
public void setCid(Integer cid) {
this.cid = cid;
}
public void setCname(String cname) {
this.cname = cname;
}
public void setStudents(List<Student> students) {
this.students = students;
}
@Override
public String toString() {
return "Clazz{" +
"cid=" + cid +
", cname='" + cname + '\'' +
", students=" + students +
'}';
}
}
public class Student {
private Integer sid;
private String sname;
private Integer age;
private String gender;
private Clazz clazz;
private String[] hobby;
private Map<String,Teacher> teacherMap;
public Student() {
}
public Student(Integer sid, String sname, Integer age, String gender) {
this.sid = sid;
this.sname = sname;
this.age = age;
this.gender = gender;
}
public Integer getSid() {
return sid;
}
public String getSname() {
return sname;
}
public Integer getAge() {
return age;
}
public String getGender() {
return gender;
}
public Clazz getClazz() {
return clazz;
}
public String[] getHobby() {
return hobby;
}
public Map<String, Teacher> getTeacherMap() {
return teacherMap;
}
public void setSid(Integer sid) {
this.sid = sid;
}
public void setSname(String sname) {
this.sname = sname;
}
public void setAge(Integer age) {
this.age = age;
}
public void setGender(String gender) {
this.gender = gender;
}
public void setClazz(Clazz clazz) {
this.clazz = clazz;
}
public void setHobby(String[] hobby) {
this.hobby = hobby;
}
public void setTeacherMap(Map<String, Teacher> teacherMap) {
this.teacherMap = teacherMap;
}
@Override
public String toString() {
return "Student{" +
"sid=" + sid +
", sname='" + sname + '\'' +
", age=" + age +
", gender='" + gender + '\'' +
", clazz=" + clazz +
", hobby=" + Arrays.toString(hobby) +
", teacherMap=" + teacherMap +
'}';
}
}
2、配置 Bean
右击目录 resources,依次选择 New -> XML Configuration File -> Spring Config,创建 Spring 配置文件 applicationContext.xml。
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"
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
https://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!--
bean 标签:用于配置 bean,将对象交给 IOC 容器管理,其中
id 用于设置 bean 的唯一标识;class 用于设置 bean 对应类型的全类名。
-->
<bean id="student" class="com.example.pojo.Student">
<!--
property 标签:用于给对象的属性赋值,其中 name 用于设置属性名;value 用于设置属性值;
ref 用于设置要引用的 IOC 容器中 bean 的 id,从而将 bean 对应对象赋值给当前属性。
此时,Spring 使用 bean 对应类型的全类名,通过反射调用无参构造创建对象,再通过 setXxx() 为对象的属性赋值。
-->
<property name="sid" value="1001"></property>
<property name="sname" value="张三"></property>
<!-- 为属性赋 null 值 -->
<property name="age">
<null />
</property>
<!-- 为属性赋含有 >、<等符号 的值,使用 CDATA 节将内容原样解析 -->
<property name="gender">
<value><![CDATA[<男>]]></value>
</property>
<!-- 通过引用外部已声明的 bean,为属性赋 类类型 的值 -->
<!--<property name="clazz" ref="clazz"></property>-->
<!-- 通过内部 bean,为属性赋 类类型 的值 -->
<property name="clazz">
<!-- 内部 bean 仅用于给属性赋值,不能在外部通过 IOC 容器获取,因此可以省略 id 属性 -->
<bean id="clazzInner" class="com.example.pojo.Clazz">
<property name="cid" value="3002"></property>
<property name="cname" value="飞船班"></property>
</bean>
</property>
<!-- 为属性赋 数组类型 的值 -->
<property name="hobby">
<array>
<value>抽烟</value>
<value>喝酒</value>
<value>烫头</value>
</array>
</property>
<!-- 通过引用外部已声明的集合类型的 bean,为属性赋 Map 集合类型 的值 -->
<!--<property name="teacherMap" ref="teacherMap"></property>-->
<!-- 通过 map 标签,为属性赋 Map 集合类型 的值 -->
<property name="teacherMap">
<map>
<entry key="2001" value-ref="teacherOne"></entry>
<entry key="2002" value-ref="teacherTwo"></entry>
</map>
</property>
</bean>
<bean id="clazz" class="com.example.pojo.Clazz">
<property name="cid" value="3001"></property>
<property name="cname" value="火箭班"></property>
<!-- 通过引用外部已声明的集合类型的 bean,为属性赋 List 集合类型 的值 -->
<!--<property name="students" ref="studentList"></property>-->
<!-- 通过 list 标签,为属性赋 List 集合类型 的值 -->
<property name="students">
<list>
<ref bean="student"></ref>
</list>
</property>
</bean>
<!-- 通过 util 约束,配置集合类型的 bean -->
<util:list id="studentList">
<ref bean="student"></ref>
</util:list>
<!-- 通过 util 约束,配置集合类型的 bean -->
<util:map id="teacherMap">
<entry key="2001" value-ref="teacherOne"></entry>
<entry key="2002" value-ref="teacherTwo"></entry>
</util:map>
<bean id="teacherOne" class="com.example.pojo.Teacher">
<!--
constructor-arg 标签:用于指定有参构造,其中 name 用于设置参数名;value 用于设置参数值。
此时,Spring 使用 bean 对应类型的全类名,通过反射调用与 constructor-arg 标签参数匹配的有参构造创建对象。
-->
<constructor-arg name="tid" value="2001"></constructor-arg>
<constructor-arg name="tname" value="李白"></constructor-arg>
</bean>
<bean id="teacherTwo" class="com.example.pojo.Teacher">
<property name="tid" value="2002"></property>
<property name="tname" value="陶渊明"></property>
</bean>
</beans>
要点精炼:基于 XML 配置 Bean,依赖注入之 setter 注入、依赖注入之构造器注入,为属性赋字面量类型的值、null 值、含有特殊符号的值、类类型的值、数组类型的值、List 集合类型的值、Map 集合类型的值。
3、获取 Bean
根据 id、类型、id & 类型,从 IOC 容器中获取 Bean 对象。
java
//通过读取类路径下 XML 格式的配置文件,创建 IOC 容器对象
ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
//(1)根据 id,从 IOC 容器中获取 bean 对象
Student studentOne = (Student) ioc.getBean("student");
/*
(2)根据类型,从 IOC 容器中获取 bean 对象
注意:要求容器中有且仅有一个类型匹配的 bean,否则报错 NoSuchBeanDefinitionException/NoUniqueBeanDefinitionException;
满足 bean 唯一性的前提下,通过 bean 的类型、bean 所继承的类的类型、bean 所实现的接口的类型均可获取 bean 对象。
*/
Student studentTwo = ioc.getBean(Student.class);
//(3)根据类型 & id,从 IOC 容器中获取 bean 对象
Teacher teacher = ioc.getBean("teacherOne", Teacher.class);
4、Spring 管理数据源
pom.xml 添加依赖。
XML
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.16</version>
</dependency>
<!-- 数据源 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.31</version>
</dependency>
右击目录 resources,依次选择 New -> Resource Bundle,创建属性配置文件 jdbc.properties。
java
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2b8
jdbc.username=root
jdbc.password=root
jdbc.initialSize=10
jdbc.maxActive=20
jdbc.maxWait=5000
Spring 配置文件 applicationContext.xml,配置数据源。
XML
<!-- 配置 Alibaba Druid 数据库连接池 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
<!-- 数据库连接池,初始化连接数,默认为 0 -->
<property name="initialSize" value="${jdbc.initialSize}"></property>
<!-- 数据库连接池,最大连接数,默认为 8 -->
<property name="maxActive" value="${jdbc.maxActive}"></property>
<!-- 从数据库连接池获取连接的最大等待时间,默认为 -1(无限等待) -->
<property name="maxWait" value="${jdbc.maxWait}"></property>
</bean>
<!-- 引入外部属性文件 -->
<context:property-placeholder location="jdbc.properties"></context:property-placeholder>
根据类型,从 IOC 容器中获取数据源。
java
//通过读取类路径下 XML 格式的配置文件,创建 IOC 容器对象
ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
//根据类型,从 IOC 容器中获取 Druid 数据库连接池对象
DruidDataSource dataSource = ioc.getBean(DruidDataSource.class);
//从数据库连接池,获取数据库连接对象
System.out.println(dataSource.getConnection());
5、自动装配
IOC 容器按照既定规则,匹配某个 bean,为指定 bean 依赖的类类型或接口类型属性赋值的过程。
通过 bean 标签的属性 autowire 设置自动装配的规则,值 no、default 表不装配,属性使用默认值;byType 表根据 setter 参数类型匹配 bean,为属性赋值,无匹配则不装配,多个匹配则抛 NoUniqueBeanDefinitionException;byName 表截取 setter 名称作为 bean 的 id 匹配 bean,为属性赋值,无匹配则不装配。
值得注意的是,基于 XML 配置 Bean,设置 autowire 为 byType / byName,Spring 会先调用无参构造创建对象,再 set 注入,为对象类类型或接口类型的属性赋值;字符串、数字等字面量类型的属性,跳过不赋值,需手动 property 赋值。
三、基于注解管理 Bean
1、准备
创建 Maven 工程,pom.xml 添加依赖。
XML
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.1</version>
</dependency>
</dependencies>
2、标记
com.example.controller 包下,创建类 UserController,添加注解 @Controller 将其标识为控制层组件。
java
@Controller
public class UserController {
@Autowired
private UserService userService;
//添加用户信息
public void saveUser(){
userService.saveUser();
}
}
com.example.service 包下,创建接口 UserService;com.example.service.impl 包下,创建实现类 UserServiceImpl,添加注解 @Service 将其标识为业务层组件。
java
public interface UserService {
//保存用户信息
void saveUser();
}
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
public void saveUser() {
userDao.saveUser();
}
}
com.example.dao 包下,创建接口 UserDao;com.example.dao.impl 包下,创建实现类 UserDaoImpl,添加注解 @Repository 将其标识为持久层组件。
java
public interface UserDao {
//保存用户信息
void saveUser();
}
@Repository
public class UserDaoImpl implements UserDao {
@Override
public void saveUser() {
System.out.println("保存成功!");
}
}
此外,注解 @Component 用于将类标识为普通组件,@Controller、@Service、@Repository 都是其派生注解,基本功能一致,仅语义分层区别,方便开发者区分代码层次;注解只是做标记,为了让 Spring 知道开发者在哪些地方标记了什么注解,还需要配置组件扫描。
3、扫描
右击目录 resources,依次选择 New -> XML Configuration File -> Spring Config,创建 Spring 配置文件 applicationContext.xml。
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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 扫描组件 -->
<context:component-scan base-package="com.example">
<!--
不扫描谁
type 用于设置排除依据,值 annotation 表根据注解排除,此时 expression 用于设置要排除的注解的全类名;
值 assignable 表根据类型排除,此时 expression 为要排除的类型的全类名;
SSM 整合时,springMVC 只扫描控制层,spring 扫描其他层。
-->
<!--<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>-->
<!--<context:exclude-filter type="assignable" expression="com.example.controller.UserController"/>-->
<!--
只扫描谁,此时需要在 context:component-scan 标签中设置属性 use-default-filters 的值为 false
use-default-filters 属性默认值为 true,表所设置包下的所有类都需要扫描,此时可以使用排除扫描
use-default-filters 属性设置值为 false,表所设置包下的所有类都不需要扫描,此时可以使用包含扫描
-->
<!--<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>-->
<!--<context:include-filter type="assignable" expression="com.example.controller.UserController"/>-->
</context:component-scan>
</beans>
通过 注解 & 扫描 配置的 bean,id 默认为类的小驼峰(如:userController),可通过标识类的注解的 value 属性值设置 bean 的自定义 id(如:@Controller("controller"));此时,Spring 通过反射调用默认无参构造创建对象。
4、获取 Bean
根据 id、类型、id & 类型,从 IOC 容器中获取 Bean 对象。
java
//通过读取类路径下 XML 格式的配置文件,创建 IOC 容器对象
ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
//(1)根据 id,从 IOC 容器中获取 bean 对象
UserController userController = (UserController) ioc.getBean("userController");
//(2)根据类型,从 IOC 容器中获取 bean 对象
UserService userService = ioc.getBean(UserService.class);
//(3)根据类型 & id,从 IOC 容器中获取 bean 对象
UserDao userDao = ioc.getBean("userDaoImpl", UserDao.class);
5、自动装配
成员变量添加 @Autowired 注解,即可完成自动装配。
Spring 默认根据成员变量的类型,在 IOC 容器中匹配某个 bean,为成员变量赋值。若有多个类型匹配的 bean,则将成员变量的变量名作为 bean 的 id,在 IOC 容器中匹配某个 bean,为成员变量赋值;若有多个类型匹配的 bean,但这些 bean 的 id 均与要赋值成员变量的变量名不一致,则抛 NoUniqueBeanDefinitionException,此时,可以在要赋值成员变量的变量名上添加注解 @Qualifier,通过该注解 value 属性值指定某个 bean 的 id,为成员变量赋值。若没有任何一个类型匹配的 bean,则抛 NoSuchBeanDefinitionException,因为 @Autowired 注解中属性 required 默认值为 true,要求必须完成自动装配,可以将属性 required 设置为 false,此时能装配则装配,无法装配则使用成员变量默认值。
值得注意的是,基于注解配置 Bean,成员变量添加 @Autowired 注解,Spring 会先调用默认无参构造创建对象,再利用反射直接给成员变量赋值,不属于 setter 注入、构造器注入;而 @Autowired 注解也可标记在 set 方法、构造器上,此处不做探讨。
四、其他知识
1、Bean 的作用域
规定 Spring IOC 容器,何时创建对象、创建多少个对象,以及对象的生效范围。基于 XML 管理 Bean,通过 bean 标签的属性 scope 设置 bean 的作用域,值 singleton 表单例(默认)、prototype 表多例(每次获取均为新对象);基于注解管理 Bean,通过注解 @Scope 设置 bean 的作用域,值 ConfigurableBeanFactory.SCOPE_SINGLETON 表单例(默认)、ConfigurableBeanFactory.SCOPE_PROTOTYPE 表多例(每次获取均为新对象)。
2、Bean 的生命周期
bean 为单例,获取 IOC 容器时,创建对象、依赖注入、执行后置处理器的 postProcessBeforeInitialization() 方法、执行初始化方法、执行后置处理器的 postProcessAfterInitialization() 方法;IOC 容器关闭时,执行销毁方法,代码中没有地方持有这个 bean 的引用后,等待 JVM 发生 GC 回收对象。
bean 为多例,获取 bean 时,创建对象、依赖注入、执行后置处理器的 postProcessBeforeInitialization() 方法、执行初始化方法、执行后置处理器的 postProcessAfterInitialization() 方法;不会执行销毁方法,代码中没有地方持有这个 bean 的引用后,等待 JVM 发生 GC 回收对象。
基于 XML 管理 Bean,通过 bean 标签的属性 init-method 设置 bean 的初始化方法,destroy-method 设置 bean 的销毁方法;基于注解管理 Bean,通过注解 @PostConstruct 设置 bean 的初始化方法,@PreDestroy 设置 bean 的销毁方法。
3、Bean 的后置处理器
用于在 Bean 生命周期初始化前后,添加额外操作。创建类 MyBeanPostProcessor 实现 BeanPostProcessor 接口,重写 postProcessBeforeInitialization()、postProcessAfterInitialization() 方法;
java
public class MyBeanPostProcessor implements BeanPostProcessor { //Ctrl+O,选择需要实现的方法
//在 bean 生命周期初始化之前执行,参数 bean 为当前 IOC 容器管理的 bean、beanName 为 bean 的 id
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
//在 bean 生命周期初始化之后执行,参数 bean 为当前 IOC 容器管理的 bean、beanName 为 bean 的 id
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
Spring 配置文件 applicationContext.xml,配置后置处理器。此处以 XML 方式进行配置,也可使用注解方式配置 bean。
XML
<bean class="com.example.process.MyBeanPostProcessor"></bean>
值得注意的是,Bean 的后置处理器不是单独针对某一个 bean 生效,而是针对 IOC 容器中所有 bean 都会执行。
4、FactoryBean
Spring 提供的工厂 Bean 接口,用于封装复杂对象的创建逻辑。com.example.pojo 包下,创建实体类 User。
java
public class User {
private Integer id;
private String username;
private String password;
private Integer age;
public User() {
}
public User(Integer id, String username, String password, Integer age) {
this.id = id;
this.username = username;
this.password = password;
this.age = age;
}
public Integer getId() {
return id;
}
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
public Integer getAge() {
return age;
}
public void setId(Integer id) {
this.id = id;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", age=" + age +
'}';
}
}
创建类 UserFactoryBean 实现 FactoryBean 接口,泛型配置为最终交给 IOC 容器管理的对象类型,重写 getObject()、getObjectType()、isSingleton() 方法;
java
public class UserFactoryBean implements FactoryBean<User> { //指定泛型为最终交给 IOC 容器管理的对象类型
@Override
public User getObject() throws Exception {
//在这里手写复杂创建逻辑!
return new User(); //返回最终交给 IOC 容器管理的对象
}
@Override
public Class<?> getObjectType() {
return User.class; //返回最终交给 IOC 容器管理的对象的类型
}
@Override
public boolean isSingleton() {
return true; //设置最终交给 IOC 容器管理的对象是否单例,true表单例(默认)、false表多例
}
}
Spring 配置文件 applicationContext.xml,配置 UserFactoryBean。此处以 XML 方式进行配置,也可使用注解方式配置 bean。
XML
<bean id="userBean" class="com.example.factory.UserFactoryBean"></bean>
根据 id,从 IOC 容器中获取 Bean 对象。
java
//通过读取类路径下 XML 格式的配置文件,创建 IOC 容器对象
ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
//根据 id,从 IOC 容器中获取 bean 对象
User user = (User) ioc.getBean("userBean");
//加 & 符号,从 IOC 容器中获取工厂本身 UserFactoryBean 对象
UserFactoryBean userFactoryBean = (UserFactoryBean) ioc.getBean("&userBean");
值得注意的是,BeanFactory 是 IOC 容器最顶层根接口,是 Bean 的工厂,负责创建、获取、管理所有 Bean 对象;而 FactoryBean 是 Spring 提供的工厂 Bean 接口,用于封装复杂对象的创建逻辑,Spring 先实例化 FactoryBean 工厂,再调用 getObject() 获取目标对象放入容器,对外提供使用,工厂本身也可通过 id 加 & 前缀,从 IOC 容器中获取。
本文相关项目,请关注 程序员钟霖 ,回复 CSDN获取。
友情链接
【附安装包】Java/JDK介绍、下载、安装、配置与使用(保姆级教程)_java jdk-CSDN博客