我有一个带有两个上下文的spring mvc应用程序(在我的@Import
子类中声明)
现在,我可以将一个存储库注入控制器,但为什么呢?
@Autowired
??)。文档暗示他们有父子关系,但通过检查Web上下文,我不在其中的存储库bean。 答案 0 :(得分:1)
两个上下文都存储在同一个servlet上下文中。
如果您注意到AbstractDispatcherServletInitializer
,AbstractAnnotationConfigDispatcherServletInitializer
的父类,则会在onStartup
方法上进行注册
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
super.onStartup(servletContext);
registerDispatcherServlet(servletContext);
}
为此,首先调用其父onStartup方法,然后首先添加由createRootApplicationContext
类的AbstractAnnotationConfigDispatcherServletInitializer
方法创建的rootApplicationContext,最后将其添加到onStartup方法上接收的ServletContext中:
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
registerContextLoaderListener(servletContext);
}
/**
* Register a {@link ContextLoaderListener} against the given servlet context. The
* {@code ContextLoaderListener} is initialized with the application context returned
* from the {@link #createRootApplicationContext()} template method.
* @param servletContext the servlet context to register the listener against
*/
protected void registerContextLoaderListener(ServletContext servletContext) {
WebApplicationContext rootAppContext = createRootApplicationContext();
if (rootAppContext != null) {
ContextLoaderListener listener = new ContextLoaderListener(rootAppContext);
listener.setContextInitializers(getRootApplicationContextInitializers());
servletContext.addListener(listener);
}
else {
logger.debug("No ContextLoaderListener registered, as " +
"createRootApplicationContext() did not return an application context");
}
}
然后,AbstractDispatcherServletInitializer
调用其registerDispatcherServlet
方法,调用createServletApplicationContext
类中的抽象方法AbstractAnnotationConfigDispatcherServletInitializer
并创建dispatcherServlet
然后添加到相同的ServletContext:
/**
* Register a {@link DispatcherServlet} against the given servlet context.
* <p>This method will create a {@code DispatcherServlet} with the name returned by
* {@link #getServletName()}, initializing it with the application context returned
* from {@link #createServletApplicationContext()}, and mapping it to the patterns
* returned from {@link #getServletMappings()}.
* <p>Further customization can be achieved by overriding {@link
* #customizeRegistration(ServletRegistration.Dynamic)} or
* {@link #createDispatcherServlet(WebApplicationContext)}.
* @param servletContext the context to register the servlet against
*/
protected void registerDispatcherServlet(ServletContext servletContext) {
String servletName = getServletName();
Assert.hasLength(servletName, "getServletName() must not return empty or null");
WebApplicationContext servletAppContext = createServletApplicationContext();
Assert.notNull(servletAppContext,
"createServletApplicationContext() did not return an application " +
"context for servlet [" + servletName + "]");
FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext);
dispatcherServlet.setContextInitializers(getServletApplicationContextInitializers());
ServletRegistration.Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet);
Assert.notNull(registration,
"Failed to register servlet with name '" + servletName + "'." +
"Check if there is another servlet registered under the same name.");
registration.setLoadOnStartup(1);
registration.addMapping(getServletMappings());
registration.setAsyncSupported(isAsyncSupported());
Filter[] filters = getServletFilters();
if (!ObjectUtils.isEmpty(filters)) {
for (Filter filter : filters) {
registerServletFilter(servletContext, filter);
}
}
customizeRegistration(registration);
}
这就是为什么你可以在控制器中注入一个存储库,因为这两个上下文在同一个ServletContext中。
答案 1 :(得分:0)
我通常从AbstractAnnotationConfigDispatcherServletInitializer.getRootConfigClasses()
这个类返回一个上下文,然后使用ComponentScan和/或Imports找到@Configurations和@Components等。
除非您显式创建父子上下文,否则所有bean,组件和服务都会在单个ApplicationContext中结束。通过显式我的意思是你必须在调用refresh()之前在ApplicationContext上调用setParent(),所以你通常知道你何时完成它。
这意味着你可以从相同的应用程序上下文中将@AutoWire转换为另一个spring bean(如果你有嵌套的上下文,则autowired bean可能来自父上下文)
修改强>
如果使用AbstractAnnotationConfigDispatcherServletInitializer.getServletConfigClasses()
并从初始化程序返回两个上下文,则servletConfig上下文将是子上下文,根上下文将是父上下文。这意味着您可以将Bean从RootConfig上下文自动装配到servletConfig上下文bean中,但不是相反。 - 这就是为什么我通常只返回来自getRootConfigClasses()
的{{1}}和null
的上下文。