由于动态切入点效率十分低下,并且一般并不会使用动态切入点。因此Spring 只提供了一个动态切入点:ControlFlowPointcut 类,它指定了执行aop 的类,即只有该类调用aop 方法时,方法才会动态的织入通知,其他类调用aop 方法和普通的方法调用一样。例子如下:
1)通知代码
- public class Before implements MethodBeforeAdvice {
- public void before( Method method, Object[] args, Object target)throws Throwable {
- System.out.println( "");
- System.out.println( "Before.before()");
- System.out.println( "target: " + target.toString());
- System.out.println( "method name: " + method.getName());
- Type[] type = method.getGenericParameterTypes();
- for (int i = 0; i < type.length; i++) {
- System.out.println( type[i].toString() + ": " + args[i]);
- }
- System.out.println( "--end--");
- System.out.println( "");
- }
- }
public class Before implements MethodBeforeAdvice {
public void before( Method method, Object[] args, Object target)throws Throwable {
System.out.println( "");
System.out.println( "Before.before()");
System.out.println( "target: " + target.toString());
System.out.println( "method name: " + method.getName());
Type[] type = method.getGenericParameterTypes();
for (int i = 0; i < type.length; i++) {
System.out.println( type[i].toString() + ": " + args[i]);
}
System.out.println( "--end--");
System.out.println( "");
}
}
2)ControlFlowPointcut 的指定类
- public class Argument implements ApplicationContextAware {
- private Implement impl;
- public void setApplicationContext(ApplicationContext context)throws BeansException {
- impl = (Implement)context.getBean( "aop");
- }
- public void test() {
- System.out.println( "Argument.test()");
- impl.test();
- }
- public void test2(Implement i) {
- i.test();
- }
- }
public class Argument implements ApplicationContextAware {
private Implement impl;
public void setApplicationContext(ApplicationContext context)throws BeansException {
impl = (Implement)context.getBean( "aop");
}
public void test() {
System.out.println( "Argument.test()");
impl.test();
}
public void test2(Implement i) {
i.test();
}
}
3)目标对象
- public class Target implements Implement {
- public void test() {
- System.out.println( "Target.test()");
- }
- }
public class Target implements Implement {
public void test() {
System.out.println( "Target.test()");
}
}
4)接口定义
- public interface Implement {
- void test();
- }
public interface Implement {
void test();
}
5)配置文件
- <beans>
- <bean id="arg" class="spring.Argument" />
- <bean id="advice" class="org.springframework.aop.support.DefaultPointcutAdvisor">
- <property name="advice">
- <bean class="spring.Before" />
- </property>
- <property name="pointcut">
- <bean class="org.springframework.aop.support.ControlFlowPointcut">
- <constructor-arg>
- <value>spring.Argument</value>
- </constructor-arg>
- </bean>
- </property>
- </bean>
- <bean id="aop" class="org.springframework.aop.framework.ProxyFactoryBean">
- <property name="proxyInterfaces">
- <list>
- <value>spring.Implement</value>
- </list>
- </property>
- <property name="interceptorNames">
- <list>
- <value>advice</value>
- </list>
- </property>
- <property name="target">
- <bean class="spring.Target" />
- </property>
- </bean>
- </beans>
6)测试代码
- public static void main(String[] args) {
- ApplicationContext context = new ClassPathXmlApplicationContext( "spring.xml");
- Argument arg = (Argument)context.getBean( "arg");
- arg.test();
- System.out.println( "----------------");
- Implement impl = (Implement)context.getBean( "aop");
- impl.test();
- System.out.println( "----------------");
- arg.test2( impl);
- }
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext( "spring.xml");
Argument arg = (Argument)context.getBean( "arg");
arg.test();
System.out.println( "----------------");
Implement impl = (Implement)context.getBean( "aop");
impl.test();
System.out.println( "----------------");
arg.test2( impl);
}
()
1.概念
Spring的切入点模型能够使切入点独立于通知类型被重用。 同样的切入点有可能接受不同的通知。
org.springframework.aop.Pointcut接口是重要的接口,用来指定通知到特定的类和方法目标,完整的接口定义如下。
public interface Pointcut {
ClassFilter getClassFilter();
MethodMatcher getMethodMatcher();
}
将Pointcut接口分成两个部分有利于重用类和方法的匹配部分,并且组合细粒度的操作(如和另一个方法匹配器执行一个“并”的操作)。
ClassFilter接口被用来将切入点限制到一个给定的目标类的集合。如果matches()永远返回true,所有的目标类都将被匹配。
public interface ClassFilter {
boolean matches(Class clazz);
}
MethodMatcher接口通常更加重要,完整的接口如下。
public interface MethodMatcher {
boolean matches(Method m, Class targetClass);
boolean isRuntime();
boolean matches(Method m, Class targetClass, Object[] args);
}
matches(Method, Class)方法被用来测试这个切入点是否匹配目标类的给定方法。这个测试可以在AOP代理创建的时候执行,避免在所有方法调用时都需要进行测试。如果2个参数的匹配方法对某个方法返回true,并且MethodMatcher的isRuntime()也返回true,那么3个参数的匹配方法将在每次方法调用的时候被调用。这使切入点能够在目标通知被执行之前立即查看传递给方法调用的参数。
大部分MethodMatcher都是静态的,意味着isRuntime()方法返回false。这种情况下,3个参数的匹配方法永远不会被调用。
如果可能,尽量使切入点是静态的,使当AOP代理被创建时,AOP框架能够缓存切入点的测试结果。
2.切入点的运算
Spring支持的切入点的运算有并和交。
并表示只要任何一个切入点匹配的方法。交表示两个切入点都要匹配的方法。并通常比较有用。
切入点可以用org.springframework.aop.support.Pointcuts类的静态方法来组合,或者使用同一个包中的ComposablePointcut类。
3.实用切入点实现
Spring提供几个实用的切入点实现,一些可以直接使用,另一些需要子类化来实现应用相关的切入点。
(1)静态切入点
静态切入点只基于方法和目标类,而不考虑方法的参数。静态切入点足够满足大多数情况的使用。Spring可以只在方法第一次被调用的时候计算静态切入点,不需要在每次方法调用的时候计算。
让我们看一下Spring提供的一些静态切入点的实现。
— 正则表达式切入点
一个很显然的指定静态切入点的方法是正则表达式。除了Spring以外,其他的AOP框架也实现了这一点。org.springframework.aop.support.RegexpMethodPointcut是一个通用的正则表达式切入点,它使用Perl 5的正则表达式的语法。
使用这个类你可以定义一个模式的列表。如果任何一个匹配,那个切入点将被计算成true(所以,结果相当于是这些切入点的并集)。
用法如下。
<bean id="settersAndAbsquatulatePointcut"
class="org.springframework.aop.support.RegexpMethodPointcut">
<property name="patterns">
<list>
<value>.*get.*</value>
<value>.*absquatulate</value>
</list>
</property>
</bean>
RegexpMethodPointcut一个实用子类,RegexpMethodPointcutAdvisor允许我们同时引用一个通知(通知可以是拦截器、before通知、throws通知等)。这简化了bean的装配,因为一个bean可以同时当作切入点和通知,如下所示。
<bean id="settersAndAbsquatulateAdvisor"
class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="interceptor">
<ref local="beanNameOfAopAllianceInterceptor"/>
</property>
<property name="patterns">
<list>
<value>.*get.*</value>
<value>.*absquatulate</value>
</list>
</property>
</bean>
RegexpMethodPointcutAdvisor可以用于任何通知类型。RegexpMethodPointcut类需要Jakarta ORO正则表达式包。
— 属性驱动的切入点
一类重要的静态切入点是元数据驱动的切入点。它使用元数据属性的值,典型地,使用源代码级元数据。
(2)动态切入点
动态切入点的演算代价比静态切入点高得多。它们不仅考虑静态信息,还要考虑方法的参数。这意味着它们必须在每次方法调用的时候都被计算,并且不能缓存结果,因为参数是变化的。
— 控制流切入点
Spring的控制流切入点概念上和AspectJ的cflow切入点一致,虽然没有其那么强大(当前没有办法指定一个切入点在另一个切入点后执行)。一个控制流切入点匹配当前的调用栈。例如,连接点被com.mycompany.web包或者SomeCaller类中一个方法调用的时候,触发该切入点。控制流切入点的实现类是org.springframework.aop.support.ControlFlowPointcut。
4.切入点超类
Spring提供非常实用的切入点的超类帮助你实现你自己的切入点。
因为静态切入点非常实用,你很可能子类化StaticMethodMatcherPointcut,如下所示。 这只需要实现一个抽象方法(虽然可以改写其他的方法来自定义行为)。
class TestStaticPointcut extends StaticMethodMatcherPointcut {
public boolean matches(Method m, Class targetClass) {
// return true if custom criteria match
}
}
当然也有动态切入点的超类。
Spring 1.0 RC2或以上版本,自定义切入点可以用于任何类型的通知。
5.自定义切入点
因为Spring中的切入点是Java类,而不是语言特性(如AspectJ),因此可以定义自定义切入点,无论静态还是动态。但是,没有直接支持用AspectJ语法书写的复杂的切入点表达式。不过,Spring的自定义切入点也可以任意的复杂。