Spring AOP (二)

前面学习了AOP的几种不同的通知(advice)类型,但是有时候我们并不需要所有的方法都被自动拦截,这时就需要有条件的进行拦截了。这时,我们就需要用到 Pointcut and Advice ,即切入点和通知。


知识点

在 Spring AOP 中,有 3 个常用的概念,Advices 、 Pointcut 、 Advisor

Advices :表示一个 method 执行前或执行后的动作。

Pointcut :表示根据 method 的名字或者正则表达式去拦截一个 method 。

Advisor : Advice 和 Pointcut 组成的独立的单元,并且能够传给 proxy factory 对象。

**今天我们主要来接触后面两个,这两天天天去图书馆啃书,感觉还算充实。O(∩_∩)O哈哈~**


Pointcut

前面的例子中,如果我们细心就会发现advice作用于被代理的类的方式需要明确指定哪些类的方法需要使用advice,也就是将advice与代理的Joinpoint建立联系,如果我们有200个类都需要都需要同样的advice,这么做显然不经济。毫无悬念,Pointcut就起了这样的作用,它能够使用间接的方法为advice选择对象。如果觉得这种方式比较抽象,我们也可以将Pointcut理解为一个特定的Joinpoint集合,也就是通过一些特定的条件来筛选Joinpoint,所得到的集合就是Pointcut。


Pointcut 类型

在Spring中Pointcut有两种类型,分别是静态和动态Pointcut。所谓静态Pointcut指的是Pointcut的使用是在代理创建的时候进行的,而动态Pointcut则是指条件不但包括静态Pointcut,还有可能包含方法调用的返回值等调用后产生的变化所决定的条件。但动态的Pointcut性能是最差的,几乎没有优化的空间实际中应尽量避免使用动态Pointcut。

Spring 预定义 Pointcut

Spring 为我们预置了一部分常用的Pointcut,这其中包括选择所有setter和getter方法的常量,通过名称进行筛选的NameMatchMethodPointcut,使用正则表达式进行筛选的JdkRegexpMethodPointcut,以及不常用的捕获特定方法调用的ControlFlowPointcut,分别用于静态和动态Pointcut基类的StaticMethodMatcherPointcut和DynamicMethodMatcherPointcut。其中有涉及到正则表达式的Pointcut,需要先补习下相关知识。

微软的正则介绍


简单实现

NameMatchMethodPointcut

PointcutMethod类

package boy.aop.pointcut;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class PointcutMethod {
    
    private Log log = LogFactory.getLog(PointcutMethod.class);
    
    private int age;
    
    private String name;
    
    private String title;

    public Log getLog() {
        System.out.println("我们处理 getLog 方法");
        return log;
    }

    public void setLog(Log log) {
        System.out.println("我们处理 setLog 方法");
        this.log = log;
    }

    public int getAge() {
        System.out.println("我们处理 getAge 方法");
        return age;
    }

    public void setAge(int age) {
        System.out.println("我们处理 setAge 方法");
        this.age = age;
    }

    public String getName() {
        System.out.println("我们处理 getName 方法");
        return name;
    }

    public void setName(String name) {
        System.out.println("我们处理 setName 方法");
        this.name = name;
    }
    
    public String getTitle() {
        System.out.println("我们处理 getName 方法");
        return name;
    }

    public void setTitle(String name) {
        System.out.println("我们处理 setName 方法");
        this.name = name;
    }
    
    

}

NameMatchMethodTest类

package boy.aop.pointcut;

import org.aopalliance.aop.Advice;
import org.springframework.aop.Advisor;
import org.springframework.aop.BeforeAdvice;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.aop.support.NameMatchMethodPointcut;

import boy.aop.advice.*;

public class NameMatchMethodTest {
    
    public static void main(String[] args) {
        PointcutMethod target = new PointcutMethod();
        NameMatchMethodPointcut pc = new NameMatchMethodPointcut();
        pc.addMethodName("getAge")
            .addMethodName("getName")
            .addMethodName("getTitle");
        
        Advisor advisor = new DefaultPointcutAdvisor(pc, new BeforeMethod());
        
        ProxyFactory pf = new ProxyFactory();
        
        pf.addAdvisor(advisor);
        pf.setTarget(target);
        PointcutMethod proxy = (PointcutMethod) pf.getProxy();
        
        proxy.setAge(25);
        proxy.setName("Boy");
        proxy.setTitle("Freakboy");
        proxy.getAge();
        proxy.getName();
        proxy.getTitle();
        
        
    }

}

运行结果

22:55:35.826 [main] DEBUG org.springframework.aop.framework.CglibAopProxy - Method is declared on Advised interface: public abstract java.lang.Class org.springframework.aop.TargetClassAware.getTargetClass()
我们处理 setAge 方法
我们处理 setName 方法
我们处理 setName 方法
BeforeMethod : Before method succ!
我们处理 getAge 方法
BeforeMethod : Before method succ!
我们处理 getName 方法
BeforeMethod : Before method succ!
我们处理 getName 方法

使用正则表达式(JdkRegexpMethodPointcut)

package boy.aop.pointcut;

import org.springframework.aop.Advisor;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.aop.support.JdkRegexpMethodPointcut;
import boy.aop.advice.BeforeMethod;

public class RegexPointcut {
    
    public static void main(String[] args) {
        PointcutMethod target = new PointcutMethod();

        JdkRegexpMethodPointcut pc = new JdkRegexpMethodPointcut();
        pc.setPattern(".*get.*");
        
        Advisor advisor = new DefaultPointcutAdvisor(pc, new BeforeMethod());
        
        ProxyFactory pf = new ProxyFactory();
        
        pf.addAdvisor(advisor);
        pf.setTarget(target);
        PointcutMethod proxy = (PointcutMethod) pf.getProxy();
        
        proxy.setAge(25);
        proxy.setName("Boy");
        proxy.setTitle("Freakboy");
        proxy.getAge();
        proxy.getName();
        proxy.getTitle();
    }

}

这里我们使用了.*get.*表达式,合理的正则应该是^[get].+,但是Spring中的JdkRegexpMethodPointcut名称正则匹配机制是完全合格的方法名(FQN)来匹配方法名,我们刚刚需要匹配的是getXXX方法,而实际去匹配的则是boy.aop.pointcut.PointcutMethod.getXXX方法,也就为什么我们这里的pattern为什么是.*get.*,正则的灵活运用,对于我们连简化程序是非常重要的,我们应该多多了解,多多运用。

最后由 不一样的少年 编辑于2016年12月13日 23:13