在Spring中,相关的api提供了对某类方法的拦截,也提供了对某些类的代理,以下是本人对spring参考手册的理解,纯属一家之言。对方法的拦截通过定义配置Advisior,Advice,Pointcut而实现;通过Advised接口实现代理类的生成,以下分两方面拦截者和代理类来阐述
I、拦截者
一、Spring中的Advisor,Advice,Point概述
1、Advisor:充当Advice和Pointcut的适配器,类似使用Aspect的@Aspect注解的类(前一章节所述)。一般有advice和pointcut属性。
祖先接口为org.springframework.aop.Advisor,应用中可直接使用org.springframework.aop.support.DefaultPointcutAdvisor
2、Advice:用于定义拦截行为,祖先接口为org.aopalliance.aop.Advice,该接口只是标识接口,应用中可直接实现BeforeAdvice ,ThrowsAdvice,MethodInterceptor ,AfterReturningAdvice ,IntroductionInterceptor 等子接口
3、Pointcut:用于定义拦截目标集合,祖先接口为org.springframework.aop.Pointcut
二、Spring中的Advisor,Advice,Point的应用
1、编写Advisor实现类
在此可直接使用org.springframework.aop.support.DefaultPointcutAdvisor
2、编写Advice实现类
public class PlayAdvice implements MethodBeforeAdvice{
public void before(Method method, Object[] args, Object target)
throws Throwable {
System.out.println("my before advice");
// method.invoke(target, args); 如果再调用这句,则目标方法会执行多一次
}
}
3、编写Pointcut实现类
public class PlayPointcut implements Pointcut {
public ClassFilter getClassFilter() {
return new PlayClassFilter();
}
public MethodMatcher getMethodMatcher() {
return new PlayMethodMatcher();
}
}
//PlayClassFilter的定义
class PlayClassFilter implements ClassFilter {
public boolean matches(Class clazz) {
if(clazz.getSimpleName().equals("Play"))
return true;
return false;
}
}
//PlayMethodMatcher的定义
class PlayMethodMatcher implements MethodMatcher {
public boolean isRuntime() {
return true;
}
public boolean matches(Method method, Class c) {
if(c.getSimpleName().equals("Play")&&method.getName().contains("Service"))
return true;
return false;
}
public boolean matches(Method method, Class c, Object[] args) {
if(c.getSimpleName().equals("Play")&&method.getName().contains("Service"))
return true;
return false;
}
}
4、编写目标类
public class Play {
public void playService(String what){
System.out.println("play "+what);
}
}
5、在配置文件中配置
<bean id="adviceBean" class="com.hss.sp.aop.PlayAdvice"/>
<bean id="pointcutBean" class="com.hss.sp.aop.PlayPointcut"/>
<bean id="playService" class="com.hss.sp.service.Play"/>
<bean
class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="advice" ref="adviceBean"></property>
<property name="pointcut" ref="pointcutBean"></property>
</bean>
6、测试,结果
BeanFactory bf=new ClassPathXmlApplicationContext("applicationContext.xml");
Play play=(Play)bf.getBean("playService");
play.playService("pingpong");
输出:
my before advice
play pingpong
II、代理者
在Spring中创建了AOP代理之后,你能够使用org.springframework.aop.framework.Advised
接口对它们进行管理。 任何AOP代理都能够被转型为这个接口,不论它实现了哪些其它接口。这个接口包括下面的方法:
Advisor[] getAdvisors(); void addAdvice(Advice advice) throws AopConfigException; void addAdvice(int pos, Advice advice) throws AopConfigException; void addAdvisor(Advisor advisor) throws AopConfigException; void addAdvisor(int pos, Advisor advisor) throws AopConfigException; int indexOf(Advisor advisor); boolean removeAdvisor(Advisor advisor) throws AopConfigException; void removeAdvisor(int index) throws AopConfigException; boolean replaceAdvisor(Advisor a, Advisor b) throws AopConfigException; boolean isFrozen();
getAdvisors()
方法将为每个已经被加入工厂的通知器,拦截器或者其它通知类型返回一个通知器。如果你曾经添加一个通知器,那么所返回的通知器将是你加入的对象。 如果你曾经加入一个拦截器或者其它通知类型,Spring将把它们包装在一个通知器里,后者使用一个永远返回true的切入点。因此如果你曾经加入一个MethodInterceptor
, 返回的通知器将是一个DefaultPointcutAdvisor
,它可以返回你加入的MethodInterceptor
和一个匹配所有类和方法的切入点。
addAdvisor()
方法可以用来添加任何通知器。通常保存切入点和通知的通知器是DefaultPointcutAdvisor
,它可以用于任何通知或切入点(但不包括引入类型)。
缺省情况下,你可以加入或移除通知器或者拦截器甚至当代理已经被创建之后。唯一的限制是无法加入或者移除一个引入通知器,因为工厂中获得的已有代理不能显示接口的改变(你可以通过从工厂里获取一个新的代理来避免这个问题)。
下面是一个简单的例子,它把一个AOP代理转型为Advised
接口,检查并操作它的通知:
Advised advised = (Advised) myObject; Advisor[] advisors = advised.getAdvisors(); int oldAdvisorCount = advisors.length; System.out.println(oldAdvisorCount + " advisors"); // Add an advice like an interceptor without a pointcut // Will match all proxied methods // Can use for interceptors, before, after returning or throws advice advised.addAdvice(new DebugInterceptor()); // Add selective advice using a pointcut advised.addAdvisor(new DefaultPointcutAdvisor(mySpecialPointcut, myAdvice)); assertEquals("Added two advisors", oldAdvisorCount + 2, advised.getAdvisors().length);