Spring整合JUnit单元测试
为了简化Junit的测试,使用Spring框架也可以整合测试
引入Sprin测试整合单元测试的环境
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
在具体的测试类上添加注解
@RunWith(SpringJUnit4ClassRunner.class)
//如果我们使用的配置文件的方式,则加载类路径的配置文件
//@ContextConfiguration("classpath:applicationContext.xml")
//如果我们使用的是配置类的方式则加载配置类的字节码类型
@ContextConfiguration(classes = SpringConfig.class)
@RunWith(value = SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class TestAccountDao2 {
@Autowired //记得注入dao
private IAccountDao dao;
@Test
public void testFindAll() {
//执行查询所有的操作
List<Account> list = dao.findAll();
for (Account account : list) {
System.out.println(account);
}
}
}
使用AOP技术对DAO层进行增强功能
Spring框架的核心功能之AOP技术
spring框架的aop技术实现其实就是基于动态代理
动态代理:
程序在执行过程中自动生成代理对象,通过代理对象的执行方法,使得被代理对象源代码不更改的情况下实现其功能扩展。
jdk动态代理:
jdk内置的动态代理要求代理对象要实现接口,jdk提供了三个类,Proxy,Method,InvocationHandler用于创建代理对象,实现功能扩展。
cglib 动态代理:
cglib是第三方类库。要求被代理的类必须可以被继承,其子类就是功能增强了的代理对象,要求被代理类及其方法不能被final修饰
AOP是OOP(面向对象)的延续,是函数式编程的一种衍生范畴。
利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑的各个部分进行隔离,从而使得业务逻辑各个部分之间的耦合度降低,提高=程序的可重用性,同时提高了开发效率。
AOP
使用AOP主要是可以在不修改源代码的情况下对程序进行增强。
面向切面编程,切面就是我们需要增强的功能,也是一种思想,是基于jdk,cglib动态代理的,是动态代理的规范化。
- 使用的时候先找到被代理对象的切面,也就是除了业务之外的功能。
- 确定切面执行的时间
- 确定切面的执行位置,给谁增强功能。
AOP的相关术语:
- Jointpoint(连接点):指那些被拦截到的点,在spring中这些点指的是方法,因为Spring只支持方法类型的连接点。
- Pointcut(切入点):指要对哪些Jointpoint进行拦截的定义,也就是打算做增强的那个方法。
- Adivice(通知/增强):指拦截到Jointpoint之后所要做的事情就是通知,通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能)
- Introduction(引介):引介是一种特殊的通知在不修改代码的前提下,Introduction可以在运行期为类动态地添加一些方法或Field
- Target(目标对象):代理的目标对象
- Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程。
- Proxy(代理):一个类被AOP织入增强后,就会产生一个结果代理类。
- Aspect(切面):是切入点和通知的结合,切面是以后自己来编写和配置的。
AOP技术实现框架
使用aspectJ框架,在maven项目pom.xml中添加依赖包
-
切面执行时间常用以下注解。
-
@Before:前置通知,在目标类的方法执行之前执行。配置文件信息:<aop:before method=“before” pointcut-ref=“myPointcut3”/>
可以对方法的参数做校验。
-
@AfterReturning:后置通知,方法正常执行后的通知,在配置文件中编写具体的配置:
<aop:after-returning method=“afterReturning” pointcut-ref=“myPointcut2”/>
应用:可以修改方法的返回值
-
@Around:环绕通知,方法的执行前后执行,
在配置文件中编写具体的配置:
<aop:around method=“around” pointcut-ref=“myPointcut2”/>
要注意:目标的方法默认不执行,需要使用ProceedingJoinPoint对来让目标对象的方法执行。 环绕通知:方法执行之前和方法执行之后进行通知,默认的情况下,目标对象的方法不能执行的。需要手动让目标对象的方法执行
public void around(ProceedingJoinPoint joinPoint){ //ProceedingJoinPoint 固定的一个类 使用他然后目标对象的方法执行. System.out.println("环绕通知1..."); try { // 手动让目标对象的方法去执行 joinPoint.proceed(); } catch (Throwable e) { e.printStackTrace(); } System.out.println("环绕通知2..."); }
-
@AfterThrowing:异常抛出通知
-
在抛出异常后通知
-
在配置文件中编写具体的配置:
throwing=“e” 这个e的意思就是你切面类里面配置的afterThrowingLog(Exception e)这个增强的方法的形参的名称//当你目标对象中delCustomer()方法中出现了异常,那么切面类里面配置的afterThrowingLog()这个增强的方法就会执行
<aop:after-throwing method=“afterThrowingLog”
pointcut=“execution(public * ….Customer*.delCustomer(…))” throwing=“e”/>
//切面类中 抛出异常后通知 需要在增强的方法形参里面提供一个异常对象,当你切入点里面抛出你配置的这个异常类型
-
-
@After:最终通知,在目标类的方法执行之后执行,如果程序出现了异常,最终方法也会执行,在配置文件中编写具体的配置:
<aop:after method=“after” pointcut-ref=“myPointcut3”/>
应用:例如像释放资源
-
-
切面的执行位置采用切入点表达式
以上几个注解均有属性value,属性值为切入点表示式的字符串,表示切面执行的位置
语法格式:
execution(访问权限 方法返回值 方法声明(参数) 异常类型)3.符号
*---- 0至多个任意字符串
… -----用在方法参数中,表示任意多个参数用在包名后,表示当前包及其子包
±-----用在类名后,表示当前类及其子类,用在接口后, 表示当前接口及其实现类。
例如:
execution(public * *(…)) //指定切入点为:任意公共方法。
代码演示:
//接口
public interface Target {
void doSome(String name,Integer age);
}
/**
- 被代理对象
*/
public class TargetImpl implements Target {
@Override
public void doSome(String name,Integer age) {
//在执行doSome方法前增加一个功能
System.out.println("targetImpl的doSome方法执行了");
}
}
//@Aspect注解声明这个类是切面类
@Aspect
public class ProxyByAspectJ {
/**
* 声明需要增加的功能
* 注意:
* 1.这个功能需是public修饰
* 2.这个功能没有返回值
*/
//Before:前置通知注解,在被代理对象方法执行之前执行
//execution(public * *.doSome(..)):任意包中的doSome方法,可以写具体切入点表示式
@Before(value="execution(public * *.doSome(..))")
public void doBefore(){
System.out.println("在doSome方法之前执行这个功能");
}
}
在xml配置中声明aspectj框架中的自动代理生成器标签引入具体的AOP的schema约束
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--创建被代理对象-->
<bean id="target" class="com.itheima.TargetImpl"/>
<!--创建切面类对象-->
<bean id="proxy" class="com.itheima.ProxyByAspectJ"/>
<!--声明自动生成代理生成器,在内存中改变被代理对象的结构为代理对象-->
<context:aspectj-autoproxy/>
</beans>
通用化切入点的表达式:
<!--定义切面 指定拦截方法时 做什么-->
<bean id="xmlAopDemoUserLog" class="com.ganji.demo.service.aspect.XmlAopDemoUserLog"></bean>
<aop:config>
<aop:aspect ref="xmlAopDemoUserLog"> <!--指定切面-->
<!--定义切点-->
<aop:pointcut id="logpoint" expression="execution(* com.ganji.demo.service.user.UserService.GetDemoUser(..))"></aop:pointcut>
<!--定义连接点-->
<aop:before pointcut-ref="logpoint" method="beforeLog"></aop:before>
<aop:after pointcut-ref="logpoint" method="afterLog"></aop:after>
<aop:after-returning pointcut-ref="logpoint" method="afterReturningLog"></aop:after-returning>
<aop:after-throwing pointcut-ref="logpoint" method="afterThrowingLog"></aop:after-throwing>
</aop:aspect>
</aop:config>
Spring框架的AOP技术
步骤一:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.7</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
步骤二:创建Spring的配置文件,引入具体的schema约束;
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
</beans>
步骤三:创建包结构,编写具体的接口和实现类
org.itheima.demo1
CustomerDao -- 接口
CustomerDaoImpl -- 实现类
步骤四:将目标类配置到Spring中
<bean id="customerDao" class="org.westos.demo1.CustomerDaoImpl"/>
步骤五:定义切面类
添加切面和通知的注解
@Aspect – 定义切面类的注解
@Aspect
public class MyAspectAnno {
@Before(value="execution(public void org.westos.demo1.CustomerDaoImpl.save())")
public void log(){
System.out.println("记录日志...");
}
}
步骤六:在配置文件中定义切面类
<bean id="myAspectAnno" class="com.itheima.demo1.MyAspectAnno"/>
步骤七:在配置文件中开启自动代理
<aop:aspectj-autoproxy/> 放在最前面
步骤八:完成测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class Demo1 {
@Resource(name="customerDao")
private CustomerDao customerDao;
@Test
public void run1(){
customerDao.save();
customerDao.update();
}
}
通知类型采用注解的方式:
配置通用的切入点
* 使用@Pointcut定义通用的切入点
@Aspect
public class MyAspectAnno {
//随便定义一个方法,上面用注解定义一个切入点
@Pointcut(value="execution(public void com.itheima.demo1.CustomerDaoImpl.save())")
public void fn(){}
//引用这个定义的切入点 格式:切面类名.方法名
@Before(value="MyAspectAnno.fn()")
public void log(){
System.out.println("记录日志...");
}
/**
* 环绕通知
*/
@Around(value="MyAspectAnno.fn()")
public void around(ProceedingJoinPoint joinPoint){
System.out.println("环绕通知1...");
try {
// 让目标对象的方法执行
joinPoint.proceed();
} catch (Throwable e) {
e.printStackTrace();
}
System.out.println("环绕通知2...");
}
}