AOP
AOP(Aspect Oriented Programing,面向切面编程)用于在不侵入原有代码的基础上,为代码统一添加新功能,例如性能监控、日志记录、事务管理等。AOP的原理如图所示:
其中:
- 连接点(Jointpoint),表示方法执行的某个位置,如方法调用前、方法调用后、方法抛出异常等;
- 通知(Advice),表示需要在某个连接点加入的新功能,通知包括以下5种类型:Before advice,After returning advice,After throwing advice,After (finally) advice,Around advice;
- 切入点(Pointcut),用于描述某些类的某些方法;
- 切面(Aspect),通知和切入点组成切面,通过切面实现在指定类的指定方法上,加入通知。
Spring AOP的4种实现
Spring AOP的实现基于动态代理,有以下4种实现方式:
- 基于Aspect注解;
- 基于aop标签配置;
- 基于ProxyFactoryBean;
- 基于DefaultAdvisorAutoProxyCreator。
通过一个例子分别说明上述4种实现方式。例子中包含TestService接口及其实现TestServiceImpl,TestServiceImpl的test方法输出“execute test method”:1
2
3
4
5
6
7package com.magicwt.service;
public interface TestService {
public void test();
}
1 | package com.magicwt.service.impl; |
main方法初始化Spring上下文,获取TestService实例,并执行test方法:1
2
3
4
5
6
7
8
9
10
11
12
13
14package com.magicwt;
import com.magicwt.service.TestService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("context.xml");
TestService testService = (TestService) applicationContext.getBean("testService");
testService.test();
}
}
Spring上下文配置文件context.xml:1
2
3
4
5
6
7
8
9
10
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">
<bean id="testService" class="com.magicwt.service.impl.TestServiceImpl"/>
</beans>
执行main方法,输出:
execute test method
以下通过Spring AOP,在test方法调用前和调用后,增加切面。
基于Aspect注解
新建类TestAspect: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
35package com.magicwt.aop;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
/**
* 配置切面和组件
*/
public class TestAspect {
/**
* 配置切入点,com.magicwt.service包下所有类的所有方法
*/
"execution(* com.magicwt.service..*(..))") (
public void pointcut() {}
/**
* 方法调用前通知
*/
"pointcut()") (
public void before() {
System.out.println("execute before advice");
}
/**
* 方法调用后通知
*/
"pointcut()") (
public void after() {
System.out.println("execute after advice");
}
}
context.xml修改为:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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">
<bean id="testService" class="com.magicwt.service.impl.TestServiceImpl"/>
<!-- 扫描com.magicwt.aop包下的组件,进行初始化 -->
<context:component-scan base-package="com.magicwt.aop"/>
<!-- 根据注解自动创建代理,织入切面 -->
<aop:aspectj-autoproxy />
</beans>
执行main方法,输出:
execute before advice
execute test method
execute after advice
基于aop标签配置
TestAspect:1
2
3
4
5
6
7
8
9
10
11
12
13package com.magicwt.aop;
public class TestAspect {
public void before() {
System.out.println("execute before advice");
}
public void after() {
System.out.println("execute after advice");
}
}
context.xml修改为: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
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd">
<bean id="testService" class="com.magicwt.service.impl.TestServiceImpl"/>
<bean id="testAspect" class="com.magicwt.aop.TestAspect"/>
<aop:config>
<!-- 配置切面 -->
<aop:aspect ref="testAspect">
<!-- 配置切入点,com.magicwt.service包下所有类的所有方法 -->
<aop:pointcut id="pointcut" expression="execution(* com.magicwt.service..*(..))"/>
<!-- 配置方法调用前通知 -->
<aop:before method="before" pointcut-ref="pointcut"/>
<!-- 配置方法调用后通知 -->
<aop:after method="after" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
</beans>
执行main方法,输出:
execute before advice
execute test method
execute after advice
基于ProxyFactoryBean
新建类TestAdvice,实现接口MethodBeforeAdvice、AfterReturningAdvice,重写before、afterReturning方法:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19package com.magicwt.aop;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class TestAdvice implements MethodBeforeAdvice, AfterReturningAdvice {
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("execute before advice");
}
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("execute after advice");
}
}
context.xml修改为: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
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">
<bean id="realTestService" class="com.magicwt.service.impl.TestServiceImpl"/>
<bean id="testAdvice" class="com.magicwt.aop.TestAdvice"/>
<!-- 配置切入点,com.magicwt.service包下所有类的所有方法 -->
<bean id="pointcut" class="org.springframework.aop.aspectj.AspectJExpressionPointcut">
<property name="expression" value="execution(* com.magicwt.service..*(..))"/>
</bean>
<!-- 配置切面 -->
<bean id="advisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="advice" ref="testAdvice"/>
<property name="pointcut" ref="pointcut"/>
</bean>
<!-- 创建代理,织入切面 -->
<bean id="testService" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="realTestService"/>
<property name="interceptorNames" value="advisor" />
<property name="proxyInterfaces" value="com.magicwt.service.TestService" />
</bean>
</beans>
执行main方法,输出:
execute before advice
execute test method
execute after advice
基于DefaultAdvisorAutoProxyCreator
与基于ProxyFactoryBean的实现相比,只需修改context.xml: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
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">
<bean id="testService" class="com.magicwt.service.impl.TestServiceImpl"/>
<bean id="testAdvice" class="com.magicwt.aop.TestAdvice"/>
<!-- 配置切入点,com.magicwt.service包下所有类的所有方法 -->
<bean id="pointcut" class="org.springframework.aop.aspectj.AspectJExpressionPointcut">
<property name="expression" value="execution(* com.magicwt.service..*(..))"/>
</bean>
<!-- 配置切面 -->
<bean id="advisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="advice" ref="testAdvice"/>
<property name="pointcut" ref="pointcut"/>
</bean>
<!-- 通过DefaultAdvisorAutoProxyCreator自动创建代理,织入切面 -->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>
</beans>
执行main方法,输出:
execute before advice
execute test method
execute after advice