1.1初识Spring框架
1.1.1 概念
Spring的体系结构
Spring 由 20 多个模块组成,它们可以分为数据访问/集成(Data Access/Integration)、 Web、面向切面编程(AOP, Aspects)、提供JVM的代理 (Instrumentation)、消息发送(Messaging)、 核心容器(Core Container)和测试(Test)。
Spring是轻量级的ioc(控制反转)和aop(面向切面)容器框架。通过控制反转的技术达到松耦合的目的,提供面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务进行内聚性的开发。
IOC是什么
IOC为控制反转,实质上就是个Map。它将对象的创建权交给容器,在项目启动时,通过Dom4J加载xml文件,读取bean节点,找到对应的ID,根据全限定类名使用反射机制创建对象到Map中,其中注解也是通过反射创建对象。
java中创建对象的方式如下:
1
2
3
4
5
61.构造方法
2.反射
3.序列化
4.克隆
5.动态代理
6.IOC
DI是什么
- DI是依赖注入,是IOC的具体实现手段,指的是在程序进行中将对象动态注入到类的实现机制。
AOP是什么
- AOP为面向切面编程,就是将交叉业务逻辑封装成切面,利用 AOP 容器的功能将切面织入到 主业务逻辑中。所谓交叉业务逻辑是指,通用的、与主业务逻辑无关的代码,如安全检查、 事务、日志、缓存等。
优势
(1)轻量:Spring的所需要的jar包都非常小,一般1M以下,几百kb。核心功能所需要的jar包总共3M左右。
Spring框架运行占有资源少,运行效率高,不依赖其他jar。
(2) 针对接口编程,解耦合
(3) AOP编程的支持
(4) 方便集成各种优秀框架
1.1.2 Bean的装配
默认方式
spring会读取xml配置文件的
1.1.3 Bean的作用域
- singleton:每个实例都是单例的,是默认的。
- prototype:多例的,每次使用getBean()都会产生一个新的实例。
- session:在一个session中,实例是共享的。
- request:在一次request请求时,他们的实例是共享的,每次新的HTTP请求会产生新的实例。
- Application:bean在一个servletContext的生命周期中复用一个实例。
- Websocket:bean被定义在Websocket的生命周期中复用一个单例对象。
2.1.1 基于XML的DI
2.1.2 注入分类
- set注入
set注入也叫设值注入,是指通过setter方法传入被调用者的实例属性进行赋值。
普通类型:<property name="name" value="truly"/>
引用类型:<property name="name" ref="truly"/>
- 构造注入
执行类的有参构造,在构造对象的同时给属性赋值。
< constructor-arg />标签中用于指定参数的属性有:
- name:指定参数名称,指的是构造方法中的形参。
- index:指明该参数对应着构造器的第几个参数,从 0 开始。不过,该属性不要也行,但要注意,若参数类型相同,或之间有包含关系,则需要保证赋值顺序要与构造器中的参数顺序一致。
<constructor-arg name="name" value="truly"
<constructor-arg index=0 value="truly"
- 引用类型自动注入
对于引用类型属性的注入,也可不在配置文件中显示的注入。可以通过为< bean/>标签 设置 autowire 属性值,为引用类型属性进行隐式自动注入(默认是不自动注入引用类型属 性)。根据自动注入判断标准的不同,可以分为两种:
- byName:根据名称自动注入
<bean id="test" class="com.truly.test" autowire="byName>....</bean>"
- byType: 根据类型自动注入
<bean id="test" class="com.truly.test" autowire="byType>....</bean>"
使用 byType 方式自动注入,要求:配置文件中被调用者 bean 的 class 属性指定的类, 要与代码中调用者 bean 类的某引用类型属性类型同源。即要么相同,要么有 is-a 关系(子类,或是实现类)。但这样的同源的被调用 bean 只能有一个。多于一个,容器就不知该匹配 哪一个了。就会报错。
基于注解的DI
在pom.xml中添加spring-context依赖
类上加入注解@Componet(“test”)
PS:@Component 不指定 value 属性,bean 的 id 是类名的首字母小写。
配置文件上加入context的xmlns,配置包扫扫描路径
<context:componet-scan base-package="com.truly.test"
另外,Spring 还提供了 3 个创建对象的注解:
➢ @Repository 用于对 DAO 实现类进行注解
➢ @Service 用于对 Service 实现类进行注解
➢ @Controller 用于对 Controller 实现类进行注解
简单类型属性注入@Value
需要在属性上使用注解@Value,该注解的 value 属性用于指定要注入的值。
@Value: 简单类型的属性赋值
属性: value 是String类型的,表示简单类型的属性值
位置: 在属性定义的上面或者在set方法上。
byType自动注入@Autowired
需要在引用属性上使用注解@Autowired,该注解默认使用按类型自动装配 Bean 的方式。
优先自动匹配相同数据类型**的对象,找不到再找同源类型的对象,而不是像xml一样同时找同源类。
byName自动注入@Autowired与@Qualifier
需要在引用属性上联合使用注解@Autowired 与@Qualifier。@Qualifier 的 value 属性用 于指定要匹配的 Bean 的 id 值。类中无需 set 方法,也可加到 set 方法上。
spring中通过注解给引用类型赋值,使用的是自动注入原理 ,支持byName, byType,如果要使用byName方式,需要做的是:
1.在属性上面加入@Autowired
2.在属性上面加入@Qualifier(value=“bean的id”) :表示使用指定名称的bean完成赋值。
JDK注解@Resource自动注入
Spring提供了对 jdk中@Resource注解的支持。@Resource 注解既可以按名称匹配Bean, 也可以按类型匹配 Bean。默认是按名称注入。使用该注解,要求 JDK 必须是 6 及以上版本。 @Resource 可在属性上,也可在 set 方法上。
1.byType注入引用类型属性
@Resource 注解若不带任何参数,采用默认按名称的方式注入,按名称不能注入 bean, 则会按照类型进行 Bean 的匹配注入。
2.byName注入引用类型属性
@Resource 注解指定其 name 属性,则 name 的值即为按照名称进行匹配的 Bean 的 id。
3.1.1 注解与XML对比
注解优点是:
方便
直观
高效(代码少,没有配置文件的书写那么复杂)。
其弊端也显而易见:以硬编码的方式写入到 Java 代码中,修改是需要重新编译代码的。
XML 方式优点是:
- 配置和代码是分离的
在 xml 中做修改,无需编译代码,只需重启服务器即可将新的配置加载。
xml 的缺点是:编写麻烦,效率低,大型项目过于复杂。
4.1.1 AOP面向切面编程
面向切面编程对有什么好处?
1.减少重复;
2.专注业务;
注意:面向切面编程只是面向对象编程的一种补充。
AOP相关术语
Aspect: 切面,给你的目标类增加的功能,就是切面。 像上面用的日志,事务都是切面。切面的特点: 一般都是非业务方法,独立使用的。
Orient:面向, 对着。
Programming:编程
怎么理解面向切面编程 ?
1)需要在分析项目功能时,找出切面。
2)合理的安排切面的执行时间(在目标方法前, 还是目标方法后)
3)合理的安全切面执行的位置,在哪个类,哪个方法增加增强功能
说一个切面有三个关键的要素:
1)切面的功能代码,切面干什么。(what)
2)切面的执行位置,使用Pointcut表示切面执行的位置。(where)
3)切面的执行时间,使用Advice表示时间,在目标方法之前,还是目标方法之后。(when)
1.切面(Aspect)
切面泛指交叉业务逻辑。上例中的事务处理、日志处理就可以理解为切面。常用的切面 是通知(Advice)。实际就是对主业务逻辑的一种增强。
表示增强的功能, 就是一堆代码,完成某个一个功能。非业务功能,常见的切面功能有日志, 事务, 统计信息, 参数检查, 权限验证。
2.连接点(JoinPoint)
连接点指可以被切面织入的具体方法。通常业务接口中的方法均为连接点。
连接业务方法和切面的位置。 就某类中的业务方法
3.切入点(Pointcut)
切入点指声明的一个或多个连接点的集合。通过切入点指定一组方法。
被标记为 final 的方法是不能作为连接点与切入点的。因为最终的是不能被修改的,不能被增强的。
4.目标对象(Target)
目标对象指将要被增强的对象 。即包含主业务逻辑的类的对象。上 例 中 的 StudentServiceImpl 的对象若被增强,则该类称为目标类,该类对象称为目标对象。当然,不被增强,也就无所谓目标不目标了。
5.通知(Advice)
通知表示切面的执行时间,Advice 也叫增强。上例中的 MyInvocationHandler 就可以理 解为是一种通知。换个角度来说,通知定义了增强代码切入到目标代码的时间点,是目标方法执行之前执行,还是之后执行等。通知类型不同,切入时间不同。
切入点定义切入的位置,通知定义切入的时间。
切面的执行时间, 这个执行时间在规范中叫做Advice(通知,增强), 在aspectj框架中使用注解表示的。也可以使用xml配置文件中的标签:
(1)前置通知
(2)后置通知
(3)环绕通知
(4)异常通知
(5)最终通知
AspectJ 的切入点表达式
AspectJ 定义了专门的表达式用于指定切入点。表达式的原型是:
execution(public void com.truly.test.method(..))
意思是对truly包下的test类中的method方法进行织入。
表达式:execution(访问权限 方法返回值 方法声明(参数) 异常类型)
AspectJ的开发环境
1.maven 依赖
1 | <dependencies> |
2.引入AOP约束
在 AspectJ 实现 AOP 时,要引入 AOP 的约束。配置文件中使用的 AOP 约束中的标签, 均是 AspectJ 框架使用的,而非 Spring 框架本身在实现 AOP 时使用的。 AspectJ 对于 AOP 的实现有注解和配置文件两种方式,常用是注解方式。
注解方式
1 | 1.创建目标类 |
1.@Before前置通知-方法有JoinPoint参数
在目标方法执行之前执行。被注解为前置通知的方法,可以包含一个 JoinPoint 类型参数。该类型的对象本身就是切入点表达式。通过该参数,可获取切入点表达式、方法签名、 目标对象等。不光前置通知的方法,可以包含一个 JoinPoint 类型参数,所有的通知方法均可包含该参数。JoinPoint必须是形式参数的第一个。
JoinPoint 代表的是业务方法,这个方法中要加入切面功能,在下面这个例子中,JoinPoint 就是具体的doSome方法。
1 | @Before(切点表达式) |
2.@AfterReturning后置通知-注解有returning属性
在目标方法执行之后执行。由于是目标方法之后执行,所以可以获取到目标方法的返回值。该注解的 returning 属性就是用于指定接收方法返回值的变量名的。所以,被注解为后置通知的方法,除了可以包含 JoinPoint 参数外,还可以包含用于接收返回值的变量。该变量最好为 Object 类型,因为目标方法的返回值可能是任何类型。
注意:returning 属性自定义变量名必须和通知方法的形参名一样。
1 | @AfterReturning(value=切点表达式,returning="res") |
3.@Around 环绕通知-增强方法有 ProceedingJoinPoint 参数
在目标方法执行之前之后执行。被注解为环绕增强的方法要有返回值,Object 类型。并 且方法可以包含一个 ProceedingJoinPoint 类型的参数。接口 ProceedingJoinPoint 其有一个 proceed()方法,用于执行目标方法。若目标方法有返回值,则该方法的返回值就是目标方法的返回值。最后,环绕增强方法将其返回值返回。该增强方法实际是拦截了目标方法的执行。 接口增加方法:
注意:环绕通知的返回值就是目标方法的执行结果,可以被修改。ProceedingJoinPoint继承了JoinPoint。
1 | @Around(value=切点表达式") |
4.@AfterThrowing 异常通知-注解中有 throwing 属性
在目标方法抛出异常后执行。该注解的 throwing 属性用于指定所发生的异常类对象。 当然,被注解为异常通知的方法可以包含一个参数 Throwable,参数名称为 throwing 指定的名称,表示发生的异常对象。
1 | @AfterThrowing(value=切点表达式") |
5.@After 最终通知
无论目标方法是否抛出异常,该增强均会被执行。
1 | @After(value=切点表达式") |
@Pointcut 定义切入点
当较多的通知增强方法使用相同的 execution 切入点表达式时,编写、维护均较为麻烦。 AspectJ 提供了@Pointcut 注解,用于定义 execution 切入点表达式。 其用法是,将@Pointcut 注解在一个方法之上,以后所有的 execution 的 value 属性值均 可使用该方法名作为切入点。代表的就是@Pointcut 定义的切入点。这个使用@Pointcut 注解 的方法一般使用 private 的标识方法,即没有实际作用的方法。
实践代码
1 | package com.truly.base; |
4.1.2 Spring对AOP实现方式的管理
1.当有接口时,默认使用的是JDK动态代理
2.当没有接口时,默认使用的是cglib动态代理
当有接口时,还是想强制使用cglib动态代理
- 在配置文件中引入aop,再配置
<aop:aspectj-autoproxy proxy-target-class="true"/>
5.1.1 Spring集成其他框架
以Mybatis为例
Spring 像插线板一样,mybatis 框架是插头,可以容易的组合到一起。插线板 spring 插 上 mybatis,两个框架就是一个整体。
- 实现 Spring 与 MyBatis 的整合常用的方式:扫描的 Mapper 动态代理
回顾mybatis使用步骤
1.定义dao接口 ,StudentDao
2.定义mapper文件 StudentDao.xml
3.定义mybatis的主配置文件 mybatis.xml
4.创建dao的代理对象, StudentDao dao = SqlSession.getMapper(StudentDao.class);
- 引入maven依赖
1 | <dependencies> |
- 创建实体类
- 创建接口
- 创建映射文件
1 | <?xml version="1.0" encoding="UTF-8" ?> |
- 创建mybatis主配置文件
1 | <?xml version="1.0" encoding="UTF-8" ?> |
- 创建spring配置文件
1 | <?xml version="1.0" encoding="UTF-8"?> |
6.1.1 Spring的事务
事务是指一组sql语句的集合, 集合中有多条sql语句可能是insert , update ,select ,delete, 我们希望这些多个sql语句都能成功,或者都失败, 这些sql语句的执行是一致的,作为一个整体执行。
1)事务的隔离级别:有4个值。
DEFAULT:采用 DB 默认的事务隔离级别。MySql 的默认为 REPEATABLE_READ; Oracle默认为 READ_COMMITTED。
➢ READ_UNCOMMITTED:读未提交。未解决任何并发问题。
➢ READ_COMMITTED:读已提交。解决脏读,存在不可重复读与幻读。
➢ REPEATABLE_READ:可重复读。解决脏读、不可重复读,存在幻读
➢ SERIALIZABLE:串行化。不存在并发问题。
2)事务的超时时间: 表示一个方法最长的执行时间,如果方法执行时超过了时间,事务就回滚。
单位是秒, 整数值, 默认是 -1.
3)事务的传播行为 :控制业务方法是不是有事务的, 是什么样的事务的。
行为,表示你的业务方法调用时,事务在方法之间是如何使用的。
PROPAGATION_REQUIRED
PROPAGATION_REQUIRES_NEW
PROPAGATION_SUPPORTS
PROPAGATION_MANDATORY
PROPAGATION_NESTED
PROPAGATION_NEVER
PROPAGATION_NOT_SUPPORTED
Spring的事务管理分类
- 编程式事务:就是通过编程的方式自己去实现事务。
- 声明式事务:就是我们只需要声明一个事务就可以了,不需要我们手动去编写事务管理代码。
在 Spring 中通常可以通过以下两种方式来实现对事务的管理:
(1)使用 Spring 的事务注解管理事务
(2)使用 AspectJ 的 AOP 配置管理事务
事务定义接口 TransactionDefinition 中定义了事务描述相关的三类常量:事务隔离级别、 事务传播行为、事务默认超时时限,及对它们的操作
定义了五个事务隔离级别常量
DEFAULT:采用 DB 默认的事务隔离级别。MySql 的默认为 REPEATABLE_READ; Oracle默认为 READ_COMMITTED。
➢ READ_UNCOMMITTED:读未提交。未解决任何并发问题。
➢ READ_COMMITTED:读已提交。解决脏读,存在不可重复读与幻读。
➢ REPEATABLE_READ:可重复读。解决脏读、不可重复读,存在幻读
➢ SERIALIZABLE:串行化。不存在并发问题