现在的位置: 首页 > 综合 > 正文

Spring创建切面(方法名匹配切面)

2012年11月27日 ⁄ 综合 ⁄ 共 8430字 ⁄ 字号 评论关闭

      在之前说增强的时候,有一个问题那就是,增强被织入了目标类的所有方法中,加入我们希望有选择的织入到目标类的特定方法中,就需要使用切点进行目标连接点的定位了,增强提供了连接点方位信息:如织入到方法前还是方法后,而切点进一步描述织入到哪些类的哪些方法上。

      Spring通过org.springframework.aop.Pointcut接口描述切点,Pointcut由ClassFilter和MethodMatcher构成,它功过ClassFliter定位到某些特定类上,通过MethodMatcher定位到某些特定方法上,这样Pointcut就有了描述某些类的某些特定方法的能力。

      Spring支持两种方法匹配器:静态方法匹配器和动态方法匹配器:

      1.所谓静态方法匹配器,它仅对方法签名(包括方法名和入参类型、顺序)进行匹配;

      2.动态匹配器,会在运行期检查方法入参的值。

      静态匹配仅会判别一次;而动态匹配因为每次调用方法的入参可能都不一样,所以每次调用方法都会判断,因此动态匹配对性能的影响很大,一般情况下,动态匹配不常用。方法匹配器的类型由isRuntime()返回值决定,返回false表示静态方法匹配器,反之则是动态方法匹配器;

 一、静态方法匹配器(静态普通方法名匹配切面)

      1.两个业务类

package spring.aop.StaticMethodMatcherPointcutAdvisorDemo;

import org.springframework.aop.framework.ProxyFactory;


public class Waiter {

    public void greetTo(String name) {
        System.out.println("waiter greet to " + name + "...");
    }

    public void serveTo(String name) {
        System.out.println("waiter serving to " + name + "...");
    }
    
    public static void main(String[] args) {
        Waiter waiter = new Waiter();
        GreetingAdvisor grettingAdvisor = new GreetingAdvisor();
        GreetingBeforeAdvice greetingBeforeAdvice = new GreetingBeforeAdvice();
        ProxyFactory pf = new ProxyFactory();
        pf.setOptimize(true);
        grettingAdvisor.setAdvice(greetingBeforeAdvice);
        pf.addAdvisor(grettingAdvisor);
        pf.setTarget(waiter);
        //pf.addAdvice(greetingBeforeAdvice);

        Waiter waiterProxy = (Waiter)pf.getProxy();
        waiterProxy.greetTo("lee");
        waiterProxy.serveTo("bin");
    }

}
package spring.aop.StaticMethodMatcherPointcutAdvisorDemo;

public class Seller {
    
    public void greetTo(String name) {
        System.out.println("seller greet to " + name + "...");
    }

}

      2.定义一个切面,在Waiter#greetTo()方法调用前织入一个增强

package spring.aop.StaticMethodMatcherPointcutAdvisorDemo;

import java.lang.reflect.Method;

import org.springframework.aop.ClassFilter;
import org.springframework.aop.support.StaticMethodMatcherPointcutAdvisor;

public class GreetingAdvisor extends StaticMethodMatcherPointcutAdvisor {

    /**
     * 
     */
    private static final long serialVersionUID = -3558599577522442437L;

    @Override
    public boolean matches(Method arg0, Class<?> arg1) {
        // 切点方法匹配规则:方法名必须为greetTo
        return "greetTo".equals(arg0.getName());
    }

    // 切点类匹配规则:Waiter的类或者子类
    @Override
    public ClassFilter getClassFilter() {
        return new ClassFilter() {

            @Override
            public boolean matches(Class<?> arg0) {
                return Waiter.class.isAssignableFrom(arg0);
            }

        };
    }

}

      3.增强类的配合,定义一个前置增强

package spring.aop.StaticMethodMatcherPointcutAdvisorDemo;

import java.lang.reflect.Method;

import org.springframework.aop.MethodBeforeAdvice;

public class GreetingBeforeAdvice implements MethodBeforeAdvice {

    @Override
    public void before(Method method, Object[] aobj, Object obj)
            throws Throwable {
        System.out.println("输出切点:" + obj.getClass().getName() + "."
                + method.getName());
        String clientName = (String) aobj[0];
        System.out.println("how are you! Mr." + clientName);
    }

}

如果我们直接运行Waiter.java里的main方法可以得到如下输出:

输出切点:spring.aop.StaticMethodMatcherPointcutAdvisorDemo.Waiter.greetTo
how are you! Mr.lee
waiter greet to lee...
waiter serving to bin...

      说明:我们定义的GreetingAdvisor包含了两个判定,ClassFilter和MethodMatcher这样我们就只定位到了Waiter的greetTo方法;

      当然我们也可以通过spring配置的方式,配置如下:

<!-- 配置切面:静态方法匹配切面 -->
    <bean id="waiterTarget" class="spring.aop.StaticMethodMatcherPointcutAdvisorDemo.Waiter" />
    <bean id="sellerTarget" class="spring.aop.StaticMethodMatcherPointcutAdvisorDemo.Seller" />
    <bean id="greetingBeforeAdvice" class="spring.aop.StaticMethodMatcherPointcutAdvisorDemo.GreetingBeforeAdvice" />
    <bean id="greetingAdvisor" class="spring.aop.StaticMethodMatcherPointcutAdvisorDemo.GreetingAdvisor"
          p:advice-ref="greetingBeforeAdvice" />
    <bean id="greetingParent" abstract="true" class="org.springframework.aop.framework.ProxyFactoryBean"
          p:interceptorNames="greetingAdvisor"
          p:proxyTargetClass="true" />
    <bean id="getWaiter" parent="greetingParent" p:target-ref="waiterTarget" />
    <bean id="getSeller" parent="greetingParent" p:target-ref="sellerTarget" />

      有时候我们可能需要对满足一定命名规范的方法进行织入增强,这个时候我们可以使用正则表达式方法匹配切面,配置如下:

<!-- 配置切面:静态方法匹配切面 -->
    <bean id="waiterTarget" class="spring.aop.StaticMethodMatcherPointcutAdvisorDemo.Waiter" />
    <bean id="sellerTarget" class="spring.aop.StaticMethodMatcherPointcutAdvisorDemo.Seller" />
    <bean id="greetingBeforeAdvice" class="spring.aop.StaticMethodMatcherPointcutAdvisorDemo.GreetingBeforeAdvice" />
    <bean id="regexpAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"
          p:advice-ref="greetingBeforeAdvice">
        <property name="patterns">
            <list>
                <value>.*greet.*</value>
            </list>
        </property>
    </bean>
    <bean id="greetingParent" abstract="true" class="org.springframework.aop.framework.ProxyFactoryBean"
          p:interceptorNames="regexpAdvisor"
          p:proxyTargetClass="true" />
    <bean id="getWaiter" parent="greetingParent" p:target-ref="waiterTarget" />
    <bean id="getSeller" parent="greetingParent" p:target-ref="sellerTarget" />

      以上是通过方法名来匹配切面,下面我们看动态切面;

 

二、动态切面

     在低版本中,Spring提供了用于创建动态切面的DynamicMethodMatcherPointcutAdvisor抽象类,因为该类在功能上和其他类有重叠,会给开发者造成选择上的困惑,因此在Spring2.0中已经过期,我们可以使用DefaultPointcutAdvisor和DynamicMethodMatcherPointcut来完成相同的功能。DynamicMethodMatcherPointcut是一个抽象类,它将isRuntime()标示成final并返回true,这样其子类就是一个动态的切点了,该抽象类默认匹配所有类和方法,因此需要通过扩展该类编写符合需求的动态切点。

package spring.aop.DynamicMethodMatcherPointcutDemo;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

import org.springframework.aop.ClassFilter;
import org.springframework.aop.support.DynamicMethodMatcherPointcut;

import spring.aop.beforeadvicedemo.Waiter;

public class GreetingDynamicPointcut extends DynamicMethodMatcherPointcut {

    private static List<String> specialClientList = new ArrayList<String>();

    static {
        specialClientList.add("nicholas");
        specialClientList.add("lee");
    }

    // 对类进行静态切点检查
    public ClassFilter getClassFilter() {
        return new ClassFilter() {

            @Override
            public boolean matches(Class<?> arg0) {
                System.out.println("调用getClassFilter()对" + arg0.getName()
                        + "做静态检查");
                return Waiter.class.isAssignableFrom(arg0);
            }

        };
    }

    @Override
    public boolean matches(Method arg0, Class<?> arg1) {
        System.out.println("调用matches()" + arg1.getName() + "."
                + arg0.getName() + "做静态检查");
        return "greetTo".equals(arg0.getName());
    }

    @Override
    public boolean matches(Method arg0, Class<?> arg1, Object[] arg2) {
        System.out.println("调用matches()" + arg1.getName() + "."
                + arg0.getName() + "做动态检查");
        String clientName = (String) arg2[0];
        return specialClientList.contains(clientName);
    }

}
<!-- 动态切面配置 -->
    <bean id="dynamicAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
        <property name="pointcut">
            <bean class="spring.aop.DynamicMethodMatcherPointcutDemo.GreetingDynamicPointcut" />
        </property>
        <property name="advice">
            <bean class="spring.aop.StaticMethodMatcherPointcutAdvisorDemo.GreetingBeforeAdvice" />
        </property>
    </bean>
    <bean id="getDynamicWaiter" class="org.springframework.aop.framework.ProxyFactoryBean"
          p:interceptorNames="dynamicAdvisor" 
          p:target-ref="waiterTarget"
          p:proxyTargetClass="true"  />

 

三、流程切面

      Spring的流程切面由DefaultPointcutAdvisor和ControlFlowPointcut实现。流程切点代表由某个方法直接或间接发起调用的其他方法,看一个实例,我们通过一个WaiterDelegate类代理Waiter所有的方法:

      1.WaiterDelegate代理类

package spring.aop.ControlFlowPointcutDemo;

import spring.aop.StaticMethodMatcherPointcutAdvisorDemo.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()方法发起调用的其他方法都织入GreetingBeforeAdvice增强,那么我们就必须使用流程切面来完成:

<!-- 流程控制切面配置 -->
    <bean id="controlFlowPointcut" class="org.springframework.aop.support.ControlFlowPointcut">
        <!-- 指定流程切点的类 -->
        <constructor-arg type="java.lang.Class" value="spring.aop.ControlFlowPointcutDemo.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="greetingBeforeAdvice"
    />
    <bean id="getControlWaiter" class="org.springframework.aop.framework.ProxyFactoryBean"
          p:interceptorNames="controlFlowAdvisor" 
          p:target-ref="waiterTarget"
          p:proxyTargetClass="true"  />
package spring.aop.ControlFlowPointcutDemo;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import spring.aop.StaticMethodMatcherPointcutAdvisorDemo.Waiter;

public class test {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext(
                "classpath:applicationContext.xml");
        Waiter waiter = (Waiter) ctx.getBean("getControlWaiter");
        WaiterDelegate wd = new WaiterDelegate();
        wd.setWaiter(waiter);
        wd.service("lee");
        //waiter.serveTo("lee");
    }
}

输出:

输出切点:spring.aop.StaticMethodMatcherPointcutAdvisorDemo.Waiter.greetTo
how are you! Mr.lee
waiter greet to lee...

输出切点:spring.aop.StaticMethodMatcherPointcutAdvisorDemo.Waiter.serveTo
how are you! Mr.lee
waiter serving to lee...

     总结:流程切面和动态切面从某种程度上来说是一类切面,都需要在运行期间判断动态的环境,开销很大,JVM1.4上,流程切面比别的切面要慢5倍!

抱歉!评论已关闭.