对于之前介绍过的Before Advice、After Advice、Around Advice、Throw Advice,从使用者的角度来看,它们“影响了目标物件上某些方法的行为”,例如让某些方法看来似乎增加了一些记录的动作。
Introduction是个特别的Advice,从使用者的角度来看,它“影响了目标物件的行为定义,直接增加了目标物件的职责(具体来说就是增加了可操作的方法)”,例如让某个已定义好的物件,在不修改该物件之类别档案的情况下,却可以增加一些额外的操作方法到物件之上。
就Java程式语言类别设计的观点来说,动态为物件增加可操作的方法显得不可思议,事实上在Spring AOP中,您可以透过实作org.springframework.aop.IntroductionInterceptor来实现Introduction。
IntroductionInterceptor继承了MethodInterceptor与DynamicIntroductionAdvice介面,其中implementsInterface()方法(继承自DynamicIntroductionAdvice)如果返回true的话,表示目前的 IntroductionInterceptor实作了给定的介面(也就是要额外增加行为的介面),此时您要使用invoke()呼叫介面上的方法,让目标物件执行额外的行为,您不可能使用MethodInvocation的proceed()方法,因为您要执行的是物件上原来没有的行为,呼叫 proceed()方法没有意义。
从文字上来理解Introduction会比较抽象,举个实际的例子来说,假设您的系统中已经有以下的类别:
* ISome.java
package onlyfun.caterpillar;
public interface ISome {
public void doSome();
}
* Some.java
package onlyfun.caterpillar;
public class Some implements ISome {
public void doSome() {
System.out.println("原来物件的职责。。。");
}
}
您希望在不修改原始档案的情况下,为Some类别增加一些可操作的方法,也许您甚至连原始码档案都没有,只有.class档案,您唯一知道的也许是他们的API说明,在不对它们作出修改的情况下,您希望Some类别可以增加doOther()方法。
在Spring中,您可以藉由实作IntroductionInterceptor介面来完成上面的任务,首先您为doOther()方法建立介面:
* IOther.java
package onlyfun.caterpillar;
public interface IOther {
public void doOther();
}
接着定义一个OtherIntroduction类别实作IntroductionInterceptor介面,并在实作IntroductionInterceptor介面的同时,也实作IOther介面,例如:
* OtherIntroduction.java
package onlyfun.caterpillar;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.IntroductionInterceptor;
public class OtherIntroduction
implements IntroductionInterceptor, IOther {
// 是否实作自IOther介面
public boolean implementsInterface(Class clazz) {
return clazz.isAssignableFrom(IOther.class);
}
public Object invoke(MethodInvocation methodInvocation)
throws Throwable {
// 如果呼叫的方法来自IOther介面的定义
if(implementsInterface(methodInvocation.getMethod().getDeclaringClass())) {
// 呼叫执行额外加入(mixin)的行为
return methodInvocation.getMethod().invoke(this, methodInvocation.getArguments());
}
else {
return methodInvocation.proceed();
}
}
public void doOther() {
System.out.println("增加的职责。。。");
}
}
接着您要在Bean定义档中将Introduction缝合至Some物件之上,使用org.springframework.aop.support.DefaultIntroductionAdvisor就可以了,例如:
* beans-config.xml
<?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="some"
class="onlyfun.caterpillar.Some"/>
<bean id="otherIntroduction"
class="onlyfun.caterpillar.OtherIntroduction"/>
<bean id="otherAdvisor"
class="org.springframework.aop.support.DefaultIntroductionAdvisor">
<constructor-arg index="0">
<ref bean="otherIntroduction"/>
</constructor-arg>
<constructor-arg index="1">
<value>onlyfun.caterpillar.IOther</value>
</constructor-arg>
</bean>
<bean id="proxyFactoryBean"
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<value>onlyfun.caterpillar.ISome</value>
</property>
<property name="target">
<ref bean="some"/>
</property>
<property name="interceptorNames"> DefaultIntroductionAdvisor的bean id
<list>
<value>otherAdvisor</value>
</list>
</property>
</bean>
</beans>
DefaultIntroductionAdvisor在建构时,需要给它IntroductionInterceptor的实例,以及所要代理额外行为的介面,现在,来撰写一个简单的程式测试,从这个程式当中,您可以更进一步了解何谓为物件额外增加行为:
* SpringAOPDemo.java
package onlyfun.caterpillar;
import org.springframework.context.ApplicationContext;
import org.springframework.context.
support.FileSystemXmlApplicationContext;
public class SpringAOPDemo {
public static void main(String[] args) {
ApplicationContext context =
new FileSystemXmlApplicationContext("beans-config.xml");
ISome some = (ISome) context.getBean("proxyFactoryBean");
some.doSome();
// 看来好像some物件动态增加了职责
((IOther) some).doOther();
}
}
对于some所参考的物件来说,它原先并不会有doOther()方法可供操作,然而透过Spring AOP的Introduction机制,现在some所参考的物件多了doOther()方法可以操作。(introduction解释为传入)
Introduction是个特别的Advice,从使用者的角度来看,它“影响了目标物件的行为定义,直接增加了目标物件的职责(具体来说就是增加了可操作的方法)”,例如让某个已定义好的物件,在不修改该物件之类别档案的情况下,却可以增加一些额外的操作方法到物件之上。
就Java程式语言类别设计的观点来说,动态为物件增加可操作的方法显得不可思议,事实上在Spring AOP中,您可以透过实作org.springframework.aop.IntroductionInterceptor来实现Introduction。
IntroductionInterceptor继承了MethodInterceptor与DynamicIntroductionAdvice介面,其中implementsInterface()方法(继承自DynamicIntroductionAdvice)如果返回true的话,表示目前的 IntroductionInterceptor实作了给定的介面(也就是要额外增加行为的介面),此时您要使用invoke()呼叫介面上的方法,让目标物件执行额外的行为,您不可能使用MethodInvocation的proceed()方法,因为您要执行的是物件上原来没有的行为,呼叫 proceed()方法没有意义。
从文字上来理解Introduction会比较抽象,举个实际的例子来说,假设您的系统中已经有以下的类别:
* ISome.java
package onlyfun.caterpillar;
public interface ISome {
public void doSome();
}
* Some.java
package onlyfun.caterpillar;
public class Some implements ISome {
public void doSome() {
System.out.println("原来物件的职责。。。");
}
}
您希望在不修改原始档案的情况下,为Some类别增加一些可操作的方法,也许您甚至连原始码档案都没有,只有.class档案,您唯一知道的也许是他们的API说明,在不对它们作出修改的情况下,您希望Some类别可以增加doOther()方法。
在Spring中,您可以藉由实作IntroductionInterceptor介面来完成上面的任务,首先您为doOther()方法建立介面:
* IOther.java
package onlyfun.caterpillar;
public interface IOther {
public void doOther();
}
接着定义一个OtherIntroduction类别实作IntroductionInterceptor介面,并在实作IntroductionInterceptor介面的同时,也实作IOther介面,例如:
* OtherIntroduction.java
package onlyfun.caterpillar;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.IntroductionInterceptor;
public class OtherIntroduction
implements IntroductionInterceptor, IOther {
// 是否实作自IOther介面
public boolean implementsInterface(Class clazz) {
return clazz.isAssignableFrom(IOther.class);
}
public Object invoke(MethodInvocation methodInvocation)
throws Throwable {
// 如果呼叫的方法来自IOther介面的定义
if(implementsInterface(methodInvocation.getMethod().getDeclaringClass())) {
// 呼叫执行额外加入(mixin)的行为
return methodInvocation.getMethod().invoke(this, methodInvocation.getArguments());
}
else {
return methodInvocation.proceed();
}
}
public void doOther() {
System.out.println("增加的职责。。。");
}
}
接着您要在Bean定义档中将Introduction缝合至Some物件之上,使用org.springframework.aop.support.DefaultIntroductionAdvisor就可以了,例如:
* beans-config.xml
<?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="some"
class="onlyfun.caterpillar.Some"/>
<bean id="otherIntroduction"
class="onlyfun.caterpillar.OtherIntroduction"/>
<bean id="otherAdvisor"
class="org.springframework.aop.support.DefaultIntroductionAdvisor">
<constructor-arg index="0">
<ref bean="otherIntroduction"/>
</constructor-arg>
<constructor-arg index="1">
<value>onlyfun.caterpillar.IOther</value>
</constructor-arg>
</bean>
<bean id="proxyFactoryBean"
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<value>onlyfun.caterpillar.ISome</value>
</property>
<property name="target">
<ref bean="some"/>
</property>
<property name="interceptorNames"> DefaultIntroductionAdvisor的bean id
<list>
<value>otherAdvisor</value>
</list>
</property>
</bean>
</beans>
DefaultIntroductionAdvisor在建构时,需要给它IntroductionInterceptor的实例,以及所要代理额外行为的介面,现在,来撰写一个简单的程式测试,从这个程式当中,您可以更进一步了解何谓为物件额外增加行为:
* SpringAOPDemo.java
package onlyfun.caterpillar;
import org.springframework.context.ApplicationContext;
import org.springframework.context.
support.FileSystemXmlApplicationContext;
public class SpringAOPDemo {
public static void main(String[] args) {
ApplicationContext context =
new FileSystemXmlApplicationContext("beans-config.xml");
ISome some = (ISome) context.getBean("proxyFactoryBean");
some.doSome();
// 看来好像some物件动态增加了职责
((IOther) some).doOther();
}
}
对于some所参考的物件来说,它原先并不会有doOther()方法可供操作,然而透过Spring AOP的Introduction机制,现在some所参考的物件多了doOther()方法可以操作。(introduction解释为传入)