第二堂課主要在介紹AOP 觀念

AOP(Aspect-Oriented Programming)

以圖解的方式

Btw: 此課程中會用到interface介面,而我們可以快速的建立interface介面

則是,點選 class >> 右建 >> Retuctor >> Extract Interface >> 取名稱,然後確定即可

而通常我們會把Interface的名稱 ex: SeqNumBean  變成 ISeqNum

 

本次課程中要把原先的程式 利用 AOP 的觀念做改寫

//原先

 

app-config.spring.xml

<bean id="SeqNumBean"class="my.test.spring3.bean.SeqNumBean" />

 

SeqNumBean.java

public class SeqNumBean {
   private int seqNum;

   public String getSeqNum() {
   String pattern = "0000";
   DecimalFormat fmt = new DecimalFormat(pattern);
   String seqNum = fmt.format(this.seqNum);
   return seqNum;
   }

   public void next(){
   seqNum++;
   }
}

AdviceMain.java(執行檔)

public class AdviceMain {
   public static void main(String[] args) {
// TODO Auto-generated method stub

   ApplicationContext appCtx;
   appCtx = new ClassPathXmlApplicationContext("app-config.spring.xml");
   SeqNumBean seqNumBean = (SeqNumBean)appCtx.getBean("seqNumBean");

   seqNumBean.next();
   System.out.println("Seq Num:" + seqNumBean.getSeqNum());
   seqNumBean.next();
   System.out.println("Seq Num:" + seqNumBean.getSeqNum());
   }
}

 

=================================== 更改後

BTW :AOP我真的看的好吃力!! 有點力力啦啦的~

先說明:

Spring提供了四種不同的Advice
Before :MethodBeforeAdvice
 方法執行前
能修改方法的輸入參數
After :AfterReturningAdvice
方法執行後
Around :MethodInterceptor
方法執行前/後
可自行決定Target的方法是否執行
可以修改方法回傳結果
Throw :ThrowsAdvice
例外發生後

首先將 SeqNumBean  變成 ISeqNum 也就是變成interface

OK後

ISeqNum.java (interface) :

public interface ISeqNum {

   public abstract String getSeqNum();

   public abstract void next();

}

主要 SeqNumBean.java :

public class SeqNumBean implements ISeqNum {
   private int seqNum;

   public String getSeqNum() {
  /*
    String pattern = "0000";
    DecimalFormat fmt = new DecimalFormat(pattern);
    String seqNum = fmt.format(this.seqNum);
  */
    String seqNum = String.valueOf(this.seqNum);
    return seqNum;
   }

   public void next(){
    seqNum++;
   }
}

 

 SeqNumDateFmtAdvice.java :  // (advice) Around :MethodInterceptor

public class SeqNumDateFmtAdvice implements MethodInterceptor {
private final DecimalFormat numFmt ;
private final SimpleDateFormat dateFmt;
private final String seqNumPattern;

public SeqNumDateFmtAdvice(String numPattern,
      String datePattern,
      String seqNumPattern){
      numFmt = new DecimalFormat(numPattern);
      dateFmt = new SimpleDateFormat(datePattern);
      this.seqNumPattern = seqNumPattern;
    }
   public Object invoke(MethodInvocation invocation) throws Throwable {
   // TODO Auto-generated method stub
   //System.out.println("Invoke:" + invocation.getMethod());

      String result = (String)invocation.proceed(); 
      Number num =numFmt.parse(result);
      result = String.format(seqNumPattern,
      numFmt.format(num),
      dateFmt.format(new Date()));
      return result;
   }
}

已上之其中

invoke()中MethodInvocation的參數包括了下一個Interceptor的資訊、被執行的方法、及方法所需之參數,其返回值是被呼叫方法的返回值。

我們仍需要一個代理物件,就可以使用org.springframework.aop.framework.ProxyFactoryBean,它需要知道代理的介面與物件,以及所要介入的Interceptor

(http://www.javaworld.com.tw/confluence/pages/viewpage.action?pageId=2427  已上解釋參考)

 

app-config.spring.xml :

<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-3.0.xsd">

   <bean id="targetSeqNum" class="my.test.spring3.bean.SeqNumBean"/>

   <bean id="nameMatchAdvisor"
      class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
      <property name="advice">
         <ref bean="fmtAdvice"/>
      </property>
         <property name="mappedName" value="getSeqNum"/>
      </bean>

   <bean id="seqNumBean"
      class="org.springframework.aop.framework.ProxyFactoryBean">
   <property name="proxyInterfaces">
      <value>my.test.spring3.bean.ISeqNum</value>
   </property>
   <property name="target">
      <ref bean="targetSeqNum"/>
   </property>
   <property name="interceptorNames">
      <list>
         <value>nameMatchAdvisor</value>
      </list>
   </property>
</bean>


   <bean id="fmtAdvice" class="my.test.spring3.advice.SeqNumDateFmtAdvice">
      <constructor-arg index="0" value="000" />
      <constructor-arg index="1" value="MMdd" />
      <constructor-arg index="2" >
      <value>%2$s-%1$s</value>
      </constructor-arg>
   </bean>

</beans>

AOP 有四個重要的流程:

1.Target

2.Aspect

3.AspectJoinpoint

4.Advice

 Interceptor執行的順序是interceptorNames中設定的順序,最後是target目標物件,

可看到 target參考了 targetSeqNum ,而targetSeqNum擇連到SeqNumBean 使用下面的程式來測試一下:

SeqNumBean.java :

 

public class SeqNumBean implements ISeqNum {
   private int seqNum;

 

      public String getSeqNum() {
         String seqNum = String.valueOf(this.seqNum);
         return seqNum;
   }

 

   public void next(){
      seqNum++;
   }
}

 

最後來執行我們已上所寫好的程式

AdviceMain (執行檔):

public class AdviceMain {
   public static void main(String[] args) {
// TODO Auto-generated method stub

   ApplicationContext appCtx;
      appCtx = new ClassPathXmlApplicationContext("app-config.spring.xml");
      ISeqNum seqNumBean = (ISeqNum)appCtx.getBean("seqNumBean");

      seqNumBean.next();
      System.out.println("Seq Num:" + seqNumBean.getSeqNum());
      seqNumBean.next();
      System.out.println("Seq Num:" + seqNumBean.getSeqNum());

   }
}

// 可以看到 執行檔 利用 seqNumBean 這個 bean (id=seqNumBean )  ,

去做 proxyInterfaces、interceptorNames、target  三個流程

 

// 輸出結果:

十一月 24, 2013 3:51:26 下午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
資訊: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@350204ce: startup date [Sun Nov 24 15:51:26 CST 2013]; root of context hierarchy
十一月 24, 2013 3:51:26 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
資訊: Loading XML bean definitions from class path resource [app-config.spring.xml]
十一月 24, 2013 3:51:26 下午 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
資訊: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@4b4c9bfb: defining beans [targetSeqNum,nameMatchAdvisor,seqNumBean,fmtAdvice]; root of factory hierarchy
Seq Num:1124-001
Seq Num:1124-002

 

心得:

所以 AOP外圍架構為

1.Target

2.Aspect

3.AspectJoinpoint

4.Advice  

而內為:proxyInterfaces、interceptorNames、target  三個流程

補充:

而AOP 已上方案例來看

 <bean id="fmtAdvice" class="my.test.spring3.advice.SeqNumDateFmtAdvice">
      <constructor-arg index="0" value="000" />
      <constructor-arg index="1" value="MMdd" />
      <constructor-arg index="2" >
      <value>%2$s-%1$s</value>
      </constructor-arg>
 </bean>

<bean id="nameMatchAdvisor"
      class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
      <property name="advice">
         <ref bean="fmtAdvice"/>
      </property>
      <property name="mappedName" value="getSeqNum"/>
</bean>

mappedName 指的是 所對應的這個方法getSeqNum 當呼叫到它時才進入Invoke也就是MethodInterceptor內

若無對應到,那呼叫到getSeqNum就不會再進入invoke內的多一層程式驗證

 

以上方xml程式面布局來看 其流程

1. (proxyInterfaces)對應interface   

2. (target)對應1所實做interface的class 

3.(interceptorNames)設定advice架構及給予進入advice規則

org.springframework.aop.support.NameMatchMethodPointcutAdvisor(ex:<property name="mappedName" value="getSeqNum"/>)

 

 

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 JoshS 的頭像
    JoshS

    JoshS的部落格

    JoshS 發表在 痞客邦 留言(0) 人氣()