本文将详细介绍Spring5注解驱动开发的细节。其中Spring AOP的源码分析见【Spring】Spring5 AOP源码分析。
IoC 容器
导入maven依赖
1 | <dependency> |
IoC容器部分案例汇总:
1 | // 导入外部资源文件 |
使用AnnotationConfigApplicationContext
类获取IoC容器中的组件
1 |
|
本文将详细介绍Spring5注解驱动开发的细节。其中Spring AOP的源码分析见【Spring】Spring5 AOP源码分析。
导入maven依赖
1 | <dependency> |
IoC容器部分案例汇总:
1 | // 导入外部资源文件 |
使用AnnotationConfigApplicationContext
类获取IoC容器中的组件
1 |
|
1 | <dependencies> |
为避免资源导出问题可以添加以下代码(使得普通目录下的资源文件也能导出)
1 | <!--静态资源导出问题--> |
1 |
|
1 | # Global logging configuration |
1 | com.mysql.jdbc.Driver = |
1 |
|
1 |
|
1 |
|
1 |
|
1 |
|
Model(模型):数据模型,提供要展示的数据,因此包含数据和行为,可以认为是领域模型或JavaBean组件(包含数据和行为),不过现在一般都分离开来:Value Object(数据Dao) 和 服务层(行为Service)。也就是模型提供了模型数据查询和模型数据的状态更新等功能,包括数据和业务。
View(视图):负责进行模型的展示,一般就是我们见到的用户界面,客户想看到的东西。
Controller(控制器):接收用户请求,委托给模型进行处理(状态改变),处理完毕后把返回的模型数据返回给视图,由视图负责展示。 也就是说控制器做了个调度员的工作。
最典型的MVC就是JSP + servlet + javabean的模式。
MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具包,只做增强不做改变。为简化开发工作、提高生产率而生。
持久化是将程序数据在持久状态和瞬时状态间转换的机制。
事务需要添加到 JavaEE 三层结构里面 Service 层(业务逻辑层)。在 Spring 进行事务管理操作有两种方式:编程式事务管理、声明式事务管理(推荐使用):
TransactionTemplate
或者直接使用底层的PlatformTransactionManager
。对于编程式事务管理,Spring推荐使用TransactionTemplate
。@Transactional
注解的方式),便可以将事务规则应用到业务逻辑中。显然声明式事务管理要优于编程式事务管理,这正是Spring倡导的非侵入式的开发方式。声明式事务管理使业务代码不受污染,一个普通的POJO对象,只要加上注解就可以获得完全的事务支持。和编程式事务相比,声明式事务唯一不足地方是,它的最细粒度只能作用到方法级别,无法做到像编程式事务那样可以作用到代码块级别。但是即便有这样的需求,也存在很多变通的方法,比如,可以将需要进行事务管理的代码块独立为方法等等。
Spring事务原理与AOP原理十分相似,其详细的源码分析见【Spring】Spring5 事务源码分析。
1 | <dependency> |
1 |
|
1 |
|
其中,可以在xml中配置数据库并开启事务管理器:
1 | <!-- 1、在 spring 配置文件,开启事务注解,引入名称空间!--> |
1 |
|
在 Spring 配置文件中进行配置:
1 | <!--1 创建事务管理器--> |
read-only
:设置事务为只读事务,不需要增删改操作。可以提高查询速度。timeout
:超时,事务超出指定执行时长后自动终止并回滚。isolation
:设置隔离级别运行时异常(非检查异常)发生时默认回滚,编译时异常(检查异常)默认不回滚
rollBackFor
:可以让原来默认不回滚的异常回滚noRollBackFor
:可以让原来默认回滚的异常不回滚当一个新的事务创建时,就会被绑定到当前线程上。
TransactionAspectSupport类中的ThreadLocal<TransactionInfo>
在当前线程保存了一个事务的信息TransactionInfo:
该线程会伴随着这个事务整个生命周期,直到事务提交、回滚或挂起(临时解绑)时该线程才会取消与该事务的绑定。
同时一个线程只能绑定一个事务,若当前线程原本正绑定的事务还未执行完毕就被新的事务所挂起,则该线程与该事务进行临时解绑,并绑定到新创建的事务上;直到新建的事务提交或回滚后,该线程才会结束与该新建事务的绑定,再次重新绑定之前的事务。
上述过程实现的原理为使用链表结构:创建一张TransactionInfo
链表,将新创建的事务TransactionInfo
链接到旧的事务TransactionInfo
的尾部,待新事务执行完毕后再指回旧的事务TransactionInfo
:
当新创建的事务结束时恢复旧的事务状态:
什么是事务挂起,如何实现挂起
对事务的配置在Spring内部会被封装成TransactionInfo,线程绑定了事务,自然也绑定了事务相关的TransactionInfo。挂起事务时,把TransactionInfo取出临时存储,等待执行完成后,把之前临时存储的TransactionInfo重新绑定到该线程上。
关于事务挂起的举例:(某事务挂起之后,任何操作都不在该事务的控制之下)
例如: 方法A支持事务,方法B不支持事务,即PROPAGATION_NOT_SUPPORTED
。方法A调用方法B:
事务传播行为就是多个事务方法相互调用时,事务如何在这些方法间传播。Spring支持7种事务传播行为:
propagation_required
(需要事务,有就加入,没有就新建):如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中,这是最常见的选择。(如果设置为required,则事务的其他属性继承于大事务)好男人。propagation_supports
(支持事务,有就加入,没有就非事务):支持当前事务,如果没有当前事务,就以非事务方法执行。懒男人propagation_mandatory
(强制使用当前事务,有就加入,没有就抛异常):使用当前事务,如果没有当前事务,就抛出异常。上述三种类型都支持当前事务,当前如果有事务就加入。
propagation_required_new
(必须新建事务,当前有就挂起):新建事务,如果当前存在事务,把当前事务挂起。挑剔男propagation_not_supported
(不支持事务,当前有就挂起):以非事务方式执行操作,如果当前存在事务,就把当前事务挂起(挂起指自己新建一个数据库连接,不再使用之前的数据库连接,在代码中体现为两个方法的connection不相同,详细介绍见上文)。减肥男propagation_never
(强制非事务,当前有就抛异常):以非事务方式执行操作,如果当前事务存在则抛出异常IllegalTransactionStateException
,该方法内的代码无法运行。神经病上述三种类型都不支持当前事务,当前如果有事务,要么挂起,要么抛异常。
propagation_nested
:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与propagation_required类似的操作Spring 默认的事务传播行为是 PROPAGATION_REQUIRED
,它适合于绝大多数的情况。
假设 ServiveX#methodX()
都工作在事务环境下(即都被 Spring 事务增强了),假设程序中存在如下的调用链:Service1#method1()->Service2#method2()->Service3#method3()
,那么这 3 个服务类的 3 个方法通过 Spring 的事务传播机制都工作在同一个事务中。
1 |
|
1 |
|
使用过上述案例进行实验,1代表插入成功,2代表插入失败:
createUser()
没有事务,其仍然能插入数据;addAccount()
有事务,其出现异常不能成功插入数据createUser()
有事务,出现异常后其不能插入数据;addAccount()
没有声明事务,但其被createUser()调用,仍然会被事务包裹,出现异常不能成功插入数据。若某个方法包含事务,其调用的其他方法也会包含事务addAccount()
将createUser()
的事务挂起,挂起指自己新建一个数据库连接,不再使用之前的数据库连接,在代码中体现为两个方法的connection不相同,详细介绍见上文。因此addAccount()
插入成功(因为没有事务,异常也能插入),createUser()
插入失败(因为addAccount()
抛出了异常,被重新恢复的事务所捕获从而插入失败)addAccount()
不支持事务,直接抛出IllegalTransactionStateException
。所以直接无法运行该方法内插入的语句,所以插入失败;createUser()
因为有事务,所以捕获到addAccount()
抛出的异常后回滚,插入失败场景6详细分析:假设Spring IoC中有组件AccountServiceImpl
,该组件中的addAccount()
方法被@Transactional
注解修饰,代表该方法将开启事务。
Spring容器启动时将使用事务后置处理器AutoProxyRegistrar会为该组件创建一个动态代理对象accountProxy
(类似于 Spring AOP 原理),该对象将被注入到容器中,其他程序在调用getBean()
获取该类的对象时,将获取到该类的动态代理对象,而非原始对象。此时在调用该代理对象accountProxy
的addAccount()
时,将有事务包裹。
而若不调用该代理对象的addAccount()
,而是将该方法直接写在本类中,直接调用本类里的该方法,则不会交由Spring事务管理器拦截,此时的方法和普通方法一样。如果直接 new 一个对象也无法使用事务管理。
结论:只有Spring事务代理对象的方法才能被事务拦截器所拦截。直接调用方法无法被拦截(即使该方法被@Transactional
注解修饰)。
rollbackFor
里定义的异常类型,也会失效(运行时异常(非检查异常)发生时默认回滚,编译时异常(检查异常)默认不回滚)@Transactional(rollbackFor = Exception.class)
throw new RuntimeException(e);
TransactionStatus.setRollbackOnly()
:在 catch 块添加 TransactionInterceptor.currentTransactionStatus().setRollbackOnly();
@Order(Ordered.LOWEST_PRECEDENCE - 1)
(不推荐)编程式事务指需要手动调用事务管理器包裹业务代码进行提交回滚。其需要使用TransactionTemplate
或者直接使用底层的PlatformTransactionManager
。对于编程式事务管理,Spring推荐使用TransactionTemplate
。
Spring事务与JDBC事务的关系:上中下三个框分别代表基于AOP的声明式事务、编程式事务、JDBC事务。其中基于AOP的声明式事务原理见文章【Spring】Spring5 事务源码分析。
编程式事务中的两个重要类:
编程式事务使用示例:
可利用TransactionAspectSupport获取当前线程方法栈中的事务状态,在不同的事务中该状态对象不同:
1 | TransactionStatus status = TransactionAspectSupport.currentTransactionStatus(); |
@EnableTransactionManagement 注解向容器中添加AutoProxyRegistrar和ProxyTransactionManagementConfiguration组件,二者作用分别为:
invoke()
方法,由事务管理器控制事务的提交与回滚。Spring事务原理与AOP原理十分相似,都包含有后置处理器和拦截器思想,在组件创建后包装出代理对象、在代理对象执行目标方法时进行拦截,使用事务管理器控制事务的提交与回滚。
详细的源码分析见文章【Spring】Spring5 事务源码分析。
AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
Spring AOP的源码分析见【Spring】Spring5 AOP源码分析。
面向切面编程(方面),利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率;
通俗描述:不通过修改源代码方式,在主干功能里面添加新功能。
使用登录例子说明 AOP:
AOP 底层使用动态代理 ,动态代理有两种情况:
第一种 有接口情况,使用JDK 动态代理 ;创建接口实现类代理对象,增强类的方法
第二种 没有接口情况,使用CGLIB 动态代理创建子类的代理对象,增强类的方法(该方法不需要实现接口,由CGLIB创建代理对象)
Servlet是Sun公司开发动态web的一门技术。Sun在这些API中提供了一个接口:Servlet。开发Servlet程序需要完成两个步骤:
把实现了Servlet接口的Java程序叫做Servlet
Sevlet接口Sun公司提供有两个默认的实现类:HttpServlt,GenericServlet
构建一个普通的Maven项目(不带模板),删掉里面的src目录,这个空的工程就是Maven的主工程。之后在这个项目里建立Module,新建的Module均为Maven父项目的子项目。
关于Maven父子工程的理解:在父项目中会有
1 | <modules> |
父项目中的Maven依赖环境Jar包子项目可以直接使用
Maven环境优化:修改web.xml(与本地Tomcat中的内容一致)
1 | <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" |
1 | package com.zhao.servlet; |
1 | <!-- 注册Servlet --> |
1、什么是IoC
2、IoC底层
xml解析、工厂模式、反射
3、Spring提供的IoC容器实现的两种方式(两个接口)
BeanFactory
接口:IoC容器基本实现是Spring内部接口的使用接口,不提供给开发人员进行使用(加载配置文件时候不会创建对象,在获取对象时才会创建对象。)ApplicationContext
接口:BeanFactory
接口的子接口,提供更多更强大的功能,提供给开发人员使用(加载配置文件时候就会把在配置文件对象进行创建)推荐使用!4、ApplicationContext接口的实现类
5、管理Bean的方式有两种:
Node 是一个让 JavaScript 运行在服务端的开发平台(让前端项目独立运行在服务端),它让 JavaScript 成为与 PHP、Python、Perl、Ruby 等服务端语言平起平坐的脚本语言。 发布于2009年5月,由 Ryan Dahl 开发,实质是对 Chrome V8 引擎进行了封装。
简单的说 Node.js 就是运行在服务端的 JavaScript。 Node.js 是一个基于 Chrome JavaScript 运行时建立的一个平台。底层架构是:javascript。 文件后缀:.js
前后端分离项目中的前端项目通常基于 Node.js 开发,使得前端项目能独立部署在服务器端(而不像后端程序需要 Tomcat 来部署)
Node.js 是脱离浏览器环境运行的 JavaScript 程序
Node.js 是一个事件驱动 I/O 服务端 JavaScript 环境,基于 Google 的 V8 引擎,V8 引擎执行Javascript 的速度非常快,性能非常好。
如果想开发更复杂的基于 Node.js 的应用程序后台,需要进一步学习 Node.js 的 Web 开发相关框架 express,art-template、koa等。
1 | // 导入模块是require 就类似于import java.io |
1 | node httpserver.js |
1 | npm install mysql |
1 | //1: 导入mysql依赖包, mysql属于第三方的模块就类似于 java.sql一样的道理 |
1 | node db |