歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> Spring中的AOP

Spring中的AOP

日期:2017/3/1 10:38:24   编辑:Linux編程

何為AOP

AOP,面向切面編程。

在不改動代碼的前提下,靈活的在現有代碼的執行順序前後,添加進新規機能。

來一個簡單的Sample:

目標類:

[java]
  1. package com.hyron.tony;
  2. public class CustomerService {
  3. private String name;
  4. private String url;
  5. public void setName(String name) {
  6. this.name = name;
  7. }
  8. public void setUrl(String url) {
  9. this.url = url;
  10. }
  11. public void printName() {
  12. System.out.println("Customer name : " + this.name);
  13. }
  14. public void printURL() {
  15. System.out.println("Customer website : " + this.url);
  16. }
  17. public void printThrowException() {
  18. throw new IllegalArgumentException();
  19. }
  20. }

advice:只以Around advice為例

[java]
  1. import java.util.Arrays;
  2. import org.aopalliance.intercept.MethodInterceptor;
  3. import org.aopalliance.intercept.MethodInvocation;
  4. public class HijackAroundMethod implements MethodInterceptor {
  5. @Override
  6. public Object invoke(MethodInvocation methodInvocation) throws Throwable {
  7. System.out.println("Method name : "
  8. + methodInvocation.getMethod().getName());
  9. System.out.println("Method arguments : "
  10. + Arrays.toString(methodInvocation.getArguments()));
  11. // same with MethodBeforeAdvice
  12. System.out.println("HijackAroundMethod : Before method hijacked!");
  13. try {
  14. // proceed to original method call
  15. Object result = methodInvocation.proceed();
  16. // same with AfterReturningAdvice
  17. System.out.println("HijackAroundMethod : Before after hijacked!");
  18. return result;
  19. } catch (IllegalArgumentException e) {
  20. // same with ThrowsAdvice
  21. System.out
  22. .println("HijackAroundMethod : Throw exception hijacked!");
  23. throw e;
  24. }
  25. }
  26. }

編織切入關系的配置文件:

[html]
  1. <bean id="customerService" class="com.mkyong.customer.services.CustomerService">
  2. <property name="name" value="Yong Mook Kim" />
  3. <property name="url" value="http://www.mkyong.com" />
  4. </bean>
  5. <bean id="hijackAroundMethodBean" class="com.mkyong.aop.HijackAroundMethod" />
  6. <bean id="customerServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
  7. <property name="target" ref="customerService" />
  8. <property name="interceptorNames">
  9. <list>
  10. <value>hijackAroundMethodBean</value>
  11. </list>
  12. </property>
  13. </bean>

Sample的啟動:

[java]
  1. import org.springframework.context.ApplicationContext;
  2. import org.springframework.context.support.ClassPathXmlApplicationContext;
  3. import com.mkyong.customer.services.CustomerService;
  4. public class App {
  5. public static void main(String[] args) {
  6. ApplicationContext appContext = new ClassPathXmlApplicationContext(
  7. new String[] { "Spring-Customer.xml" });
  8. CustomerService cust = (CustomerService) appContext
  9. .getBean("customerServiceProxy");
  10. System.out.println("*************************");
  11. cust.printName();
  12. System.out.println("*************************");
  13. cust.printURL();
  14. System.out.println("*************************");
  15. try {
  16. cust.printThrowException();
  17. } catch (Exception e) {
  18. }
  19. }
  20. }

以上代碼,用customerServiceProxy代理CustomerService的執行

在customerServiceProxy的配置中,定義了用hijackAroundMethodBean作為方法攔截器,在hijackAroundMethodBean中利用invoke方法,攔截住所有的方法調用,塞入自己的邏輯業務。

AOP的兩種實現

上面看到的是Spring的Sample。

其實,Spring的AOP也是調用了其他開源技術實現。

比較常用的是JDK自己的Proxy,和開源的CGLIB

兩者的區別,Proxy需要Advice必須從接口繼承過來。如果切入的目標物是實體類,則無法使用。

CGLIB則可以用於直接覆蓋實體類的方法。

Spring對以上兩種都有支持。

Spring的底層實現

Spring在配置文件中,通過ProxyFactoryBean編織和實現了切面的構成。

我們在執行以下這行話的時候

CustomerService cust = (CustomerService) appContext

.getBean("customerServiceProxy");

其實是將動態對象的生成委托給了ProxyFactoryBean

當配置文件中 <bean>的class屬性配置的實現類是FactoryBean時,通過getBean方法返回的不是FactoryBean本身,而是 FactoryBean#getObject()方法所返回的對象,相當於FactoryBean#getObject()代理了getBean()方 法。

執行順序如下:

1. ProxyFactoryBean中的getObject

[java]
  1. /**
  2. * Return a proxy. Invoked when clients obtain beans from this factory bean.
  3. * Create an instance of the AOP proxy to be returned by this factory.
  4. * The instance will be cached for a singleton, and create on each call to
  5. * <code>getObject()</code> for a proxy.
  6. * @return a fresh AOP proxy reflecting the current state of this factory
  7. */
  8. public Object getObject() throws BeansException {
  9. initializeAdvisorChain();
  10. if (isSingleton()) {
  11. return getSingletonInstance();
  12. }
  13. else {
  14. if (this.targetName == null) {
  15. logger.warn("Using non-singleton proxies with singleton targets is often undesirable. " +
  16. "Enable prototype proxies by setting the 'targetName' property.");
  17. }
  18. return newPrototypeInstance();
  19. }
  20. }

2. ProxyFactoryBean中的initializeAdvisorChain

從配置文件中的advice list中取得interceptorNames,並將其加入advisorChain

[java] view plaincopyprint?
  1. for (String name : this.interceptorNames) {
  2. if (logger.isTraceEnabled()) {
  3. logger.trace("Configuring advisor or advice '" + name + "'");
  4. }
  5. if (name.endsWith(GLOBAL_SUFFIX)) {
  6. if (!(this.beanFactory instanceof ListableBeanFactory)) {
  7. throw new AopConfigException(
  8. "Can only use global advisors or interceptors with a ListableBeanFactory");
  9. }
  10. addGlobalAdvisor((ListableBeanFactory) this.beanFactory,
  11. name.substring(0, name.length() - GLOBAL_SUFFIX.length()));
  12. }
Copyright © Linux教程網 All Rights Reserved