<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <!-- saved from url=(0032)http://www.onjava.com/lpt/a/5303 --> <HTML><HEAD><TITLE>ONJava.com: An Introduction to Aspect-Oriented Programming with the Spring Framework, Part 2</TITLE> <META http-equiv=Content-Type content="text/html; charset=euc-kr"> <SCRIPT language=javascript src="/common.js"></SCRIPT>

<META content=NOARCHIVE name=GOOGLEBOT> <META content="NOINDEX, NOFOLLOW" name=ROBOTS> <META content="MSHTML 6.00.2800.1400" name=GENERATOR></HEAD> <BODY text=#000000 bgColor=#ffffff><A href="http://www.onjava.com/"><IMG height=82 alt=ONJava.com src="http://onjava.com/images/onjava/onjava_logo.jpg" width=294 align=left border=0></A> <DUMMY>   </DUMMY> <BR clear=all> <FONT face=verdana,arial,helvetica size=1>Published on <B><A href="http://www.onjava.com/">ONJava.com</A></B> (<A href="http://www.onjava.com/">http://www.onjava.com/</A>)<BR><!-- ---------- End of PERL --------------------- --> http://www.onjava.com/pub/a/onjava/2004/10/20/springaop2.html<BR> <A href="http://www.onjava.com/pub/a/general/print_code.html">See this</A> if you're having trouble printing code examples</FONT><BR><BR clear=all><!-- CS_PAGE_BREAK --> <H2><IMG height=91 alt="An Introduction to Aspect-Oriented Programming with the Spring Framework, Part 2" hspace=10 src="http://www.onjava.com/onjava/2004/07/14/graphics/111-spring_aop.gif" width=111 align=left border=0> An Introduction to Aspect-Oriented Programming with the Spring Framework, Part 2</H2>by <A href="http://www.onjava.com/pub/au/1725">Russell Miles</A><BR>10/20/2004<BR clear=all><!-- sidebar begins --><!-- don't move sidebars --><!-- sidebar ends --> <P>이 연재물의 <A href="http://openframework.or.kr/JSPWiki/Wiki.jsp?page=AOPSpring1">part one</A>에서, 관점지향으로 작성한 "HelloWorld"들(tracing, logging)을 구현하는 방법을 여러분에게 보여 줬다.

<A href="http://www.springframework.org/">Spring framework</A>이 제공하는 Aspect-Oriented Programming (AOP)를 이용하여, before-, after-, exception-based advice을 사용하는 방법을 알아봤다. 그리고 간단한 정규식 표현 기반의 pointcut들을 사용하는 방법도 역시 살펴보았다. 이전 기사의 tracing, logging은 여러분에게 시작점을 주기 위해 제공된 예제들이라면, 이번 기사는 새로운 형태의 advice를 살펴봄으로서 몇개의 더 진보된 과정들을 수행한다. 그 새로운 형태의 advice는 다음과 같다.: <CODE>around</CODE> advice.</P> <P><CODE>around</CODE>형태의 Advice는 part one에서 보여줬던것 보다 더 멋진 AO개념이다. 이 기사는 각각의 <CODE>around</CODE> advice형태들을 설명한다. 그래서 여러분 자신의 Spring AOP application들 안에 <CODE>around</CODE> advice를 신중하게 그리고 정확하게 사용할수 있게 하기 위함이다. 이 기사는 어떠한 방법으로 <CODE>around</CODE> advice가 여러분의 애플리케이션들 내부에서 서로 작용(Cuckoo's Egg aspect-oriented design patternd을 구현하기 위해)하는 방식을 사이에 가로 채거나 변경하기 위해 advice 특징들이 어떻게 사용되어지는가를 여러분들에게 보여주는 것의로 결론 맺을 것이다.</P> <H3>A Quick Recap on Spring AOP, IoC, and Proxies</H3> <P><A href="http://openframework.or.kr/JSPWiki/Wiki.jsp?page=AOPSpring1">part one</A>에서, Spring이 AOP를 어떠한 방식으로 구현하는 것에 대한 상세한 내용을 깊이 있게 다루지 못하고 Spring의 AOP특징들의 몇가지를 quick tour방식으로 여러분들과 진행했다. Spring framework이 수행하는 업무, 특히 AOP 기능들,을 어떻게 처리하는지 이해하기 위해서, 먼저 여러분들은 Spring이 Inversion of Control (IoC) design pattern에 기반하는 lightweight framework(경량 프레임웍)라는 것을 이해해야 한다.</P> <P><EM>Reader's Note: 이 기사는 IoC pattern에 관해 아주 깊이 있게 다루려는데 목적을 두지 않는다: 단지, 이 기사의 목적은 여러분들에게 IoC 디자인 패턴이 Spring AOP 구현에 어떻게 영향을 주었는지에 대한 이해를 주기 위함이다. IoC pattern에 대해 더 자세한 설명을 위해서는 이기사의 끝에 <A href="http://www.onjava.com/pub/a/onjava/2004/10/20/springaop2.html?page=2#resources">Resources</A>를 참조하기 바란다.</EM></P> <P>IoC design pattern은 근간에 주위에서 늘 있어 왔다. 가장 분명한 예들 중에 하나는 J2EE 아키텍쳐에서 그 자체이다. Enterprise 개발의 출현에 따라, 그리고 특별한 J2EE platform에서, 애플리케이션들은 외부 container에 의해 제공되는 것 bean의 생성, persistence, messaging, sessions, transaction management와 같은 기능들에 의존하기 시작하였다.</P> <P>IoC는 J2EE container와 많은 유사점이 있는 <EM>framework of components</EM>의 개념을 이야기 하고 있다. IoC framework은 여러분의 component들이 종속적이고 ,Sam Newman의 기사를 인용하면, "component 연결을 위한 딱풀(glue for connecting the components)"로 제공하는 기능들을 나누어 놓는다.</P> <P>여러분의 component들이 의존하고 있는 기능의 <EM>제어(control)</EM>는 <EM>역행화된다.(is inverted)</EM> 그로인해 외부 framework은 최대한 투명성있게 기능들을 제공할수 있다. Componet들이 의존하고 있는 기능들은 전통적인 component들이 책임을 지고있다. 하지만 분화된 framework이 이러한 기능들을 제공하고 설정한다는 것을 IoC pattern은 명시적으로 인지하고 있다.</P> <P>그림 1 IoC pattern을 구성하고 있는 다른 component 역할들의 예를 보여준다.</P> <P><IMG height=216 alt="Figure 1" src="http://www.onjava.com/onjava/2004/10/20/graphics/fig1.png" width="534"><BR><I>Figure 1. Sequence diagram showing when no aspects are applied to the BusinessLogic bean.</I></P> <P>여러분의 컴포넌트들로 부터 서비스 제어의 디커플링화 시키기 위해서 IoC 패턴은 3가지 다른 접근법들을 사용한다 : Type 1, Type 2, and Type 3.</P> <UL> <LI> <P><STRONG>Type 1: Interface Injection</STRONG><BR>이는 많은 J2EE 구현체들에게서 사용되어지고 있다. 명시적으로 여러분의 컴포넌트들은 설정 메타데이타와 관련된 interface set을 따른다. 이런 방식은 framework이 올바르게 여러분의 컴포넌트들을 관리하기 위해서 이다.</P> <LI> <P><STRONG>Type 2: Setter Injection</STRONG><BR>여러분의 컴포넌트들이 어떻게 상호작용될수 있지를 설정하기 위해 외부 메타데이타를 사용한다. <A href="http://www.onjava.com/pub/a/onjava/2004/07/14/springaop.html">part one</A>에서 여러분은 어떻게 여러분의 Spring 컴포넌트가 <EM>springconfig.xml</EM>를 사용해서 설정되어졌는지 알아보았다. 이는 IoC 방식의 Setter Injection를 정확히 사용하고 있는 예이다.</P> <LI> <P><STRONG>Type 3: Constructor Injection</STRONG><BR>여러분의 컴포넌트는 framework에 의해 등록되어지며, 그 컴포넌트들이 생성되어질때 사용되어지기 위해 파라미터들을 포함하며, 그리고 framework은 기술적용된 모든 기능들을 가지고 있는 컴포넌트의 인스턴스들을 제공한다.</P></LI></UL> <P>IoC는 컴포넌트 개발 및 엔터프라이즈 개발에 있어서 점진적으로 호응을 얻고있다는 것을 입증하고 있다. 실예로 IoC의 몇몇 예들로서는 전통적인 J2EE 솔루션들이다. 예를 들어 <A href="http://www.jboss.org/">JBoss</A>, Apache Foundation의 <A href="http://avalon.apache.org/">Avalon</A> project, 그리고 이 기사의 주제인 Spring framework이 그 실예가 될수 있다. 사실, Spring framework은 IoC 패턴으로 <EM>구축</EM>되어있다. 이는 spring framework이 자신의 lightweight기능들을 spring framework에 의존적인 애플리케이션의 컴포넌트로 삽입하는 것을 돕기 위함이다.</P> <P>그럼, IoC가 Spring AOP에 가지는 의미는 무엇일까? Spring에서 IoC특성들은 여러분의 애플리케이션에 aspects를 적용하기 위해 IoC <EM>springconfig.xml</EM>설정 파일을 사용하는것 이면에 있는 motivation들 중의 하나이다. <EM>springconfig.xml</EM>설정은 Spring framework runtime시에 여러분의 애플리케이션의 컴포넌트들이 삽입되어지는 기능의 타입들에 대해여 알려준다. 그리고 경량 AOP 기능들이 같은 방식으로 적용되는 것은 당연한 것이다. 그러면 Spring은 여러분의 class들과 bean들 주변에 지시한 해당 AOP의 특징들을 구현하기 위해 Proxy패턴을 사용한다.</P> <P>그림 2은 어떻게 Spring, Sping IoC framework이 <EM>springconfig.xml</EM>파일에서 발견된 IoC 설정에 따라 proxy object들을 사용하여 AOP 기능들을 제공하는지를 보여주고 있다.</P> <P><A href="http://www.onjava.com/onjava/2004/10/20/graphics/fig2_full.png" target=_new><IMG height=292 alt="Click for larger view" src="http://www.onjava.com/onjava/2004/10/20/graphics/fig2.png" width=432 border=0></A><BR><I>Figure 2. The </I>springconfig.xml<I> configuration influencing the Spring framework IoC in order to then supply AOP proxies into one of the sequence diagrams from part one (Click for full-sized image)</I></P> <P>여러분은 이 연재물의 나머지부분 전체에 있는 시퀀스 다이어그램들상에 proxy object들이 포함된것을 볼수 있을 것이다. 이는 단지 Spring AOP에 "마술"이 없다는것을 보여주기 위함이며, 실예로서 단지object-oriented design pattern들의 좋은 예제 일뿐이다.</P> <H3>Back to AOP: <CODE>around</CODE> Advice를 이용한 능동적 Aspects(이하 Active Aspects로 표현함.)</H3> <P>part one에서 여러분들은 어떠한 방법으로 tracing과 logging이 Spring AOP를 이용하여 구현되어질수 있는지를 알아 보았다. Tracing 과 logging은 모두 수동적 aspects 이며,(이하 <EM>passive</EM> aspects로 표현함.) 애플리케이션에서 passiv aspects의 존재는 애플리케이션의 행동중 나머지부분에 대해서는 영향을 주지 못한다.; tracing과 logging aspects 둘다 adivce의 형태중에 passiv <CODE>before</CODE> 와 <CODE>after</CODE>를 사용했다.</P> <P>그러나 만약 여러분의 애플리케이션의 일반적인 행동을 바꾸기 원하신다면 어떻게 하겠는가? 예를들어 만약 여러분이 실제로 method를 오버라이드하기 원한다면 어떻게 하겠는가? 이러한것들을 이루기 위해 여러분들은 더욱더 advice의 <EM>active</EM> <CODE>around</CODE>형태것을 사용해야한다.</P> <P>아래에 보이는것 처럼, <A href="http://www.onjava.com/pub/a/onjava/2004/07/14/springaop.html">part one</A>에서 간단한 예제 애플리케이션은 <CODE>IBusinessLogic</CODE> interface, <CODE>BusinessLogic</CODE> class, 그리고 <CODE>MainApplication</CODE> class들를 갖고 있다.</P> <PRE><CODE>public interface IBusinessLogic { public void foo(); }

public class BusinessLogic implements IBusinessLogic { public void foo() { System.out.println( "Inside BusinessLogic.foo()"); } }

import org.springframework.context.ApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext;

public class MainApplication { public static void main(String args) { // Read the configuration file ApplicationContext ctx = new FileSystemXmlApplicationContext( "springconfig.xml");

//Instantiate an object IBusinessLogic testObject = (IBusinessLogic) ctx.getBean( "businesslogicbean");

// Execute the public // method of the bean testObject.foo(); } }</CODE></PRE> <P><CODE>BusinessLogic</CODE> class의 인스턴스에서 <CODE>foo()</CODE> method에 호출을 override 하기 위해, 아래 <CODE>AroundAdvice</CODE> class에서 보여지는것과 같이 여러분은 어떠한 <CODE>around</CODE> advice를 생성해야만 한다.</P> <PRE><CODE>import org.aopalliance.intercept.MethodInvocation; import org.aopalliance.intercept.MethodInterceptor;

public class AroundAdvice implements <STRONG>MethodInterceptor</STRONG> { <STRONG>public Object invoke( MethodInvocation invocation) throws Throwable {</STRONG> System.out.println( "Hello world! (by " + this.getClass().getName() + ")");

return null; } }</CODE></PRE> <P>Spring에서 <CODE>around</CODE> advice의 역할로서 사용되어지기 위해서, <CODE>AroundAdvice</CODE> class는 <CODE>MethodInterceptor</CODE> interface와 그것의 유일한 <CODE>invoke(..)</CODE> method를 구현해야만 한다. override된 method가 간섭받을때마다 <CODE>invoke(..)</CODE> method를 호출한다. 여러분의 애플리케이션에 <CODE>AroundAdvice</CODE>를 적용하기 위한 마지막 과정은 그 애플리케이션의 <EM>springconfig.xml</EM>파일에 포함된 Spring runtime 설정을 변경하는것이다.</P> <PRE><CODE><?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">

<beans>

<!-- Bean configuration --> <bean id="businesslogicbean" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces"> <value>IBusinessLogic</value> </property> <property name="target"> <ref local="beanTarget"/> </property> <property name="interceptorNames"> <list> <value><STRONG>theAroundAdvisor</STRONG></value> </list> </property> </bean> <!-- Bean Classes --> <bean id="beanTarget" class="BusinessLogic"/>

<!-- Advisor pointcut definition for around advice --> <bean id="<STRONG>theAroundAdvisor</STRONG>" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <property name="advice"> <ref local="<STRONG>theAroundAdvice</STRONG>"/> </property> <property name="pattern"> <value>.*</value> </property> </bean> <!-- Advice classes --> <bean id="<STRONG>theAroundAdvice</STRONG>" class="<STRONG>AroundAdvice</STRONG>"/>

</beans></CODE></PRE> <P>위 <I>springconfig.xml</I>설정에 따르면, <CODE>theAroundAdvisor</CODE>는 <CODE>BusinessLogic</CODE> class에 있는 메소드들에 대해 모든 호출을 간섭하게된다. 다음으로, 어떠한 메소드를 간섭할때 <CODE>AroundAdvice</CODE> class 내부에 기술되어진 advice를 사용하도록 인지시키기위해 <CODE>theAroundAdvice</CODE>에 <CODE>theAroundAdvisor</CODE>를 덧붙이게 된다.

다음, <CODE>theAroundAdvice</CODE>에 <CODE>theAroundAdvisor</CODE>를 붙인다. 이는 어떤 메소드를 가로챌(intercept)때 <CODE>AroundAdvice</CODE> class 안에 작성된 충고(advice)를 사용 되도록 지시하기 위함이다.

<CODE>around</CODE> advice에 대하여 올바른 설정을 작성 하였다. 다음 순서는 <CODE>MainApplication</CODE> class를 실행한다. Figure 3에서 보는 것 처럼 <CODE>BusinessLogic</CODE> bean의 <CODE>foo()</CODE>메소드를 가로채고 오버라이드 할 것이다.</P> <P><IMG height=223 alt="Figure 3" src="http://www.onjava.com/onjava/2004/10/20/graphics/fig3.png" width=550><BR><I>Figure 3. Using <CODE>around</CODE> advice to override the call to the <CODE>foo()</CODE> method in the <CODE>BusinessLogic</CODE> class</I></P> <P>이전 예제에서 <CODE>AroundAdvice</CODE> class 안에 <CODE>invoke(..)</CODE> 메소드가 <CODE>BusinessLogic</CODE> class 안에 <CODE>foo()</CODE> 메소드를 완전히 오버라이드한 것을 보았다. <CODE>invoke(..)</CODE> method는 원래의 <CODE>foo()</CODE> 메소드를 전혀 실행(invoke) 하지 않았다. 실제적으로 여러분들이 <CODE>around</CODE> advice 안쪽에서 부터 <CODE>foo()</CODE> 메소드를 실행되길 원한다면, 여러분은 <CODE>invoke(..)</CODE> 메소드 상에 <CODE>MethodInvocation</CODE> 인자에서 사용 가능한 <CODE>proceed()</CODE> 메소드를 사용할 수 있다.</P><PRE><CODE>public class AroundAdvice implements MethodInterceptor { public Object invoke( <STRONG>MethodInvocation invocation</STRONG>) throws Throwable { System.out.println( "Hello world! (by " + this.getClass().getName() + ")");

<STRONG>invocation.proceed();</STRONG>

System.out.println("Goodbye! (by " + this.getClass().getName() + ")");

return null; } }</CODE></PRE> <P>그림 3에서 보여진 원래의 <CODE>around</CODE> advice 실행과 비교 했을 때, 그림 4는 <CODE>proceed()</CODE> 메소드 호출이 수행의 순차 처리에 어떻게 영향을 미치는지 보여준다.</P> <P><IMG height=203 alt="Figure 4" src="http://www.onjava.com/onjava/2004/10/20/graphics/fig4.png" width=550><BR><I>Figure 4. Using <CODE>proceed()</CODE> from within the <CODE>around</CODE> advice to invoke the original method</I></P> <P><CODE>proceed()</CODE> 호출 시점에 진행되는 것은 가로채진 메소드, 이경우에는 <CODE>foo()</CODE> 메소드, 를 실행되느것을 지시하는 것이며, <CODE>MethodInvocation</CODE> object안에 포함된 정보의 사용을 실행하는 것이다. <CODE>MethodInvocation</CODE> class 상에 가용한 다른 메소드들 You can affect this information by calling the other methods available on the <CODE>MethodInvocation</CODE> class.</P> <P>You might want to change the information contained within the <CODE>MethodInvocation</CODE> class in order to set new values to the parameters on the intercepted method prior to it being invoked using <CODE>proceed()</CODE>.</P> <P>The parameters that were originally passed to the intercepted method can be changed by calling the <CODE>getArguments()</CODE> method on the <CODE>MethodInvocation</CODE> object and then by setting one of the parameter objects in the returned array.</P> <P>If the <CODE>foo()</CODE> method on the <CODE>IBusinessClass</CODE> and <CODE>BusinessLogic</CODE> classes was changed to take an integer simple type then you can change the value passed to an intercepted call to <CODE>foo(int)</CODE> from within the <CODE>notify(..)</CODE> method in the <CODE>AroundAdvice</CODE>, as shown below.</P><PRE><CODE>public class AroundAdvice implements MethodInterceptor { public Object invoke( MethodInvocation invocation) throws Throwable { System.out.println( "Hello world! (by " + this.getClass().getName() + ")"); <STRONG>invocation.getArguments()[0] = new Integer(20);</STRONG> invocation.proceed(); System.out.println( "Goodbye! (by " + this.getClass().getName() + ")"); return null; } }</CODE></PRE> <P>In this example, the first parameter on the method intercepted is assumed to be an <CODE>int</CODE>. The arguments themselves are passed as objects, and so the primitive <CODE>int</CODE> type parameter is changed to the new value in the corresponding array by wrapping it in an instance of the <CODE>Integer</CODE> class. If you set the parameter to a value that is other than an <CODE>Integer</CODE> object, then you will get an <CODE>IllegalArgumentException</CODE> thrown at runtime.</P> <P>You will also notice that the <CODE>invoke(..)</CODE> method must contain a return statement, as the <CODE>method</CODE> requires a return value. However, the <CODE>foo()</CODE> method that is being overridden does not return an object, and so the <CODE>invoke(..)</CODE> method can be closed by returning a <CODE>null</CODE>. If you do return an object, even though the <CODE>foo()</CODE> method does not require one, then this object would be ignored.</P> <P>If the <CODE>foo()</CODE> method did require a return value, then you would need to return either an object of the same class, or a subclass, of the <CODE>foo()</CODE> method's original return type. If the <CODE>foo()</CODE> method returns a simple type, an <CODE>integer</CODE> for instance, then you will need to return an object of the <CODE>Integer</CODE> class, which will be automatically unboxed by the AOP proxy when the method is overridden, as shown in Figure 5.</P> <P><IMG height=294 alt="Figure 5" src="http://www.onjava.com/onjava/2004/10/20/graphics/fig5.png" width=550><BR><I>Figure 5. Boxing and auto-unboxing of return values from <CODE>around</CODE> advice</I></P> <TABLE cellSpacing=0 cellPadding=0 width="100%" border=0> <TBODY> <TR> <TD> <P class=secondary><!-- CS_PAGE_INDEX --></P></TD> <TD> <P class=secondary align=right><A href="http://www.onjava.com/lpt/a/<!--CS_NEXT_REF-->"></A></P></TD></TR></TBODY></TABLE><!-- CS_PAGE_BREAK --> <P><!-- CS_PAGE_INDEX --></P> <H3>Using Spring AOP to Implement the Cuckoo's Egg Design Pattern</H3> <P>Aspect-oriented programming is still a fairly young discipline, especially when compared to its evolutionary parent: object-oriented programming. Design patterns are usually understood to be generic solutions to common problems and, as AO is so young, there are relatively few aspect-oriented design patterns yet discovered.</P> <P>One pattern of usage that is starting to emerge I am calling here the <EM>Cuckoo's Egg</EM> design pattern. This pattern has other aliases and object-oriented peers including Mock Object and Mock Testing; even the Proxy pattern has certain similarities.</P> <P>The Cuckoo's Egg aspect-oriented design pattern can be defined as the transparent and modular replacement of a <EM>feature</EM> within an application's context. Much as a cuckoo deposits its own egg into another bird's nest surreptitiously, the Cuckoo's Egg design pattern deposits a replacement feature implementation in place of an existing feature, causing as little disturbance as possible.</P> <P>This replacement can be implemented statically, dynamically, partially, fully, across parts of an object, or across component boundaries. Using AO the feature replacement is achieved transparently without the need for any changes throughout the rest of the application. The replacement feature, which is to take the place of an existing feature within the application, fulfills the cuckoo's-egg role. Figure 6 shows the main components of the Cuckoo's Egg design pattern.</P> <P><IMG height=278 alt="Figure 6" src="http://www.onjava.com/onjava/2004/10/20/graphics/fig6.png" width=527><BR><I>Figure 6. The main roles in the Cuckoo's Egg design pattern</I></P> <P>The Cuckoo's Egg design pattern relies on the concept of <CODE>around</CODE> advice. You need to have the power of the active and intrusive <CODE>around</CODE> advice in order to intercept and effectively replace an existing feature within an application.</P> <P>For more information on the Cuckoo's Egg design pattern, and an alternative implementation in AspectJ, see the <A href="http://www.oreilly.com/catalog/aspectjckbk/">AspectJ Cookbook</A> (O'Reilly), due for release in December 2004.</P> <P>To implement the Cuckoo's Egg design pattern using Spring AOP, you need to declare an <CODE>around</CODE> advice that intercepts all of the calls to the feature that is to be replaced. Unlike <A href="http://www.springframework.org/docs/reference/aop.html#aop-ts-swap">hot-swappable target sources</A>, a feature of Spring AOP that will be covered in another article in this series, the explicit use of <CODE>around</CODE> advice allows your Cuckoo's Egg implementation to effectively cross object boundaries (and therefore bean boundaries) to deal with an entire feature's replacement, as shown in Figure 7.</P> <P><IMG height=285 alt="Figure 7" src="http://www.onjava.com/onjava/2004/10/20/graphics/fig7.png" width=293><BR><I>Figure 7. A component crossing bean boundaries</I></P> <P>The following code shows a simple application with two beans where a feature spans multiple areas of the application. The feature to be replaced can be thought of as encompassing the <CODE>foo()</CODE> method on the <CODE>IBusinessLogic</CODE> bean and the <CODE>bar()</CODE> method on the <CODE>IBusinessLogic2</CODE> bean. The <CODE>baz()</CODE> method on the <CODE>IBusinessLogic2</CODE> bean is <EM>not</EM> part of the feature and so is not included in the replacement.</P><PRE><CODE>public interface IBusinessLogic { public void foo(); }

public interface IBusinessLogic2 { public void bar(); public void baz(); }</CODE></PRE> <P>The full source code for this example is available for download in the <A href="http://www.onjava.com/lpt/a/5303#resources">Resources</A> section at the end of this article.</P> <P>The <CODE>ReplacementFeature</CODE> class takes the role of the cuckoo's egg by providing the replacement implementation that is to be transparently introduced into your application. The <CODE>ReplacementFeature</CODE> class implements all of the methods that are to be replaced through its introduction.</P><PRE><CODE>public class ReplacementFeature { public void foo() { System.out.println( "Inside ReplacementFeature.foo()"); } public void bar() { System.out.println( "Inside ReplacementFeature.bar()"); } }</CODE></PRE> <P>You now need to declare some <CODE>around</CODE> advice to intercept calls to the cross-bean feature's methods. The <CODE>CuckoosEgg</CODE> class provides some <CODE>around</CODE> advice that examines the method that has been intercepted and then passes the appropriate method call to the instance of the <CODE>ReplacementFeature</CODE> class.</P><PRE><CODE>public class CuckoosEgg implements MethodInterceptor { public ReplacementFeature replacementFeature = new ReplacementFeature(); public Object invoke(MethodInvocation invocation) throws Throwable { if (invocation.getMethod().getName().equals("foo")) { replacementFeature.foo(); } else { replacementFeature.bar(); } return null; } }</CODE></PRE> <P>The details of the Cuckoo's Egg design, as with so much in the Spring framework, is in the <EM>springconfig.xml</EM> configuration file. The changes to the <EM>springconfig.xml</EM> file simply ensure that any calls made to the <CODE>foo()</CODE> and <CODE>bar()</CODE> methods on the <CODE>IBusinessLogic</CODE> and <CODE>IBusinessLogic2</CODE> beans are intercepted and channeled to the <CODE>CuckoosEgg</CODE> class's <CODE>around</CODE> advice.</P><PRE><CODE> ... <!--CONFIG--> <bean id="businesslogicbean" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces"> <value><STRONG>IBusinessLogic</STRONG></value> </property> <property name="target"> <ref local="beanTarget"/> </property> <property name="interceptorNames"> <list> <value><STRONG>theCuckoosEggAdvisor</STRONG></value> </list> </property> </bean> <bean id="businesslogicbean2" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces"> <value><STRONG>IBusinessLogic2</STRONG></value> </property> <property name="target"> <ref local="beanTarget2"/> </property> <property name="interceptorNames"> <list> <value><STRONG>theCuckoosEgg2Advisor</STRONG></value> </list> </property> </bean>

<!--CLASS--> <bean id="beanTarget" class="BusinessLogic"/> <bean id="beanTarget2" class="BusinessLogic2"/> <!--ADVISOR--> <bean id="<STRONG>theCuckoosEggAdvisor</STRONG>" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <property name="advice"> <ref local="<STRONG>theReplacementFeaturePart1Advice</STRONG>"/> </property> <property name="pattern"> <value><STRONG>IBusinessLogic.*</STRONG></value> </property> </bean> <bean id="<STRONG>theCuckoosEgg2Advisor</STRONG>" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <property name="advice"> <ref local="<STRONG>theReplacementFeaturePart2Advice</STRONG>"/> </property> <property name="pattern"> <value><STRONG>IBusinessLogic2.bar*</STRONG></value> </property> </bean> <!--ADVICE--> <bean id="<STRONG>theReplacementFeaturePart1Advice</STRONG>" class="<STRONG>CuckoosEgg</STRONG>"/> <bean id="<STRONG>theReplacementFeaturePart2Advice</STRONG>" class="<STRONG>CuckoosEgg</STRONG>"/> ...</CODE></PRE> <P>When the example application is run using the amended <EM>springconfig.xml</EM>, the method calls designated as part of the feature to be replaced are properly intercepted and passed to the <CODE>ReplacementFeature</CODE> class.</P> <P>You can always implement a design pattern in different ways, even within the same implementation environment. Another way of implementing the example shown above would be to implement two separate pieces of advice.</P> <P>Finally, you need to take care that the lifecycle of the feature that you are replacing using the Cuckoo's Egg design pattern, be it cross-bean or within one class, matches the object lifecycle of the feature it is replacing. In the example above there is no problem, as there is only one instance of the feature being replaced, and the singleton Cuckoo's Egg advice maintains only one replacement feature.</P> <P>This is a very simple example and it is likely that you will have to deal with a number of feature instances that need to be replaced by separate Cuckoo's Egg instances. In this case, an individual aspect instance needs to be tied to an individual instance of the feature being replaced. The next article in this series will attack this problem when it considers the use of aspect lifecycles.</P> <H3>Conclusion</H3> <P>This article has shown how to carefully use the active <CODE>around</CODE> form of advice inside of the Spring framework. The <CODE>around</CODE> form of advice is commonly used when implementing the Cuckoo's Egg design pattern, and so you were shown one example of how this aspect-oriented design pattern can be implemented using Spring AOP.</P> <P>In the third article in this series, you will be shown how to use more of the fundamental AOP concepts available in the Spring framework. These concepts will include controlling aspect lifecycles, affecting the static structure of your application using active aspects based on <CODE>introduction</CODE> advice, and gaining even finer control over your aspect weaving using the <CODE>control flow</CODE> pointcut.</P> <H3><A name=resources>Resources</A></H3> <UL> <LI><A href="http://javaboutique.internet.com/tutorials/spring_frame">A Spring framework tutorial that shows you how things work under the skin of Spring</A>

<LI><A href="http://www.onjava.com/onjava/2004/10/20/examples/springaoppart2source.zip">The complete source code to accompany this article</A> <LI><A href="http://www.springframework.org/">The Spring Java/J2EE framework</A> <LI>"<A href="http://martinfowler.com/articles/injection.html">Comparing the Inversion of Control and Dependency Injection Design Patterns</A>" <LI><A href="http://today.java.net/pub/a/today/2004/02/10/ioc.html">An explanation of the IoC pattern</A> <LI><A href="http://www.springframework.org/docs/wiki/Spring_AOP_Framework.html">Chapter 5 of the Spring framework documentation, covering the full suite of AOP features in Spring</A> </LI></UL> <P><I><A href="http://www.onjava.com/pub/au/1725">Russell Miles</A> works as a software engineer for General Dynamics UK, where he works with Java and distributed systems, although his passion at the moment is aspect orientation and, in particular, AspectJ. </I></P> <HR noShade SIZE=1>

<P>Return to <A href="http://www.onjava.com/">ONJava.com</A>.</P> <P><FONT face=verdana,arial,helvetica size=1>Copyright ?2004 O'Reilly Media, Inc.</FONT></P></BODY></HTML>

Add new attachment

Only authorized users are allowed to upload new attachments.
« This page (revision-6) was last changed on 30-Jun-2006 18:24 by 219.251.5.1