目录
一、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("运行异常。");
}
public void updateForum(Forum forum) throws Exception{
// do sth...
throw new SQLException("数据更新操作异常。");
}
}
接下来定义异常抛出增强
import java.lang.reflect.Method;
import org.springframework.aop.ThrowsAdvice;
public class TransactionManager implements ThrowsAdvice {
public void afterThrowing(Method method, Object[] args, Object target,
Exception ex) throws Throwable {
System.out.println("-----------");
System.out.println("method:" + method.getName());
System.out.println("抛出异常:" + ex.getMessage());
System.out.println("成功回滚事务。");
}
}
ThrowsAdvice是一个标签接口,它没有定义任何方法,它必须采用以下形式定义增强方法
void afterThrowing(Method method, Object[] args, Object target,
Throwable th)
在此方法中前三个参数要么提供,要么不提供,最后一个参数为Throwable或其子类。
引介增强
引介增强是一种比较特殊的增强类型,他不是在目标方法周围织入增强,而是为目标类创建新的方法和属性。引介增强的连接点是类级别的。
Spring定义了引介增强增强接口IntroductionInteceptor,该接口没有定义任何方法,Spring为该接口提供了DelegatingIntroductionInteceptor实现类,一般情况下通过扩展该实现类定义自己的引介增强类。
以下为引介增强的实现实例,实现性能监视功能可控。
首先定义一个用于标识目标类是否支持性能监视的接口。
public interface Monitorable {
void setMonitorActive(boolean active);
}
下面通过扩展DelegatingIntroductionInteceptor为目标类引入性能监视的可控功能。
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.support.DelegatingIntroductionInterceptor;
public class ControllablePerformaceMonitor
extends
DelegatingIntroductionInterceptor implements Monitorable, Testable {
private ThreadLocal<Boolean> MonitorStatusMap = new ThreadLocal<Boolean>();
public void setMonitorActive(boolean active) {
MonitorStatusMap.set(active);
}
public Object invoke(MethodInvocation mi) throws Throwable {
Object obj = null;
if (MonitorStatusMap.get() != null && MonitorStatusMap.get()) {
PerformanceMonitor.begin(mi.getClass().getName() + "."
+ mi.getMethod().getName());
obj = super.invoke(mi);
PerformanceMonitor.end();
} else {
obj = super.invoke(mi);
}
return obj;
}
public void test() {
// TODO Auto-generated method stub
System.out.println("dd");
}
}
接下来配置引介增强
<bean id="pmonitor" class="com.smart.introduce.ControllablePerformaceMonitor" />
<bean id="forumServiceTarget" class="com.smart.introduce.ForumService" />
<bean id="forumService" class="org.springframework.aop.framework.ProxyFactoryBean"
p:interfaces="com.smart.introduce.Monitorable"
p:target-ref="forumServiceTarget"
p:interceptorNames="pmonitor"
p:proxyTargetClass="true" />
在配置中,首先通interfaces属性配置所要实现的接口,因为是通过为目标类创建子类的方式生成引介增强的代理,所以必须将proxyTargetClass属性设置为true。
以下为测试类。
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import static org.testng.Assert.*;
import org.testng.annotations.*;
public class IntroduceTest {
@Test
public void introduce(){
String configPath = "com/smart/introduce/beans.xml";
ApplicationContext ctx = new ClassPathXmlApplicationContext(configPath);
ForumService forumService = (ForumService)ctx.getBean("forumService");
forumService.removeForum(10);
forumService.removeTopic(1022);
Monitorable moniterable = (Monitorable)forumService;
moniterable.setMonitorActive(true);
forumService.removeForum(10);
forumService.removeTopic(1022);
}
}
其输出信息如下。
四、创建切面
在上一小节中说明了如何创建增强,因为没有切点的原因,所以增强就织入了目标类的所有方法之中。通过定义切点,定位特定的连接点,这样切点和增强就组成了切面。
Spring通过org.springframework.aop.Pointcut接口描述切点,Pointcut由ClassFilter和MethodMatcher定位到某些特定类上,通过MethodMatcher定位到某些特定方法。
切点类型
静态方法切点:org.springframework.aop.support.StaticMethodMatcherPointcut是静态方法切点的抽象基类,默认情况下它匹配所有的类。StaticMethodMatcherPointcut包括两个主要的子类,分别是NameMatchMethodPointcut和AbstractRegexpMethodPointcut。
动态方法切点:org.springframework.aop.support.DynamicMethodMatcherPointcut是动态方法切点的抽象基类,默认情况下它匹配所有的类。
注解切点:org.springframework.aop.support.annotation.AnnotationMatchingPointcut实现类表示注解切点,该切点支持在Bean中通过注解标签定义切点。
表达式切点:org.springframework.aop.support.ExpressionPointcut接口主要是为了支持AspectJ切点表达式语法而定义的接口。
流程切点 :org.springframework.aop.support.ControlFlowPointcut实现类表示控制流程切点。
复合切点:org.springframework.aop.support.ComposablePointcut实现类是为创建多个切点而提供的方便操作类。
切面类型
Advisior:代表一般切面,仅包含一个Advice,匹配目标类所有方法,一般不会使用。
PointcutAdvisior:代表具有切点的切面。包含Advice和Pointcut两个类,IntroductionAdvisior:代表引介切面,引介切面为对应引介增强的特殊的切面,应用于类层面上。
PointcutAdvisior主要有6个具体的实现类。
DefaultPointcutAdvisior:最常用的切面类型,它可以通过任意Pointcut和Advice定义一个切面,唯一不支持的是引介切面类型。
NameMethodMatcherPointcutAdvisior:对于按正则表达式匹配方法名进行切点定义,可以通过扩展该实现类进行操作。
StaticMethodMatcherPointcutAdvisior:静态方法匹配器定义切面,默认情况下匹配所有目标类。
AspectJExpressionPointcutAdvisior:用于Aspectj切点表达式定义切点的切面。
AspectJPointcutAdvisior:用于Aspectj语法定义切点的切面。
下面通过具体的实例来描述相关切面
静态普通方法名匹配切点
首先定义两个业务类
public class Waiter {
public void serveTo(String name){
System.out.println("waiter serving "+name+"...");
}
public void greetTo(String name) {
System.out.println("waiter greet to "+name+"...");
}
}
public class Seller {
public void greetTo(String name) {
System.out.println("seller greet to "+name+"...");
}
}
接下来定义具体的切面
import java.lang.reflect.Method;
import org.springframework.aop.ClassFilter;
import org.springframework.aop.support.StaticMethodMatcherPointcutAdvisor;
public class GreetingAdvisor extends StaticMethodMatcherPointcutAdvisor {
public boolean matches(Method method, Class clazz) {
return "greetTo".equals(method.getName());
}
public ClassFilter getClassFilter(){
return new ClassFilter(){
public boolean matches(Class clazz){
return Waiter.class.isAssignableFrom(clazz);
}
};
}
}
Advisior还需要一个增强去配合,如下所示。
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(obj.getClass().getName()+"."+method.getName());
System.out.println("How are you!Mr."+clientName+".");
}
}
最后在Spring配置文件中来定义切面
<bean id="waiterTarget" class="com.smart.advisor.Waiter" />
<bean id="sellerTarget" class="com.smart.advisor.Seller" />
<bean id="greetingAdvice" class="com.smart.advisor.GreetingBeforeAdvice" />
<bean id="greetingAdvisor" class="com.smart.advisor.GreetingAdvisor"
p:advice-ref="greetingAdvice" /> //向切面注入一个增强
<bean id="parent" abstract="true"
class="org.springframework.aop.framework.ProxyFactoryBean"
p:interceptorNames="greetingAdvisor" p:proxyTargetClass="true" />
<bean id="waiter" parent="parent" p:target-ref="waiterTarget" />
<bean id="seller" parent="parent" p:target-ref="sellerTarget" />
StaticMehodMatchPointcutAdvisior除具有advice属性之外,还可以定义另外两个属性。
ClassFilter:类匹配过滤器,在GreetingAdvisior中用编码方式设定了classFilter
order:切面织入时的顺序
这样切面就只会匹配Waiter.greeTo方法。
静态正则表达式方法匹配切面
RegexpMethodPointcutAdvisior是正则表达式方法匹配的切面实现类,该类已经是功能齐备的实现类,一般情况下无需扩展该类。
具体实例如下
<bean id="regexpAdvisor"
class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"
p:advice-ref="greetingAdvice">
<property name="patterns">
<list>
<value>.*greet.*</value>
</list>
</property>
</bean>
<bean id="waiter1" class="org.springframework.aop.framework.ProxyFactoryBean"
p:interceptorNames="regexpAdvisor" p:target-ref="waiterTarget"
p:proxyTargetClass="true" />
动态切面
Spring中动态切面可以通过DefaultPointcutAdvisior和DynamicMethodMatcherPointcut,其实例如下。
DynamicMethodMatcherPointcut是一个抽象类,他将isRuntime()标识为final且返回true,这样其子类就一定是一个动态切点。
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import org.springframework.aop.support.DynamicMethodMatcherPointcut;
public class GreetingDynamicPointcut extends DynamicMethodMatcherPointcut {
private static List<String> specialClientList = new ArrayList<String>();
static {
specialClientList.add("John");
specialClientList.add("Tom");
}
public ClassFilter getClassFilter() {
return new ClassFilter() {
public boolean matches(Class clazz) {
System.out.println("调用getClassFilter()对"+clazz.getName()+"做静态检查.");
return Waiter.class.isAssignableFrom(clazz);
}
};
}
public boolean matches(Method method, Class clazz) {
System.out.println("调用matches(method,clazz)对"+clazz.getName()+"."+method.getName()+"做静态检查.");
return "greetTo".equals(method.getName());
}
public boolean matches(Method method, Class clazz, Object[] args) {
System.out.println("调用matches(method,clazz)对"+clazz.getName()+"."+method.getName()+"做动态检查.");
String clientName = (String) args[0];
return specialClientList.contains(clientName);
}
}
GreetingDynamicPointcut类既有用于静态切点检查的方法,又有用于动态切点检查的方法。因为动态切点检查会对性能造成很大的影响,所以在创建代理时会对目标类的每个连接点使用静态切点检查,如果仅通过静态切点检查就可以知道连接点是不匹配的,则运行时不在进行动态检查,如果静态切点检查是匹配的,则在运行时才进行动态切点检查。
配置如下:
<bean id="dynamicAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="pointcut">
<bean class="com.smart.advisor.GreetingDynamicPointcut" />
</property>
<property name="advice">
<bean class="com.smart.advisor.GreetingBeforeAdvice" />
</property>
</bean>
<bean id="waiter2" class="org.springframework.aop.framework.ProxyFactoryBean"
p:interceptorNames="dynamicAdvisor" p:target-ref="waiterTarget"
p:proxyTargetClass="true" />
编写一个测试类,进行测试
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import static org.testng.Assert.*;
import org.testng.annotations.*;
public class DynamicAdvisorTest {
@Test
public void dynamic() {
String configPath = "com/smart/advisor/beans.xml";
ApplicationContext ctx = new ClassPathXmlApplicationContext(configPath);
Waiter waiter = (Waiter) ctx.getBean("waiter2");
waiter.serveTo("Peter");
waiter.greetTo("Peter");
waiter.serveTo("Peter");
waiter.greetTo("John");
}
}
运行以上代码,输出台输出信息如下:
从运行结果可以看出,Spring在创建代理织入切面时,对目标类中的所有方法进行静态切点检查,在生成织入切面的代理对象之后,第一次调用类时会进行一次静态切点检查,如果匹配,在之后的检查中就不在执行静态切点检查。
在将GreetingDynamicPointcut类的getClassFilter()和matches(Method method, Class clazz)方法注释掉,将得到以下输出信息
每次调用代理对象的任何一个方法,都会执行动态切点检查,这将导致很大的性能问题。
流程切面
Spring的流程切面由DefaultPointcutAdvisior和ControlFlowPointcut实现。流程切点代表由某个方法直接或间接发起调用的其他方法。如下实例,假设通过一个WaiterDelegate类代理Waiter所有方法。
public class WaiterDelegate {
private Waiter waiter;
public void service(String clientName) {
waiter.greetTo(clientName);
waiter.serveTo(clientName);
}
public void setWaiter(Waiter waiter) {
this.waiter = waiter;
}
}
如果希望所有由WaiterDelegate#service()方法发起调用的其他方法都织入增强,就必须使用流程切面来完成目标。接下来使用DefaultPointcutAdvisior配置一个流程切面来完成这一需求。
<bean id="controlFlowPointcut" class="org.springframework.aop.support.ControlFlowPointcut">
<constructor-arg type="java.lang.Class"
value="com.smart.advisor.WaiterDelegate" />
<constructor-arg type="java.lang.String" value="service" />
</bean>
<bean id="controlFlowAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor"
p:pointcut-ref="controlFlowPointcut" p:advice-ref="greetingAdvice" />
<bean id="waiter3" class="org.springframework.aop.framework.ProxyFactoryBean"
p:interceptorNames="controlFlowAdvisor" p:target-ref="waiterTarget"
p:proxyTargetClass="true" />
接下来进行流程切面的测试
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import static org.testng.Assert.*;
import org.testng.annotations.*;
public class ControlFlowAdvisorTest {
@Test
public void control() {
String configPath = "com/smart/advisor/beans.xml";
ApplicationContext ctx = new ClassPathXmlApplicationContext(configPath);
Waiter waiter = (Waiter) ctx.getBean("waiter3");
WaiterDelegate wd = new WaiterDelegate();
wd.setWaiter(waiter);
waiter.serveTo("Peter");
waiter.greetTo("Peter");
wd.service("Peter");
}
}
其输出信息如下
复合切点切面
ComposablePointcut可以将多个切点以并集或交集的方式组合起来,提供了切点之间的复合运算的功能。
ComposablePointcut提供了三个交集运算方法
ComposablePointcut intersection(ClassFilter classFilter) 将复合切点和一个ClassFilter对象进行交集运算,得到一个结果复合切点。
ComposablePointcut intersection(MethodMatcher matcher)
ComposablePointcut intersection(MethodMatcher matcher)
ComposablePointcut提供了两个并集运算方法
ComposablePointcut union(ClassFilter classFilter)
ComposablePointcut union(MethodMatcher matcher)
ComposablePointcut中没有提供直接对两个切点进行交并集运算的方法,可以使用Spring提供的org.springframework.aop.support.Pointcuts工具类。
Pointcut intersection(Pointcut a, Pointcut b)
Pointcut union(Pointcut a, Pointcut b)
首先通过ComposablePointcut创建一个流程切点和方法名切点的相交切点。
import org.springframework.aop.Pointcut;
import org.springframework.aop.support.ComposablePointcut;
import org.springframework.aop.support.ControlFlowPointcut;
import org.springframework.aop.support.NameMatchMethodPointcut;
public class GreetingComposablePointcut {
public Pointcut getIntersectionPointcut(){
ComposablePointcut cp = new ComposablePointcut();
Pointcut pt1 = new ControlFlowPointcut(WaiterDelegate.class,"service");
NameMatchMethodPointcut pt2 = new NameMatchMethodPointcut();
pt2.addMethodName("greetTo");
return cp.intersection(pt1).intersection((Pointcut)pt2);
}
}
<bean id="gcp" class="com.smart.advisor.GreetingComposablePointcut" />
<bean id="composableAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor"
p:pointcut="#{gcp.intersectionPointcut}" p:advice-ref="greetingAdvice" />
<bean id="waiter4" class="org.springframework.aop.framework.ProxyFactoryBean"
p:interceptorNames="composableAdvisor" p:target-ref="waiterTarget"
p:proxyTargetClass="true" />
测试代码
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import static org.testng.Assert.*;
import org.testng.annotations.*;
public class ComposableAdvisorTest {
@Test
public void composable(){
String configPath = "com/smart/advisor/beans.xml";
ApplicationContext ctx = new ClassPathXmlApplicationContext(configPath);
Waiter waiter = (Waiter) ctx.getBean("waiter4");
WaiterDelegate wd = new WaiterDelegate();
wd.setWaiter(waiter);
waiter.serveTo("Peter");
waiter.greetTo("Peter");
wd.service("Peter");
}
}
其输出结果如下
引介切面
首先是引介切面的类继承关系图
在IntroductionInfo接口描述了目标类需要实现的新接口,在IntroductionAdvisior中,它仅有一个类过滤器ClassFilter,而没有MethodMatcher,这是因为引介切面是类级别的。
DefaultIntroductionAdvisior为引介切面最常用的实现类。
引介切面配置如下:
public interface Testable {
void test();
}
public interface Monitorable {
void setMonitorActive(boolean active);
}
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.support.DelegatingIntroductionInterceptor;
public class ControllablePerformaceMonitor
extends
DelegatingIntroductionInterceptor implements Monitorable, Testable {
private ThreadLocal<Boolean> MonitorStatusMap = new ThreadLocal<Boolean>();
public void setMonitorActive(boolean active) {
MonitorStatusMap.set(active);
}
public Object invoke(MethodInvocation mi) throws Throwable {
Object obj = null;
if (MonitorStatusMap.get() != null && MonitorStatusMap.get()) {
PerformanceMonitor.begin(mi.getClass().getName() + "."
+ mi.getMethod().getName());
obj = super.invoke(mi);
PerformanceMonitor.end();
} else {
obj = super.invoke(mi);
}
return obj;
}
public void test() {
// TODO Auto-generated method stub
System.out.println("dd");
}
}
<bean id="introduceAdvisor"
class="org.springframework.aop.support.DefaultIntroductionAdvisor">
<constructor-arg>
<bean class="com.smart.introduce.ControllablePerformaceMonitor" />
</constructor-arg>
</bean>
<bean id="forumServiceTarget" class="com.smart.introduce.ForumService" />
<bean id="forumService" class="org.springframework.aop.framework.ProxyFactoryBean"
p:interceptorNames="introduceAdvisor"
p:target-ref="forumServiceTarget"
p:proxyTargetClass="true"/>
五、通过AspectJ实现切面
在前面分别使用Pointcut和Advice接口描述切点和增强,并用Advisior整合二者描述切面。AspectJ则采用注解来描述切点,增强,二者只是描述方式不同,但是其本质是一样的。
AspectJ语法基础
切点表达式函数:
在Spring中支持9个@AspectJ切点表达式函数,他们用不同的方式描述目标类的连接点,根据描述对象的不同,可以大致分为4中类型
- 方法切点函数:通过描述目标类方法的信息定义连接点。
- 方法入参切点函数:通过描述目标类方法入参的信息定义连接点。
- 目标类切点函数:通过描述目标类类型的信息定义连接点。
- 代理类切点函数:通过描述目标类的代理类的信息定义连接点。
在函数入参中使用通配符
在@AspectJ中支持3种通配符
*:匹配任意字符,但它只能匹配上下文中的一个元素
. . : 匹配任意字符,可以匹配上下文种多个元素,但在表示类时,必须和 *联合使用,而在表示入参时则单独使用。
+:必须按类型匹配指定类的所有类,必须跟在类名后面,如com.demo.Car+,继承或扩展指定类的所有类,同时还包括指定类本身。
AspectJ函数按其是否支持通配符及支持程度,可以分为以下3类
六、基于schema配置切面
在基于schema的切面配置中,有两种配置方式。
一种是基本的Aspect配置, 其例子如下
这样就配置好了一个简单的切面,在< aop:config>中可以定义多个切面。< aop:aspect>
一个切面中可以定义多个增强。在①处引用的Bean为增强方法所在的类。
通过< aop:before>定义了一个增强,pointcut定义一个切点,method为增强类中具体的增强方法。
当proxy-target-class设置为true时,切面均使用CGLib动态代理技术,当设置为false时,使用java动态代理技术。
也可以配置命名切点,示例如下。
基于schema的切面配置中,绑定连接点信息与AspectJ类似。
- 所有的增强方法第一个参数可以声明为JoinPoint,访问连接点信息。
- < aop:after-returning> 可以通过returning属性绑定连接点方法的返回值。< aop:after-throwing>通过throwing方法绑定连接点方法所抛出的异常。
另一种是基于Advisior的配置。Advisior是切点和增强的复合体。