1.介绍
AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
下面我们结合一个通俗的生活例子来理解一下,比如我们往冰箱里放东西这个过程,非AOP的过程:开门-放冰箱里(大象)-关门,开门-放冰箱里(冰棍)-关门,一两次你没什么感觉,假如你往不停地往冰箱里来来回回拿放东西,岂不要累死,我们分析一下这个过程,整个流程中我们其实只想放大象,放雪糕,而开门,关门就是无用功,是我们不想重复的,这个时候如果应用AOP后,配置在调用放冰箱里前执行开门,在调用放冰箱里后执行关门,那么对于我们来说整个过程其实就是放冰箱里(大象),放冰箱里(冰棍),而开关门AOP帮你做了,这就是aop的作用。
2.maven依赖
框架我们还是依赖前面文章建立的框架,在pom.xml中引入依赖:
<!--建议我所引用包的版本集中放在这里,这样比较直观-->
<properties>
<spring.version>4.2.5.RELEASE</spring.version>
<aspectj.version>1.8.9</aspectj.version>
<cglib.version>3.2.4</cglib.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<!--spring切面aop-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
</dependency>
<dependency>
<groupId>aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${aspectj.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>${aspectj.version}</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>${cglib.version}</version>
</dependency>
3.配置文件
在spring-context.xml中开启@Aspect注解:
<!--启用AspectJ自动代理,即启用@Aspect注解-->
<aop:aspectj-autoproxy proxy-target-class="true"/>
4.定义切面
CalculateRunTime:
package com.tl.skyLine.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import java.util.Arrays;
/**
* 拥有@Aspect注解的bean都将被Spring自动识别并用于配置在Spring AOP
* Created by tl on 17/2/23.
*/
@Component
@Aspect
public class CalculateRunTime {
private long startTime;
private long endTime;
//切点,格式expression="execution(* com.tl.skyLine.service.*.*(..))"
@Pointcut("execution(* com.tl.skyLine.repository.UserDao.findOneByUsername(..))")
public void timePoint() {
}
@Before("timePoint()")
public void doBefore(JoinPoint jp) {
startTime = System.currentTimeMillis();
System.out.println("----------开始执行----------" + startTime);
System.out.println("Before增强:被织入增强处理的目标方法为:" +
jp.getSignature().getName());
System.out.println("Before增强:目标方法的参数为:" + Arrays.toString(jp.getArgs()));
System.out.println("Before增强:被织入增强处理的目标对象为:" + jp.getTarget());
System.out.println("Before增强:代理对象本身:" + jp.getThis());
}
@AfterReturning("timePoint()")
public void doAfter() {
endTime = System.currentTimeMillis();
System.out.println("执行结束时间:" + endTime);
System.out.println("执行结束,耗时:" + (endTime - startTime));
}
// can't determine precedence between two or more pieces of advice that apply to the same join point
// @Around("timePoint()")
// public Object doAround(ProceedingJoinPoint jp) {
// System.out.println("----doAround()开始----");
// System.out.println("此处可做一些类似doBefore的工作");
// Object o = null;
// try {
// //执行被通知的方法
// o = jp.proceed();
// } catch (Throwable throwable) {
// throwable.printStackTrace();
// }
// System.out.println("此处可做一些类似doAfter的工作");
// System.out.println("----doAround()结束----");
// System.out.println("返回值:" + o);
// return o;
// }
}
获取目标类链接点对象,AspectJ使用的是org.aspectj.lang.JoinPoint接口,如果是环绕增强时,使用org.aspectj.lang.ProceedingJoinPoint表示连接点对象,该类是JoinPoint的子接口。任何一个增强方法都可以通过将第一个入参声明为JoinPoint访问到连接点上下文的信息。我们先来了解一下这两个接口的主要方法:
JoinPoint
1.java.lang.Object[] getArgs():获取连接点方法运行时的入参列表;
2.Signature getSignature() :获取连接点的方法签名对象;
3.java.lang.Object getTarget() :获取连接点所在的目标对象;
4.java.lang.Object getThis() :获取代理对象本身;
ProceedingJoinPoint
ProceedingJoinPoint继承JoinPoint子接口,它新增了两个用于执行连接点方法的方法:
1.java.lang.Object proceed() throws java.lang.Throwable:通过反射执行目标对象的连接点处的方法;
2.java.lang.Object proceed(java.lang.Object[] args) throws java.lang.Throwable:通过反射执行目标对象连接点处的方法,不过使用新的入参替换原来的入参。
5.测试
@Test
public void findOne() {
User user = new User();
user.setUsername("skyLine");
user.setPassword("8888888");
userDao.store(user);
userDao.findOne(user.getId());
}
打印结果:
Before增强:被织入增强处理的目标方法为:findOneByUsername
Before增强:目标方法的参数为:[skyLine]
Before增强:被织入增强处理的目标对象为:com.tl.skyLine.repository.impl.UserDaoImpl@797501a
Before增强:代理对象本身:com.tl.skyLine.repository.impl.UserDaoImpl@797501a
—————开始执行—————1488161847740
执行结束时间:1488161847789
执行结束,耗时:49
Process finished with exit code 0