spring:`@ autowired`在哪里找豆?

时间:2017-01-09 15:00:00

标签: spring spring-mvc dependency-injection

我有一个带有两个上下文的spring mvc应用程序(在我的@Import子类中声明)

  • 根上下文包含模型,存储库等
  • mvc上下文包含控制器

现在,我可以将一个存储库注入控制器,但为什么呢?

  • Web上下文是否还包含来自根上下文的bean(类似于@Autowired ??)。文档暗示他们有父子关系,但通过检查Web上下文,我不在其中的存储库bean。
  • 或者,{{1}}是否跨多个上下文工作?而且,如果是这样,怎么样??

2 个答案:

答案 0 :(得分:1)

两个上下文都存储在同一个servlet上下文中。

如果您注意到AbstractDispatcherServletInitializerAbstractAnnotationConfigDispatcherServletInitializer的父类,则会在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的上下文。