如果你阅读了上一篇文章Spring的简单配置 ,相信你已经会搭建一个简单Spring IOC的框架了。那么说到IOC我们往往会联想到Spring AOP(面向切面编程),这次教程我们就来搭建一个简单的AOP框架,体验一下AOP的使用和好处。
为了清晰,我已经给出完整的搭建教程,本教程主要是用注解方式实现的,还有直接通过配置实现AOp的,这里就不详细讲解了,在后面直接给出源码。
工具准备 1:MyEclipse 10 + Jdk6.0 及以上
2:spring-framework-3.2.2.RELEASE-dist.zip + commons-logging-1.1.3.jar(Java日志实现) + 由于Spring3.2.2官方源代码中没有提供AOP,所以需要添加aopalliance-1.0.jar、aspectjrt.jar、aspectjweaver.jar
下载完整jar包
构建环境 1:在MyEclipse里面新建一个工程,并命名称是Spring3AOP2
2:把我提供的jar包放入项目的lib文件夹下。
配置文件 1: 编写业务类HelloServiceImpl.java1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.xhay1122.spring3.aop;
/**
* 项目名称:Spring3AOP
* 类名称:HelloServiceImpl
* 类描述: 目标对象
* 创建人:xhay
* 创建时间:2015-4-9 下午4:23:20
* 修改人:xhay
* 修改时间:2015-4-9 下午4:23:20
* 修改备注:
* @version 1.0
* 软件工程创新实验室
*/
public class HelloServiceImpl {
public String say(String _msg) {
System.out.println("HelloServiceImpl.say(msg:" + _msg + ")" );
return _msg;
}
}
切面处理类TestAnnotationAspect.java
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
package com.xhay1122.spring3.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class TestAnnotationAspect {
@Pointcut("execution(* com.xhay1122.spring3.aop.*.*(..))" )
private void pointCutMethod () {
}
//声明前置通知
@Before("pointCutMethod()" )
public void do Before(JoinPoint jp) {
System.out.println("前置通知" );
}
//声明后置通知
@AfterReturning(pointcut = "pointCutMethod()" , returning = "result" )
public void do AfterReturning(String result) {
System.out.println("后置通知" );
System.out.println("---" + result + "---" );
}
//声明例外通知
@AfterThrowing(pointcut = "pointCutMethod()" , throwing = "e" )
public void do AfterThrowing(Exception e) {
System.out.println("例外通知" );
System.out.println(e.getMessage());
}
//声明最终通知
@After("pointCutMethod()" )
public void do After(JoinPoint jp) {
System.out.println("最终通知" );
}
//声明环绕通知
@Around("pointCutMethod()" )
public Object do Around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("进入方法---环绕通知" );
long time = System.currentTimeMillis();
Object o = pjp.proceed();
time = System.currentTimeMillis() - time;
System.out.println("执行: " + time + " ms" );
System.out.println("退出方法---环绕通知" );
return o;
}
}
2:在src目录下面创建一个Spring的配置文件applicationContext.xml,内容如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?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/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
">
<aop:aspectj-autoproxy />
<!-- <bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator" /> -->
<bean id="aspectBean" class="com.xhay1122.spring3.aop.TestAnnotationAspect" />
<bean id="aService" class="com.xhay1122.spring3.aop.HelloServiceImpl" ></bean>
</beans>
3: 编写测试代码AOPClient.java1
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
package com.xhay1122.spring3.test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.xhay1122.spring3.aop.HelloServiceImpl;
public class AOPClient {
/**
* @Title: main
* @Description: TODO(这里用一句话描述这个方法的作用)
* @param @param args 描述
* @return void 返回类型
* 创建人:xhay
* 创建时间:2015-4-9 下午3:23:17
* 修改人:xhay
* 修改时间:2015-4-9 下午3:23:17
* 修改备注:
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
ApplicationContext ctx = new ClassPathXmlApplicationContext(new String[]{"applicationContext.xml" });
HelloServiceImpl a = (HelloServiceImpl)ctx.getBean("aService" );
a.say("xhay1122.com" );
}
}
到此为止,简单的spring框架就配置好了:
运行结果:通过结果我们可以看到在我们调用say方法前后,有通知的信息输出
注解方式实现AOP源码
非注解方式实现AOP源码
知识点总结: 切面(Aspect) 官方的抽象定义为“一个关注点的模块化,这个关注点可能会横切多个对象”。“切面”在ApplicationContext中来配置。
连接点(Joinpoint) 程序执行过程中的某一行为,例如,HelloServiceImpl.say()的调用。
通知(Advice) “切面”对于某个“连接点”所产生的动作。其中,一个“切面”可以包含多个“Advice”,例如TestAspect。Advice共有如下5种类型:
A 前置通知(Before advice):在某连接点(JoinPoint)之前执行的通知,但这个通知不能阻止连接点前的执行。xml中在里面使用元素进行声明;例如,TestAspect中的doBefore方法。注解中使用@Before声明;例如,TestAnnotationAspect中的doBefore方法。
B 后通知(After advice):当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。xml中在里面使用元素进行声明。
C 返回后通知(After return advice):在某连接点正常完成后执行的通知,不包括抛出异常的情况。xml中在里面使用元素进行声明。注解中使用@AfterReturning声明;
D 环绕通知(Around advice):包围一个连接点的通知,类似Web中Servlet规范中的Filter的doFilter方法。可以在方法的调用前后完成自定义的行为,也可以选择不执行。xml中在里面使用元素进行声明。
E 抛出异常后通知(After throwing advice):在方法抛出异常退出时执行的通知。xml中在里面使用元素进行声明。
通知执行顺序:前置通知→环绕通知连接点之前→连接点执行→环绕通知连接点之后→返回通知→后通知→(如果发生异常)异常通知→后通知
切入点(Pointcut) 匹配连接点的断言,在AOP中通知和一个切入点表达式关联。示例中的所有通知所关注的连接点,都由切入点表达式execution( com.xhay1122.spring3.aop. .*(..))来决定。
● 切入点表达式
execution:用于匹配方法执行的连接点;
within:用于匹配指定类型内的方法执行;
this:用于匹配当前AOP代理对象类型的执行方法;注意是AOP代理对象的类型匹配,这样就可能包括引入接口也类型匹配;注意this中使用的表达式必须是完整类名,不支持通配符;
target:用于匹配当前目标对象类型的执行方法;注意是目标对象的类型匹配,这样就不包括引入接口也类型匹配;注意target中使用的表达式必须是完整类名,不支持通配符;
args:用于匹配当前执行的方法传入的参数为指定类型的执行方法;参数类型列表中的参数必须是完整类名,通配符不支持;args属于动态切入点,这种切入点开销非常大,非特殊情况最好不要使用;
@within:用于匹配所以持有指定注解类型内的方法;注解类型也必须是完整类名;
@target:用于匹配当前目标对象类型的执行方法,其中目标对象持有指定的注解;注解类型也必须是完整类名;
@args:用于匹配当前执行的方法传入的参数持有指定注解的执行;注解类型也必须是完整类名;
@annotation:用于匹配当前执行方法持有指定注解的方法;注解类型也必须是完整类名;
bean:Spring AOP扩展的,AspectJ没有对于指示符,用于匹配特定名称的Bean对象的执行方法;
reference pointcut:表示引用其他命名切入点,只有注解风格支持,XML风格不支持。