Introduce composed annotations for web scopes
This commit introduces the following common composed annotations for web scopes. - @RequestScope - @SessionScope - @ApplicationScope Issue: SPR-13993
This commit is contained in:
parent
df7b24b8e7
commit
b423596b2e
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Copyright 2002-2016 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.web.context.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.context.annotation.ScopedProxyMode;
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
|
||||
import static java.lang.annotation.ElementType.METHOD;
|
||||
import static java.lang.annotation.ElementType.TYPE;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
import static org.springframework.web.context.WebApplicationContext.SCOPE_APPLICATION;
|
||||
|
||||
/**
|
||||
* {@code @ApplicationScope} is a specialization of {@link Scope @Scope} for a
|
||||
* component whose lifecycle is bound to the current web application.
|
||||
*
|
||||
* <p>Specifically, {@code @ApplicationScope} is a <em>composed annotation</em> that
|
||||
* acts as a shortcut for {@code @Scope("application")} with the default
|
||||
* {@link #proxyMode} set to {@link ScopedProxyMode#TARGET_CLASS TARGET_CLASS}.
|
||||
*
|
||||
* <p>{@code @ApplicationScope} may be used as a meta-annotation to create custom
|
||||
* composed annotations.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 4.3
|
||||
* @see RequestScope
|
||||
* @see SessionScope
|
||||
* @see org.springframework.context.annotation.Scope
|
||||
* @see org.springframework.web.context.WebApplicationContext#SCOPE_APPLICATION
|
||||
* @see org.springframework.web.context.support.ServletContextScope
|
||||
* @see org.springframework.stereotype.Component
|
||||
* @see org.springframework.context.annotation.Bean
|
||||
*/
|
||||
@Scope(SCOPE_APPLICATION)
|
||||
@Target({ TYPE, METHOD })
|
||||
@Retention(RUNTIME)
|
||||
@Documented
|
||||
public @interface ApplicationScope {
|
||||
|
||||
/**
|
||||
* Alias for {@link Scope#proxyMode}.
|
||||
* <p>Defaults to {@link ScopedProxyMode#TARGET_CLASS}.
|
||||
*/
|
||||
@AliasFor(annotation = Scope.class)
|
||||
ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS;
|
||||
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Copyright 2002-2016 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.web.context.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.context.annotation.ScopedProxyMode;
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
|
||||
import static java.lang.annotation.ElementType.METHOD;
|
||||
import static java.lang.annotation.ElementType.TYPE;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
import static org.springframework.web.context.WebApplicationContext.SCOPE_REQUEST;
|
||||
|
||||
/**
|
||||
* {@code @RequestScope} is a specialization of {@link Scope @Scope} for a
|
||||
* component whose lifecycle is bound to the current web request.
|
||||
*
|
||||
* <p>Specifically, {@code @RequestScope} is a <em>composed annotation</em> that
|
||||
* acts as a shortcut for {@code @Scope("request")} with the default
|
||||
* {@link #proxyMode} set to {@link ScopedProxyMode#TARGET_CLASS TARGET_CLASS}.
|
||||
*
|
||||
* <p>{@code @RequestScope} may be used as a meta-annotation to create custom
|
||||
* composed annotations.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 4.3
|
||||
* @see SessionScope
|
||||
* @see ApplicationScope
|
||||
* @see org.springframework.context.annotation.Scope
|
||||
* @see org.springframework.web.context.WebApplicationContext#SCOPE_REQUEST
|
||||
* @see org.springframework.web.context.request.RequestScope
|
||||
* @see org.springframework.stereotype.Component
|
||||
* @see org.springframework.context.annotation.Bean
|
||||
*/
|
||||
@Scope(SCOPE_REQUEST)
|
||||
@Target({ TYPE, METHOD })
|
||||
@Retention(RUNTIME)
|
||||
@Documented
|
||||
public @interface RequestScope {
|
||||
|
||||
/**
|
||||
* Alias for {@link Scope#proxyMode}.
|
||||
* <p>Defaults to {@link ScopedProxyMode#TARGET_CLASS}.
|
||||
*/
|
||||
@AliasFor(annotation = Scope.class)
|
||||
ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS;
|
||||
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Copyright 2002-2016 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.web.context.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.context.annotation.ScopedProxyMode;
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
|
||||
import static java.lang.annotation.ElementType.METHOD;
|
||||
import static java.lang.annotation.ElementType.TYPE;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
import static org.springframework.web.context.WebApplicationContext.SCOPE_SESSION;
|
||||
|
||||
/**
|
||||
* {@code @SessionScope} is a specialization of {@link Scope @Scope} for a
|
||||
* component whose lifecycle is bound to the current web session.
|
||||
*
|
||||
* <p>Specifically, {@code @SessionScope} is a <em>composed annotation</em> that
|
||||
* acts as a shortcut for {@code @Scope("session")} with the default
|
||||
* {@link #proxyMode} set to {@link ScopedProxyMode#TARGET_CLASS TARGET_CLASS}.
|
||||
*
|
||||
* <p>{@code @SessionScope} may be used as a meta-annotation to create custom
|
||||
* composed annotations.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 4.3
|
||||
* @see RequestScope
|
||||
* @see ApplicationScope
|
||||
* @see org.springframework.context.annotation.Scope
|
||||
* @see org.springframework.web.context.WebApplicationContext#SCOPE_SESSION
|
||||
* @see org.springframework.web.context.request.SessionScope
|
||||
* @see org.springframework.stereotype.Component
|
||||
* @see org.springframework.context.annotation.Bean
|
||||
*/
|
||||
@Scope(SCOPE_SESSION)
|
||||
@Target({ TYPE, METHOD })
|
||||
@Retention(RUNTIME)
|
||||
@Documented
|
||||
public @interface SessionScope {
|
||||
|
||||
/**
|
||||
* Alias for {@link Scope#proxyMode}.
|
||||
* <p>Defaults to {@link ScopedProxyMode#TARGET_CLASS}.
|
||||
*/
|
||||
@AliasFor(annotation = Scope.class)
|
||||
ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS;
|
||||
|
||||
}
|
|
@ -662,10 +662,11 @@ Spring 4.3 also improves the caching abstraction as follows:
|
|||
=== Web Improvements
|
||||
|
||||
* Built-in support for <<mvc-ann-requestmapping-head-options,HTTP HEAD and HTTP OPTIONS>>.
|
||||
* New `@RequestScope`, `@SessionScope`, and `@ApplicationScope` _composed annotations_ for web scopes.
|
||||
* New `@RestControllerAdvice` annotation with combined `@ControllerAdvice` with `@ResponseBody` semantics.
|
||||
* `@ResponseStatus` supported on the class level and inherited on all methods.
|
||||
* `@ResponseStatus` is now supported at the class level and inherited by all methods.
|
||||
* New `@SessionAttribute` annotation for access to session attributes (see <<mvc-ann-sessionattrib-global, example>>).
|
||||
* New `@RequestAttribute` annotation for access to session attributes (see <<mvc-ann-requestattrib, example>>).
|
||||
* New `@RequestAttribute` annotation for access to request attributes (see <<mvc-ann-requestattrib, example>>).
|
||||
* `@ModelAttribute` allows preventing data binding via `binding=false` attribute (see <<mvc-ann-modelattrib-method-args, reference>>).
|
||||
* `AsyncRestTemplate` supports request interception.
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2016 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.
|
||||
|
@ -17,50 +17,50 @@
|
|||
package org.springframework.context.annotation.scope;
|
||||
|
||||
import org.junit.After;
|
||||
import static org.junit.Assert.*;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.BeanNameGenerator;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.context.annotation.ScopedProxyMode;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpSession;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.context.annotation.RequestScope;
|
||||
import org.springframework.web.context.annotation.SessionScope;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
import org.springframework.web.context.support.GenericWebApplicationContext;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.springframework.context.annotation.ScopedProxyMode.DEFAULT;
|
||||
import static org.springframework.context.annotation.ScopedProxyMode.INTERFACES;
|
||||
import static org.springframework.context.annotation.ScopedProxyMode.NO;
|
||||
import static org.springframework.context.annotation.ScopedProxyMode.TARGET_CLASS;
|
||||
|
||||
/**
|
||||
* @author Mark Fisher
|
||||
* @author Juergen Hoeller
|
||||
* @author Chris Beams
|
||||
* @author Sam Brannen
|
||||
*/
|
||||
public class ClassPathBeanDefinitionScannerScopeIntegrationTests {
|
||||
|
||||
private static final String DEFAULT_NAME = "default";
|
||||
|
||||
private static final String MODIFIED_NAME = "modified";
|
||||
|
||||
private ServletRequestAttributes oldRequestAttributes;
|
||||
|
||||
private ServletRequestAttributes newRequestAttributes;
|
||||
private ServletRequestAttributes oldRequestAttributes = new ServletRequestAttributes(new MockHttpServletRequest());
|
||||
private ServletRequestAttributes newRequestAttributes = new ServletRequestAttributes(new MockHttpServletRequest());
|
||||
|
||||
private ServletRequestAttributes oldRequestAttributesWithSession;
|
||||
|
||||
private ServletRequestAttributes newRequestAttributesWithSession;
|
||||
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
this.oldRequestAttributes = new ServletRequestAttributes(new MockHttpServletRequest());
|
||||
this.newRequestAttributes = new ServletRequestAttributes(new MockHttpServletRequest());
|
||||
|
||||
MockHttpServletRequest oldRequestWithSession = new MockHttpServletRequest();
|
||||
oldRequestWithSession.setSession(new MockHttpSession());
|
||||
this.oldRequestAttributesWithSession = new ServletRequestAttributes(oldRequestWithSession);
|
||||
|
@ -72,14 +72,14 @@ public class ClassPathBeanDefinitionScannerScopeIntegrationTests {
|
|||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
RequestContextHolder.setRequestAttributes(null);
|
||||
RequestContextHolder.resetRequestAttributes();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSingletonScopeWithNoProxy() {
|
||||
public void singletonScopeWithNoProxy() {
|
||||
RequestContextHolder.setRequestAttributes(oldRequestAttributes);
|
||||
ApplicationContext context = createContext(ScopedProxyMode.NO);
|
||||
ApplicationContext context = createContext(NO);
|
||||
ScopedTestBean bean = (ScopedTestBean) context.getBean("singleton");
|
||||
|
||||
// should not be a proxy
|
||||
|
@ -98,9 +98,9 @@ public class ClassPathBeanDefinitionScannerScopeIntegrationTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testSingletonScopeIgnoresProxyInterfaces() {
|
||||
public void singletonScopeIgnoresProxyInterfaces() {
|
||||
RequestContextHolder.setRequestAttributes(oldRequestAttributes);
|
||||
ApplicationContext context = createContext(ScopedProxyMode.INTERFACES);
|
||||
ApplicationContext context = createContext(INTERFACES);
|
||||
ScopedTestBean bean = (ScopedTestBean) context.getBean("singleton");
|
||||
|
||||
// should not be a proxy
|
||||
|
@ -119,9 +119,9 @@ public class ClassPathBeanDefinitionScannerScopeIntegrationTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testSingletonScopeIgnoresProxyTargetClass() {
|
||||
public void singletonScopeIgnoresProxyTargetClass() {
|
||||
RequestContextHolder.setRequestAttributes(oldRequestAttributes);
|
||||
ApplicationContext context = createContext(ScopedProxyMode.TARGET_CLASS);
|
||||
ApplicationContext context = createContext(TARGET_CLASS);
|
||||
ScopedTestBean bean = (ScopedTestBean) context.getBean("singleton");
|
||||
|
||||
// should not be a proxy
|
||||
|
@ -140,9 +140,9 @@ public class ClassPathBeanDefinitionScannerScopeIntegrationTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testRequestScopeWithNoProxy() {
|
||||
public void requestScopeWithNoProxy() {
|
||||
RequestContextHolder.setRequestAttributes(oldRequestAttributes);
|
||||
ApplicationContext context = createContext(ScopedProxyMode.NO);
|
||||
ApplicationContext context = createContext(NO);
|
||||
ScopedTestBean bean = (ScopedTestBean) context.getBean("request");
|
||||
|
||||
// should not be a proxy
|
||||
|
@ -161,9 +161,9 @@ public class ClassPathBeanDefinitionScannerScopeIntegrationTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testRequestScopeWithProxiedInterfaces() {
|
||||
public void requestScopeWithProxiedInterfaces() {
|
||||
RequestContextHolder.setRequestAttributes(oldRequestAttributes);
|
||||
ApplicationContext context = createContext(ScopedProxyMode.INTERFACES);
|
||||
ApplicationContext context = createContext(INTERFACES);
|
||||
IScopedTestBean bean = (IScopedTestBean) context.getBean("request");
|
||||
|
||||
// should be dynamic proxy, implementing both interfaces
|
||||
|
@ -182,9 +182,9 @@ public class ClassPathBeanDefinitionScannerScopeIntegrationTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testRequestScopeWithProxiedTargetClass() {
|
||||
public void requestScopeWithProxiedTargetClass() {
|
||||
RequestContextHolder.setRequestAttributes(oldRequestAttributes);
|
||||
ApplicationContext context = createContext(ScopedProxyMode.TARGET_CLASS);
|
||||
ApplicationContext context = createContext(TARGET_CLASS);
|
||||
IScopedTestBean bean = (IScopedTestBean) context.getBean("request");
|
||||
|
||||
// should be a class-based proxy
|
||||
|
@ -203,9 +203,9 @@ public class ClassPathBeanDefinitionScannerScopeIntegrationTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testSessionScopeWithNoProxy() {
|
||||
public void sessionScopeWithNoProxy() {
|
||||
RequestContextHolder.setRequestAttributes(oldRequestAttributesWithSession);
|
||||
ApplicationContext context = createContext(ScopedProxyMode.NO);
|
||||
ApplicationContext context = createContext(NO);
|
||||
ScopedTestBean bean = (ScopedTestBean) context.getBean("session");
|
||||
|
||||
// should not be a proxy
|
||||
|
@ -224,9 +224,9 @@ public class ClassPathBeanDefinitionScannerScopeIntegrationTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testSessionScopeWithProxiedInterfaces() {
|
||||
public void sessionScopeWithProxiedInterfaces() {
|
||||
RequestContextHolder.setRequestAttributes(oldRequestAttributesWithSession);
|
||||
ApplicationContext context = createContext(ScopedProxyMode.INTERFACES);
|
||||
ApplicationContext context = createContext(INTERFACES);
|
||||
IScopedTestBean bean = (IScopedTestBean) context.getBean("session");
|
||||
|
||||
// should be dynamic proxy, implementing both interfaces
|
||||
|
@ -251,9 +251,9 @@ public class ClassPathBeanDefinitionScannerScopeIntegrationTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testSessionScopeWithProxiedTargetClass() {
|
||||
public void sessionScopeWithProxiedTargetClass() {
|
||||
RequestContextHolder.setRequestAttributes(oldRequestAttributesWithSession);
|
||||
ApplicationContext context = createContext(ScopedProxyMode.TARGET_CLASS);
|
||||
ApplicationContext context = createContext(TARGET_CLASS);
|
||||
IScopedTestBean bean = (IScopedTestBean) context.getBean("session");
|
||||
|
||||
// should be a class-based proxy
|
||||
|
@ -283,12 +283,7 @@ public class ClassPathBeanDefinitionScannerScopeIntegrationTests {
|
|||
GenericWebApplicationContext context = new GenericWebApplicationContext();
|
||||
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context);
|
||||
scanner.setIncludeAnnotationConfig(false);
|
||||
scanner.setBeanNameGenerator(new BeanNameGenerator() {
|
||||
@Override
|
||||
public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
|
||||
return definition.getScope();
|
||||
}
|
||||
});
|
||||
scanner.setBeanNameGenerator((definition, registry) -> definition.getScope());
|
||||
scanner.setScopedProxyMode(scopedProxyMode);
|
||||
|
||||
// Scan twice in order to find errors in the bean definition compatibility check.
|
||||
|
@ -300,7 +295,7 @@ public class ClassPathBeanDefinitionScannerScopeIntegrationTests {
|
|||
}
|
||||
|
||||
|
||||
public static interface IScopedTestBean {
|
||||
static interface IScopedTestBean {
|
||||
|
||||
String getName();
|
||||
|
||||
|
@ -308,7 +303,7 @@ public class ClassPathBeanDefinitionScannerScopeIntegrationTests {
|
|||
}
|
||||
|
||||
|
||||
public static abstract class ScopedTestBean implements IScopedTestBean {
|
||||
static abstract class ScopedTestBean implements IScopedTestBean {
|
||||
|
||||
private String name = DEFAULT_NAME;
|
||||
|
||||
|
@ -321,23 +316,23 @@ public class ClassPathBeanDefinitionScannerScopeIntegrationTests {
|
|||
|
||||
|
||||
@Component
|
||||
public static class SingletonScopedTestBean extends ScopedTestBean {
|
||||
static class SingletonScopedTestBean extends ScopedTestBean {
|
||||
}
|
||||
|
||||
|
||||
public static interface AnotherScopeTestInterface {
|
||||
static interface AnotherScopeTestInterface {
|
||||
}
|
||||
|
||||
|
||||
@Component
|
||||
@Scope("request")
|
||||
public static class RequestScopedTestBean extends ScopedTestBean implements AnotherScopeTestInterface {
|
||||
@RequestScope(proxyMode = DEFAULT)
|
||||
static class RequestScopedTestBean extends ScopedTestBean implements AnotherScopeTestInterface {
|
||||
}
|
||||
|
||||
|
||||
@Component
|
||||
@Scope("session")
|
||||
public static class SessionScopedTestBean extends ScopedTestBean implements AnotherScopeTestInterface {
|
||||
@SessionScope(proxyMode = DEFAULT)
|
||||
static class SessionScopedTestBean extends ScopedTestBean implements AnotherScopeTestInterface {
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue