Guice的なAOP
まずはソースを見る方がわかりやすい。SetterInterceptorというclassがInterceptor。    Injector injector = Guice.createInjector(new AbstractModule() {
        @Override
        protected void configure() {
            // 対象は以下。
            // ClassMather: AbstractModel classのSubClass
            // MethodMatche: "set"で始まるMethod
            bindInterceptor(Matchers.subclassesOf(AbstractModel.class), new AbstractMatcher<Method>() {
                public boolean matches(Method t) {
                    return t.getName().startsWith("set");
                }
            }, new SetterInterceptor());
        }
    });
ポイントはまず以下のメソッド。
- bindInterceptor(
Matcher<? super Class<?>> classMatcher, Matcher<? super Method> methodMatcher, MethodInterceptor... interceptors) 
- Matcher
 
今回は自分で作ったMatcherも使用しているが、Matchersには以下のようなものも定義されている。
- 特定のPackage内のものを指定するinPackage()
 - 特定のannotationで修飾されたものを指定するannotatedWith()
 - 引数に指定したインスタンスとequals()が成立したインスタンスのみ反応するonly()。
 - 上記のonly()は「equals()」での判断だが、「==」で判断するのがidenticalTo()。
 - 返り値の型を指定するreturns()
 
この程度でAOPできるが、試したソースをこの下に書いておく。
PropertyChangeEventを発火するInterceptorの実装
Guiceでは、AOP Allianceに準拠したInterceptorが使える。Guice専用のInterceptorもある(com.google.inject.cglib.proxy.MethodInterceptor)。ここではAOP Allianceに準拠した実装を行ってみる。import java.beans.PropertyChangeEvent;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class SetterInterceptor implements MethodInterceptor {
    public Object invoke(MethodInvocation invocation) throws Throwable {
        Method method = invocation.getMethod();
        String methodName = invocation.getMethod().getName();
        // setter method名からfield名を作成する。
        String fieldName = methodName.replaceFirst("set", "");
        fieldName = fieldName.substring(0, 1).toLowerCase() + fieldName.substring(1);
        Class<?> declaringClass = method.getDeclaringClass();
        // java.lang.Fieldを取得する。
        Field field = declaringClass.getDeclaredField(fieldName);
        if (field.getModifiers() != Field.PUBLIC) {
            field.setAccessible(true);
        }
        // setterの実行と、その前後の値を取得する。
        Object oldValue = field.get(invocation.getThis());
        Object result = invocation.proceed();
        Object newValue = field.get(invocation.getThis());
        try {
            // "firePropertyChange"固定のmethod名でEventの発火を実行する。
            Method fireMethod = declaringClass.getMethod("firePropertyChange", PropertyChangeEvent.class);
            fireMethod.invoke(invocation.getThis(), new PropertyChangeEvent(invocation.getThis(), field.getName(),
                    oldValue, newValue));
        } catch (NoSuchMethodException ex) {
            // do nothing.
        }
        return result;
    }
}
PropertyChangeEventを発するModelClass
もちろん、setter内ではPropertyEventのfireはしない。package model; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.ArrayList; import java.util.List; public abstract class AbstractModel { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } List<PropertyChangeListener> listeners = new ArrayList<PropertyChangeListener>(); public void addListener(PropertyChangeListener l) { listeners.add(l); } public void removeListener(PropertyChangeListener l) { listeners.remove(l); } public void firePropertyChange(PropertyChangeEvent event) { for (PropertyChangeListener l : listeners) { l.propertyChange(event); } } }package model; public class Column extends AbstractModel { public String type; public Column() { } public String getType() { return type; } public void setType(String type) { this.type = type; } }package model; import java.util.ArrayList; import java.util.List; public class Table extends AbstractModel { private List<Column> columns = new ArrayList<Column>(); private String propertyString; public Table() { } public List<Column> getColumns() { return columns; } public void setColumns(List<Column> columns) { this.columns = columns; } public String getPropertyString() { return propertyString; } public void setPropertyString(String propertyString) { this.propertyString = propertyString; } }
0 件のコメント:
コメントを投稿