第二堂課主要在介紹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"/>)
留言列表