Spring
Spring是一个轻量级控制反转(IOC)和面向切面(AOP)的容器框架
Spring 的特性
- 非侵入式: 基于Spring开发的应用中的对象可以不依赖于Spring的API
- 依赖注入: DI-Dependency Injection,反转控制(IOC)最经典的实现。 DI 和 IOC的关系就是:IOC是一种思想,DI 是 IOC的具体实现
- 面向切面编程 : Aspect Oriented Programming-- AOP
- 容器: Spring是一个容器,因为它包含并且管理应用对象的生命周期
- 组件化: Spring 实现了使用简单的组件配置组合成一个复杂的应用。在Spring中可以使用XML和Java注解组合这些对象。
- 一站式:在IOC和AOP的基础上可以整合各种企业应用的开源框架和优秀的第三方类库(实际上Spring自身也提供了表述层的SpringMVC和持久层的Spring JDBC)。
Spring模块

核心容器:由 spring-beans、spring-core、spring-context 和 spring-expression(Spring Expression Language, SpEL) 4 个模块组成。
数据访问及集成:由spring-jdbc、spring-tx、spring-orm、spring-jms 和 spring-oxm 5 个模块组成。
AOP 和设备支持:由 spring-aop、spring-aspects 和 spring-instrument 3 个模块组成。
Web:由 spring-web、spring-webmvc、spring-websocket 和 spring-webflux 4 个模块组成。
Spring IOC/DI
IOC(Inversion of Control)控制反转:由spring来负责控制对象的生命周期和对象间的关系。
传统的Java SE程序设计,我们通过new进行创建对象,是程序主动去创建依赖对象;而IOC是有专门一个容器来创建这些对象,即由Ioc容器来控制对象的创建;就是把new对象实例化的工作交给spring容器来完成,spring帮我们负责销毁对象,控制对象的生命周期,在需要使用对象的时候直接向spring申请即可。控制对象生存周期的不再是引用它的对象,而是spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被spring控制,所以这叫控制反转。
DI(Dependency Injection)依赖注入:组件之间的依赖关系由容器在运行期决定,由容器动态的将某个依赖关系注入到组件之中。
Spring就是通过反射(reflection)来实现依赖注入的。
注入方式:set方式注入、构造器注入、工厂方法注入,注解方式注入
set方式注入:目标对象中需要提供相关的set方法,需要调用set方法将资源传递给目标对象使用。
构造器注入:目标对象中提供带参数的构造方法,通过构造方法将资源传递给目标对象使用。
静态工厂注入:调用静态工厂的方法来获取自己需要的对象。
实例工厂注入:实例工厂的意思是获取对象实例的方法不是静态的,所以你需要首先new工厂类,再调用普通的实例方法
注解方式注入:@Autowired 注解、@Autowired注解
@Autowired注解默认按类型装配(属于Spring),默认情况下必须要求依赖对象必须存在,如果要允许null值,可以设置它的required属性为false,如:@Autowired(required=false) ,如果我们想使用名称装配可以结合@Qualifier注解进行使用。
@Resource注解默认按名称装配(属于J2EE),名称可以通过name属性进行指定,如果没有指定name属性,当注解写在字段上时,默认取字段名进行安装名称查找,如果注解写在setter方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。如果使用type属性时则按照类型进行装配。
@Resource装配顺序
1. 如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常
2. 如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常
3. 如果指定了type,则从上下文中找到类型匹配的唯一bean进行装配,找不到或者找到多个,都会抛出异常
4. 如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配。
Spring AOP
SpringAOP(Aspect Orient Programming) 是一种设计思想,称为面向切面编程,利用横切技术剖析对象内部,将业务之间共同调用的逻辑提取并封装为一个可复用的模块,这个模块被命名为切面(Aspect),该模块减少系统中的重复代码,降低模块间的耦合度,可用于日志、权限认证、事务管理等。
SpringAOP思想的实现一般基于代理模式 ,在Java中采用JDK动态代理模式,但是JDK动态代理模式只能代理接口而不能代理类。因此SpringAOP会在CGLIB、JDK动态代理之间进行切换。
使用JDK动态代理还是CGLIB?
- 如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP。
- 如果目标对象实现了接口,可以强制使用CGLIB实现AOP。
- 如果目标对象没有实现了接口,必须采用CGLIB库,Spring会自动在JDK动态代理和CGLIB之间转换。
相关术语
- 切面(Aspect):切面是通知和切点的结合,通知和切点共同定义了切面的全部内容,一般使用@Aspect实现切面的定义;
- 通知(Advice):通知定义了切面是什么以及何时使用,如Before、After;
- 切点(PonitCut):切点定义了在何处应用连接点(JoinPonit),通常使用明确的类和方法名称,或是利用正则表达式定义所匹配的类和方法名称来指定这些切点;
- 连接点(JoinPonit):连接点是在应用执行过程中能够插入切面的一个点,这个点可以是调用方法时、抛出以异常时、甚至修改一个字段时;
- 目标对象(Target):需要被代理的类,如UserService;
- 织入(Weaving):织入是把切面应用到目标对象并创建新的代理对象的过程。
通知类型
- 前置通知(Before):在目标方法被调用之前调用通知功能;
- 后置通知(After):在目标方法执行完成之后调用通知,此时不会关心方法的输出是什么;
- 返回通知(After-returning):在目标方法成功执行之后调用通知;
- 异常通知(After-throwing):在目标方法抛出异常后调用通知;
- 环绕通知(Around):在被通知的方法调用之前和调用之后执行自定义的行为。
例:
@Component @Aspect //定义切面 public class LogAop { //定义切点 @Pointcut("execution(* com.demo.service.*(..))") public void ponitCut() { //日志、权限等操作... } //前置通知,方法执行之前执行 @Before("ponitCut()") public void beforeAdvice() { System.out.println("方法执行之前执行"); } //后置通知,方法执行之后执行(不管是否发生异常) @After("ponitCut()") public void afterAdvice() { System.out.println("方法执行之后执行"); } //环绕通知。注意要有ProceedingJoinPoint参数传入 @Around("ponitCut()") public void around(ProceedingJoinPoint pjp) throws Throwable { System.out.println("环绕前"); pjp.proceed();//执行方法 System.out.println("环绕后"); } //返回通知,方法正常执行完毕之后执行 @AfterReturning(value="PointcutDeclaration()",returning="result") public void AfterReturningMethod(JoinPoint jp,Object result) { String methodName = jp.getSignature().getName(); Object[] args = jp.getArgs(); System.out.println("AfterReturningMethod The method "+ methodName +" parameter is "+Arrays.asList(args)+" "+result); System.out.println(); } //异常通知,在方法抛出异常之后执行 @AfterThrowing(value="PointcutDeclaration()",throwing="e") public void AfterThrowingMethod(JoinPoint jp,Exception e) { String methodName = jp.getSignature().getName(); System.out.println("AfterThrowingMethod The method "+ methodName +"exception :"+e); } }
Spring Transaction:@Transactional
事务是用户定义的一系列数据库操作,是数据库操作的最小工作单元,要么全部执行,要么全部不执行,是不可分割的工作单元。
事务的特性
- 原子性(Atomacity):事务包含的操作要么全部成功,要么全部失败,即使回滚也不会对数据库产生影响。
- 一致性(Consistency):事务必须使数据从一个一致性状态变换到另一个一致性状态,也就是说当一个系统在一致状态下更新后,系统中所有数据都保持一致。
- 隔离性(Isolation):当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间相互隔离。
- 持久性(Durability):事务一旦被提交,对数据库中的数据的改变就是永久性的,即使数据库系统遇到故障也不会丢失提交事务的操作。
事务的管理
- 编程式事务管理:侵入式事务管理,使用TransactionTemplate或者直接使用PlatformTransactionManager,对于编程式事务管理,Spring推荐使用TransactionTemplate。
- 声明式事务管理:非侵入式事务管理,声明式事务管理建立在AOP之上,其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,执行完目标方法之后根据执行的情况提交或者回滚。
编程式事务每次实现都要单独实现,但业务量大功能复杂时,使用编程式事务无疑是痛苦的,而声明式事务不同,声明式事务属于无侵入式,不会影响业务逻辑的实现,只需要在配置文件中做相关的事务规则声明或者通过注解的方式,便可以将事务规则应用到业务逻辑中。
显然声明式事务管理要优于编程式事务管理,这正是Spring倡导的非侵入式的编程方式。唯一不足的地方就是声明式事务管理的粒度是方法级别,而编程式事务管理是可以到代码块的,但是可以通过提取方法的方式完成声明式事务管理的配置。
声明事务原理:
AOP编程-环绕通知-方法之前或者之后进行执行
使用声明事务方式,方法一定不要try,将异常抛出去。
业务逻辑层不要使用try,将异常抛出给上一层。或者在catch中throw 异常。
不然在try中内部消化异常,AOP无法捕获异常,就无法执行回滚语句。
事务的传播
- TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。
- TransactionDefinition.PROPAGATION_MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
- TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。
- TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
- TransactionDefinition.PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
- TransactionDefinition.PROPAGATION_REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起。
- TransactionDefinition.PROPAGATION_NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。
事务的隔离
- TransactionDefinition.ISOLATION_DEFAULT:使用后端数据库默认的隔离级别。
- TransactionDefinition.ISOLATION_READ_UNCOMMITTED:最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。
- TransactionDefinition.ISOLATION_READ_COMMITTED:允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生。
- TransactionDefinition.ISOLATION_REPEATABLE_READ:对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
- TransactionDefinition.ISOLATION_SERIALIZABLE:最高的隔离级别,完全服从ACID的隔离级别,确保阻止脏读、不可重复读以及幻读,也是最慢的事务隔离级别,因为它通常是通过完全锁定事务相关的数据库表来实现的。
脏读(Dirty read):一个事务读取了被另一个事务改写但尚未提交的数据时。如果这些改变在稍后被回滚了,那么第一个事务读取的数据就会是无效的。
不可重复读(Nonrepeatable read):一个事务执行相同的查询两次或两次以上,但每次查询结果都不相同时。这通常是由于另一个并发事务在两次查询之间更新了数据。
幻读(Phantom reads):当一个事务(T1)读取几行记录后,另一个并发事务(T2)插入了一些记录时,幻读就发生了。在后来的查询中,第一个事务(T1)就会发现一些原来没有的额外记录。
Spring声明式事务配置属性
@Transactional(propagation=Propagation.REQUIRED) //事务的传播
@Transactional(isolation = Isolation.READ_UNCOMMITTED) //事务的隔离
@Transactional(readOnly=true) //只读事务
@Transactional(timeout=30) //事务超时
@Transactional(rollbackFor={RuntimeException.class, Exception.class}) //事务回滚指定多个异常类
Spring Bean
构成应用程序主干并由Spring IoC容器管理的对象称为Bean。Bean是一个由Spring IoC容器实例化、组装和管理的对象。
Spring中的bean默认都是单例的,Spring的单例是基于 BeanFactory 也就是 Spring容器的,单例Bean在此容器内只有一个,Java的单例是基于 JVM,每个 JVM 内只有一个实例。
Spring Bean 线程安全问题理解:https://segmentfault.com/a/1190000019971145。
bean的作用域:@scope("prototype")
| 类别 | 说明 |
|---|---|
| singleton | 在Spring IoC容器中仅存在一个Bean实例,Bean以单例方式存在,默认值,适用于无状态bean |
| prototype | 每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()时,相当于执行new XxxBean(),适用于有状态的Bean |
| request | 每次HTTP请求都会创建一个新的Bean,该该作用域仅适用于WebApplicationContext环境 |
| session |
同一个HTTP Session共享一个Bean,不同Session使用不同Bean,仅适用于WebApplicationContext环境 |
| globalSession | 一般用于Portlet应用环境,该作用域仅适用于WebApplicationContext环境 |
Spring bean加载过程

Spring bean生命周期
概要流程:

生命周期扩展详细流程:



浙公网安备 33010602011771号