在看这篇文章之前需要首先理解 Spring AOP 增强的知识,如果你想先了解增强的知识可以移步到 我另外一篇博客《Spring动态代理之详细DEBUG日志模式》里面有关于增强的知识 如果除了增强还有 关于CGlib 和 Proxy 代理的知识不太理解可以再移步到 《动态代理之详细DEBUG日志模式》好了,目前是建基于你都明白 动态代理 和 Spring 增强的基础上进行对Spring 中的 切点(pointcut) 和 切面(advisor)的介绍
如果只有advice 我们一般情况下将一个目标类中的所有方法都进行代理,但是我们希望可以定位的某些类的特定方法,这个时候我们就需要使用到切点(pointcut),然而切面就是切点和增强(advice)的结合。
首先我们通过一个UserService的业务去理解切面和切点的知识,一个有两个方法一个是注册 一个是登录,我们会对这两个方法进行织入演示,所以首先提出这个Service的代码,非常简单的代码:
/** * Created by yanzhichao on 27/04/2017. */ public class UserService { public void login(String username,String password){ System.out.println("Login : " + username + ", pwd : " + password); } public void register(String username,String password){ System.out.println("register : " + username + " , pwd " + password); } }
首先我们先说StaticMethodMatcherPointcutAdvice(这个类名长到我想吐)的使用,主要我们是希望通过这个静态方法匹配切面(原谅我直译)对UserService中的login进行织入。而register不织入:
/** * Created by yanzhichao on 27/04/2017. */ public class StaticLoginMethodAdvisor extends StaticMethodMatcherPointcutAdvisor { //匹配login方法进行织入 public boolean matches(Method method, Class<?> aClass) { return "login".equals(method.getName()); } //定义对什么类有效 @Override public ClassFilter getClassFilter() { return new ClassFilter() { public boolean matches(Class<?> aClass) { //定义对UserService类或者它的派生类有效 return UserService.class.isAssignableFrom(aClass); } }; } }
然后定义advice 增强,就在方法执行的时候打印一下执行的什么方法的LOG
/** * Created by yanzhichao on 27/04/2017. */ public class UserAdvice implements MethodBeforeAdvice { public void before(Method method, Object[] objects, Object o) throws Throwable { System.out.println(">>>>>>>>>>" + method.getName() + " invoked !!!"); } }
由于我想测试一下advisor中的getClassFilter方法是否有用,所以我再定义了一个AdminService
/** * Created by yanzhichao on 27/04/2017. */ public class AdminService { public void login(String username,String password){ System.out.println("Admin Login : " + username + ", pwd : " + password); } public void register(String username,String password){ System.out.println("Admin register : " + username + " , pwd " + password); } }
同样有login方法,所以再执行两个Service的login方法的时候可以看看输出结果。
最后配置文件的代码如下:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- 未代理的类 --> <bean id="userService" class="com.maxfunner.service.impl.UserService" /> <bean id="adminService" class="com.maxfunner.service.impl.AdminService" /> <bean id="UserAdvice" class="com.maxfunner.advice.UserAdvice" /> <bean id="LoginMethodAdvisor" class="com.maxfunner.advisor.StaticLoginMethodAdvisor"> <property name="advice"> <bean class="com.maxfunner.advice.UserAdvice" /> </property> </bean> <!-- 注意使用了abstract主要是作为一个配置模板去使用,由于我们没有接口实现方式去写Service所以这里使用proxyTargetClass --> <bean id="factoryBean" class="org.springframework.aop.framework.ProxyFactoryBean" abstract="true" p:proxyTargetClass="true" p:interceptorNames="LoginMethodAdvisor" /> <!-- 定义被proxy bean --> <bean id="userServiceProxy" parent="factoryBean" p:target-ref="userService" /> <bean id="adminServiceProxy" parent="factoryBean" p:target-ref="adminService" /> </beans>
上面注释上已经写的很清楚了,接下来我们使用main方法进行测试一下 然后再贴出一下输出的Log
public static void main(String[] args){ ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); UserService userService = (UserService) context.getBean("userServiceProxy"); AdminService adminService = (AdminService) context.getBean("adminServiceProxy"); userService.login("TONY","PWD"); userService.register("TONY","PWD"); adminService.login("TONY","PWD"); adminService.register("TONY","PWD"); }
输出可以看到只有userService.login才有被成功增强:
>>>>>>>>>>login invoked !!!
Login : TONY, pwd : PWD
register : TONY , pwd PWD
Admin Login : TONY, pwd : PWD
Admin register : TONY , pwd PWD
但是你会问 既然我们写定义proxy bean的时候就已经定义好那个类进行代理了为什么还有在advisor定义classFilter的方法呢,因为是为了做自动代理,这个留最后说~ 接下来我们来做一个更加简单的静态方法切面,通过正则表达式进行限定方法名匹配,这里用到RegexpMethodPointcutAdvisor类,什么都不用改变就该spring配置文件即可:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- 未代理的类 --> <bean id="userService" class="com.maxfunner.service.impl.UserService" /> <bean id="adminService" class="com.maxfunner.service.impl.AdminService" /> <bean id="UserAdvice" class="com.maxfunner.advice.UserAdvice" /> <!-- pattern是正则表达式,现在我们只针对service包下的所有类的log开头的方法进行织入 --> <bean id="LoginMethodAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <property name="patterns"> <list> <value>.*\.service\..*\.log.*</value> </list> </property> <property name="advice"> <bean