Spring的AOP其实使用的是一种代理机制,可以算是代理或是装饰模式的应用。下面是一些常用的术语: Aspect:通常翻译成切面,是一个横跨多个核心逻辑的功能,比如说散落在系统中多个角落的一块代码。 Joinpoint:通常翻译成连接点,是说程序中什么地方插入一个Aspect。 Pointcut:切入点,一组Joinpoint的集合,比如一个Aspect需要在每个以query开头的方法时执行,可以用正则(.*query.*)来表示这一组Joinpoint。 Advice:对Joinpoint的增强。 另外还有Introduct,Weaving,Interceptor,Target Object,Proxy。
常用的Advice: 1.MethodBeforeAdvice 2.AfterReturningAdvice 3.ThrowsAdvice 4.MethodInterceptor
1. 如果对UserServiceImpl增加advice增强,例如: <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" > <!-- 配置UserServiceImpl -->
<bean id="userServiceTarget" class="example.chapter4.UserServiceImpl"> <property name="userDao"> <bean class="example.chapter4.UserDao" /> </property> </bean>
<!-- 定义MethodBeforeAdvice -->
<bean id="loginMethodBeforeAdvice" class="example.chapter4.LoginMethodBeforeAdvice" />
<!-- 定义AfterReturningAdvice -->
<bean id="loginAfterReturningAdvice" class="example.chapter4.LoginAfterReturningAdvice" />
<!-- 定义ThrowsAdvice -->
<bean id="loginThrowsAdvice" class="example.chapter4.LoginThrowsAdvice" />
<!-- 定义MethodInterceptor -->
<bean id="loginAroundAdvice" class="example.chapter4.LoginAroundAdvice" />
<!-- 配置AOP -->
<bean id="userService" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="interceptorNames"> <list> <value>loginMethodBeforeAdvice</value> <value>loginAfterReturningAdvice</value> <value>loginThrowsAdvice</value> </list> </property> <property name="target" ref="userServiceTarget" /> </bean> </beans>
2.但是,有个缺点,这样使用的话,会对UserServiceImpl中每个方法都加上这几种advice,这个时候就需要Advisor。 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" > <!-- 配置UserServiceImpl -->
<bean id="userServiceTarget" class="example.chapter4.UserServiceImpl"> <property name="userDao"> <bean class="example.chapter4.UserDao" /> </property> </bean>
<!-- 使用NameMatchMethodPointcutAdvisor来定义loginBeforeAdvisor --> <bean id="loginBeforeAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor"> <!-- 指定切入方法为login --> <property name="mappedName" value="login" /> <!-- 指定Advice --> <property name="advice"> <bean class="example.chapter4.LoginMethodBeforeAdvice" /> </property> </bean>
<!-- 使用RegexpMethodPointcutAdvisor来定义createAfterAdvisor --> <bean id="createAfterAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <!-- 指定切入方法为.*create.*,拦截所有以create开头的方法 --> <property name="pattern" value=".*create.*" /> <!-- 指定Advice --> <property name="advice"> <bean class="example.chapter4.CreateAfterReturningAdvice" /> </property> </bean>
<!-- 配置AOP --> <bean id="userService" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="interceptorNames"> <list> <value>loginBeforeAdvisor</value> <value>createAfterAdvisor</value> </list> </property> <property name="target" ref="userServiceTarget" /> </bean> </beans>
这样一来,可以分别对不同的方法进行分别增强。
3.不过,还是有个问题,用户此时可以绕过AOP代理直接获取原始bean,那么,就需要自动代理来弥补这个问题。 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" > <!-- 配置UserServiceImpl -->
<bean id="userService" class="example.chapter4.UserServiceImpl"> <property name="userDao"> <bean class="example.chapter4.UserDao" /> </property> </bean>
<bean id="loginBeforeAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor"> <!-- 限定切入点仅作用于UserService的login()方法 --> <property name="pattern" value=".*UserService/.login" /> <property name="advice"> <bean class="example.chapter4.LoginMethodBeforeAdvice" /> </property> </bean>
<bean id="createAfterAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <!-- 限定切入点仅作用于UserService的create()方法 --> <property name="pattern" value=".*UserService/.create" /> <property name="advice"> <bean class="example.chapter4.CreateAfterReturningAdvice" /> </property> </bean>
<!-- 配置BeanNameAutoProxyCreator --> <!-- <bean id="beanNameAutoProxy" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> <property name="beanNames" value="userService" /> <property name="interceptorNames"> <list> <value>loginBeforeAdvisor</value> <value>createAfterAdvisor</value> </list> </property> </bean> --> <!-- 配置DefaultAdvisorAutoProxyCreator --> <bean id="defaultAutoProxy" class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" /> </beans> 经过这样配置后,可以将advisor应用的某个接口的某些方法上。 BeanNameAutoProxyCreator 与DefaultAdvisorAutoProxyCreator的不同,是后者可以自动查找advisor(不查找advice)和普通bean,然后根据advisor计算他们能够适用于哪些bean,凡是能被适用的bean都将被自动创建AOP代理。
对于.*UserService/.create的形式,如果不指定接口,spring会去找其他符合条件的类中的create方法,那么会提示CGLIB没有找到。因为AOP代理对接口适用JDK动态代理,而对类级别使用CGLIB代理。
4.还有一中Introduction的使用,它是一种拦截器,我感觉是装饰模式的一种应用 比如说有一个类User,在编译器类A并未静态实现其他接口,而想动态增强其行为,可以创建一个接口Mutable 配置代码如下: <?xml version="1.0" encoding="UTF-8"?> <bean id="introductionAdvisor" scope="prototype" <!-- 原始Bean --> </beans> |