diff --git a/org.springframework.aop/src/main/resources/org/springframework/aop/config/spring-aop-3.1.xsd b/org.springframework.aop/src/main/resources/org/springframework/aop/config/spring-aop-3.1.xsd index 591baf4a28..0c79746566 100644 --- a/org.springframework.aop/src/main/resources/org/springframework/aop/config/spring-aop-3.1.xsd +++ b/org.springframework.aop/src/main/resources/org/springframework/aop/config/spring-aop-3.1.xsd @@ -72,6 +72,9 @@ diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/AspectJAutoProxyConfigurationSelector.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/AspectJAutoProxyConfigurationSelector.java new file mode 100644 index 0000000000..6b60c7ecab --- /dev/null +++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/AspectJAutoProxyConfigurationSelector.java @@ -0,0 +1,56 @@ +/* + * Copyright 2002-2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.context.annotation; + +import java.util.Map; + +import org.springframework.aop.config.AopConfigUtils; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.core.type.AnnotationMetadata; + +/** + * Registers an {@link org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator + * AnnotationAwareAspectJAutoProxyCreator} against the current {@link BeanDefinitionRegistry} + * as appropriate based on a given @{@link EnableAspectJAutoProxy} annotation. + * + * @author Chris Beams + * @see EnableAspectJAutoProxy + * @since 3.1 + */ +public class AspectJAutoProxyConfigurationSelector implements ImportSelector { + + /** + * Register, escalate, and configure the AspectJ auto proxy creator. Always return + * an empty array, as no actual {@code @Configuration} classes are required. + */ + public String[] selectImports(ImportSelectorContext context) { + BeanDefinitionRegistry registry = context.getBeanDefinitionRegistry(); + AnnotationMetadata importingClassMetadata = context.getImportingClassMetadata(); + + Map enableAJAutoProxy = + importingClassMetadata.getAnnotationAttributes(EnableAspectJAutoProxy.class.getName()); + + AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry); + + if ((Boolean)enableAJAutoProxy.get("proxyTargetClass")) { + AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry); + } + + return new String[] { }; + } + +} diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/Configuration.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/Configuration.java index 12ddd73f4d..5d39ac635c 100644 --- a/org.springframework.context/src/main/java/org/springframework/context/annotation/Configuration.java +++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/Configuration.java @@ -299,8 +299,9 @@ import org.springframework.stereotype.Component; * classes using their respective "{@code @Enable}" annotations. See * {@link org.springframework.scheduling.annotation.EnableAsync @EnableAsync}, * {@link org.springframework.scheduling.annotation.EnableScheduling @EnableScheduling}, - * {@link org.springframework.transaction.annotation.EnableTransactionManagement @EnableTransactionManagement}, and - * {@link org.springframework.web.servlet.config.annotation.EnableWebMvc @EnableWebMvc} + * {@link org.springframework.transaction.annotation.EnableTransactionManagement @EnableTransactionManagement}, + * {@link org.springframework.context.annotation.EnableAspectJAutoProxy @EnableAspectJAutoProxy}, + * and {@link org.springframework.web.servlet.config.annotation.EnableWebMvc @EnableWebMvc} * for details. * *

Constraints when authoring {@code @Configuration} classes

diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/EnableAspectJAutoProxy.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/EnableAspectJAutoProxy.java new file mode 100644 index 0000000000..0316c28857 --- /dev/null +++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/EnableAspectJAutoProxy.java @@ -0,0 +1,113 @@ +/* + * Copyright 2002-2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.context.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Enables support for handling components marked with AspectJ's {@code @Aspect} annotation, + * similar to functionality found in Spring's {@code } XML element. + * To be used on @{@link Configuration} classes as follows: + * + *
+ * @Configuration
+ * @EnableAspectJAutoProxy
+ * public class AppConfig {
+ *     @Bean
+ *     public FooService fooService() {
+ *         return new FooService();
+ *     }
+ *
+ *     @Bean
+ *     public MyAspect myAspect() {
+ *         return new MyAspect();
+ *     }
+ * }
+ * + * Where {@code FooService} is a typical POJO component and {@code MyAspect} is an + * {@code @Aspect}-style aspect: + * + *
+ * public class FooService {
+ *     // various methods
+ * }
+ * + *
+ * @Aspect
+ * public class MyAspect {
+ *     @Before("execution(* FooService+.*(..))")
+ *     public void advice() {
+ *         // advise FooService methods as appropriate
+ *     }
+ * }
+ * + * In the scenario above, {@code @EnableAspectJAutoProxy} ensures that {@code MyAspect} + * will be properly processed and that {@code FooService} will be proxied mixing in the + * advice that it contributes. + * + *

Users can control the type of proxy that gets created for {@code FooService} using + * the {@link #proxyTargetClass()} attribute. The following enables CGLIB-style 'subclass' + * proxies as opposed to the default interface-based JDK proxy approach. + *

+ * @Configuration
+ * @EnableAspectJAutoProxy(proxyTargetClass=true)
+ * public class AppConfig {
+ *     // ...
+ * }
+ * + *

Note that {@code @Aspect} beans may be component-scanned like any other. Simply + * mark the aspect with both {@code @Aspect} and {@code @Component}: + *

+ * package com.foo;
+ *
+ * @Component
+ * public class FooService { ... }
+ *
+ * @Aspect
+ * @Component
+ * public class MyAspect { ... }
+ * + * Then use the @{@link ComponentScan} annotation to pick both up: + *
+ * @Configuration
+ * @ComponentScan("com.foo")
+ * @EnableAspectJAutoProxy
+ * public class AppConfig {
+ *     // no explicit @Bean definitions required
+ * }
+ * + * @author Chris Beams + * @since 3.1 + * @see org.aspectj.lang.annotation.Aspect + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Import(AspectJAutoProxyConfigurationSelector.class) +@Documented +public @interface EnableAspectJAutoProxy { + + /** + * Indicate whether subclass-based (CGLIB) proxies are to be created as opposed + * to standard Java interface-based proxies. The default is {@code false}. + */ + boolean proxyTargetClass() default false; + +} diff --git a/org.springframework.context/src/test/java/org/springframework/context/annotation/EnableAspectJAutoProxyTests.java b/org.springframework.context/src/test/java/org/springframework/context/annotation/EnableAspectJAutoProxyTests.java new file mode 100644 index 0000000000..444d4ed257 --- /dev/null +++ b/org.springframework.context/src/test/java/org/springframework/context/annotation/EnableAspectJAutoProxyTests.java @@ -0,0 +1,86 @@ +/* + * Copyright 2002-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * +import org.springframework.aop.aspectj.annotation.EnableAspectJAutoProxy; + + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.context.annotation; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; +import org.springframework.aop.support.AopUtils; +import org.springframework.context.ApplicationContext; + +import example.scannable.FooService; +import example.scannable.ServiceInvocationCounter; + +/** + * @author Juergen Hoeller + * @author Chris Beams + */ +public class EnableAspectJAutoProxyTests { + + @Configuration + @ComponentScan("example.scannable") + @EnableAspectJAutoProxy + static class Config_WithJDKProxy { + } + + @Configuration + @ComponentScan("example.scannable") + @EnableAspectJAutoProxy(proxyTargetClass=true) + static class Config_WithCGLIBProxy { + } + + @Test + public void withJDKProxy() throws Exception { + ApplicationContext ctx = + new AnnotationConfigApplicationContext(Config_WithJDKProxy.class); + + aspectIsApplied(ctx); + assertThat(AopUtils.isJdkDynamicProxy(ctx.getBean(FooService.class)), is(true)); + } + + @Test + public void withCGLIBProxy() throws Exception { + ApplicationContext ctx = + new AnnotationConfigApplicationContext(Config_WithCGLIBProxy.class); + + aspectIsApplied(ctx); + assertThat(AopUtils.isCglibProxy(ctx.getBean(FooService.class)), is(true)); + } + + + private void aspectIsApplied(ApplicationContext ctx) throws Exception { + FooService fooService = ctx.getBean(FooService.class); + ServiceInvocationCounter counter = ctx.getBean(ServiceInvocationCounter.class); + + assertEquals(0, counter.getCount()); + + assertTrue(fooService.isInitCalled()); + assertEquals(1, counter.getCount()); + + String value = fooService.foo(1); + assertEquals("bar", value); + assertEquals(2, counter.getCount()); + + fooService.foo(1); + assertEquals(3, counter.getCount()); + } +} \ No newline at end of file diff --git a/spring-framework-reference/src/new-in-3.1.xml b/spring-framework-reference/src/new-in-3.1.xml index afdb052d51..b73e5c7a5e 100644 --- a/spring-framework-reference/src/new-in-3.1.xml +++ b/spring-framework-reference/src/new-in-3.1.xml @@ -128,6 +128,12 @@ Javadoc + + See + org.springframework.context.annotation.EnableAspectJAutoProxy + Javadoc + + See org.springframework.scheduling.annotation.EnableScheduling Javadoc