使用AOP的好處


原始代碼的寫法

既然要通過代碼來演示,那必須要有例子,這里我的例子為:

有一個接口Dao有insert、delete、update三個方法,在insert與update被調用的前后,打印調用前的毫秒數與調用后的毫秒數

首先定義一個Dao接口:

public interface Dao { public void insert(); public void delete(); public void update(); }

然后定義一個實現類DaoImpl:
public class DaoImpl implements Dao { @Override public void insert() { System.out.println("DaoImpl.insert()"); } @Override public void delete() { System.out.println("DaoImpl.delete()"); } @Override public void update() { System.out.println("DaoImpl.update()"); } }

最原始的寫法,我要在調用insert()與update()方法前后分別打印時間,就只能定義一個新的類包一層,在調用insert()方法與update()方法前后分別處理一下
,新的類我命名為ServiceImpl,其實現為:
public class ServiceImpl { private Dao dao = new DaoImpl(); public void insert() { System.out.println("insert()方法開始時間:" + System.currentTimeMillis()); dao.insert(); System.out.println("insert()方法結束時間:" + System.currentTimeMillis()); } public void delete() { dao.delete(); } public void update() { System.out.println("update()方法開始時間:" + System.currentTimeMillis()); dao.update(); System.out.println("update()方法結束時間:" + System.currentTimeMillis()); } }

這是最原始的寫法,這種寫法的缺點也是一目了然:

方法調用前后輸出時間的邏輯無法復用,如果有別的地方要增加這段邏輯就得再寫一遍
如果Dao有其它實現類,那么必須新增一個類去包裝該實現類,這將導致類數量不斷膨脹。

使用AOP

最后來看一下使用AOP的方式,首先定義一個時間處理類,我將它命名為TimeHandler:

public class TimeHandler {   public void printTime(ProceedingJoinPoint pjp) { Signature signature = pjp.getSignature(); if (signature instanceof MethodSignature) { MethodSignature methodSignature = (MethodSignature)signature; Method method = methodSignature.getMethod(); System.out.println(method.getName() + "()方法開始時間:" + System.currentTimeMillis());       try { pjp.proceed(); System.out.println(method.getName() + "()方法結束時間:" + System.currentTimeMillis()); } catch (Throwable e) {         e.printStackTrace(); } } } }
切面方法printTime本身可以不用定義任何的參數,但是有些場景下需要獲取調用方法的類、方法簽名等信息,此時可以
在printTime方法中定義JointPoint,Spring會自動將參數注入,可以通過JoinPoint獲取調用方法的類、方法簽名等信息
。由於這里我用的aop:around,要保證方法的調用,這樣才能在方法調用前后輸出時間,因此不能直接使用JoinPoint,
因為JoinPoint沒法保證方法調用。此時可以使用ProceedingJoinPoint,ProceedingPointPoint的proceed()方法可以保
證方法調用,但是要注意一點,ProceedingJoinPoint只能和aop:around搭配,換句話說,如果aop.xml中配置的是aop:before,
然后printTime的方法參數又是ProceedingJoinPoint的話,Spring容器啟動將報錯。

接着看一下ApplicationContext.xml的配置:
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
  <bean id="daoImpl" class="org.xrq.spring.action.aop.DaoImpl" />
   <bean id="timeHandler" class="org.xrq.spring.action.aop.TimeHandler" />
  <aop:config>
    <aop:pointcut id="addAllMethod" expression="execution(* org.xrq.spring.action.aop.Dao.*(..))" />
    <aop:aspect id="time" ref="timeHandler">
    <aop:before method="printTime" pointcut-ref="addAllMethod" />
    <aop:after method="printTime" pointcut-ref="addAllMethod" />
    </aop:aspect>
  </aop:config>
</beans>

測試代碼很簡單:
public class AopTest { @Test @SuppressWarnings("resource") public void testAop() { ApplicationContext ac = new ClassPathXmlApplicationContext("spring/aop.xml"); Dao dao = (Dao)ac.getBean("daoImpl"); dao.insert(); System.out.println("----------分割線----------"); dao.delete(); System.out.println("----------分割線----------"); dao.update(); } }
到此我總結一下使用AOP的幾個優點:

  切面的內容可以復用,比如TimeHandler的printTime方法,任何地方需要打印方法執行前的時間與方法執行后的時間,都可以使用TimeHandler的printTime方法
避免使用Proxy、CGLIB生成代理,這方面的工作全部框架去實現,開發者可以專注於切面內容本身
代碼與代碼之間沒有耦合,如果攔截的方法有變化修改配置文件即可。

 
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM