《精通Spring-4.x》Spring AOP


目录

  1. AOP相关概念
  2. AOP的实现原理
  3. 创建增强类
  4. 创建切面
  5. 通过AspectJ实现切面
  6. 基于schema配置切面

一、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是切点和增强的复合体。
    在这里插入图片描述
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值