Skip to content

Commit 12bf433

Browse files
dmytrodanilenkovdmytrodanilenkov
dmytrodanilenkov
authored andcommitted
Fix for issue #45001 - ServletRegistrationBean has those properties, but @ServletRegistration hasn't: initParameters, servletRegistrationBeans, multipartConfig
Signed-off-by: Dmytro Danilenkov <[email protected]>
1 parent e87d5d7 commit 12bf433

File tree

3 files changed

+195
-0
lines changed

3 files changed

+195
-0
lines changed

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

+50
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
* @author Phillip Webb
6363
* @author Brian Clozel
6464
* @author Moritz Halbritter
65+
* @author Dmytro Danilenkov
6566
* @since 1.4.0
6667
*/
6768
public class ServletContextInitializerBeans extends AbstractCollection<ServletContextInitializer> {
@@ -321,10 +322,59 @@ private void configureFromAnnotation(ServletRegistrationBean<Servlet> bean, Serv
321322
if (registration.urlMappings().length > 0) {
322323
bean.setUrlMappings(Arrays.asList(registration.urlMappings()));
323324
}
325+
326+
if (registration.initParameters().length > 0) {
327+
bean.setInitParameters(parseInitParameters(registration.initParameters()));
328+
}
329+
330+
ServletRegistration.MultipartConfigValues multipart = registration.multipartConfig();
331+
boolean isMultipartConfigUsed = !(multipart.location().isEmpty()
332+
&& multipart.maxFileSize() == -1L
333+
&& multipart.maxRequestSize() == -1L
334+
&& multipart.fileSizeThreshold() == 0);
335+
if (isMultipartConfigUsed) {
336+
bean.setMultipartConfig(new MultipartConfigElement(
337+
multipart.location(),
338+
multipart.maxFileSize(),
339+
multipart.maxRequestSize(),
340+
multipart.fileSizeThreshold()
341+
));
342+
}
343+
344+
for (Class<? extends ServletRegistrationBean<?>> beanClass : registration.servletRegistrationBeans()) {
345+
ServletRegistrationBean<?> extraBean = this.beanFactory.getBean(beanClass);
346+
bean.getInitParameters().putAll(extraBean.getInitParameters());
347+
}
348+
349+
}
350+
351+
/**
352+
* Parses an array of "key=value" strings into a Map.
353+
* @param initParamsArray Array of strings, expected format "key=value".
354+
* @return Map of parsed key-value pairs.
355+
* @throws IllegalArgumentException if any string doesn't match the "key=value" format.
356+
*/
357+
private Map<String, String> parseInitParameters(String[] initParamsArray) {
358+
Map<String, String> initParams = new LinkedHashMap<>();
359+
for (String kv : initParamsArray) {
360+
int index = kv.indexOf('=');
361+
if (index != -1) {
362+
String key = kv.substring(0, index).trim();
363+
String value = kv.substring(index + 1).trim();
364+
initParams.put(key, value);
365+
}
366+
else {
367+
throw new IllegalArgumentException(
368+
"initParameters must be in 'key=value' format, got: " + kv);
369+
}
370+
}
371+
return initParams;
324372
}
325373

326374
}
327375

376+
377+
328378
/**
329379
* {@link RegistrationBeanAdapter} for {@link Filter} beans.
330380
*/

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

+49
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;
@@ -33,6 +35,7 @@
3335
* annotation-based alternative to {@link ServletRegistrationBean}.
3436
*
3537
* @author Moritz Halbritter
38+
* @author Dmytro Danilenkov
3639
* @since 3.5.0
3740
* @see ServletRegistrationBean
3841
*/
@@ -87,4 +90,50 @@
8790
*/
8891
int loadOnStartup() default -1;
8992

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

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

+96
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
*
4343
* @author Andy Wilkinson
4444
* @author Moritz Halbritter
45+
* @author Dmytro Danilenkov
4546
*/
4647
class ServletContextInitializerBeansTests {
4748

@@ -179,6 +180,55 @@ void shouldApplyOrderFromOrderAttribute() {
179180
.isEqualTo(ServletConfigurationWithAnnotationAndOrder.ORDER));
180181
}
181182

183+
@Test
184+
void shouldApplyExtendedServletRegistrationAnnotation() {
185+
load(ServletConfigurationWithExtendedAttributes.class);
186+
ServletContextInitializerBeans initializerBeans = new ServletContextInitializerBeans(
187+
this.context.getBeanFactory(), TestServletContextInitializer.class);
188+
189+
ServletRegistrationBean<?> bean = findServletRegistrationBeanByName(initializerBeans, "extended");
190+
191+
assertThat(bean.getServletName()).isEqualTo("extended");
192+
assertThat(bean.getUrlMappings()).containsExactly("/extended/*");
193+
194+
assertThat(bean.getInitParameters()).containsEntry("hello", "world")
195+
.containsEntry("flag", "true");
196+
197+
assertThat(bean.getMultipartConfig()).isNotNull();
198+
assertThat(bean.getMultipartConfig().getLocation()).isEqualTo("/tmp");
199+
assertThat(bean.getMultipartConfig().getMaxFileSize()).isEqualTo(1024);
200+
assertThat(bean.getMultipartConfig().getMaxRequestSize()).isEqualTo(4096);
201+
assertThat(bean.getMultipartConfig().getFileSizeThreshold()).isEqualTo(128);
202+
}
203+
204+
@Test
205+
void shouldApplyServletRegistrationAnnotationWithExtraRegistrationBeans() {
206+
load(ServletConfigurationWithExtendedAttributes.class);
207+
ServletContextInitializerBeans initializerBeans = new ServletContextInitializerBeans(
208+
this.context.getBeanFactory(), TestServletContextInitializer.class);
209+
210+
ServletRegistrationBean<?> bean = findServletRegistrationBeanByName(initializerBeans, "extendedWithExtraBeans");
211+
assertThat(bean).as("extendedWithExtraBeans registration bean").isNotNull();
212+
213+
assertThat(bean.getServletName()).isEqualTo("extendedWithExtraBeans");
214+
assertThat(bean.getUrlMappings()).containsExactly("/extra/*");
215+
216+
assertThat(bean.getInitParameters()).containsEntry("extra", "fromExtraBean");
217+
}
218+
219+
@SuppressWarnings("rawtypes")
220+
private ServletRegistrationBean findServletRegistrationBeanByName(
221+
ServletContextInitializerBeans initializerBeans, String servletName) {
222+
223+
return initializerBeans.stream()
224+
.filter(ServletRegistrationBean.class::isInstance)
225+
.map(ServletRegistrationBean.class::cast)
226+
.filter((registrationBean) -> servletName.equals(registrationBean.getServletName()))
227+
.findFirst()
228+
.orElse(null);
229+
}
230+
231+
182232
private void load(Class<?>... configuration) {
183233
this.context = new AnnotationConfigApplicationContext(configuration);
184234
}
@@ -385,4 +435,50 @@ public void onStartup(ServletContext servletContext) {
385435

386436
}
387437

438+
@Configuration(proxyBeanMethods = false)
439+
static class ServletConfigurationWithExtendedAttributes {
440+
441+
@Bean
442+
@ServletRegistration(
443+
name = "extended",
444+
urlMappings = "/extended/*",
445+
initParameters = { "hello=world", "flag=true" },
446+
multipartConfig = @ServletRegistration.MultipartConfigValues(
447+
location = "/tmp",
448+
maxFileSize = 1024,
449+
maxRequestSize = 4096,
450+
fileSizeThreshold = 128
451+
)
452+
)
453+
TestServlet testServletWithInitParametersAndMultipart() {
454+
return new TestServlet();
455+
}
456+
457+
@Bean
458+
MyExtraServletRegistrationBean myExtraServletRegistrationBean() {
459+
MyExtraServletRegistrationBean bean = new MyExtraServletRegistrationBean();
460+
bean.addInitParameter("extra", "fromExtraBean");
461+
return bean;
462+
}
463+
464+
@Bean
465+
@ServletRegistration(
466+
name = "extendedWithExtraBeans",
467+
urlMappings = "/extra/*",
468+
servletRegistrationBeans = { MyExtraServletRegistrationBean.class }
469+
)
470+
TestServlet testServletWithExtraBean() {
471+
return new TestServlet();
472+
}
473+
474+
static class MyExtraServletRegistrationBean extends ServletRegistrationBean<HttpServlet> {
475+
476+
MyExtraServletRegistrationBean() {
477+
super();
478+
}
479+
480+
}
481+
}
482+
483+
388484
}

0 commit comments

Comments
 (0)