Spring框架
一、Spring概述
Spring是Java SE/EE轻量级开源框架。它最为核心的理念是IoC(控制反转)和AOP(面向切面编程),其中,IoC是Spring的基础,它支撑着Spring对JavaBean的管理功能;AOP是Spring 的重要特性,AOP是通过预编译方式和运行期间动态代理实现程序功能,也就是说可以在不修改源代码的情况下,给程序统一添加功能。
1.1. Spring框架的作用
-
非侵入式设计:Spring框架的API不会在业务逻辑上出现,业务逻辑代码也可以从Spring框架快速地移植到其他框架。
-
降低耦合性,方便开发:所有对象的创建和依赖关系的维护工作都交给Spring容器管理,降低了组件之间耦合性。
-
支持AOP编程
-
支持声明式事务:通过Spring配置文件管理数据库事务
-
方便程序的测试:Spring提供了对Junit的支持
-
方便集成各种优秀框架
-
降低Java EE API的使用难度:Spring对Java EE开发中的一些API都进行了封装,大大降低了这些API的使用难度。
1.2. Spring的体系结构
1.核心容器模块(Core Container)
-
Beans模块。它提供了BeanFactory类,是工厂模式的经典实现,Beans模块的主要作用是创建和管理Bean对象。
-
Core模块。它提供了Spring框架的基本组成部分,包括IoC和DI功能。
-
Context模块。它构建于Beans模块和Core模块的基础之上,它可以通过ApplicationContext接口提供上下文信息。
-
SpEL模块。提供了对SpEL表达式语言的支持,SpEL表达式语言是一个在程序运行时支持操作对象图的表达式语言。
2.数据访问及集成模块(Data Access/Integration)
-
JDBC模块。它提供了一个JDBC的抽象层,消除了冗长的JDBC编码并能够解析数据库供应商特有的错误代码。
-
ORM模块。它为主流的对象关系映射API提供了集成层,用于集成主流的对象关系映射框架。
-
OXM模块。它提供了对XML映射的抽象层的支持,如JAXB、Castor等。
-
JMS模块。它主要用于传递消息,包含消息的生产和消费。
-
Transactions模块。它的主要功能是事务管理。
3.Web模块
-
Web模块。它提供了针对Web开发的集成特性,如大部分文件上传功能等。此外,Web模块还包含一个HTTP客户端和Spring远程处理支持的Web相关部分。
-
Servlet模块。它提供了Spring的模型、视图、控制器以及Web应用程序的REST Web服务实现。
-
WebSocket模块。它是Spring 4.0以后新增的模块,它提供了WebSocket 和SockJS的实现,以及对STOMP的支持。
-
Portlet模块。它类似Servlet模块的功能,提供了Portlet环境下的MVC实现。
4.其他模块
-
AOP模块。它提供了对面向切面编程的支持,程序可以定义方法拦截器和切入点,将代码按照功能进行分离,以降低程序的耦合性。
-
Aspects模块。它提供了与AspectJ集成的支持。
-
Instrumentation模块。它提供了对类工具的支持,并且实现了类加载器,该模块可以在特定的应用服务器中使用。
-
Messaging模块。它是Spring 4.0以后新增的模块,它提供了对消息传递体系结构和协议的支持。
-
Test模块。它提供了对程序单元测试和集成测试的支持。
1.3 Spring5的新特征
-
更新JDK基线:JDK 8以上
-
修订核心框架:反射增强,新注解(@Nullable和@NotNull)
-
更新核心容器:支持候选组件索引作为类路径扫描的替代方案。从索引读取实体类,会使加载组件索引开销更低
-
支持响应式编程
-
支持函数式Web框架:HandlerFunction和RouterFunction
-
支持Kotlin
-
提升测试功能:完全支持Junit 5 Jupiter
二、Spring-控制反转与依赖注入
**2.1.控制反转定义(IOC):**IoC控制反转机制指的是对象由Ioc容器统一管理,当程序需要使用对象时,可以直接从IoC容器中获取。这样对象的控制权就从应用程序转移到了IoC容器。它是借助于IoC容器实现具有依赖关系对象之间的解耦,各个对象类封装之后,通过IoC容器来关联这些对象类。
**2.2.依赖注入定义(DI):**由IoC容器在运行期间动态地将某种依赖资源注入对象之中。例如,将对象B注入(赋值)给对象A的成员变量。依赖注入的基本思想是:明确地定义组件接口,独立开发各个组件,然后根据组件的依赖关系组装运行。
2.3.区别:
- 依赖注入是从应用程序的角度描述,即应用程序依赖IoC容器创建并注入它所需要的外部资源;
- 控制反转是从IoC容器的角度描述,即IoC容器控制应用程序,由IoC容器反向地向应用程序注入应用程序所需要的外部资源。
2.4.依赖注入的实现方法
- 构造方法注入
构造方法注入是指Spring容器调用构造方法注入被依赖的实例,构造方法可以是有参的或者是无参的。Spring在读取配置信息后,会通过反射方式调用实例的构造方法,如果是有参构造方法,可以在构造方法中传入所需的参数值,最后创建类对象。
<bean id="user1" class="com.itheima.User1">
<constructor-arg name="id" value="1"></constructor-arg>
<constructor-arg name="name" value="张三"></constructor-arg>
<constructor-arg name="password"value="123"></constructor-arg>
</bean>
- 设置方法注入
它是在被注入的类中声明一个setter方法,通过setter方法的参数注入对应的值。
<bean id="user2" class="com.itheima.User2">
<property name="id" value="2"></property>
<property name="name" value="李四"></property>
<property name="password" value="456"></property>
</bean>
三、Spring中的Bean的管理
控制反转和依赖注入都是通过Bean实现的,Bean是注册到Spring容器中的Java类,任何一个Java类都可以是一个Bean。Bean由Spring进行管理。
3.1 Spring IoC容器
1.BeanFactory接口
BeanFactory接口是Spring容器最基本的接口,它的实现机制采用的是Java经典的工厂模式。BeanFactory接口提供了创建和管理Bean的方法。
方法名称 | 描述 |
---|---|
getBean(String name) | 根据bean的id或name获取Bean |
getBean(String name, Class type) | 根据bean的name或id、参数类型获取Bean |
Spring提供了几个BeanFactory接口的实现类,其中最常用的是XmlBeanFactory,它可以读取XML文件并根据XML文件中的配置信息生成BeanFactory接口的实例
BeanFactory beanFactory = new XmlBeanFactory(new FileSystemResource(“D:/applicationContext.xml”));
2.ApplicationContext接口
ApplicationContext接口建立在BeanFactory接口的基础之上,它丰富了BeanFactory接口的特性,例如,添加了对国际化、资源访问、事件传播等方面的支持。
- ApplicationContext接口的常用实现类
类名称 | 描述 |
---|---|
ClassPathXmlApplicationContext | 从类路径加载配置文件实例化接口 |
FileSystemXmlApplicationContext | 从文件系统加载配置文件,实例化接口 |
AnnotationConfigApplicationContext | 从注解中加载配置文件,实例化接口 |
WebApplicationContext | 在Web应用中使用,从相对于Web根目录的路径中加载配置文件,实例化ApplicationContext接口 |
ConfigurableWebApplicationContext | 扩展了WebApplicationContext类,它可以通过读取XML配置文件的方式实例化WebApplicationContext类 |
3.2 Bean的配置
Spring容器支持XML和Properties两种格式的配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
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
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
</beans>
<bean>
元素的常用属性
属性 | 描述 |
---|---|
id | id属性是元素的唯一标识符,Spring容器对Bean的配置和管理通过id属性完成,装配Bean时也需要根据id值获取对象。 |
name | name属性可以为Bean指定多个名称,每个名称之间用逗号或分号隔开。 |
class | class属性可以指定Bean的具体实现类,其属性值为对象所属类的全路径。 |
scope | scope属性用于设定Bean实例的作用范围,其属性值有:singleton(单例)、prototype(原型)、request、session和global session。 |
<bean>
元素的常用子元素
元素 | 描述 |
---|---|
<constructor-arg> | 使用<constructor-arg> 元素可以为Bean的属性指定值(构造注入)。 index:用于设置构造参数的序号 type:用于指定构造参数的类型 **注意:**在使用有参的注入时,无需关注参数顺序 |
<property> | <property> 元素的作用是调用Bean实例中的setter方法完成属性赋值,从而完成依赖注入(setter注入)。 |
ref | ref是<property> 、<constructor-arg> 等元素的属性,可用于指定Bean工厂中某个Bean实例的引用。 |
value | value是<property> 、<constructor-arg> 等元素的属性,用于直接指定一个常量值。 |
<list> | 元素是等元素的子元素,用于指定Bean的属性类型为List或数组。 |
<set> | 元素是等元素的子元素,用于指定Bean的属性类型为set。 |
<map> | |
<entry> | 元素是 |
3.3 Bean的实例化
- 构造方法实例化
Spring容器通过Bean对应类中默认的无参构造方法来实例化Bean
<bean id="user1" class="com.tyut.example02.pojo.User"/>
public class User {
private String username;
private String password;
public User() {
System.out.println("一个用户已经被构造");
}
}
@Test
public void test03() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
User user1 = (User) applicationContext.getBean("user1");
}
- 静态工厂实例化
使用静态方法创建Bean的实例时,Bean的class指向的是静态工厂类,用Bean中的factory-method指定定义的静态方法
<bean id="userFactory" class="com.tyut.example02.utils.UserFactory" factory-method="getUser"/>
public class UserFactory {
public static User getUser() {
return new User();
}
}
@Test
public void test04() {
ApplicationContext applicationContext =
new ClassPathXmlApplicationContext("applicationContext.xml");
User user1 = (User) applicationContext.getBean("userFactory");
System.out.println(user1);
}
- 实例工厂实例化
直接创建Bean的实例,在XML配置文件中,不使用class属性直接指向Bean实例所属的类,通过factory-bean指向Bean的实例工厂,factory-method指定调用工厂的实例方法
<!-- 指定要创建的实例工厂 -->
<bean id="userFactory" class="com.tyut.example02.utils.UserFactory"/>
<!-- 通过实例工厂创建对象:factory-bean指定实例工厂,factory-method指定实例方法 -->
<bean id="user" factory-bean="userFactory" factory-method="getUser"/>
public class UserFactory {
public User getUser() {
return new User();
}
}
@Test
public void test05() {
ApplicationContext applicationContext =
new ClassPathXmlApplicationContext("applicationContext.xml");
User user1 = (User) applicationContext.getBean("user");
System.out.println(user1);
}
3.4 Bean的作用域
作用域 | 描述 |
---|---|
singleton | 单例模式。在单例模式下,Spring 容器中只会存在一个共享的Bean实例,所有对Bean的请求,只要请求的id(或name)与Bean的定义相匹配,会返回Bean的同一个实例。 |
prototype | 原型模式,每次从容器中请求Bean时,都会产生一个新的实例。 |
request | 每一个HTTP请求都会有自己的Bean实例,该作用域只能在基于Web的Spring ApplicationContext中使用。 |
session | 每一个HTTPsession请求都会有自己的Bean实例,该作用域只能在基于Web的Spring ApplicationContext中使用。 |
global session | 限定一个Bean的作用域为Web应用(HTTPsession)的生命周期,只有在Web应用中使用Spring时,该作用域才有效。 |
- singleton 作用域
singleton是Spring容器默认的作用域,当Bean的作用域为singleton时,Spring容器就只会存在一个共享的Bean实例,该实例可重复使用。
Spring容器管理着Bean的生命周期,包括Bean的创建、初始化、销毁。因为创建和销毁Bean实例会带来一定的系统开销,所以singleton作用域可避免反复创建和销毁实例造成的资源消耗。
<bean id="scope" class="com.tyuy.scope.Scope" scope="singleton"/>
- prototype作用域
当Bean的作用域为prototype时,每次对Bean请求时都会创建一个新的Bean实例。
Spring容器只负责创建Bean实例而不再管理其生命周期。
<bean id="scope" class="com.itheima.tyut.Scope" scope=" prototype "/>
3.5 Bean的装配
Bean的装配就是Bean的依赖注入,Bean的装配方式即Bean依赖注入的方式。
- 基于XML的装配
在Spring实例化Bean的过程中,spring首先会调用Bean的默认构造方法来实例化Bean对象,然后通过反射的方式调用setter方法来注入属性值。
setter注入(property)
(1)Bean类必须提供一个默认的无参构造方法。
(2)Bean类必须为需要注入的属性提供对应的setter方法。
构造方法注入(constructor-arg)
(1)Bean类中有一个有参的构造函数
基于XML开发案例:
目标:做一个登录模拟,username=zhangsan,password = 123456
- pom.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>ssm-demo01</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- spring的基础包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.3.28</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.3.28</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.28</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>5.3.28</version>
</dependency>
<!-- spring其他依赖包 -->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<!-- junit测试包 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
- pojo包
public class User {
private String username;
private String password;
public User(String username, String password) {
this.username = username;
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
- dao包
public interface UserDao {
public User selectUserByUsername(String username);
}
public class UserDaoImpl implements UserDao{
@Override
public User selectUserByUsername(String username) {
// 模拟数据库查询操作
if ("zhangsan".equals(username)) {
return new User("zhangsan", "123456");
}
return null;
}
}
- service包
public interface UserService {
public void login(String username, String password);
}
public class UserServiceImpl implements UserService{
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void login(String username, String password) {
User user = userDao.selectUserByUsername(username);
if ("zhangsan".equals(user.getUsername()) && password.equals(user.getPassword())) {
System.out.println("登录成功");
} else {
System.out.println("登录失败");
}
}
}
- spring配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
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
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="userDao" class="com.tyut.example02.dao.UserDaoImpl"/>
<bean id="userService" class="com.tyut.example02.service.UserServiceImpl">
<property name="userDao" ref="userDao"></property>
</bean>
</beans>
- 测试类
public class Test01 {
@Test
public void test02() {
//初始化Spring容器,加载配置项
ApplicationContext applicationContext =
new ClassPathXmlApplicationContext("applicationContext.xml");
//通过容器加载实例
UserService userService = (UserService) applicationContext.getBean("userService");
userService.login("zhangsan", "123456");
userService.login("zhangsan", "1234");
}
}
- 基于注解(Annotation)的装配
注解 | 描述 |
---|---|
@Component | 指定一个普通的Bean,可以作用在任何层次。 |
@Controller | 指定一个控制器组件Bean,用于将控制层的类标识为Spring中的Bean,功能上等同于@Component。 |
@Service | 指定一个业务逻辑组件Bean,用于将业务逻辑层的类标识为Spring中的Bean,功能上等同于@Component。 |
@Repository | 指定一个数据访问组件Bean,用于将数据访问层的类标识为Spring 中的Bean,功能上等同于@Component。 |
@Scope | 指定Bean实例的作用域。 |
@Value | 指定Bean实例的注入值。 |
@Autowired | 指定要自动装配的对象。 |
@Resource | 指定要注入的对象。 |
@Qualifier | 指定要自动装配的对象名称,通常与@Autowired联合使用。 |
@PostConstruct | 指定Bean实例完成初始化后调用的方法。 |
@PreDestroy | 指定Bean实例销毁前调用的方法。 |
案例:将上述XML开发转换成注解开发
- pom.xml
<!-- aop依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.3.28</version>
</dependency>
- pojo
public class User {
private String username;
private String password;
public User(String username, String password) {
this.username = username;
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
- dao
public interface UserDao {
public User selectUserByUsername(String username);
}
@Repository("userDao")
public class UserDaoImpl implements UserDao {
@Override
public User selectUserByUsername(String username) {
// 模拟数据库查询操作
if ("zhangsan".equals(username)) {
return new User("zhangsan", "123456");
}
return null;
}
}
- Service
public interface UserService {
public void login(String username, String password);
}
@Service("userService")
public class UserServiceImpl implements UserService {
@Resource(name = "userDao")
private UserDao userDao;
@Override
public void login(String username, String password) {
User user = userDao.selectUserByUsername(username);
if ("zhangsan".equals(user.getUsername()) && password.equals(user.getPassword())) {
System.out.println("登录成功");
} else {
System.out.println("登录失败");
}
}
}
- controller
@Controller("userController")
public class UserController {
@Resource(name = "userService")
private UserService userService;
public void login(String username, String password) {
userService.login(username, password);
}
}
- 自动装配
Spring的<bean>
元素中包含一个autowire属性,可以通过设置autowire属性的值实现Bean的自动装配。
属性值 | 描述 |
---|---|
default(默认值) | 由<bean> 的上级元素<beans> 的default-autowire属性值确定。 |
byName | 根据<bean> 元素id属性的值自动装配。 |
byType | 根据<bean> 元素的数据类型(Type)自动装配,如果一个Bean的数据类型,兼容另一个Bean中的数据类型,则自动装配。 |
constructor | 根据构造函数参数的数据类型进行byType模式的自动装配。 |
no | 默认值,不使用自动装配,Bean依赖必须通过元素或ref属性定义。 |
<bean id="userDao" class="com.tyut.example02.dao.UserDaoImpl"/>
<bean id="userService" class="com.tyut.example02.service.UserServiceImpl" autowire="byName">
</bean>
3.6 Bean的生命周期
Bean的生命周期是指Bean实例被创建、初始化和销毁的过程。在Bean的两种作用域singleton和prototype中,Spring容器对Bean的生命周期的管理是不同的。在singleton作用域中,Spring容器可以管理Bean的生命周期,控制着Bean的创建、初始化和销毁。在prototype作用域中, Spring容器只负责创建Bean实例,不会管理其生命周期。
- 初始化容器:1.创建对象 -> 2.执行构造方法 -> 3.执行属性注入(set操作) -> 4.执行Bean初始化操作
- 执行Bean的具体方法
- 执行关闭Bean
在Bean的生命周期中,有两个时间节点尤为重要,这两个时间节点分别是Bean实例初始化后和Bean实例销毁前,在这两个时间节点通常需要完成一些指定操作。因此,常常需要对这两个节点进行监控。
- Spring容器提供了@PostConstruct用于监控Bean对象初始化节点
- 提供了@PreDestroy用于监控Bean对象销毁节点。
@Component("student")
public class Student {
@Value("1")
private String id;
@Value("张三")
private String name; // 省略getter/setter方法,以及toString()方法
@PostConstruct
public void init(){System.out.println("Bean的初始化完成,调用init()方法"); }
@PreDestroy
public void destroy(){System.out.println("Bean销毁前调用destroy()方法"); }}
public class StudentTest {
public static void main(String[] args){
ApplicationContext applicationContext=new
ClassPathXmlApplicationContext("applicationStudent.xml");
Student student=(Student)applicationContext.getBean("student");
System.out.println(student);
//销毁Spring容器中的所有Bean
AbstractApplicationContext ac=(AbstractApplicationContext) applicationContext;
ac.registerShutdownHook();
}}