1. 程式人生 > >Spring中的兩種AOP織入方式

Spring中的兩種AOP織入方式

Spring實現aop是依賴兩種技術,一種是jdk動態代理,被切入的類需要實現介面,如果在配置檔案中不指明實現什麼介面,
spring會自動搜尋其實現介面並織入advice,別一種是藉助動態修改類的技術,使用cglib動態地擴充套件類來實現切面,
cglib可以實現位元組碼級地修改,執行效率比jdk動態代理要高,但建立例項時沒有前者快.預設情況下,使用jdk動態代理,
通過下面的配置,可以顯式指明到底使用哪種代理方式.起作用的是proxyTargetClass這個屬性,為true的時候,代表要擴充套件
織入的類,使用cglib,預設為false.如果你指定為false,卻又沒有實現介面,去呼叫方法的時候,一個Proxy$0不能轉換之類的
異常就會如約而至.
<bean id="forumService" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="interceptorNames">
 <list>
<value>transactionManager</value>
 </list>
</property>
<property name="target" ref="forumServiceTarget"/>
<property name="proxyTargetClass" value="true"/>
</bean> 
另外一個使用aop很常見的問題,我認為是切點的定義,使用註解簡單,但如果一個advice要切到很多地方,則要修改很多類,
把它們加上註解,倒不如擴充套件一下StaticMethodMatcherPointcutAdvisor,通過實現match方法匹配切點,如:
package spring.aop;




import org.springframework.aop.support.StaticMethodMatcherPointcutAdvisor;


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


public class Advisor extends StaticMethodMatcherPointcutAdvisor{
    private static List<String> classNames;
    private static List<String>  methodNames;
    static {
        classNames = Arrays.asList("spring.Person","spring.Horse");
        methodNames=Arrays.asList("eat");
    }
    public boolean matches(Method method, Class<?> targetClass) {
        return  classNames.contains(targetClass.getName()) && methodNames.contains(method.getName());
    }
}
其它類,bean,攔截器,配置檔案:
package spring;


public class Horse {
    public void eat(String food) {
        System.out.println("A horse is eating " + food);
    }
}
package spring;




public class Person {
    public void eat(String food) {
        System.out.println("A person is eating " + food);
    }


    public void sleep(String name) {
        System.out.println("Be quiet! " + name + " is sleeping");
    }
}
package spring.aop;




import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;


public class AroundInterceptor implements MethodInterceptor {
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("intercepting " + invocation.getThis().getClass() + "#" + invocation.getMethod().getName());
        return invocation.proceed();
    }
}
package spring;




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


public class Test {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("spring/applicationContext.xml");


        Horse horse = (Horse) ctx.getBean("horse");
        Person person = (Person) ctx.getBean("person");


        horse.eat("grass");
        person.eat("meat");


        person.sleep("theoffspring");


    }
}
applicationContext.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN"
        "http://www.springframework.org/dtd/spring-beans-2.0.dtd">


<beans>
    <bean id="horse" class="spring.Horse"/>
    <bean id="person" class="spring.Person"/>


    <bean id="advice" class="spring.aop.AroundInterceptor"/>
    <bean id="advisor" class="spring.aop.Advisor">
        <property name="advice" ref="advice"/>
    </bean>


    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>
</beans>
執行Test測試類,結果如下,證明everything is ok,切點是隻攔截horse和person類的eat方法:
intercepting class spring.Horse#eat
A horse is eating grass
intercepting class spring.Person#eat
A person is eating meat
Be quiet! theoffspring is sleeping
最後要提一下,一定要使用自動代理技術,這裡使用的是DefaultAdvisorAutoProxyCreator,它會自動搜尋符合切點的
所有spring bean進行織入,極其方便.以免手工為每個bean建立織入的配置.
目前我在公司做的一個監控專案需要對幾十個執行著的工程進行資料攔截,自動同步到另一個數據庫中去,
涉及的action,業務方法不計其數,有的業務類是別的部門開發的,我們無權修改,打到公共jar包裡,有了上述技術,
解決起來輕而易舉.