初识SpringAOP

我们知道,使用面向对象编程(OOP)有一些弊端,当需要为多个不具有继承关系的对象引入同一个公共行为时,例如日志、安全监测、权限管理等,只有在每个对象里引用公共的行为,这样程序中就产生了大量的重复代码,即不美观也不利于维护,所以就有了一个对面向对象编程的补充,即面向方面编程(AOP)。AOP所关注的方向是横向的,不同于OOP的纵向。

在Sping中,你可以使用@Aspect注解非常容易地定义一个切面。Spring采用@Aspect注解对POJO进行标注,从而定义一个包含切点信息和增强横切逻辑的切面,可以将这个切面植入到匹配的目标Bean中。下面我们来看下如何完成AOP的配置和实现。

开始配置AOP

首先我们先创建一个用于拦截的Bean。

假设这个类在我们实际应用中会有核心的业务,并且需要在这个类前后加入日志来跟踪调试。

1
2
3
4
5
6
7
8
9
10
11
12
//执行项目业务逻辑的类
public class TestBean {

public void test1(String str) {
System.out.println("service test1 with string: " + str);
}

public void test2() {
System.out.println("service test2...");
}

}
接着我们开始创建AOP类。

这个类是负责监听所有类的test方法在执行前和执行后的时候,打印相应的日志,并且还能控制这些方法在执行前和执行后之间的一些处理(即环绕方法)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@Aspect
public class AspectTest {

@Pointcut(value = "execution(* com.learn.aop.test*(..))")
public void test() {}

@Before(value = "test()")
public void beforeTest() {
System.out.println("beforeTest");
}

@After(value = "test()")
public void afterTest() {
System.out.println("afterTest");
}

@Around(value = "test()")
public Object aroundTest(ProceedingJoinPoint pjp) {
Object obj = null;
System.out.println("aroundTest1");
try {
obj = pjp.proceed();
} catch (Throwable e) {
e.printStackTrace();
}
System.out.println("aroundTest2");
return obj;
}

}
然后我们再创建一个spring-aop.xml配置文件。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
">

<!--开启AOP监听-->
<aop:aspectj-autoproxy />
<bean id="testBean" class="com.learn.aop.TestBean" />
<bean class="com.learn.aop.AspectTest" />

</beans>

至此,我们已经初步完成了AOP的配置。现在开始来测试看看效果。

开始测试。
1
2
3
4
5
6
7
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-aop.xml");
TestBean testBean = (TestBean) context.getBean("testBean");
testBean.test1("aop");
testBean.test2();

}

如果不出意外,那么会打印结果:

1
2
3
4
5
6
7
8
9
10
aroundTest1
beforeTest
service test1 with string: aop
aroundTest2
afterTest
aroundTest1
beforeTest
service test2...
aroundTest2
afterTest

Spring实现了对所有类的test方法进行扫描,我们可以很方便地在这些方法执行前后做一些日志的记录,这样就能与业务代码解耦。那么Spring究竟是如何实现AOP的?

我们看下spring-aop.xml这个配置文件,其中<aop:aspectj-autoproxy />的配置会让Spring支持带有@Aspect注解的AOP类。
然后再看下@Pointcut(value = "execution(* com.learn.aop.test*(..))")@Pointcut是指哪些方法需要被执行”AOP”,里面的值是一个表达式(关于这个表达式的写法,大家可以问下度娘)。在注解下方定义一个test()方法,注意这个方法必须要定义(方法名可以随意取一个),之后定义的执行前after、执行后before以及环绕方法around都是需要依赖它的,它就相当于一个“扫描器”。接着我们就可以定义@After@Before以及@Around方法了。只需要在你定义的方法上加上这三种注解,对应的value值是“扫描器”的方法。

这样,一个简单的AOP实现就完成了。