【问题标题】:Spring context as runtime dependencySpring 上下文作为运行时依赖项
【发布时间】:2016-02-25 14:13:17
【问题描述】:

我对 spring 文档的this section 感到困惑。

例如,要创建应用程序上下文并使用依赖注入来配置应用程序,您的 Maven 依赖项将如下所示:

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>4.2.5.RELEASE</version>
        <scope>runtime</scope>
    </dependency> 
</dependencies>

请注意,如果您不需要针对 Spring API 进行编译,则可以将范围声明为运行时,这通常是基本依赖注入用例的情况。

我知道 JSR 330(DI 注释)。但是你如何与ApplicationContext 类解耦呢?如果你确实与它解耦了,为什么还要依赖 spring?

例如,如何将quick start 重写为spring-context 作为运行时依赖项?或者“基本的依赖注入用例”是什么?

【问题讨论】:

    标签: java spring


    【解决方案1】:

    我认为“基本用例”是指基于 XML 的应用程序上下文。该文档说,如果您不直接在代码中使用 Spring 库,那么您不必将这些库包含在编译类路径中。 XML 配置就是这种情况,因为与 Spring 相关的所有内容都是在 XML 中配置的,因此不会被编译。

    在您链接的快速入门中,作者正在使用基于注释的应用程序上下文配置,这需要在编译和运行时都包含 Spring 库。

    示例 XML 配置: http://www.springbyexample.org/examples/intro-to-ioc-creating-a-spring-application.html

    应该只有几个关键点需要应用程序代码直接访问 IoC 容器 [...]。如果您正在开发一个 Web 应用程序,您可能根本不需要直接访问 IoC 容器,因为它会自动处理您的控制器及其所需的任何 bean 的实例化。

    我并不完全熟悉它,但看起来您也可以使用 JSR330,正如您建议的那样,通过 XML 配置使用注释自动装配 bean。 见here。这将允许使用注解,但不需要在编译时配置中包含 Spring。

    【讨论】:

      【解决方案2】:

      1

      首先,我们来谈谈DI。

      来自Spring Doc的备注,

      依赖管理和依赖注入是不同的东西。

      • 依赖管理是“组装所有需要的库(jar 文件),并在运行时将它们放到您的类路径中,也可能在编译时”。
      • 依赖注入是,假设您想在您的类中创建一个Service 对象,而不是使用service = new Service(); 创建它,而是让spring 框架处理该bean 的生命周期。

      依赖管理示例:

      <dependencies>
          <dependency>
              <groupId>org.springframework</groupId>
              <artifactId>spring-context</artifactId>
              <version>4.2.5.RELEASE</version>
          </dependency> 
      </dependencies>
      

      这样你的项目中就有了所有这些 jar 文件。

      spring-context-4.2.5.RELEASE.jar
      spring-aop-4.2.5.RELEASE.jar
      spring-beans-4.2.5.RELEASE.jar
      spring-core-4.2.5.RELEASE.jar
      

      依赖注入示例:

      在您的quick-start 示例中,您使用构造函数注入将MessageService 注入MessagePrinter。您没有在任何地方创建MessageService。 Spring 容器为您创建它。

      @Component
      public class MessagePrinter {
          final private MessageService service;
          @Autowired
          public MessagePrinter(MessageService service) {
              this.service = service;
          }
          public void printMessage() {
              System.out.println(this.service.getMessage());
          }
      }
      
      @Configuration
      @ComponentScan
      public class Application {
          @Bean
          MessageService mockMessageService() {
              return new MessageService() {
                  public String getMessage() {
                    return "Hello World!";
                  }
              };
          }
          ...
      }
      

      2

      现在我们来谈谈transitive dependency and run-time dependency

      传递依赖

      这意味着发现您自己的依赖项所需的库并自动包含它们。
      例如,如果您在 pom.xml 中指定了依赖项 A 和 B。 A 依赖于 C、D。B 依赖于 E。您无需在配置中包含 C、D、E。 由于传递依赖,C、D、E 会被自动包含进来。

      运行时依赖

      限制传递依赖是依赖作用域的一种。

      "这个范围表示不需要依赖 编译,但用于执行。它在运行时和测试中 类路径,但不是编译类路径。”

      3

      现在的问题是:对于 DI,是否存在“不需要针对 Spring API 进行编译”的情况,而是可以将范围设置为运行时?类似问题here.

      是的,我能想到的一个例子是 Web 应用程序。假设我正在使用带有 Spring 插件的 Strtuts。 (以下示例来自 Manning 的“Struts 2 in Action”)

      我想告诉 Spring 创建一个 Login 类的实例,用作请求的操作对象。

      web.xml添加一个spring web context listener

      <listener>
          <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
      </listener>
      

      定义一个名为springManagedLoginAction的bean Login in applicationContext.xml

      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
          <bean id="portfolioService" class="manning.chapterNine.utils.PortfolioServiceJPAImpl"/>
          <bean id="springManagedLoginAction" class="manning.chapterNine.Login" scope="prototype">
              <property name="portfolioService" ref="portfolioService"/>
          </bean>
      </beans>
      

      struts-config-login.xml的动作类中使用这个bean

      <action name="Login" class="springManagedLoginAction">
          <result type="redirectAction">
              <param name="actionName">AdminPortfolio</param>
              <param name="namespace">/chapterEight/secure</param>
          </result>
          <result name="input">/chapterEight/Login.jsp</result>
      </action>
      

      【讨论】:

      • ;tldr 旧版垃圾。 XML 配置创建了对 spring 的隐式依赖。 ApplicationContext可以被spring感知执行环境实例化。
      【解决方案3】:

      我不确定我是否正确理解了您的问题,但在我看来,如果您使用 JSR-330 注释,为什么您要问为什么需要具有 spring-context 依赖项。

      嗯,从我的角度来看,答案是如果你只使用 JSR-330 注释,你实际上并不需要对 Spring 的依赖,但为了让它们工作,你需要一些理解它们并正确构建的库您和 spring-context 的依赖关系图就是这样的库之一。

      之所以是运行时依赖,是因为你可以在运行时切换这个提供者,至少理论上是这样。

      【讨论】:

      • 我已经多次提到我对依赖具体 JSR-330 实现毫无意义的担忧。我很高兴您的意见与我的一致。但我正在寻找这种逻辑谬误的理由。
      【解决方案4】:

      您可以在单独的包中使用对 javax.inject (@Named, @Inject) 的依赖来实现您的 bean。它们可以从基于 spring 的项目或任何其他具有自己的 DI 实现的容器中使用。

      这是将基于 spring 的组件重新加工为 javax.inject 的示例(无需将项目拆分为不同的包) http://www.mkyong.com/spring3/spring-3-and-jsr-330-inject-and-named-example/

      【讨论】:

        【解决方案5】:

        如果我理解正确,您基本上是在询问依赖注入器是如何初始化的,以及如何在类中注入依赖项。在您提供的快速入门示例中,应用程序上下文是在主类中手动创建的。

        ApplicationContext context = 
              new AnnotationConfigApplicationContext(Application.class);
        

        根据documentation

        独立的应用程序上下文,接受带注释的类作为输入 - 特别是 @Configuration 注释的类,但也很简单 使用 javax.inject 的 @Component 类型和符合 JSR-330 的类 注释。允许使用一一注册课程 register(Class...) 以及使用的类路径扫描 扫描(字符串...)

        另一种初始化 spring 的方法是在 web.xml 中使用 ContextLoaderListner,它将在程序启动时为您引导 spring 应用程序上下文。

        关于如何在web.xml中启动spring的问题已经回答here了。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-08-25
          • 1970-01-01
          • 2023-03-27
          • 1970-01-01
          • 1970-01-01
          • 2013-05-23
          • 1970-01-01
          • 2019-06-28
          相关资源
          最近更新 更多