1. Spring整合junit
注意:spring5.x必须对应junit4.12及以上版本,否则报init初始化错误
就是为了避免重复代码
其实可以用junit的注解@Before去做的,但是公司开发的时候开发工程师和测试工程师时两个人,测试工程师只关注service.findAll()这个接口,对于上面红色框框出的代码,可能测试工程师不懂spring,所以必须由开发工程师写,但是开发又不写@before代码块,只写这个空测试类
所以可以通过注解/xml的方式在空类上面创建ioc容器
- 导坐标
- 测试类上方加注解
@RunWith(SpringJUnit4ClassRunner.class)
- 告知spring的运行器用的是注解还是xml
@ContextConfiguration
两个属性:locations:指定xml的位置,记得加上classpath:
classes:指定注解类所在位置
- 注入
2. Spring中的AOP
2.1 AOP的概念以及作用(了解,代码看看就行)
AOP的本质是动态代理,就是方法增强,比如原来有一个方法funA(),里面输出了一句”world”,现在通过AOP/动态代理等方式对funA()进行改造,使得可以在执行funA之前先输出一句”hello”,在执行funA之后输出”!!!!”,这就是方法增强。
为什么要这么做呢?在实际开发中体现为:我们在业务层编写的时候只需要关注业务逻辑,而不用对每个方法都
开启事务
业务逻辑
提交事务(正常)
回滚事务(异常)
释放连接
如果每个方法都要这样写就太麻烦了,因此我们通过aop来实现对业务层的方法增强,之后事务管理器负责:
开启事务
提交事务(正常)
回滚事务(异常)
释放连接
而这样依赖业务层只有:
业务逻辑
使用AOP/动态代理后
而事务的管理交给代理类(这里先用动态代理介绍方法增强,AOP原理相同,随便看一下代码,后期我们使用spring的事务管理器,就不用自己写代码了)
1 | /** |
至于里面的txManager我们用的是自己写的事务管理器(随便看一下代码,后期我们使用spring的事务管理器,就不用自己写代码了)
1 | /** |
同时,为了解决事务和线程没有绑定,导致有异常不能回滚的现象,我们还编写了一个事务和线程绑定的类(随便看看就行,后期我们用spring自带的事务管理器,避免了这个问题)
1 | /* |
2.1.1 动态代理的写法(了解,小插曲)
有人说,哎呀 我不会写动态代理,这又是匿名内部类的又是什么Proxy
我说你会你就会,你认真看啊
其实要记的就只有
你得知道动态代理是什么类下的什么方法吧:
Proxy.newProxyInstance()
参数列表其实是不用记的,你敲下1就会有提示,按类型填就行了,比如他会告诉你第一个参数是类加载器,你只需要记住填的是
“要增强的方法的类”
的类加载器,就不会错,第二个也是,你只要记住是“要增强的方法的类”
原本实现的接口就行new InvocationHandler()
,这个是要记住的,你敲下前几个字母就会自动补全下面的invoke方法了至于里面的方法,我们方法增强是为了事务管理,所以里面才写事务的相关代码,如果方法增强是为了别的,那就视情况而定呀,不是固定代码,
要注意的只有method.invoke()返回值是个Object的returnValue对象
关于动态代理的内容,在此不做过多的展开(单独做了动态代理笔记),只是为了说明方法增强是怎么回事,如何解决事务等方法重复代码的问题,以及了解AOP其实本质就是动态代理,实现了方法增强。
2.2 spring中基于xml的AOP配置步骤
把通知Bean也交给spring来管理
使用aop:config标签表明开始AOP配置
使用aop:aspect标签表明配置切面
id:是给切面提供一个标识
ref:指定通知类bean的id
- 在aop:aspect标签内部使用对应标签来配置通知的类型
我们现在的示例是想在切入点方法执行前执行printLog,所以是前置通知
aop:before:表示配置前置通知
method属性:指定Logger类中哪个方法是前置通知
pointcut属性:指定切入点的表达式,指的是对业务层中哪些方法增强
2.3 切入点表达式的写法:
关键字:execution(表达式)
表达式:访问修饰符 返回值 包名.包名.包名…类名.方法名(参数列表)
标准写法:
public void com.wjw.service.impl.AccountServiceImpl.saveAccount()
访问修饰符可以省略:
void com.wjw.service.impl.AccountServiceImpl.saveAccount()
返回值可以使用通配符*表示任意返回值:
* com.wjw.service.impl.AccountServiceImpl.saveAccount()
包名可以用通配符表示任意包,但是有几级包(从com开始计算),就有多少个*.:
* *.*.*.*.AccountServiceImpl.saveAccount()
或者可以使用*..表示当前包及其子包:
* *..AccountServiceImpl.saveAccount()
类名和方法名都可以用*来实现通配:
* *..*.*()
参数列表可以直接写数据类型:
基本类型直接写名称:* *..*.*(int)
引用类型写包名.类名:* *..*.*(java.lang.String)
可以实现通配:
可以用*实现通配,代表有参数且任意类型:* *..*.*(*)
可以用..实现通配,有无参数都可以且可以任意类型:* *..*.*(..)
全通配写法:
* *..*.*(..)
实际开发中常用切入点表达式写法都是切入到业务层下的所有方法:* com.wjw.service.impl.*.*(..)
2.4 通知类型
<aop:before>
前置通知,在切入点方法执行前执行
<aop:after-returning>
后置通知,在切入点正常执行后执行
<aop:after-throwing>
异常通知,在切入点方法执行异常后执行(相当于catch)
<aop:after>
最终通知,无论是否正常都会执行(相当于finally)
<aop:around>
环绕通知,其实环绕通知就是上面所有通知的总和,在环绕通知中,代码写在哪里就是什么通知。Spring框架为我们提供了一个接口:ProceedingJoinPoint。该接口有一个方法proceed(),此方法就相当于明确调用切入点方法。该接口可以作为环绕通知的方法参数
简单地说,spring的环绕通知就是我们可以用代码的方式写所有的通知,位置就是不同的方式。(其实就是动态代理本来的写法)
2.5 简化重复pointcut,可以单独抽取出来
<aop:pointcut>
标签 id是唯一标识,expression就是原来的表达式
原来表达式不再用pointcut而是用point-ref,值就是<aop:pointcut>
的id
也可以将<aop:pointcut>
标签配置在<aop:aspect>
外面,这样所有切面可用,注意:一定要在aspect前面
3. 注解方式AOP
要使用注解AOP,必须如下配置
要注意,这段约束在官网是没有现成的,先搜xmlns:aop的约束复制下来,再找xmlns:context找到红框中的三段复制下来
<aop:aspectj-autoproxy>
是开启注解AOP的钥匙
切面类有这几点
3.1 @Aspect
类上方添加,表明这是一个切面
3.2 @Pointcut
和<aop:pointcut>
一样,代表一个表达式,注意写法是通过空函数的形式
1 | @ Pointcut(“execution(“* com.wjw.service.impl.*.*(..)”)”) |
3.3 @Before(“methodName()”)
注意不要少了括号,前置通知
3.4 @AfterRuturning(“methodName()”)
注意不要少了括号,后置通知
3.5 @AfterThrowing(“methodName()”)
注意不要少了括号,异常通知
3.6 @After(“methodName()”)
注意不要少了括号,最终通知
注意事项:
spring的注解方式AOP的通知顺序是:前置 最终 后置/异常
最终通知的位置不对
而使用环绕通知的话,因为是自己写的,所以位置和xml还有理解中的是一致的
完全不用XML的方式
1 |
|
就是在“完全不用XML方式实现ioc”的那个配置类上方再加上@EnableAspectJAutoProxy
4. Spring自带的事务管理器:声明式事务控制
还记得之前“九.1”那个案例吗(翻回去看),我们现在使用AOP代替了动态代理,同样解决了事务重复代码的问题,对事务控制可以抽取出来了
可是,之前案例中的两个类是自己写的:
TransactionManager:事务控制的类(包含提交,回滚等方法),自己写太不现实,我们现在要改用Spring自带的事务管理器。
ConnectionUtils:这是之前为了防止异常无法回滚的问题而写的类,现在我们通过Spring自带的事务管理器,可以避免这个问题。
4.1 基于XML的声明式事务控制
使用步骤:
1 | spring中基于xml的声明式事务控制基本步骤 |
也就是说,对于前置通知是事务的开启,后置通知是事务的提交,异常通知是事务的回滚,最终通知是连接的释放这种基本上是固定操作
spring封装了一个DataSourceTransactionManager自动完成,只要是在2中的transaction-manager中引用了这个类,那么就不必关注这几个操作了
更多的是关注不同方法对于事务的处理(事务的属性)比如在上一个事务未完成前下一个事务是否可以读取(find就可以其他操作则不行)
数据源还是原来的,也给出截图
4.2 基于注解的声明式事务控制
使用步骤
1 | spring中基于注解的声明式事务控制基本步骤 |
惊喜的发现,由于spring的事务控制是写好的,所以连aop那些@Aspect啊@Before啊都不用了,spring知道执行之后commit,在异常之后AfterThrowing,只要@Transactional后所有方法都自动添加了事务
别忘了这些细节的东西
所以只能自己补上(@AutoWired可以不要,用的是xml)
4.3 纯注解的声明式事务控制
算了,别整这些有的没的,反正也没人用
附录:
ioc下通过继承JdbcDaoSupport如何注入数据源
这是由于类中使用的是继承方式
事实上就自动获得了所有父类的方法,而父类的class文件里我们可以看到setDataSource方法,因此我们只要根据这个set方法设置dao的注入就好了