文章目录
Spring框架概述
- Spring是一个轻量级的开源的JavaEE框架
- Spring可以解决企业应用开发的复杂性
- Spring有两个核心部分:IOC和AOP
- IOC:控制反转,把创建对象过程交给Spring进行管理
- Aop:面向切面编程,不修改源代码进行功能的增强
- Spring特点
- 方便解耦,简化开发
- Aop编程支持
- 方便程序测试
- 方便和其他框架进行整合
- 方便进行事务操作
- 降低API开发难度
IOC容器
IOC(概念和原理)
- 什么是IOC
- IOC又名控制反转,把对象的创建和对象之间的调用的过程,交给Spring进行管理
- 使用IOC的目的:为了降低耦合度
- IOC的底层原理
- xml解析、工厂模式、反射
- xml解析、工厂模式、反射
IOC接口
-
IOC思想是基于IOC容器完成的,IOC容器底层是对象工厂
-
Spring提供了IOC容器的两种实现方式(两个接口)
- BeanFactory: IOC容器的最基本的实现,是Spring内部使用的接口,不提供开发人员使用
- ApplicationContext: BeanFactory接口的子接口,提供更多更强大的功能,一般由开发人员使用
注:
BeanFactory
和ApplicationContext
我们在开发的时候都可以用,但值得注意的是如果你使用BeanFactory
那么Spring在加载配置文件的时候不会立即创建对象,而是在你要使用的时候才会给你创建,而ApplicationContext
则会在加载配置文件的时候就会把被配置的对象进行创建。 -
ApplicationContext接口的实现类
IOC操作Bean
Bean 管理指的就是两个操作: Spring对象创建、Spring对象注入属性
Bean管理操作有两种方式:基于xml配置文件实现、基于注解实现
IOC操作Bean(基于xml)
- 基于xml创建对象
- 在Spring配置文件中,使用bean标签,在标签里添加对应的属性,就可以实现对象的创建
- bean标签的常见属性:
- id属性:该bean的唯一标识
- class属性:定义 bean 的类型并使用完全限定的类名
- 创建对象时,默认使用无参构造器创建对象
- 基于xml进行依赖注入
- DI: 依赖注入,就是注入属性
- 第一种注入方式,使用set方式进行依赖注入
- 使用
property
标签实现
<bean id="" class="">
<property name="" value="" />
<property name="" ref="" />
</bean>
name的值是属性名,value用于对基本数据类型+String的赋值,ref用于对引用对象的赋值,ref的值是bean对象的id
例子:
<bean id="UserDaoImpl" class="com.spring.dao.impl.UserDaoImpl"/>
<bean id="UserSeviceImpl" class="com.spring.service.UserSeviceImpl">
<property name="name" value="例子"/>
<property name="userDao" ref="UserDaoImpl"/>
</bean>
- p命名空间实现
1. xml中加入命名空间
xmlns:p="http://www.springframework.org/schema/p"
<bean id="" class="" p:属性名1="" p:属性名2="" />
例子:
<bean id="UserMapperImpl" class="com.spring.dao.impl.UserMapperImpl" />
<bean id="UserServiceImpl" class="com.spring.service.impl.UserServiceImpl"
p:userMapper-ref="UserMapperImpl"/>
- 第二种注入方式,使用有参构造进行依赖注入
使用constructor-arg
标签实现
<bean id="user" class="com.spring.pojo.User">
<constructor-arg name="id" value="12"/>
<constructor-arg name="password" value="123" />
<constructor-arg name="userName" value="张三" />
<constructor-arg name="userCode" value="VTB1233123" />
</bean>
-
特殊值的依赖注入
-
空值和特殊符号
(1) null值:标签null
(2)属性值中包含特殊符号:<![CDATA[属性值]]>
-
Array、List、Map
-
FactoryBean
Spring有两种Bean:普通类型、工厂Bean
普通bean:再配置文件中定义的bean类型就是返回类型
工厂bean:在配置文件定义的bean类型可以和返回值类型不一样
第一步:创建类,让这个类作为工厂bean,实现接口FactoryBean
第二步:实现接口里面的方法,在实现的方法里面定义bean类型 -
bean的作用域
我们在Spring中定义一个bean时,必须要声明该bean的作用域,默认为singletno,即,单例模式,Spring框架支持六个作用域singletno,prototype、request、session、application、websocket
作用域 | 描述 |
---|---|
singleton | 在Spring IoC容器中仅存在一个Bean实例,Bean以单例方式存在,默认值 |
prototype | 每次从容器中调用bean时,都会放回一个新的实例 |
request | 将单个bean定义的范围限定为单个HTTP请求的生命周期。也就是说,每个HTTP请求都有一个自己的bean实例,它是在单个bean定义的后 面创建的。仅在可感知网络的Spring ApplicationContext上下文中有效。 |
session | 将单个bean定义的范围限定为单个HTTP请求的生命周期。也就是说,每个HTTP请求都有一个自己的bean实例,它是在单个bean定义的后 面创建的。仅在可感知网络的Spring ApplicationContext上下文中有效。 |
application | 将单个bean定义的作用域限定为ServletContext的生命周期。仅在可感知网络的Spring ApplicationContext上下文中有效。 |
websocket | 将单个bean定义的作用域限定为WebSocket的生命周期。仅在可感知网络的Spring ApplicationContext上下文中有效。 |
- Bean的生命周期
生命周期:对象从创建到销毁的过程
Bean的生命周期:
- 通过构造器创建Bean实例(无参构造)
- 为Bean的属性设置值和对bean引用
- 调用bean的初始化方法(需要进行配置初始化的方法)
- 定义一个初始化时要执行方法
- bean属性:init-method
- bean可以使用了(对象获取到)
- 当容器关闭时,调用bean的销毁的方法(需要进行配置销毁的方法)
- 定义一个初始化时要执行的方法
- bean属性:destroy-method
Bean的后置处理器:
1. 定义Bean的后置处理器后,会在bean的初始化前和初始化后会分别执行后置处理器方法
2. 要想创建一个Bean的后置处理器,需要创建一个类,并且这个类要实现接口BeanPostProcess
3. 有后置处理器的Bean的生命周期
1. 通过构造器创建Bean实例(无参构造)
2. 为Bean的属性设置值和对bean引用
-------------------------
3. 后置处理器初始化前的操作
-------------------------
4. 调用bean的初始化方法(需要进行配置初始化的方法)
-------------------------
5. 后置处理器初始化后的操作
-------------------------
6. bean可以使用了(对象获取到)
7. 当容器关闭时,调用bean的销毁的方法(需要进行配置销毁的方法)
生命周期例子:
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="user" class="com.spring.beanpostproess.pojo.User"
init-method="init" destroy-method="destroy">
<property name="name" value="123"/>
</bean>
<bean class="com.spring.beanpostproess.MyBeanPost" />
</beans>
User.java
public class User {
private String name;
public User() {
System.out.println("无参构造器创建Bean");
}
public void setName(String name) {
System.out.println("调用set设置方法设置属性值");
this.name = name;
}
public void init(){
System.out.println("执行初始化方法");
}
public void destroy(){
System.out.println("执行销毁销毁方法");
}
}
MyBeanPost.java
public class MyBeanPost implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("before");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("after");
return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
}
}
IOC操作Bean(基于注解)
-
自动装配
-
Autowired:根据属性类型进行自动装配
-
Qualifier: 根据属性名称进入注入
-
Resource:既可以根据类型注入,也可以根据名称注入
-
Value:注入普通类型属性
-
完全注解开发
-
创建配置类,替代xml配置文件
@Configuration // 作为配置类,替代xml配置文件
@ComponentScan(basePackages={"扫描包路径"}) // 开启组件扫描
public class SpringConfig{}
- 加载配置类
ApplicationContext context =
new AnnotationConfigApplicationContext(SpringConfig.class);
AOP
AOP(概念)
什么是AOP
- 面向切面编程,利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各个方面之间的耦合度降低,提高程序的可可从用性,提高了开发的效率
- 通俗理解:不通过修改代码的方式,在主干功能里面添加新功能
AOP(底层原理)
AOP底层使用的是动态代理,动态代理分两种情况:
- 有接口情况,使用JDK动态代理
创建接口实现类代理对象,增强类的方法
- 没有接口情况,使用CGLIB动态代理
创建子类的代理对象,增强类的方法
-
JDK动态代理简单实现:
-
使用JDK动态代理,使用Proxy类里面的方法创建代理对象
调用newProxyInstance方法
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
返回指定接口的代理类的实例,该接口将方法调用分派给指定的调用处理程序。
ClassLoader loader: 代理类的类加载器
Class<?>[] interfaces:代理类实现的接口实现类class文件列表
InvocationHandler h: 调度方法调用的调用处理函数(增强方法类)
- 实现代码
// 代理类:Demo.java
public class Demo {
public static void main(String[] args) {
Class[] interfaces = {UserDao.class};
UserDaoImpl userDaoImpl = new UserDaoImpl();
UserDao userDao =
(UserDao) Proxy.newProxyInstance(
Demo.class.getClassLoader(),
interfaces,
new UserDaoProxy(userDaoImpl));
int add = userDao.add(1, 2);
System.out.println(add);
}
}
class UserDaoProxy implements InvocationHandler {
// 被代理的对象
private Object obj;
// 通过构造器获取
public UserDaoProxy(Object obj) {
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 方法之前
System.out.println("方法执行前" + method.getName() + " 方法参数:" + args);
// 被增强的方法执行
Object res = method.invoke(obj, args);
// 方法之后
System.out.println("方法执行后" + method.getName());
return res;
}
}
// 接口:UserDao.java
public interface UserDao {
int add(int a,int b);
}
// 三系列:UserDaoImpl.java
public class UserDaoImpl implements UserDao {
public int add(int a, int b){
return a+b;
}
}
JdbcTemplate
什么是JdbcTemplate
JdbcTemplate是Spring框架对JDBC进行的封装,使用它可以更方便的对数据库进行操作
事务
什么是事务
事务是数据库操作最基本单元,逻辑上一组操作,要么都成功,如果有一个失败所有操作都失败
事务四个特性(ACID)
-
原子性:操作不可分割,要么都成功,一个失败都失败
-
一致性:在事务开始之前和事务结束以后,数据库的完整性没有被破坏。
这表示写入的资料必须完全符合所有的预设规则,这包含资料的精确度、串联性以及后续数据库可以自发性地完成预定的工作。
-
隔离性:并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的
-
持久性:一个事务被提交之后,它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响
事务的实现
事务操作基本流程
- 开启事务
- 进行业务操作
- 没有发生异常,提交事务;出现异常,事务回滚
spring事务管理介绍
-
事务一般添加到JavaEE三层架构的Service层中
-
在Spring进行事务管理操作
- 编程式事务(不常用)
- 声明式事务
-
声明式事务管理
- 基于注解实现(常用)
- 基于xml配置文件方式
-
在Spring进行声明式事务管理,底层使用AOP原理
-
Spring事务管理API
- 提供一个接口(PlatformTransactionManager),代表事务管理器,这个接口针对不同框架,有不同实现类
Spring事务操作(注解声明式事务管理)
Spring事务操作步骤
-
在Spring配置文件配置事务管理器
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean>
-
在Spring配置文件,开启事务
-
添加命名空间
xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
-
开启事务注解
<tx:annotation-driven transaction-manager="transactionManager" />
-
-
在Service类上面(或Service类里面的方法上面)添加事务注解
- @Transactional,这个注解可以添加到类上,也可以添加到方法上
- 添加到类上,这个类里面的所有的方法都添加事务
- 添加到方法上,为这个方法添加事务
@Service @Transactional public class UserServiceImpl implements UserService { }
参数配置
参数名 | 描述 |
---|---|
propagation | 事务的传播类型 |
isolation | 隔离级别 |
timeout | 超时时间 |
readOnly | 是否只读 |
rollbackFor | 回滚 |
rollbackForClassName | |
noRollbackFor | 不回滚 |
noRollbackForClassName |
propagation: 当一个事务方法被另一个事务方法调用时这个事务如何进行
传播属性 | 描述 |
---|---|
REQUIRED | 如果有事务在运行,当前的方法就在这个事务内运行,否则,就启动一个新的事务,并在自己的事务内运行 |
REQUIRED_NEW | 当前的方法必须启动新事务,并在它自己的事务内运行,如果有事务正在运行,应该将它挂起 |
SUPPORTS | 如果有事务在运行,当前的方法就在这个事务内运行,否则它可以不运行在事务中 |
NOT_SUPPORTS | 当前的方法不应该运行在事务中,如果有运行的事务,将它挂起 |
MANDATORY | 当前的方法必须运行在事务内部,如果没有正在运行的事务,就抛异常 |
NEVER | 当前的方法不应该运行在事务内部,如果有正在运行的事务,就抛异常 |
NESTED | 如果事务在运行,当前的方法就应该在这个事务的嵌套事务内运行,否则,就启动一个新的事务,并在它自己的事务内运行 |
isolation: 事务有特性为隔离性,多事务操作之间不会产生影响。如果不考虑隔离性在并发访问数据库时,读取数据时会产生很多问题:
脏读: 读取未提交数据
时间顺序 | 转账事务 | 取款事务 |
---|---|---|
1 | 开始事务 | |
2 | 开始事务 | |
3 | 查询账户余额为2000元 | |
4 | 取款1000元,余额被更改为1000元 | |
5 | 查询账户余额为1000元(产生脏读) | |
6 | 取款操作发生未知错误,事务回滚,余额变更为2000元 | |
7 | 转入2000元,余额被更改为3000元(脏读的1000+2000) | |
8 | 提交事务 | |
备注 | 按照正确逻辑,此时账户余额应该为4000元 |
不可重复读: 前后多次读取,数据内容不一致
时间顺序 | 事务A | 事务B |
---|---|---|
1 | 开始事务 | |
2 | 第一次查询,小明的年龄为20岁 | |
3 | 开始事务 | |
4 | 其他操作 | |
5 | 更改小明的年龄为30岁 | |
6 | 提交事务 | |
7 | 第二次查询,小明的年龄为30岁 | |
备注 | 按照正确逻辑,事务A前后两次读取到的数据应该一致 |
幻读: 前后多次读取,数据总量不一致
时间顺序 | 事务A | 事务B |
---|---|---|
1 | 开始事务 | |
2 | 第一次查询,数据总量为100条 | |
3 | 开始事务 | |
4 | 其他操作 | |
5 | 新增100条数据 | |
6 | 提交事务 | |
7 | 第二次查询,数据总量为200条 | |
备注 | 按照正确逻辑,事务A前后两次读取到的数据总量应该一致 |
隔离性级别: ×(未解决)、√(解决)
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
读未提交(Read uncommitted) | × | × | × |
读已提交(Read committed) | √ | × | × |
可重复读(Repeatable read)(mysql默认) | √ | √ | × |
可串行化(Serializable) | √ | √ | √ |
timeout: 事务必须在一定时间内进行提交,如果规定时间内没有提交,事务就会回滚(默认值-1,一直不超时)
readOnly: 是否只读
- 读:查询操作;写:添加、修改,删除操作
- readOnly默认值false,表示增删改查都可以
- 设置readOnly值为true后,表示只能查询,不能增删改
rollbackFor: 设置出现那些异常进行回滚
noRollbackFor: 设置出现那些异常不进行回滚
xml配置方式实现事务
…
完全注解开发
…
Spring5的新功能
整个Spring5框架代码基于JDK8
- 通过使用泛型等特性提高可读性
- 对java8提高直接的代码支撑
- 运行时兼容JDK9
- Java EE 7API需要Spring相关的模块支持
- 运行时兼容Java EE8 API
- 取消的包,类和方法
- 包 beans.factory.access
- 包 dbc.support.nativejdbc
- 从spring-aspects 模块移除了包mock.staicmock,不在提AnnotationDrivenStaticEntityMockingControl支持
- 许多不建议使用的类和方法在代码库中删除
核心特性
-
访问Resuouce时提供getFile或和isFile防御式抽象
-
有效的方法参数访问基于java 8反射增强
-
在Spring核心接口中增加了声明default方法的支持一贯使用JDK7 Charset和StandardCharsets的增强
-
兼容JDK9
-
Spring 5.0框架自带了通用的日志封装
Spring5移除了Log4jConfigListener,官方建议使用Log4j2
Spring5框架整合Log4j- 导入jar包
- 创建log4j.xml
<?xml version="1.0" encoding="utf-8"?> <!-- 日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL --> <!-- Configuration后面的status用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,可以看到log4j2内部各种详细输出 --> <configuration status="INFO"> <!-- 先定义所有的appender --> <appenders> <!-- 控制日志输出的格式 --> <console name="Console" target="SYSTEM_OUT"> <PatternLauout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" /> </console> </appenders> <!-- 定义logger,只有定义了logger并引入了appender,appender才会生效 --> <!-- root: 用于指定项目的根让着你,如果没有单独指定Logger,则会使用root作为默认的日志输出 --> <loggers> <root level="info"> <appender-ref ref="Console" /> </root> </loggers> </configuration>
-
持续实例化via构造函数(修改了异常处理)
-
Spring 5.0框架自带了通用的日志封装
-
spring-jcl替代了通用的日志,仍然支持可重写
-
自动检测log4j 2.x, SLF4J, JUL(java.util.Logging)而不是其他的支持
-
访问Resuouce时提供getFile或和isFile防御式抽象
-
基于NIO的readableChannel也提供了这个新特性
核心容器
- 支持候选组件索引(也可以支持环境变量扫描)
- 支持@Nullable注解
@Nullable
注解可以使用在属性、方法和方法参数- 使用在属性上面,属性值可以为空
- 使用在方法上面,方法返回值可以为空
- 使用在方法参数上面,参数值可以为空
- 函数式风格GenericApplicationContext/AnnotationConfigApplicationContext
- 基本支持bean API注册
- 在接口层面使用CGLIB动态代理的时候,提供事物,缓存,异步注解检测
- XML配置作用域流式
- Spring WebMVC
- 全部的Servlet 3.1 签名支持在Spring-provied Filter实现
- 在Spring MVC Controller方法里支持Servlet4.0 PushBuilder参数
- 多个不可变对象的数据绑定(Kotlin/Lombok/@ConstructorPorties)
- 支持jackson2.9
- 支持JSON绑定API
- 支持protobuf3
- 支持Reactor3.1 Flux和Mono
SpringWebFlux
- 新的spring-webflux模块,一个基于reactive的spring-webmvc,完全的异步非阻塞,旨在使用enent-loop执行模型和传统的线程池模型。
- Reactive说明在spring-core比如编码和解码
- spring-core相关的基础设施,比如Encode 和Decoder可以用来编码和解码数据流;DataBuffer 可以使用java ByteBuffer或者Netty ByteBuf;ReactiveAdapterRegistry可以对相关的库提供传输层支持。
- 在spring-web包里包含HttpMessageReade和HttpMessageWrite
测试方面的改进
- 完成了对JUnit 5’s Juptier编程和拓展模块在Spring TestContext框架
- SpringExtension:是JUnit多个可拓展API的一个实现,提供了对现存Spring TestContext Framework的支持,使用@ExtendWith(SpringExtension.class)注解引用。
- @SpringJunitConfig:一个复合注解
- @ExtendWith(SpringExtension.class) 来源于Junit Jupit
- @ContextConfiguration 来源于Srping TestContext框架
- @DisabledIf 如果提供的该属性值为true的表达或占位符,信号:注解的测试类或测试方法被禁用
- 在Spring TestContext框架中支持并行测试
- 具体细节查看Test 章节 通过SpringRunner在Sring TestContext框架中支持TestNG, Junit5,新的执行之前和之后测试回调。
- 在testexecutionlistener API和testcontextmanager新beforetestexecution()和aftertestexecution()回调。MockHttpServletRequest新增了getContentAsByteArray()和getContentAsString()方法来访问请求体
- 如果字符编码被设置为mock请求,在print()和log()方法中可以打印Spring MVC Test的redirectedUrl()和forwardedUrl()方法支持带变量表达式URL模板。
- XMLUnit 升级到了2.3版本。