Framework Services for AOP

Up to now, we have had to write a lot of code to advise objects and generate the proxies for them. Although this in itself is not a huge problem, it does mean that all advice configuration is hardcoded into your application, removing some of the benefits of being able to advise a method implementation transparently. Thankfully, Spring provides additional framework services that allow you to create an advised proxy in your application configuration and then inject this proxy into a target bean just like any other dependency.

Using the declarative approach to AOP configuration is preferable to the manual, program- matic mechanism. When you use the declarative mechanism, you not only externalize the configuration of advice, but you also reduce the chance of coding errors. You can also take advantage of DI and AOP combined to enable AOP so it can be used in a completely transparent environment.

Configuring AOP Declaratively

Declarative configuration of AOP does not require any special configuration wizardry. AOP configuration is just like standard bean configuration; the only difference is that instead of exposing your bean directly, you use the ProxyFactoryBean class, which implements FactoryBean, to create a proxy automatically.

Introducing ProxyFactoryBean

The ProxyFactoryBean class is the central class for declarative AOP configuration. It is an implementation of FactoryBean that allows you to specify a bean to target and it provides a set of advice and advisors for that bean that are eventually merged into an AOP proxy. Because you can use both advisor and advice with the ProxyFactoryBean, you can configure not only the advice declaratively, but the pointcuts as well.

ProxyFactoryBean shares a common interface with ProxyFactory and ProxyConfig, and as a result, it exposes many of the same flags such as frozen, optimize, and exposeProxy. The values for these flags are passed directly to the underlying ProxyFactory, which allows you to configure the factory declaratively as well.

ProxyFactoryBean in Action

Using ProxyFactoryBean is actually very simple. You define a bean that will be the target bean and then using ProxyFactoryBean, you define the bean that your application will actually access, using the target bean as the proxy target. Where possible, define the target bean as an anonymous bean inside the proxy bean declaration. This prevents your application from accidentally accessing the unadvised bean. However, in some cases, such as the sample we are about to show you, you may want to create more than one proxy for the same bean, so you should use a normal top-level bean for this case.

Listings 7-11 and 7-12 show two classes, one of which has a dependency on the other.

Listing 7-11: The MyDependency Class

image from book
package com.apress.prospring.ch7.pfb;      public class MyDependency {          public void foo() {         System.out.println("foo()");     }          public void bar() {         System.out.println("bar()");     } } 
image from book

Listing 7-12: The MyBean Class

image from book
package com.apress.prospring.ch7.pfb;      public class MyBean {          private MyDependency dep;          public void execute() {         dep.foo();         dep.bar();     }          public void setDep(MyDependency dep) {         this.dep = dep;     } }
image from book

For this example, we are going to create two proxies for a single MyDependency instance, both with the same basic advice shown in Listing 7-13.

Listing 7-13: The MyAdvice Class

image from book
package com.apress.prospring.ch7.pfb;      import java.lang.reflect.Method;      import org.springframework.aop.MethodBeforeAdvice;      public class MyAdvice implements MethodBeforeAdvice {          public void before(Method method, Object[] args, Object target)             throws Throwable {         System.out.println("Executing: " + method);     }      }
image from book

The first proxy will just advise the target using the advice directly, thus all methods will be advised. For the second proxy, we will configure a JdkRegexpMethodPointcut and a DefaultPointcutAdvisor so that only the foo() method of the MyDependency class is advised. To test the advice, we will create two bean definitions of type MyBean, each of which will be injected with a different proxy. Then we will invoke the execute() method on each of these beans and observe what happens when the advised methods on the dependency are invoked.

Listing 7-14 shows the configuration for this example.

Listing 7-14: Declarative AOP Configuration

image from book
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"  "http://www.springframework.org/dtd/spring-beans.dtd"> <beans>     <bean  >         <property name="dep">             <ref local="myDependency1"/>         </property>     </bean>          <bean  >         <property name="dep">             <ref local="myDependency2"/>         </property>     </bean>          <bean             />          <bean             >         <property name="target">             <ref local="myDependencyTarget"/>         </property>         <property name="interceptorNames">             <list>                 <value>advice</value>             </list>         </property>     </bean>          <bean             >         <property name="target">             <ref local="myDependencyTarget"/>         </property>         <property name="interceptorNames">             <list>                 <value>advisor</value>             </list>         </property>     </bean>          <bean  />          <bean             >         <property name="advice">             <ref local="advice"/>         </property>         <property name="pointcut">             <bean >                 <property name="pattern">                     <value>.*foo.*</value>                 </property>             </bean>         </property>     </bean> </beans>
image from book

This code should be familiar to you. Notice that we are not really doing anything special; we are simply setting the properties that we set in code using Spring's DI capabilities. The only points of interest are that we use an anonymous bean for the pointcut and we use the ProxyFactoryBean class. We prefer to use anonymous beans for pointcuts when they are not being shared because it keeps the set of beans that are directly accessible as small and as application-relevant as possible. The important point to realize when you are using ProxyFactoryBean is that the ProxyFactoryBean declaration is the one to expose to your application and the one to use when you are fulfilling dependencies. The underlying target bean declaration is not advised, so you should only use this bean when you want to bypass the AOP framework, although in general, your application should not be aware of the AOP framework and thus should not want to bypass it. For this reason, you should use anonymous beans wherever possible to avoid accidental access from the application.

Listing 7-15 shows a simple class that grabs the two MyBean instances from the ApplicationContext and then runs the execute() method for each one.

Listing 7-15: The ProxyFactoryBeanExample Class

image from book
package com.apress.prospring.ch7.pfb;      import org.springframework.context.ApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext;      public class ProxyFactoryBeanExample {          public static void main(String[] args) {         ApplicationContext ctx = new FileSystemXmlApplicationContext(                 "./ch7/src/conf/pfb.xml");                  MyBean bean1 = (MyBean)ctx.getBean("myBean1");         MyBean bean2 = (MyBean)ctx.getBean("myBean2");                  System.out.println("Bean 1");         bean1.execute();              System.out.println("\nBean 2");         bean2.execute();     } }
image from book

Running this example results in the following output:

Bean 1 Executing: public void com.apress.prospring.ch7.pfb.MyDependency.foo() foo() Executing: public void com.apress.prospring.ch7.pfb.MyDependency.bar() bar()      Bean 2 Executing: public void com.apress.prospring.ch7.pfb.MyDependency.foo() foo() bar()

As expected, both the foo() and bar() methods in the first proxy are advised, because no pointcut was used in its configuration. For the second proxy, however, only the foo() method was advised due to the pointcut used in the configuration.

Using ProxyFactoryBean for Introductions

You can not only use the ProxyFactoryBean class for advising an object, but also for introducing mixins to your objects. Remember from our earlier discussion on introductions that you must use an IntroductionAdvisor to add an introduction; you cannot add an introduction directly. The same rule applies when you are using ProxyFactoryBean with introductions. When you are using ProxyFactoryBean, it becomes much easier to configure your proxies if you created a custom Advisor for your mixin as discussed earlier. Listing 7-16 shows a sample configuration for the IsModifiedMixin introduction we discussed earlier.

Listing 7-16: Configuring Introductions with ProxyFactoryBean

image from book
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"  "http://www.springframework.org/dtd/spring-beans.dtd"> <beans>     <bean             >         <property name="target">             <bean                    >                 <property name="name">                     <value>Rob Harrop</value>                 </property>             </bean>         </property>         <property name="interceptorNames">             <list>                 <value>advisor</value>             </list>         </property>         <property name="proxyTargetClass">             <value>true</value>         </property>     </bean>          <bean             /> </beans>
image from book

As you can see from the configuration, we use the IsModifiedAdvisor class as the advisor for the ProxyFactoryBean, and because we do not need to create another proxy of the same target object, we use an anonymous declaration for the target bean. Listing 7-17 shows a modification of the previous introduction example that obtains the proxy from the ApplicationContext.

Listing 7-17: The IntroductionConfigExample Class

image from book
package com.apress.prospring.ch7.introductions;      import org.springframework.context.ApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext;      public class IntroductionConfigExample {          public static void main(String[] args) {         ApplicationContext ctx = new FileSystemXmlApplicationContext(                 "./ch7/src/conf/introductions.xml");              TargetBean bean = (TargetBean) ctx.getBean("bean");         IsModified mod = (IsModified) bean;              // test interfaces         System.out.println("Is TargetBean?: " + (bean instanceof TargetBean));         System.out.println("Is IsModified?: " + (bean instanceof IsModified));              // test is modified implementation         System.out.println("Has been modified?: " + mod.isModified());         bean.setName("Rob Harrop");         System.out.println("Has been modified?: " + mod.isModified());         bean.setName("Joe Schmoe");         System.out.println("Has been modified?: " + mod.isModified());     } } 
image from book

Running this example yields exactly the same output as the previous introduction example, but this time the proxy is obtained from the ApplicationContext and no configuration is present in the application code.

ProxyFactoryBean Summary

When you use ProxyFactoryBean, you can configure AOP proxies that provide all the flexibility of the programmatic method without needing to couple your application to the AOP configuration. Unless you need to perform decisions at runtime as to how your proxies should be created, it is best to use the declarative method of proxy configuration over the programmatic method.

Using Automatic Proxying

When you use ProxyFactoryBean, you gain an excellent way to create advised beans external to your application. However, the ProxyFactoryBean approach does require some level of configuration, and for applications that make extensive use of AOP, this configuration can be quite time-consuming to create. For this reason, Spring provides some convenience classes to create proxies of beans in your ApplicationContext automatically. Aside from the obvious benefit of reduced coding, this feature also prevents an application from retrieving the unadvised bean, because Spring actually swaps the bean for the proxy in the ApplicationContext.

Here are the three core options you have for autoproxying with Spring:

  • Using the BeanNameAutoProxyCreator class: This class allows you to specify a list of bean names and the set of advice and advisors to apply to them. Spring automatically proxies all beans with the specified advice. In addition, you can use wildcards in the bean names, which enables you to match all beans following a similar naming scheme.

  • Using the DefaultAdvisorAutoProxyCreator: This is an extremely powerful autoproxy class that automatically proxies all beans in your ApplicationContext with any advisor that is relevant. This class only works with advisors, not advice, because it relies on the pointcut of the advisor when determining whether or not the advisor should be applied to a particular bean.

  • Using source level metadata: This is a special case of DefaultAdvisorAutoProxyCreator usage. When using source level metadata, the Advisor used by DefaultAdvisorAutoProxyCreator uses pointcuts that are driven by the source level metadata. For example, when configuring transactions in Spring, you can define the transaction behavior at method level using metadata, and then using DefaultAdvisorAutoProxyCreator, you can have the transactional proxy created automatically for you. We will not be looking at how you use this feature in your own applications, but we do look at using metadata-driven proxying in Chapter 12.

Using BeanNameAutoProxyCreator

The BeanNameAutoProxyCreator is the ideal class when you want to apply the same set of advice to an arbitrary set of beans in your ApplicationContext automatically. BeanNameAutoProxyCreator requires that you specify the names of the beans that are the advice and the names of the beans to be advised, and that's it. When specifying the names of the beans to be advised, you can use wildcards, which allows you to autoproxy a set of beans that follow the same naming structure. In fact, this is one of the best features of BeanNameAutoProxyCreator—it allows you to apply a set of advice uniformly to a set of beans based on the way they are named. Provided that you have some kind of naming convention in place for beans that are related, it is simple to apply advice across whole sets of related beans.

Listing 7-18 shows a simple class, AutoBean, that we are going to autoproxy in this example.

Listing 7-18: The AutoBean Class

image from book
package com.apress.prospring.ch7.autoproxying;      public class AutoBean {          public void foo() {         System.out.println("foo()");     } }
image from book

For this example, we are going to define two beans of type AutoBean in our ApplicationContext and then autoproxy them using BeanNameAutoProxyCreator to add the SimpleBeforeAdvice class that we created earlier. The configuration for this is shown in Listing 7-19.

Listing 7-19: Using BeanNameAutoProxyCreator

image from book
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"  "http://www.springframework.org/dtd/spring-beans.dtd"> <beans>          <bean                   >         <property name="beanNames">              <list>                 <value>foo*</value>                 <value>barBean</value>             </list>             </property>             <property name="interceptorNames">             <list>                 <value>advice</value>             </list>         </property>     </bean>          <bean  />     <bean  />          <bean  /> </beans>
image from book

Here you can see that we have defined two beans, fooBean and barBean, both of type AutoBean. These are the beans we are going to proxy. The advice bean will be the advice that is applied to both beans. Notice that for the proxyCreator bean, we have defined the list of interceptor names as we would with the ProxyFactoryBean. Also supplied to proxyCreator is the list of bean names. We have specified barBean and foo*. The foo* name will match any bean whose name begins with foo.

In Listing 7-20, you can see an example of how the automatically generated proxies are accessed.

Listing 7-20: Accessing the Auto Generated Proxies

image from book
package com.apress.prospring.ch7.autoproxying;      import org.springframework.context.ApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext;      public class BeanNameExample {          public static void main(String[] args) {         ApplicationContext ctx = new FileSystemXmlApplicationContext(         "./ch7/src/conf/bnapc.xml");                  AutoBean fooBean = (AutoBean)ctx.getBean("fooBean");         AutoBean barBean = (AutoBean)ctx.getBean("barBean");                  fooBean.foo();         barBean.foo();     } } 
image from book

Notice that when accessing the beans, we use the bean names as defined in the ApplicationContext configuration. The reason for this is that the BeanNameAutoProxyCreator actually replaces the original bean instance with the automatically generated proxy instance. Running this example results in the following output:

Before method: public void com.apress.prospring.ch7.autoproxying.AutoBean.foo() foo() Before method: public void com.apress.prospring.ch7.autoproxying.AutoBean.foo() foo()

As you can see, both calls to AutoBean.foo() were advised, as we expected. This example only autogenerated two proxies, so you do not really get a full appreciation of the amount of configuration code saved. However, when you need to proxy three or more beans, you really start to notice the configuration savings. Whenever you need to advise a set of beans in an identical manner, the BeanNameAutoProxyCreator is an ideal solution, especially if all your beans share a similar naming structure.

Using DefaultAdvisorAutoProxyCreator

Using the DefaultAdvisorAutoProxyCreator enables you to advise any bean in your ApplicationContext automatically by simply providing the appropriate advisors. The DefaultAdvisorAutoProxyCreator uses the pointcut of each advisor in the ApplicationContext to decide whether it applies to a particular bean. It performs this check for each advisor present and for each bean in your ApplicationContext. If a bean has no applicable advisors in the ApplicationContext, meaning none of the defined pointcuts match any of the joinpoints on the bean, then no proxy is created for it.

For this example we are going to use the AutoBean class from the previous example, but also the OtherBean class as shown in Listing 7-21.

Listing 7-21: The OtherBean Class

image from book
package com.apress.prospring.ch7.autoproxying;      public class OtherBean {          public void foo() {         System.out.println("foo()");     } }
image from book

As you can see, this is just a duplicate of the AutoBean class, but it helps illustrate the part pointcuts play when you are using DefaultAdvisorAutoProxyCreator. The next piece of the puzzle is a pointcut that only matches the AutoBean class and matches all methods on that class. This code is shown in Listing 7-22.

Listing 7-22: The MyPointcut Class

image from book
package com.apress.prospring.ch7.autoproxying;      import org.springframework.aop.ClassFilter; import org.springframework.aop.MethodMatcher; import org.springframework.aop.Pointcut;      public class MyPointcut implements Pointcut {          public ClassFilter getClassFilter() {         return new ClassFilter() {                        public boolean matches(Class cls) {                 return (cls == AutoBean.class);             }         };     }          public MethodMatcher getMethodMatcher() {         return MethodMatcher.TRUE;     } }
image from book

Here you can see a pointcut with a ClassFilter that only matches the AutoBean class and a MethodMatcher that matches all methods. Because of the ClassFilter in this pointcut, any Advisor that is associated with it is only applied to beans of type AutoBean. Listing 7-23 shows a configuration using the DefaultAdvisorAutoProxyCreator.

Listing 7-23: Using DefaultAdvisorAutoProxyCreator

image from book
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"  "http://www.springframework.org/dtd/spring-beans.dtd"> <beans>          <bean   />          <bean  />     <bean  />          <bean             >            <property name="pointcut">                <bean />            </property>            <property name="advice">                <bean />            </property>     </bean> </beans>
image from book

Here you can see that we have set up the advisor bean with an instance of MyPointcut as the pointcut and SimpleBeforeAdvice as the advice. The DefaultAdvisorAutoProxyCreator bean, which requires no configuration beyond its declaration, attempts to advise both autoBean and otherBean automatically with the advisor bean. However, the MyPointcut on the advisor bean prevents it from being applied to otherBean because the ClassFilter only matches the AutoBean class. Listing 7-24 shows an example of accessing both autoBean and otherBean in an application.

Listing 7-24: The DefaultCreatorExample Class

image from book
package com.apress.prospring.ch7.autoproxying;      import org.springframework.aop.support.AopUtils; import org.springframework.context.ApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext;      public class DefaultCreatorExample {          public static void main(String[] args) {         ApplicationContext ctx = new FileSystemXmlApplicationContext(         "./ch7/src/conf/dpac.xml");                  AutoBean autoBean = (AutoBean)ctx.getBean("autoBean");         OtherBean otherBean = (OtherBean)ctx.getBean("otherBean");                  autoBean.foo();         System.out.println(AopUtils.isAopProxy(autoBean));                  otherBean.foo();         System.out.println(AopUtils.isAopProxy(otherBean));     } }
image from book

Here you can see that we grab both autoBean and otherBean from the ApplicationContext and then invoke the foo() method on both. Only the foo() method of autoBean is advised. Notice that we also use the AopUtils class supplied by Spring to test whether or not the two beans are actually AOP proxies. Because otherBean had no valid advisors in the ApplicationContext, it should not have been proxied; the output from this example shows this to be true:

Before method: public void com.apress.prospring.ch7.autoproxying.AutoBean.foo() foo() true foo() false 

As you can see, the only advised method was the foo() method of autoBean, and otherBean was not a proxy. The DefaultAdvisorAutoProxyCreator class is a useful class when you have a set of advisors or pointcuts that are capable of filtering out non-target classes appropriately. However, in a large application, it can become difficult to see exactly where advice will be applied and you may find that you are adding unnecessary advice to your objects.

Autoproxying Summary

Using autoproxying can save you a lot of configuration coding, but it is not as explicit as using ProxyFactoryBean, and it can quickly become confusing as to which beans are being advised, especially when you are using DefaultAdvisorAutoProxyCreator. That said, the BeanNameAutoProxyCreator class is extremely useful for advising beans in large amounts, and it does not make your configuration too confusing. In general, we find that BeanNameAutoProxyCreator is very useful for applying uniform advice across a large selection of beans, but when we are dealing with just one or two beans, we prefer to stick to ProxyFactoryBean, because it makes the configuration much more explicit.



Pro Spring
Pro Spring
ISBN: 1590594614
EAN: 2147483647
Year: 2006
Pages: 189

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net