Skip to content

Commit 4efa91d

Browse files
committed
mvc:annotation-driven exposes default Validator and ConversionService as top-level beans (SPR-6377)
1 parent a9d4a58 commit 4efa91d

File tree

3 files changed

+88
-72
lines changed

3 files changed

+88
-72
lines changed

org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/AnnotationDrivenBeanDefinitionParser.java

+49-61
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,16 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16+
1617
package org.springframework.web.servlet.config;
1718

19+
import org.w3c.dom.Element;
20+
1821
import org.springframework.beans.factory.config.BeanDefinition;
19-
import org.springframework.beans.factory.config.BeanDefinitionHolder;
22+
import org.springframework.beans.factory.config.RuntimeBeanReference;
2023
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
2124
import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
22-
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
25+
import org.springframework.beans.factory.support.RootBeanDefinition;
2326
import org.springframework.beans.factory.xml.BeanDefinitionParser;
2427
import org.springframework.beans.factory.xml.ParserContext;
2528
import org.springframework.core.convert.ConversionService;
@@ -30,7 +33,6 @@
3033
import org.springframework.web.bind.support.ConfigurableWebBindingInitializer;
3134
import org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter;
3235
import org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping;
33-
import org.w3c.dom.Element;
3436

3537
/**
3638
* {@link org.springframework.beans.factory.xml.BeanDefinitionParser} that parses the {@code annotation-driven} element to configure
@@ -46,84 +48,70 @@
4648
* <li>Configures the validator if specified, otherwise defaults to a fresh {@link Validator} instance created by the default {@link LocalValidatorFactoryBean} <i>if the JSR-303 API is present in the classpath.
4749
* </ul>
4850
* </ol>
51+
*
4952
* @author Keith Donald
53+
* @author Juergen Hoeller
5054
* @since 3.0
5155
*/
52-
public class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
56+
class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
57+
58+
private static final boolean jsr303Present = ClassUtils.isPresent(
59+
"javax.validation.Validator", AnnotationDrivenBeanDefinitionParser.class.getClassLoader());
60+
5361

5462
public BeanDefinition parse(Element element, ParserContext parserContext) {
5563
Object source = parserContext.extractSource(element);
56-
BeanDefinitionHolder handlerMappingHolder = registerDefaultAnnotationHandlerMapping(element, source, parserContext);
57-
BeanDefinitionHolder handlerAdapterHolder = registerAnnotationMethodHandlerAdapter(element, source, parserContext);
58-
64+
65+
RootBeanDefinition mappingDef = new RootBeanDefinition(DefaultAnnotationHandlerMapping.class);
66+
mappingDef.setSource(source);
67+
mappingDef.getPropertyValues().add("order", 0);
68+
String mappingName = parserContext.getReaderContext().registerWithGeneratedName(mappingDef);
69+
70+
RootBeanDefinition bindingDef = new RootBeanDefinition(ConfigurableWebBindingInitializer.class);
71+
bindingDef.setSource(source);
72+
bindingDef.getPropertyValues().add("conversionService", getConversionService(element, source, parserContext));
73+
bindingDef.getPropertyValues().add("validator", getValidator(element, source, parserContext));
74+
75+
RootBeanDefinition adapterDef = new RootBeanDefinition(AnnotationMethodHandlerAdapter.class);
76+
adapterDef.setSource(source);
77+
adapterDef.getPropertyValues().add("webBindingInitializer", bindingDef);
78+
String adapterName = parserContext.getReaderContext().registerWithGeneratedName(adapterDef);
79+
5980
CompositeComponentDefinition compDefinition = new CompositeComponentDefinition(element.getTagName(), source);
6081
parserContext.pushContainingComponent(compDefinition);
61-
parserContext.registerComponent(new BeanComponentDefinition(handlerMappingHolder));
62-
parserContext.registerComponent(new BeanComponentDefinition(handlerAdapterHolder));
82+
parserContext.registerComponent(new BeanComponentDefinition(mappingDef, mappingName));
83+
parserContext.registerComponent(new BeanComponentDefinition(adapterDef, adapterName));
6384
parserContext.popAndRegisterContainingComponent();
6485

6586
return null;
6687
}
6788

68-
// internal helpers
69-
70-
private BeanDefinitionHolder registerDefaultAnnotationHandlerMapping(Element element, Object source, ParserContext context) {
71-
BeanDefinitionBuilder builder = createBeanBuilder(DefaultAnnotationHandlerMapping.class, source);
72-
builder.addPropertyValue("order", 0);
73-
return registerBeanDefinition(builder.getBeanDefinition(), context);
74-
}
7589

76-
private BeanDefinitionHolder registerAnnotationMethodHandlerAdapter(Element element, Object source, ParserContext context) {
77-
BeanDefinitionBuilder builder = createBeanBuilder(AnnotationMethodHandlerAdapter.class, source);
78-
builder.addPropertyValue("webBindingInitializer", createWebBindingInitializer(element, source, context));
79-
return registerBeanDefinition(builder.getBeanDefinition(), context);
80-
}
81-
82-
private BeanDefinition createWebBindingInitializer(Element element, Object source, ParserContext context) {
83-
BeanDefinitionBuilder builder = createBeanBuilder(ConfigurableWebBindingInitializer.class, source);
84-
addConversionService(builder, element, source, context);
85-
addValidator(builder, element, source, context);
86-
return builder.getBeanDefinition();
87-
}
88-
89-
private void addConversionService(BeanDefinitionBuilder builder, Element element, Object source, ParserContext context) {
90+
private Object getConversionService(Element element, Object source, ParserContext parserContext) {
9091
if (element.hasAttribute("conversion-service")) {
91-
builder.addPropertyReference("conversionService", element.getAttribute("conversion-service"));
92-
} else {
93-
builder.addPropertyValue("conversionService", createDefaultConversionService(element, source, context));
92+
return new RuntimeBeanReference(element.getAttribute("conversion-service"));
93+
}
94+
else {
95+
RootBeanDefinition conversionDef = new RootBeanDefinition(FormattingConversionServiceFactoryBean.class);
96+
conversionDef.setSource(source);
97+
String conversionName = parserContext.getReaderContext().registerWithGeneratedName(conversionDef);
98+
return new RuntimeBeanReference(conversionName);
9499
}
95100
}
96101

97-
private void addValidator(BeanDefinitionBuilder builder, Element element, Object source, ParserContext context) {
102+
private Object getValidator(Element element, Object source, ParserContext parserContext) {
98103
if (element.hasAttribute("validator")) {
99-
builder.addPropertyReference("validator", element.getAttribute("validator"));
100-
} else {
101-
if (ClassUtils.isPresent("javax.validation.Validator", AnnotationDrivenBeanDefinitionParser.class.getClassLoader())) {
102-
builder.addPropertyValue("validator", createDefaultValidator(element, source, context));
103-
}
104+
return new RuntimeBeanReference(element.getAttribute("validator"));
105+
}
106+
else if (jsr303Present) {
107+
RootBeanDefinition validatorDef = new RootBeanDefinition(LocalValidatorFactoryBean.class);
108+
validatorDef.setSource(source);
109+
String validatorName = parserContext.getReaderContext().registerWithGeneratedName(validatorDef);
110+
return new RuntimeBeanReference(validatorName);
111+
}
112+
else {
113+
return null;
104114
}
105-
}
106-
107-
private BeanDefinition createDefaultConversionService(Element element, Object source, ParserContext context) {
108-
BeanDefinitionBuilder builder = createBeanBuilder(FormattingConversionServiceFactoryBean.class, source);
109-
return builder.getBeanDefinition();
110-
}
111-
112-
private BeanDefinition createDefaultValidator(Element element, Object source, ParserContext context) {
113-
BeanDefinitionBuilder builder = createBeanBuilder(LocalValidatorFactoryBean.class, source);
114-
return builder.getBeanDefinition();
115-
}
116-
117-
private BeanDefinitionHolder registerBeanDefinition(BeanDefinition definition, ParserContext context) {
118-
String beanName = context.getReaderContext().generateBeanName(definition);
119-
context.getRegistry().registerBeanDefinition(beanName, definition);
120-
return new BeanDefinitionHolder(definition, beanName);
121-
}
122-
123-
private BeanDefinitionBuilder createBeanBuilder(Class<?> clazz, Object source) {
124-
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(clazz);
125-
builder.getRawBeanDefinition().setSource(source);
126-
return builder;
127115
}
128116

129117
}

org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/MvcNamespaceHandler.java

+3
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,14 @@
2121

2222
/**
2323
* {@link NamespaceHandler} for Spring MVC configuration namespace.
24+
*
2425
* @author Keith Donald
26+
* @since 3.0
2527
*/
2628
public class MvcNamespaceHandler extends NamespaceHandlerSupport {
2729

2830
public void init() {
2931
registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());
3032
}
33+
3134
}

org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/config/MvcNamespaceTests.java

+36-11
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,80 @@
1-
package org.springframework.web.servlet.config;
1+
/*
2+
* Copyright 2002-2009 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
216

3-
import static org.junit.Assert.assertEquals;
4-
import static org.junit.Assert.assertFalse;
5-
import static org.junit.Assert.assertNotNull;
6-
import static org.junit.Assert.assertTrue;
17+
package org.springframework.web.servlet.config;
718

819
import java.util.Date;
920
import java.util.Locale;
10-
1121
import javax.validation.Valid;
1222
import javax.validation.constraints.NotNull;
1323

24+
import static org.junit.Assert.*;
1425
import org.junit.Before;
1526
import org.junit.Test;
27+
1628
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
1729
import org.springframework.context.i18n.LocaleContextHolder;
1830
import org.springframework.core.convert.ConversionFailedException;
31+
import org.springframework.core.convert.ConversionService;
1932
import org.springframework.core.io.ClassPathResource;
2033
import org.springframework.format.annotation.DateTimeFormat;
2134
import org.springframework.format.annotation.DateTimeFormat.ISO;
35+
import org.springframework.format.support.FormattingConversionServiceFactoryBean;
2236
import org.springframework.mock.web.MockHttpServletRequest;
2337
import org.springframework.mock.web.MockHttpServletResponse;
2438
import org.springframework.mock.web.MockServletContext;
2539
import org.springframework.stereotype.Controller;
2640
import org.springframework.validation.BindingResult;
2741
import org.springframework.validation.Errors;
2842
import org.springframework.validation.Validator;
43+
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
2944
import org.springframework.web.bind.annotation.RequestMapping;
3045
import org.springframework.web.bind.annotation.RequestParam;
3146
import org.springframework.web.context.support.GenericWebApplicationContext;
3247
import org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter;
3348
import org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping;
3449

50+
/**
51+
* @author Keith Donald
52+
*/
3553
public class MvcNamespaceTests {
3654

3755
private GenericWebApplicationContext container;
38-
56+
3957
@Before
4058
public void setUp() {
4159
container = new GenericWebApplicationContext();
4260
container.setServletContext(new MockServletContext());
4361
LocaleContextHolder.setLocale(Locale.US);
4462
}
45-
63+
4664
@Test
4765
public void testDefaultConfig() throws Exception {
4866
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(container);
4967
reader.loadBeanDefinitions(new ClassPathResource("mvc-config.xml", getClass()));
50-
assertEquals(2, container.getBeanDefinitionCount());
68+
assertEquals(4, container.getBeanDefinitionCount());
5169
DefaultAnnotationHandlerMapping mapping = container.getBean(DefaultAnnotationHandlerMapping.class);
5270
assertNotNull(mapping);
5371
assertEquals(0, mapping.getOrder());
5472
AnnotationMethodHandlerAdapter adapter = container.getBean(AnnotationMethodHandlerAdapter.class);
5573
assertNotNull(adapter);
74+
assertNotNull(container.getBean(FormattingConversionServiceFactoryBean.class));
75+
assertNotNull(container.getBean(ConversionService.class));
76+
assertNotNull(container.getBean(LocalValidatorFactoryBean.class));
77+
assertNotNull(container.getBean(Validator.class));
5678

5779
TestController handler = new TestController();
5880

@@ -68,12 +90,13 @@ public void testDefaultConfig() throws Exception {
6890
public void testCustomConversionService() throws Exception {
6991
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(container);
7092
reader.loadBeanDefinitions(new ClassPathResource("mvc-config-custom-conversion-service.xml", getClass()));
71-
assertEquals(3, container.getBeanDefinitionCount());
93+
assertEquals(4, container.getBeanDefinitionCount());
7294
DefaultAnnotationHandlerMapping mapping = container.getBean(DefaultAnnotationHandlerMapping.class);
7395
assertNotNull(mapping);
7496
assertEquals(0, mapping.getOrder());
7597
AnnotationMethodHandlerAdapter adapter = container.getBean(AnnotationMethodHandlerAdapter.class);
7698
assertNotNull(adapter);
99+
assertNotNull(container.getBean(LocalValidatorFactoryBean.class));
77100

78101
TestController handler = new TestController();
79102

@@ -88,12 +111,13 @@ public void testCustomConversionService() throws Exception {
88111
public void testCustomValidator() throws Exception {
89112
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(container);
90113
reader.loadBeanDefinitions(new ClassPathResource("mvc-config-custom-validator.xml", getClass()));
91-
assertEquals(3, container.getBeanDefinitionCount());
114+
assertEquals(4, container.getBeanDefinitionCount());
92115
DefaultAnnotationHandlerMapping mapping = container.getBean(DefaultAnnotationHandlerMapping.class);
93116
assertNotNull(mapping);
94117
assertEquals(0, mapping.getOrder());
95118
AnnotationMethodHandlerAdapter adapter = container.getBean(AnnotationMethodHandlerAdapter.class);
96119
assertNotNull(adapter);
120+
assertNotNull(container.getBean(FormattingConversionServiceFactoryBean.class));
97121

98122
TestController handler = new TestController();
99123

@@ -150,4 +174,5 @@ public void setField(String field) {
150174
}
151175

152176
}
177+
153178
}

0 commit comments

Comments
 (0)