有時候,您會希望物件在運行過程中能增加額外的職責,具體而言就是為物件動態加入一些方法,這在目前的Java語言來說是沒有這種機制的。
例如,您定義了下面的介面與實作類別:
代碼:
package onlyfun.caterpillar;
public interface ISomeInterface {
public void setName(String name);
public String getName();
}
代碼:
package onlyfun.caterpillar;
public class SomeObject implements ISomeInterface {
private String name;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
這只是一個簡單的JavaBean物件,您如同平常的使用它,有一天您希望在程式的某個運行過程中,能限制其為immutable,也就是說您希望物件有能力能鎖定自己的setter方法,讓物件的內容無法改變。
純就Java類別與介面的設計來說,其方法是無法動態增加的,在Spring的AOP框架中提供有Introduction Advice,可以讓您在不變更上列程式的情況下,達到您的要求,事實上這個例子,是從Spring參考手冊而來的,我們將手冊中的例子完整實作出來。
Spring的Introduction Advice是一個特殊的Advice,它並不使用在PointCut層級上,而是使用於類別層級上,當物件上發生某個介面轉換時,會通知Introduction Advice介入。
首先我們定義一個介面,當有一個物件被指定轉換至這個介面時,Introduction Advice將介入:
代碼:
package onlyfun.caterpillar;
public interface ILockable {
public void lock();
public void unlock();
public boolean locked();
}
Introduction Advice實作上面的介面,並繼承org.springframework.aop.support.DelegatingIntroductionInterceptor,在我們的需求中,介面轉換後Interceptor介入,我們定義當呼叫目標物件的setter時發生例外,這相當於鎖定settor無法使用,例外可用於通知上層應用程式目前物件為鎖定狀態:
代碼:
package onlyfun.caterpillar;
import org.springframework.aop.support.DelegatingIntroductionInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class LockMixinInterceptor extends DelegatingIntroductionInterceptor
implements ILockable {
private boolean locked;
public void lock() {
this.locked = true;
}
public void unlock() {
this.locked = false;
}
public boolean locked() {
return this.locked;
}
public Object invoke(MethodInvocation invocation) throws Throwable {
if (locked() && invocation.getMethod().getName().indexOf("set") == 0)
throw new Exception("Object Locked!!");
return super.invoke(invocation);
}
}
我們需要一個Introduction Advisor,將Interceptor及ILockable介面關聯在一起,一個最簡單的方法是使用org.springframework.aop.support.DefaultIntroductionAdvisor,如下:
代碼:
package onlyfun.caterpillar;
import org.springframework.aop.support.DefaultIntroductionAdvisor;
public class LockMixinAdvisor extends DefaultIntroductionAdvisor {
public LockMixinAdvisor() {
super(new LockMixinInterceptor(), ILockable.class);
}
}
當然,別忘了要設定Bean定義檔:
代碼:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "- "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="someObject" class="onlyfun.caterpillar.SomeObject"/>
<bean id="lockMixinAdvisor" class="onlyfun.caterpillar.LockMixinAdvisor"/>
<bean id="someObjectProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<value>onlyfun.caterpillar.ISomeInterface</value>
</property>
<property name="target">
<ref bean="someObject"/>
</property>
<property name="interceptorNames">
<list>
<value>lockMixinAdvisor</value>
</list>
</property>
</bean>
</beans>
設定完成,我們使用下面的程式來簡單的測試一下:
代碼:
package onlyfun.caterpillar;
import java.io.*;
import org.springframework.context.*;
import org.springframework.context.support.*;
public class Main {
public static void main(String[] args) throws IOException {
ApplicationContext context = new FileSystemXmlApplicationContext("bean.xml");
ISomeInterface someObjectProxy = (ISomeInterface) context.getBean("someObjectProxy");
ILockable lockObject = (ILockable) someObjectProxy;
someObjectProxy.setName("justin");
System.out.println(someObjectProxy.getName());
try {
lockObject.lock();
someObjectProxy.setName("momor");
System.out.println(someObjectProxy.getName());
}
catch(Throwable e) {
e.printStackTrace();
System.out.println("press any key to continue....");
System.in.read();
}
lockObject.unlock();
someObjectProxy.setName("momor");
System.out.println(someObjectProxy.getName());
}
}
對我們原先設定的SomeObject來說,不需要作任何的更改,也可以於運行期間額外增加其鎖定功能。