目录
Spring 有两种类型 bean,一种普通 bean,另外一种工厂 bean(FactoryBean)。
普通 bean:在配置文件中的 id 定位 bean实例,定义的 class 类型就是返回类型。
工厂 bean:在配置文件定义 bean 类型可以和返回类型不一样。
通过两个步骤即可实现工厂 bean:
建立一个 Factory 类,获取 Course 实例对象。在 xml 中 bean 类型写的是 Factory 类,但是可以返回 Course(通过继承接口 FactoryBean
(1)代码
(1-1)Factory 类:
- package com.demo.factory;
-
- import com.demo.pojo.Course;
- import org.springframework.beans.factory.FactoryBean;
-
- public class Factory implements FactoryBean
{ - @Override
- public Course getObject() throws Exception {
- Course course = new Course();
- course.setName("course01");
- return course;
- }
-
- @Override
- public Class> getObjectType() {
- return null;
- }
-
- @Override
- public boolean isSingleton() {
- return FactoryBean.super.isSingleton();
- }
- }
(1-2)Course 类:
- package com.demo.pojo;
-
- public class Course {
- private String name;
-
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
-
- @Override
- public String toString() {
- return "Course{" +
- "name='" + name + '\'' +
- '}';
- }
- }
(1-3)FactoryBean.xml:
- "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="CourseFactory" class="com.demo.factory.Factory">
-
- bean>
-
- beans>
(1-4)测试代码
- import com.demo.pojo.Course;
- import org.junit.Test;
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
-
- public class FactoryTest {
- @Test
- public void test() {
- ApplicationContext context = new ClassPathXmlApplicationContext("FactoryBean.xml");
- Course course = context.getBean("CourseFactory", Course.class);
- System.out.println(course);
- }
- }
(2)输出结果

bean 的作用域,可以设置我们创建的 bean 实例,是单实例还是多实例。默认情况下,Spring 会将 bean 设置为单实例对象。
单例其实就在内存中该对象只有一个内存对应地址。无论你多少个线程访问那个对象,都是同一个地址。这样节省内存。
(1)单实例的输出结果


可以看到,两个实例的地址是一样的。说明该 bean 对象是一个单实例对象。
在 spring 配置文件中的 bean 标签里面有属性(scope)用于设置单实例还是多实例。
(1)scope 的属性值
(2)修改 scope = "prototype" 后

(3)singleton 和 prototype 区别
(4)属性值为 request 和 session
Bean 对象从创建到销毁的过程。有如下 5 个步骤:
(1)代码
(1-1)Course 类
- package com.demo.pojo;
-
- public class Course {
- private String name;
-
- public Course() {
- System.out.println("1.无参构造 bean 实例");
- }
-
- // 创建执行的初始化方法
- public void initFunc() {
- System.out.println("3.执行初始化方法");
- }
-
- // 创建销毁的方法
- public void destroyFunc() {
- System.out.println("5.执行销毁方法");
- }
-
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- System.out.println("2.调用 set 方法");
- this.name = name;
- }
-
- @Override
- public String toString() {
- return "Course{" +
- "name='" + name + '\'' +
- '}';
- }
- }
(1-2)LifeBean.xml
- "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="course" class="com.demo.pojo.Course" init-method="initFunc" destroy-method="destroyFunc">
- <property name="name" value="C++从入门到入土">property>
- bean>
-
- beans>
(1-3)测试代码
- import com.demo.pojo.Course;
- import org.junit.Test;
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
-
- public class LifeBeanTest {
- @Test
- public void test() {
- ApplicationContext context = new ClassPathXmlApplicationContext("LifeBean.xml");
- Course course = context.getBean("course", Course.class);
- System.out.println("4.getBean 对象:" + course);
- // 最后要手动销毁 bean 实例,Application 的子类才有 close 方法
- ((ClassPathXmlApplicationContext)context).close();
- }
- }
(2)输出结果

Bean 的生命周期除了前面的 5 个步骤外,还有两个步骤,分别位于初始化方法前和初始化方法后。
(1)创建类,实现接口 BeanPostProcessor
- package com.demo.impl;
-
- import org.springframework.beans.BeansException;
- import org.springframework.beans.factory.config.BeanPostProcessor;
-
- public class PostProcess implements BeanPostProcessor {
- @Override
- public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
- System.out.println("初始化之前执行 postProcessBeforeInitialization");
- return bean;
- }
-
- @Override
- public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
- System.out.println("初始化之后执行 postProcessAfterInitialization");
- return bean;
- }
- }
(2)xml 配置文件,配置后置处理器
- "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="course" class="com.demo.pojo.Course" init-method="initFunc" destroy-method="destroyFunc">
- <property name="name" value="C++从入门到入土">property>
- bean>
-
-
- <bean id="postProcess" class="com.demo.impl.PostProcess">bean>
- beans>
(3)输出结果

(1)bean 标签属性 autowire,配置自动装配:
(2)易错点
注意目标类路径所指向的类是否与配置的类一致,比如有很多 Util 类,不论在 java 文件中导入错误的类路径,还是在 xml 中写错了类路径,都无法匹配 set() 方法。
(1)代码
(1-1)Employee 类
- package com.demo.autowire;
-
- import com.demo.autowire.Department;
-
- public class Employee {
- private Department department;
-
- @Override
- public String toString() {
- return "Employee{" +
- "department=" + department +
- '}';
- }
-
- public Department getDepartment() {
- return department;
- }
-
- public void setDepartment(Department department) {
- this.department = department;
- }
- }
(1-2)Department 类
- package com.demo.autowire;
-
- public class Department {
- private String name;
-
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
-
- }
(1-3)AutoWireBean.xml:
- "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="employee" class="com.demo.autowire.Employee" autowire="byName">
-
- bean>
- <bean id="department" class="com.demo.autowire.Department">
- <property name="name" value="技术部门">property>
- bean>
-
- beans>
(1-4)测试代码
- import com.demo.autowire.Employee;
- import org.junit.Test;
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
-
- public class AutoWireBeanTest {
- @Test
- public void test() {
- ApplicationContext context = new ClassPathXmlApplicationContext("AutoWireBean.xml");
- Employee employee = context.getBean("employee", Employee.class);
- System.out.println(employee);
- System.out.println(employee.getDepartment().getName());
- }
- }
(2)输出结果

将 autowire = "byName" 改成 byType 即可。但是如果使用 byType,同一个类型就只能有 1 个 bean。
在实际开发中,更多情况下使用的是注解的方法来做到自动注入。
当一个类中的属性非常的多的时候,使用
下面以数据库的直接配置和引用外部文件作比较为例。
下面用一个自定义的 Druid 类,包含一个数据库连接池属性,然后通过写死配置信息的方式注入 Druid 的数据库连接池。
需要用到 druid 和 jdbc,jdbc 根据自己使用的数据库引入依赖,导 jar 包或者 maven 都可以。
(1)配置 druid 连接池
- "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="druid" class="com.demo.pojo.Druid">
- <property name="dataSource" ref="dataSource">property>
- bean>
-
- <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
- <property name="driverClassName" value="org.postgresql.Driver">property>
- <property name="url" value="jdbc:postgresql://localhost:5432/MyDatabase">property>
- <property name="username" value="postgres">property>
- <property name="password" value="123456">property>
- bean>
-
-
- beans>
(2)Druid 类(包含 DruidDataSource 属性)
- package com.demo.pojo;
-
- import com.alibaba.druid.pool.DruidDataSource;
-
- public class Druid {
- private DruidDataSource dataSource;
-
- public void setDataSource(DruidDataSource dataSource) {
- this.dataSource = dataSource;
- }
-
- public DruidDataSource getDataSource() {
- return dataSource;
- }
-
- }
(3)测试代码
- import com.alibaba.druid.pool.DruidDataSource;
- import com.demo.pojo.Druid;
- import org.junit.Test;
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
-
- public class DruidTest {
- @Test
- public void test1() {
- ApplicationContext context = new ClassPathXmlApplicationContext("Druid.xml");
- Druid druid = context.getBean("druid", Druid.class);
- try {
- DruidDataSource druidDataSource = druid.getDataSource();
- System.out.println(druidDataSource.getConnection());
- } catch (Exception e) {
- e.printStackTrace();
- }
-
- }
- }
(4)输出结果

引入外部配置文件,只需要在上面的基础上修改 xml 配置文件即可:
(1)修改 xml 文件
- "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 http://www.springframework.org/schema/util/spring-util.xsd
- http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
-
-
- <context:property-placeholder location="druid.properties"/>
-
- <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
- <property name="driverClassName" value="${prop.driverClassName}">property>
- <property name="url" value="${prop.url}">property>
- <property name="username" value="${prop.username}">property>
- <property name="password" value="${prop.password}">property>
- bean>
-
- <bean id="druid" class="com.demo.pojo.Druid">
- <property name="dataSource" ref="dataSource">property>
- bean>
-
-
- beans>
(2)duird.properties
- prop.url = jdbc:postgresql://localhost:5432/bookmarket
- prop.username = postgres
- prop.password = 123456
- prop.driverClassName = org.postgresql.Driver
注解是代码特殊标记,格式:@注解名称(属性名称=属性值, 属性名称=属性值..)
上面四个注解功能是一样的,都可以用来创建 bean 实例,只是为了区分不同的层次而用不同的名称。
(1)引入依赖 spring-aop-5.2.6.RELEASE

需要注意的是,此时 JDK 版本如果不兼容,就会报错:
(2)开启组件扫描,使得 spring 可以找到我们写的类
在 xml 配置文件中,添加 context 命名空间,然后写上:

(3)创建类,在类上面添加创建对象注解
@Component,包括另外几个注解,其中的 value 属性值,代表了
- package com.demo.service.impl;
-
- import com.demo.dao.UserDao;
- import com.demo.service.UserService;
- import org.springframework.stereotype.Component;
-
- @Component(value = "userService")
- public class UserServiceImpl implements UserService {
- private UserDao userDao;
-
- public UserDao getUserDao() {
- return userDao;
- }
-
- public void setUserDao(UserDao userDao) {
- this.userDao = userDao;
- }
-
- @Override
- public void func() {
- System.out.println("调用 UserService 的 func");
- userDao.func();
- }
- }
(4)测试代码
- import com.demo.service.UserService;
- import com.demo.service.impl.UserServiceImpl;
- import org.junit.Test;
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
-
- public class AopBeanTest {
- @Test
- public void test() {
- ApplicationContext context = new ClassPathXmlApplicationContext("AopBean.xml");
- UserService userService = context.getBean("userService", UserServiceImpl.class);
- userService.func();
- }
-
- }

前面我们知道,对于一个 base-package,Spring 会扫描这个目录下的所有 class。而通过添加其他配置,可以规定哪些需要扫描、哪些不需要扫描。
(1)use-default-filters
当它的值为 false 时,表示不使用默认的 filter,而使用自己配置的 filter。
(2)如何自己配置 filter
(2-1)设置需要扫描哪些内容
(2-2)设置不需要扫描哪些内容,而是扫描其他的所有内容
(3)内容如何规定
在 context:include-filter 和 context:exclude-filter 中,有两个属性配合使用:
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
表示目标内容就是带有注解 @Controller 的类。
@Autowired:根据属性类型进行自动装配。
其缺点在于,如果一个接口有多个实现类(比如有 DaoImpl1、DaoImpl2),那么就无法得知在 UserServiceImpl 中的属性 UserDao,具体应该被注入哪一个实现类,因此 @Autowired 只适合仅有一种实现类的情况。
(1)创建 service 和 dao 对象,在 service 和 dao 的 Impl 类中添加创建对象注解
Service 添加 @Service,Dao添加 @Repository。
(2)在 service 类添加 dao 类型属性,在属性上面使用注解
注意:不需要添加 set 方法,Spring 已经封装好 set 方法。
(3)代码
(3-1)UserDaoImpl 类
- package com.demo.dao.impl;
-
- import com.demo.dao.UserDao;
- import org.springframework.stereotype.Repository;
-
- @Repository
- public class UserDaoImpl implements UserDao {
- @Override
- public void func() {
- System.out.println("调用 UserDao 的 func");
- }
- }
(3-2)UserServiceImpl 类
- package com.demo.service.impl;
-
- import com.demo.dao.UserDao;
- import com.demo.service.UserService;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Service;
-
- @Service
- public class UserServiceImpl implements UserService {
- @Autowired
- private UserDao userDao;
-
- @Override
- public void func() {
- System.out.println("调用 UserService 的 func");
- userDao.func();
- }
- }
(3-3)AnnotationBean.xml
- "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 http://www.springframework.org/schema/context/spring-context.xsd">
-
- <context:component-scan base-package="com.demo" use-default-filters="false">
- <context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
- <context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
- context:component-scan>
-
- beans>
(3-4)测试代码
- import com.demo.service.UserService;
- import org.junit.Test;
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
-
- public class AnnotationBeanTest {
- @Test
- public void test() {
- ApplicationContext context = new ClassPathXmlApplicationContext("AnnotationBean.xml");
- UserService userService = context.getBean("userServiceImpl", UserService.class);
- userService.func();
- }
-
- }
(4)输出结果

@Qualifier:根据属性名称进行自动装配,并且需要跟 @Autowired 一起使用。
当有多个实现类,比如:Impl1、Impl2,这时候 @Autowired 配合上 @Qualifier(value = "Impl1"),就可以明确注入 Impl1 这个实现类。
简单来说,就是 @Autowired 确定了接口,Qualifier 确定了具体的实现类。
(1)在 AnnotationBean.xml 和 测试代码 不变的情况下,修改以下内容
(1-1)UserDaoImpl1 类
- package com.demo.dao.impl;
-
- import com.demo.dao.UserDao;
- import org.springframework.stereotype.Repository;
-
- @Repository(value = "userDaoImpl1")
- public class UserDaoImpl1 implements UserDao {
- @Override
- public void func() {
- System.out.println("调用 UserDaoImpl1 的 func");
- }
- }
(1-2)UserDaoImpl2 类
- package com.demo.dao.impl;
-
- import com.demo.dao.UserDao;
- import org.springframework.stereotype.Repository;
-
- @Repository(value = "userDaoImpl2")
- public class UserDaoImpl2 implements UserDao {
- @Override
- public void func() {
- System.out.println("调用 UserDaoImpl2 的 func");
- }
- }
(1-3)UserServiceImpl 类
- package com.demo.service.impl;
-
- import com.demo.dao.UserDao;
- import com.demo.service.UserService;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.beans.factory.annotation.Qualifier;
- import org.springframework.stereotype.Service;
-
- @Service
- public class UserServiceImpl implements UserService {
- @Autowired
- @Qualifier(value = "userDaoImpl2")
- private UserDao userDao;
-
- @Override
- public void func() {
- System.out.println("调用 UserService 的 func");
- userDao.func();
- }
- }
(2)输出结果

@Resource:可以根据类型注入,也可以根据名称注入。
需要注意的是,@Resource 是 javax 提供的注解,不是 Spring 官方提供的,因此建议还是使用 @Autowired 和 @Qualifier。
(1)修改 UserServiceImpl 类
- package com.demo.service.impl;
-
- import com.demo.dao.UserDao;
- import com.demo.service.UserService;
- import org.springframework.stereotype.Service;
-
- import javax.annotation.Resource;
-
- @Service
- public class UserServiceImpl implements UserService {
- @Resource(name = "userDaoImpl2")
- private UserDao userDao;
-
- @Override
- public void func() {
- System.out.println("调用 UserService 的 func");
- userDao.func();
- }
- }
(2)输出结果

@Value:注入普通类型属性。
不需要使用 xml 文件去配置,仅使用注解,就可以实现创建对象和属性注入。
(1)创建配置类,替代 xml 配置文件:
- package com.demo.config;
-
- import org.springframework.context.annotation.ComponentScan;
- import org.springframework.context.annotation.Configuration;
-
- @Configuration // 作为配置类,代替 xml 配置文件
- @ComponentScan(basePackages = {"com.demo"})
- public class SpringConfig {
-
- }
(2)测试代码
- import com.demo.config.SpringConfig;
- import com.demo.service.UserService;
- import org.junit.Test;
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.annotation.AnnotationConfigApplicationContext;
-
- public class AnnotationConfigTest {
- @Test
- public void test() {
- ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
- UserService userService = context.getBean("userServiceImpl", UserService.class);
- userService.func();
- }
-
- }
(3)输出结果
