Skip to content

Commit 18eb5a2

Browse files
committed
Take the servlet context from the container, not a bean reference
The bean-definition wrap mode wired the resolver's ServletContext as a RuntimeBeanReference to the "servletContext" bean. That bean is registered by the container when the web server starts, so it exists only at runtime and has no bean definition at all. Spring's ahead-of-time processing refreshes the context without starting a web server, and determining the wrapper's constructor requires the type of every supplied argument, so the reference fails the whole run: AotBeanProcessingException: Error processing bean with name 'jspViewResolver' Caused by: NoSuchBeanDefinitionException: No bean named 'servletContext' available SiteMeshViewResolver now implements ServletContextAware and takes its context from the container, which is how a servlet-scoped collaborator is normally supplied and leaves nothing in the definition to resolve early. A three-argument constructor is added for that path; the four-argument one stays for callers that build the resolver themselves, including the instance-level SiteMeshViewResolverBeanPostProcessor, which constructs it outside any aware callback. servletContextBeanName is deprecated on the definition post-processor, where it no longer has an effect.
1 parent eacfc70 commit 18eb5a2

2 files changed

Lines changed: 51 additions & 6 deletions

File tree

spring-webmvc-sitemesh/src/main/java/org/sitemesh/webmvc/SiteMeshViewResolver.java

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.sitemesh.webapp.DispatchMode;
2626

2727
import org.springframework.core.Ordered;
28+
import org.springframework.web.context.ServletContextAware;
2829
import org.springframework.web.servlet.SmartView;
2930
import org.springframework.web.servlet.View;
3031
import org.springframework.web.servlet.ViewResolver;
@@ -37,14 +38,14 @@
3738
* are passed through un-wrapped so decorator renders themselves do not
3839
* trigger nested decoration.
3940
*/
40-
public class SiteMeshViewResolver implements ViewResolver, Ordered {
41+
public class SiteMeshViewResolver implements ViewResolver, Ordered, ServletContextAware {
4142

4243
private static final String DEFAULT_LAYOUT_PATH_PREFIX = "/layouts";
4344

4445
private final ViewResolver innerViewResolver;
4546
private final ContentProcessor contentProcessor;
4647
private final DecoratorSelector<SiteMeshContext> decoratorSelector;
47-
private final ServletContext servletContext;
48+
private ServletContext servletContext;
4849

4950
private String layoutPathPrefix = DEFAULT_LAYOUT_PATH_PREFIX;
5051
private int order;
@@ -53,7 +54,25 @@ public class SiteMeshViewResolver implements ViewResolver, Ordered {
5354

5455
/**
5556
* Creates a resolver that wraps {@code innerViewResolver} and decorates
56-
* the views it resolves.
57+
* the views it resolves, taking its servlet context from the container
58+
* through {@link ServletContextAware}.
59+
*
60+
* @param innerViewResolver the resolver whose views are wrapped
61+
* @param contentProcessor parses buffered view output into a
62+
* {@link org.sitemesh.content.Content}
63+
* @param decoratorSelector selects the decorator path(s) for the parsed
64+
* content
65+
*/
66+
public SiteMeshViewResolver(ViewResolver innerViewResolver,
67+
ContentProcessor contentProcessor,
68+
DecoratorSelector<SiteMeshContext> decoratorSelector) {
69+
this(innerViewResolver, contentProcessor, decoratorSelector, null);
70+
}
71+
72+
/**
73+
* Creates a resolver with the servlet context supplied directly, for
74+
* callers constructing the resolver outside the container's
75+
* {@link ServletContextAware} callback.
5776
*
5877
* @param innerViewResolver the resolver whose views are wrapped
5978
* @param contentProcessor parses buffered view output into a
@@ -79,6 +98,20 @@ public SiteMeshViewResolver(ViewResolver innerViewResolver,
7998
: Ordered.LOWEST_PRECEDENCE;
8099
}
81100

101+
@Override
102+
public void setServletContext(ServletContext servletContext) {
103+
this.servletContext = servletContext;
104+
}
105+
106+
/**
107+
* The servlet context this resolver renders against.
108+
*
109+
* @return the servlet context
110+
*/
111+
protected ServletContext getServletContext() {
112+
return servletContext;
113+
}
114+
82115
@Override
83116
public View resolveViewName(String viewName, Locale locale) throws Exception {
84117
View innerView = innerViewResolver.resolveViewName(viewName, locale);

spring-webmvc-sitemesh/src/main/java/org/sitemesh/webmvc/SiteMeshViewResolverPostProcessor.java

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,11 @@
4242
* under {@link #setSiteMeshViewResolverBeanName(String) siteMeshViewResolverBeanName}
4343
* (which by default replaces the original bean under its own name), with the
4444
* remaining constructor arguments wired as {@link RuntimeBeanReference
45-
* references} to the content processor, decorator selector and servlet
46-
* context beans.</p>
45+
* references} to the content processor and decorator selector beans. The
46+
* servlet context is not among them: the resolver receives it from the
47+
* container through {@link org.springframework.web.context.ServletContextAware},
48+
* which keeps the definition free of a reference to a bean that exists only
49+
* once the web server has started.</p>
4750
*
4851
* <p>Embedding the original definition keeps the undecorated resolver out of
4952
* reach of {@code getBeansOfType(ViewResolver)} sweeps: a delegating resolver
@@ -114,7 +117,6 @@ public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) t
114117
}
115118
args.addIndexedArgumentValue(1, new RuntimeBeanReference(contentProcessorBeanName));
116119
args.addIndexedArgumentValue(2, new RuntimeBeanReference(decoratorSelectorBeanName));
117-
args.addIndexedArgumentValue(3, new RuntimeBeanReference(servletContextBeanName));
118120
wrapperDefinition.getPropertyValues().add("dispatchMode", dispatchMode);
119121
wrapperDefinition.getPropertyValues().add("includeErrorPages", includeErrorPages);
120122

@@ -309,7 +311,14 @@ public void setDecoratorSelectorBeanName(String decoratorSelectorBeanName) {
309311
* the resolver. Default: {@code "servletContext"}.
310312
*
311313
* @return the servlet context bean name
314+
* @deprecated the resolver receives its servlet context from the container
315+
* through {@link org.springframework.web.context.ServletContextAware}, so
316+
* this name is no longer used. Registering the bean definition ahead of the
317+
* container start means there is nothing to reference: the servlet context
318+
* exists only once the web server has started, which is also why a
319+
* reference to it cannot be processed ahead of time.
312320
*/
321+
@Deprecated(since = "3.3.0", forRemoval = true)
313322
public String getServletContextBeanName() {
314323
return servletContextBeanName;
315324
}
@@ -319,7 +328,10 @@ public String getServletContextBeanName() {
319328
* into the resolver.
320329
*
321330
* @param servletContextBeanName the servlet context bean name
331+
* @deprecated see {@link #getServletContextBeanName()}; this setting has no
332+
* effect.
322333
*/
334+
@Deprecated(since = "3.3.0", forRemoval = true)
323335
public void setServletContextBeanName(String servletContextBeanName) {
324336
this.servletContextBeanName = servletContextBeanName;
325337
}

0 commit comments

Comments
 (0)