Skip to content

Commit 29100b0

Browse files
author
dmytrodanilenkov
committed
Fix for issue #45000 - ServletRegistrationBean has those properties, but @ServletRegistration hasn't: initParameters, servletRegistrationBeans, multipartConfig
1 parent e87d5d7 commit 29100b0

File tree

3 files changed

+189
-0
lines changed

3 files changed

+189
-0
lines changed

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/ServletContextInitializerBeans.java

+43
Original file line numberDiff line numberDiff line change
@@ -321,10 +321,53 @@ private void configureFromAnnotation(ServletRegistrationBean<Servlet> bean, Serv
321321
if (registration.urlMappings().length > 0) {
322322
bean.setUrlMappings(Arrays.asList(registration.urlMappings()));
323323
}
324+
325+
if (registration.initParameters().length > 0) {
326+
bean.setInitParameters(parseInitParameters(registration.initParameters()));
327+
}
328+
329+
ServletRegistration.MultipartConfigValues multipart = registration.multipartConfig();
330+
boolean isMultipartConfigUsed = !(multipart.location().isEmpty()
331+
&& multipart.maxFileSize() == -1L
332+
&& multipart.maxRequestSize() == -1L
333+
&& multipart.fileSizeThreshold() == 0);
334+
if (isMultipartConfigUsed) {
335+
bean.setMultipartConfig(new MultipartConfigElement(
336+
multipart.location(),
337+
multipart.maxFileSize(),
338+
multipart.maxRequestSize(),
339+
multipart.fileSizeThreshold()
340+
));
341+
}
342+
343+
for (Class<? extends ServletRegistrationBean<?>> beanClass : registration.servletRegistrationBeans()) {
344+
ServletRegistrationBean<?> extraBean = this.beanFactory.getBean(beanClass);
345+
bean.getInitParameters().putAll(extraBean.getInitParameters());
346+
}
347+
348+
}
349+
350+
private Map<String, String> parseInitParameters(String[] initParamsArray) {
351+
Map<String, String> initParams = new LinkedHashMap<>();
352+
for (String kv : initParamsArray) {
353+
int index = kv.indexOf('=');
354+
if (index != -1) {
355+
String key = kv.substring(0, index).trim();
356+
String value = kv.substring(index + 1).trim();
357+
initParams.put(key, value);
358+
}
359+
else {
360+
throw new IllegalArgumentException(
361+
"initParameters must be in 'key=value' format, got: " + kv);
362+
}
363+
}
364+
return initParams;
324365
}
325366

326367
}
327368

369+
370+
328371
/**
329372
* {@link RegistrationBeanAdapter} for {@link Filter} beans.
330373
*/

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/ServletRegistration.java

+33
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
import java.lang.annotation.Target;
2424

2525
import jakarta.servlet.Servlet;
26+
import jakarta.servlet.annotation.MultipartConfig;
27+
import jakarta.servlet.annotation.WebInitParam;
2628

2729
import org.springframework.core.Ordered;
2830
import org.springframework.core.annotation.AliasFor;
@@ -87,4 +89,35 @@
8789
*/
8890
int loadOnStartup() default -1;
8991

92+
/**
93+
* Init parameters to set on the servlet, as {@code "key=value"} pairs.
94+
*/
95+
String[] initParameters() default {};
96+
97+
/**
98+
* (Optional) Additional servlet-registration beans to apply.
99+
* Usually left empty unless you need custom bean logic.
100+
*/
101+
Class<? extends ServletRegistrationBean<?>>[] servletRegistrationBeans() default {};
102+
103+
/**
104+
* Multi-part configuration. Mirrors {@link jakarta.servlet.annotation.MultipartConfig}.
105+
* If you omit it (no fields changed), it will not set a multipart config.
106+
*/
107+
MultipartConfigValues multipartConfig() default @MultipartConfigValues;
108+
109+
/**
110+
* Nested annotation that parallels the fields of {@link jakarta.servlet.annotation.MultipartConfig}.
111+
*/
112+
@Target({})
113+
@Retention(RetentionPolicy.RUNTIME)
114+
@Documented
115+
@interface MultipartConfigValues {
116+
117+
String location() default "";
118+
long maxFileSize() default -1L;
119+
long maxRequestSize() default -1L;
120+
int fileSizeThreshold() default 0;
121+
122+
}
90123
}

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/ServletContextInitializerBeansTests.java

+113
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,73 @@ void shouldApplyOrderFromOrderAttribute() {
179179
.isEqualTo(ServletConfigurationWithAnnotationAndOrder.ORDER));
180180
}
181181

182+
@Test
183+
@SuppressWarnings("unchecked")
184+
void shouldApplyExtendedServletRegistrationAnnotation() {
185+
load(ServletConfigurationWithExtendedAttributes.class);
186+
// Grab all initializers in the context, including beans that are adapted from @ServletRegistration
187+
ServletContextInitializerBeans initializerBeans = new ServletContextInitializerBeans(
188+
this.context.getBeanFactory(), TestServletContextInitializer.class);
189+
190+
// We expect two registrations in this config: 'testServletWithInitParametersAndMultipart'
191+
// and 'testServletWithExtraBean'. So let's filter them individually or pick the one we want to assert.
192+
193+
// 1) Check the one with initParameters + multipartConfig
194+
ServletRegistrationBean<?> bean = findServletRegistrationBeanByName(initializerBeans, "extended");
195+
assertThat(bean).as("extended servlet registration bean").isNotNull();
196+
197+
// Verify that the standard attributes were applied
198+
assertThat(bean.getServletName()).isEqualTo("extended");
199+
assertThat(bean.getUrlMappings()).containsExactly("/extended/*");
200+
201+
// Verify our new initParameters
202+
assertThat(bean.getInitParameters()).containsEntry("hello", "world")
203+
.containsEntry("flag", "true");
204+
205+
// Verify multi-part config
206+
assertThat(bean.getMultipartConfig()).isNotNull();
207+
assertThat(bean.getMultipartConfig().getLocation()).isEqualTo("/tmp");
208+
assertThat(bean.getMultipartConfig().getMaxFileSize()).isEqualTo(1024);
209+
assertThat(bean.getMultipartConfig().getMaxRequestSize()).isEqualTo(4096);
210+
assertThat(bean.getMultipartConfig().getFileSizeThreshold()).isEqualTo(128);
211+
}
212+
213+
@Test
214+
@SuppressWarnings("unchecked")
215+
void shouldApplyServletRegistrationAnnotationWithExtraRegistrationBeans() {
216+
load(ServletConfigurationWithExtendedAttributes.class);
217+
ServletContextInitializerBeans initializerBeans = new ServletContextInitializerBeans(
218+
this.context.getBeanFactory(), TestServletContextInitializer.class);
219+
220+
// 2) Check the one referencing 'servletRegistrationBeans'
221+
ServletRegistrationBean<?> bean = findServletRegistrationBeanByName(initializerBeans, "extendedWithExtraBeans");
222+
assertThat(bean).as("extendedWithExtraBeans registration bean").isNotNull();
223+
224+
// Confirm standard attributes
225+
assertThat(bean.getServletName()).isEqualTo("extendedWithExtraBeans");
226+
assertThat(bean.getUrlMappings()).containsExactly("/extra/*");
227+
228+
// Confirm that the extra init param from MyExtraServletRegistrationBean was merged
229+
assertThat(bean.getInitParameters()).containsEntry("extra", "fromExtraBean");
230+
}
231+
232+
/**
233+
* Simple helper method to locate a specific ServletRegistrationBean by its name
234+
* from the given ServletContextInitializerBeans collection.
235+
*/
236+
@SuppressWarnings("rawtypes")
237+
private ServletRegistrationBean findServletRegistrationBeanByName(
238+
ServletContextInitializerBeans initializerBeans, String servletName) {
239+
240+
return initializerBeans.stream()
241+
.filter(ServletRegistrationBean.class::isInstance)
242+
.map(ServletRegistrationBean.class::cast)
243+
.filter((registrationBean) -> servletName.equals(registrationBean.getServletName()))
244+
.findFirst()
245+
.orElse(null);
246+
}
247+
248+
182249
private void load(Class<?>... configuration) {
183250
this.context = new AnnotationConfigApplicationContext(configuration);
184251
}
@@ -385,4 +452,50 @@ public void onStartup(ServletContext servletContext) {
385452

386453
}
387454

455+
@Configuration(proxyBeanMethods = false)
456+
static class ServletConfigurationWithExtendedAttributes {
457+
458+
@Bean
459+
@ServletRegistration(
460+
name = "extended",
461+
urlMappings = "/extended/*",
462+
initParameters = { "hello=world", "flag=true" },
463+
multipartConfig = @ServletRegistration.MultipartConfigValues(
464+
location = "/tmp",
465+
maxFileSize = 1024,
466+
maxRequestSize = 4096,
467+
fileSizeThreshold = 128
468+
)
469+
)
470+
TestServlet testServletWithInitParametersAndMultipart() {
471+
return new TestServlet();
472+
}
473+
474+
@Bean
475+
MyExtraServletRegistrationBean myExtraServletRegistrationBean() {
476+
MyExtraServletRegistrationBean bean = new MyExtraServletRegistrationBean();
477+
bean.addInitParameter("extra", "fromExtraBean");
478+
return bean;
479+
}
480+
481+
@Bean
482+
@ServletRegistration(
483+
name = "extendedWithExtraBeans",
484+
urlMappings = "/extra/*",
485+
servletRegistrationBeans = { MyExtraServletRegistrationBean.class }
486+
)
487+
TestServlet testServletWithExtraBean() {
488+
return new TestServlet();
489+
}
490+
491+
static class MyExtraServletRegistrationBean extends ServletRegistrationBean<HttpServlet> {
492+
493+
MyExtraServletRegistrationBean() {
494+
super();
495+
}
496+
497+
}
498+
}
499+
500+
388501
}

0 commit comments

Comments
 (0)