gpt4 book ai didi

java - 在独立的 java Spring spring 应用程序(非 web 应用程序,无容器)中摆脱 getBean

转载 作者:搜寻专家 更新时间:2023-10-31 19:40:33 27 4
gpt4 key购买 nike

在 web 应用程序中我们真的不需要做..

ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-context.xml");
ctx.getBean("beanId");

因为一般的做法是像这样在 web.xml 中使用 ContextLoaderServlet 加载上下文文件并注入(inject)所有具有依赖性的 beans..

<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring-context.xml /WEB-INF/applicationContext.xml</param-value>
</context-param>

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

<!-- or use the ContextLoaderServlet instead of the above listener
<servlet>
<servlet-name>context</servlet-name>
<servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
-->

但是,在没有容器的独立 Java 应用程序中,我最终会执行 ctx.getBean("xyz");。有没有一种干净的方法可以做到这一点,无法在网上找到示例。

我检查了.. Simple Spring, use of ClasspathApplicationContext for standalone apps, how to reuse? ,其中谈到使用 SingletonBeanFactoryLocator,但它最终使用的是 context.getBean()。

我还查看了 ServiceLocatorFactoryBean,但那又是通过使用代理按需获取 beans。

我正在寻找一种从我的独立 Java 应用程序的 main() 程序加载上下文文件(所有 bean)的解决方案,这样我就不想按需获取 bean。

示例代码:

public interface IReader {
public String read();
}

public class TextFileReader implements IReader {

private StringBuilder builder = null;
private Scanner scanner = null;

public TextFileReader(String fileName) throws FileNotFoundException {
scanner = new Scanner(new File(fileName));
builder = new StringBuilder();
}

public String read() {
while (scanner.hasNext()) {
builder.append(scanner.next());
builder.append(",");
}
return builder.toString();
}
}



public class SpringNoConextDataReaderClient {

private IReader reader = null;

public void setReader(TextFileReader reader) {
this.reader = reader;
}

private String fetchDataOne() {
return reader.read();
}

private String fetchDataTwo() {
return reader.read();
}

public static void main(String[] args) {

final ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");
String fetchedData = context.getBean(SpringNoConextDataReaderClient.class).fetchDataOne(); // <-- reader is injected as TextFileReader in fetchDataOne which reads the file

SpringNoConextDataReaderClient client = new SpringNoConextDataReaderClient();
client.fetchDataOne(); // <-- reader is null and throws NPE, probably its lifetime ended with previous call?

System.out.println("Example 1.1: Got data without context: " + fetchDataOne);
}

}

spring-context.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"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">

<bean id="reader" class="com.himalay.spring.core.basic.readers.TextFileReader">
<constructor-arg value="src/main/resources/data.txt" />
</bean>

<bean id="springNoConextDataReaderClient" class="com.himalay.spring.core.basic.SpringNoConextDataReaderClient">
<property name="reader"><ref bean = "reader" /></property>
</bean>

</beans>

谢谢。

最佳答案

在独立应用程序中,您需要创建 ApplicationContext 的实例自己并使用它来加载至少一个 bean。但是你加载的一个 bean 可以使用所有的 Spring 魔法,比如 @Autowired等等,不需要使用 getBean任何更多。因此,您可以使用 getBean 加载一个 Bootstrap bean。然后让这个 bean 做其他所有事情。像这样的东西:

@Component
public class Main
{
@Autowired
protected MyDependencyClass2 someClass1;
@Autowired
protected MyDependencyClass2 someClass2;
// ...
// or if you need an entity manager
@PersistenceContext
protected EntityManager em;
// etc.

protected void mainInternal(String[] args)
throws Exception
{
// do everything here
// all dependencies are initialized
// ...
}

public static void main(String[] args)
throws Exception
{
// Bootstrap Spring and let it create and configure beans.
final ApplicationContext context =
new ClassPathXmlApplicationContext("spring-context.xml");
context.getBean(Main.class).mainInternal(args);
}
}

注意:通常使用 getBean(Class) 的变体更安全或 getBean(String,Class)需要 Class<T>参数。


如果你调用new Main() ,依赖项将不会被初始化。 Spring 不知道您使用 new 创建的实例,仅关于它自己创建的实例。这是 Spring 的一个关键概念。它不仅创建该类的一个实例,还管理它与其他 bean 的依赖关系,可以使用方面等处理创建的实例。这在您使用 new 自己创建的实例上是不可能的。 .

这里的重点是,如果您将所有代码从 mainmainInternal , 所有 您需要的依赖项将被初始化。不仅Main ,还有它的依赖关系,它们的依赖关系等等。因此,如果您的应用程序是使用 Spring 正确构建的,并且如果它仅使用 Spring 功能(例如 @Autowired )管理依赖关系,那么您将获得一个类似于您所拥有的环境在网络应用程序中。

所以在这种情况下,正确的过程是:制作应用程序启动依赖项所需的所有 beans Main .它们将与所有依赖项一起初始化,您可以在 mainInternal 中安全地使用它们或者不管它叫什么。


编辑:评论你的例子。正如我所解释的,Spring 只管理它创建的对象,而不是你使用 new 创建的对象。 .在您的示例中,您使用

SpringNoConextDataReaderClient client = new SpringNoConextDataReaderClient();

所以 client不会由 Spring 管理,也不会设置或解析它的依赖项。可以这样想:如果您自己创建了一个对象,Spring 怎么会知道它呢?

此外,您的示例设计不当。 Spring 的主要思想是管理程序组件 并将它们连接在一起使用 Inversion of control原则。在大多数情况下,此类程序组件应为 singleton objects在应用程序的整个生命周期中都存在。 (也可能有生命周期较短的组件,例如一个 HTTP 请求或一个 HTTP session 范围,但这超出了这个问题的范围。)重要的是,这样的单例组件一旦它们不应该改变它们的内部状态'已初始化。

另一方面,Spring 并不意味着管理您的数据对象,例如 IReader。 . IReader不是程序的组件,它是您创建的对象,从文件中读取并随后处理。更好的设计是:

  • 有一个为您提供 IReader 的单例 bean按需,例如

    public class TextFileReaderProvider {
    public IReader createReader() { ... }
    }
  • 将此提供程序连接到 SpringNoConextDataReaderClient喜欢

    public class SpringNoConextDataReaderClient {
    @Autowired
    protected TextFileReaderProvider readerProvider;

    public SomeResult doMyComputation() {
    IReader r = readerProvider.createReader();
    try {
    // compute the result
    return theResult;
    } finally {
    r.close();
    }
    }
    }

    (或者代替 @Autowired 在 XML 中手动配置依赖项)。

  • main ,让 Spring 为您提供 SpringNoConextDataReaderClient 的实例并调用doMyComputation()对此。

这种设计会引导您将软件分成不同的组件,这些组件可以在整个应用程序中重复使用,并且不会出现并发问题。

关于java - 在独立的 java Spring spring 应用程序(非 web 应用程序,无容器)中摆脱 getBean,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11971389/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com