AOP简介及Spring AOP的4种实现

AOP

AOP(Aspect Oriented Programing,面向切面编程)用于在不侵入原有代码的基础上,为代码统一添加新功能,例如性能监控、日志记录、事务管理等。AOP的原理如图所示:

aop
其中:

  1. 连接点(Jointpoint),表示方法执行的某个位置,如方法调用前、方法调用后、方法抛出异常等;
  2. 通知(Advice),表示需要在某个连接点加入的新功能,通知包括以下5种类型:Before advice,After returning advice,After throwing advice,After (finally) advice,Around advice;
  3. 切入点(Pointcut),用于描述某些类的某些方法;
  4. 切面(Aspect),通知和切入点组成切面,通过切面实现在指定类的指定方法上,加入通知。

Spring AOP的4种实现

Spring AOP的实现基于动态代理,有以下4种实现方式:

  1. 基于Aspect注解;
  2. 基于aop标签配置;
  3. 基于ProxyFactoryBean;
  4. 基于DefaultAdvisorAutoProxyCreator。

通过一个例子分别说明上述4种实现方式。例子中包含TestService接口及其实现TestServiceImpl,TestServiceImpl的test方法输出“execute test method”:

1
2
3
4
5
6
7
package com.magicwt.service;

public interface TestService {

public void test();

}

1
2
3
4
5
6
7
8
9
10
11
package com.magicwt.service.impl;

import com.magicwt.service.TestService;

public class TestServiceImpl implements TestService {

public void test() {
System.out.println("execute test method");
}

}

main方法初始化Spring上下文,获取TestService实例,并执行test方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package 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
<?xml version="1.0" encoding="UTF-8"?>
<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
35
package com.magicwt.aop;

import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

/**
* 配置切面和组件
*/
@Component
@Aspect
public class TestAspect {

/**
* 配置切入点,com.magicwt.service包下所有类的所有方法
*/
@Pointcut("execution(* com.magicwt.service..*(..))")
public void pointcut() {}

/**
* 方法调用前通知
*/
@Before("pointcut()")
public void before() {
System.out.println("execute before advice");
}

/**
* 方法调用后通知
*/
@After("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
<?xml version="1.0" encoding="UTF-8"?>
<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
13
package 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
<?xml version="1.0" encoding="UTF-8"?>
<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
19
package com.magicwt.aop;

import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

public class TestAdvice implements MethodBeforeAdvice, AfterReturningAdvice {

@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("execute before advice");
}

@Override
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
<?xml version="1.0" encoding="UTF-8"?>
<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
<?xml version="1.0" encoding="UTF-8"?>
<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