Dashboard > OpenSource Project > ... > Spring > 入門 45 - Introduction Advice
OpenSource Project Log In   View a printable version of the current page.
入門 45 - Introduction Advice
Added by cheetah, last edited by cheetah on Apr 13, 2005
Labels: 
(None)

  有時候,您會希望物件在運行過程中能增加額外的職責,具體而言就是為物件動態加入一些方法,這在目前的Java語言來說是沒有這種機制的。

  例如,您定義了下面的介面與實作類別:

 代碼:

ISomeInterface.java
package onlyfun.caterpillar; 

public interface ISomeInterface { 
    public void setName(String name); 
    public String getName(); 
}

 代碼:

SomeObject.java
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將介入:

 代碼:

ILockable.java
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無法使用,例外可用於通知上層應用程式目前物件為鎖定狀態:

 代碼:

LockMixinInterceptor.java
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,如下:

 代碼:

LockMixinAdvisor.java
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 "-//SPRING/DTD BEAN/EN" "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>

  設定完成,我們使用下面的程式來簡單的測試一下:

 代碼:

Main.java
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; 
        
        // Object isn't locked. It's ok to use setter. 
        someObjectProxy.setName("justin"); 
        System.out.println(someObjectProxy.getName()); 
        
        try { 
            // Object is locked. 
            lockObject.lock(); 
            // It isn't ok to use setter. 
            someObjectProxy.setName("momor"); 
            // The belowing code wouldn't be executed.... 
            System.out.println(someObjectProxy.getName()); 
        } 
        catch(Throwable e) { 
            e.printStackTrace(); 
            System.out.println("press any key to continue...."); 
            System.in.read(); 
        } 
        
        // Object is unlocked. 
        lockObject.unlock(); 
        // It's ok to use setter again. 
        someObjectProxy.setName("momor"); 
        System.out.println(someObjectProxy.getName()); 
    } 
}

  對我們原先設定的SomeObject來說,不需要作任何的更改,也可以於運行期間額外增加其鎖定功能。

Site powered by a free Open Source Project / Non-profit License (more) of Confluence - the Enterprise wiki.
Learn more or evaluate Confluence for your organisation.
Powered by Atlassian Confluence, the Enterprise Wiki. (Version: 2.1.5a Build:#411 Mar 16, 2006) - Bug/feature request - Contact Administrators