目录
一、AOP的相关概念
- 连接点 按照我的理解,连接点就是程序中的一个个方法(因为在Spring中仅支持方法的连接点),只能在方法的调用前后,方法抛出异常等地方织入增强。Spring中使用切点对执行点进行定位,方位定义在增强中。
- 切点:因为程序中存在许多的连接点,则通过切点来定位特定的连接点,在Spring中切点通过org.springframework.aop.Pointcut接口进行描述。
- 增强:增强是织入目标类连接点上的一段代码,同时增强也包含用于定位连接点的方位信息,所以在Spring中提供的增强接口都是带方位名的,比如BeforeAdvice、AfterReturningAdvice等。
- 目标对象:增强的织入目标类
- 引介:引介是一种特殊的增强,他为类添加一些属性和方法。利用引介,可以动态的为业务类添加其他接口的实现逻辑,让业务类成为这个接口的实现类.
- 织入:织入是将增强添加到目标类的连接点上的过程。
- 切面:切点和增强即组成切面
二、AOP的实现原理
SPring AOP的底层实现原理为动态代理。Java中提供了两种动态代理方式,JDK动态代理和CGLib动态代理。前者是针对接口的代理,而后者则是针对子类创建动态代理。对于这两种代理的学习,可以参考以下博客。
https://blog.csdn.net/zs520ct/article/details/79593196
https://blog.csdn.net/cckevincyh/article/details/54962920
三、创建增强类
按照增强在目标类方法中连接点的位置,可以分为如下五类 - 前置增强
- 后置增强
- 环绕增强
- 异常抛出增强
- 引介增强
以下实例均为《精通 Spring 4.x》中的源码
前置增强
首先定义一个接口
public interface Waiter {
void greetTo(String name);
void serveTo(String name);
}
之后是一个简单的实现类
public class NaiveWaiter implements Waiter {
public void greetTo(String name) {
System.out.println("greet to "+name+"...");
}
public void serveTo(String name){
System.out.println("serving "+name+"...");
}
}
接下来是增强类,用来保证NaiveWaiter类在每次执行动作时,都能进行"问候"!
import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;
public class GreetingBeforeAdvice implements MethodBeforeAdvice {
public void before(Method method, Object[] args, Object obj) throws Throwable {
String clientName = (String)args[0];
System.out.println("How are you!Mr."+clientName+".");
}
}
查看测试结果
import org.springframework.aop.BeforeAdvice;
import org.springframework.aop.framework.ProxyFactory;
import static org.testng.Assert.*;
import org.testng.annotations.*;
public class BeforeAdviceTest {
@Test
public void before() {
Waiter target = new NaiveWaiter();
BeforeAdvice advice = new GreetingBeforeAdvice();
ProxyFactory pf = new ProxyFactory();
pf.setInterfaces(target.getClass().getInterfaces());
pf.setOptimize(true);
pf.setTarget(target);
pf.addAdvice(advice);
Waiter proxy = (Waiter)pf.getProxy();
proxy.greetTo("John");
proxy.serveTo("Tom");
}
}
程序的输出结果如下
如上测试结果,因为只是在目标类target中设置了增强,而没有具体的切点,此时增强是针对目标类中所有方法的。
在如上的测试代码中出现了pf.setOptimize(true); 代码,该方法让ProxyFactory启用代理优化,这样针对接口的代理也会使用CGLib的代理。
在Spring中配置
<bean id="greetingBefore" class="com.smart.advice.GreetingBeforeAdvice" />
<bean id="target" class="com.smart.advice.NaiveWaiter" />
<bean id="waiter"
class="org.springframework.aop.framework.ProxyFactoryBean"
p:proxyInterfaces="com.smart.advice.Waiter"
p:target-ref="target"
p:interceptorNames="greetingBefore"
/>
ProxyFactory的几个常用的可配置属性
target:代理的目标对象
proxyInterfaces:代理所要实现的接口,可以是多个接口,该属性还有一个别名,interfaces。
interceptorNames:需要织入目标对象的bean列表,采用Bean的名称指定,这些Bean必须实现了 org.aopalliance.interceptor.MethodInteceptor或org.springframework.aop.Advisior的bean
optimize:当设置为true时,强制使用CGLib动态代理。CGLib动态代理在创建时速度慢,但其创建出的代理对象运行效率较高,而JDK动态代理则正相反
proxyTargetClass:是否对类进行代理,当设置为true时,使用CGlib动态代理。
后置增强
import java.lang.reflect.Method;
import org.springframework.aop.AfterReturningAdvice;
public class GreetingAfterAdvice implements AfterReturningAdvice {
public void afterReturning(Object returnObj, Method method, Object[] args,
Object obj) throws Throwable {
System.out.println("Please enjoy yourself!");
}
}
<bean id="waiter"
class="org.springframework.aop.framework.ProxyFactoryBean"
p:proxyInterfaces="com.smart.advice.Waiter"
p:target-ref="target"
p:interceptorNames="greetingBefore,greetingAfter"
/>
执行结果如下
环绕增强
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class GreetingInterceptor implements MethodInterceptor {
public Object invoke(MethodInvocation invocation) throws Throwable {
Object[] args = invocation.getArguments();
String clientName = (String)args[0];
System.out.println("How are you!Mr."+clientName+".");
Object obj = invocation.proceed();
System.out.println("Please enjoy yourself!");
return obj;
}
}
异常抛出增强
首先定义一个异常抛出类
import java.sql.SQLException;
public class ForumService {
public void removeForum(int forumId) {
// do sth...
throw new RuntimeException("运行异常。"