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

在独立的JavaSpring应用程序中摆脱getBean(非web应用程序,无容器),java,spring,Java,Spring,在web应用程序中,我们实际上不需要这样做 ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-context.xml"); ctx.getBean("beanId"); 因为一般做法是加载上下文文件,并使用web.xml中的ContextLoaderServlet将所有bean与依赖项一起注入,如下所示 <context-param> <param-name>contextConf

在web应用程序中,我们实际上不需要这样做

ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-context.xml");
ctx.getBean("beanId");
因为一般做法是加载上下文文件,并使用web.xml中的ContextLoaderServlet将所有bean与依赖项一起注入,如下所示

<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>
--> 

上下文配置位置
/WEB-INF/spring-context.xml/WEB-INF/applicationContext.xml
org.springframework.web.context.ContextLoaderListener
然而,在一个没有容器的独立java应用程序中,我最终执行了ctx.getBean(“xyz”)。有没有一个干净的方法可以做到这一点,无法找到一个例子在线

我查了一下,它谈到使用SingletonBeanFactoryLocator,但它最终使用了context.getBean()

我还研究了ServiceLocatoryFactoryBean,但这也是通过使用代理按需获取Bean

我正在寻找一种解决方案,从我的独立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);
    }

}
公共接口IReader{
公共字符串读取();
}
公共类TextFileReader实现IReader{
私有StringBuilder=null;
专用扫描仪=空;
公共TextFileReader(字符串文件名)引发FileNotFoundException{
扫描仪=新扫描仪(新文件(文件名));
生成器=新的StringBuilder();
}
公共字符串读取(){
while(scanner.hasNext()){
append(scanner.next());
生成器。追加(“,”);
}
返回builder.toString();
}
}
公共类SpringNoConextDataReaderClient{
专用IReader reader=null;
public void setReader(TextFileReader){
this.reader=读取器;
}
私有字符串fetchDataOne(){
返回reader.read();
}
私有字符串fetchDataTwo(){
返回reader.read();
}
公共静态void main(字符串[]args){
final ApplicationContext context=new ClassPathXmlApplicationContext(“spring context.xml”);

字符串fetchedData=context.getBean(SpringNoConextDataReaderClient.class).fetchDataOne()在一个独立的应用程序中,您需要自己创建一个
ApplicationContext
的实例,并使用它来加载至少一个bean。但是您加载的一个bean可以使用所有的Spring魔法,如
@Autowired
等,并且不再需要使用
getBean
。因此,您可以使用一个引导bean来加载
getBean
然后让这一个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);
    }
}
注意:通常,使用或的变体(采用
参数)更安全


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

这里的要点是,如果您将所有代码从
main
移动到
main internal
,那么您所需的所有依赖项都将被初始化。不仅
main
,而且它的依赖项、它们的依赖项等也将被初始化。因此,如果您的应用程序是使用Spring正确构建的,并且它只使用Spring管理依赖项,那么功能(如
@Autowired
),然后您将获得与web应用程序中类似的环境

因此,在本例中,正确的过程是:生成
Main
的应用程序启动依赖项所需的所有bean。它们将与其所有依赖项一起初始化,您可以在
Main internal
或它调用的任何程序中安全地使用它们


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

SpringNoConextDataReaderClient client = new SpringNoConextDataReaderClient();
因此,
client
将不由Spring管理,也不会设置或解析它的依赖项

另外,您的示例设计得不好。Spring的主要思想是管理程序组件,并原则上将它们连接在一起。在大多数情况下,这些程序组件都是在应用程序的整个生命周期中存在的。(也可以使用寿命较短的组件,例如一个HTTP请求或一个HTTP会话作用域,但这超出了本问题的范围。)重要的一点是,此类单例组件在初始化后不应更改其内部状态

另一方面,Spring并不是用来管理您的数据对象,例如
IReader
IReader
不是程序的组件,而是您创建、读取文件并随后处理的对象。更好的设计是:

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

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

    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为您获取一个
    SpringNoContextDataReaderClient
    的实例,并在此基础上调用
    domaycompution()

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

1.我正在寻找一种解决方案,在这种解决方案中,我们可以始终存在初始化的依赖项,例如:在我的main方法中,我会……
main m=new main();m.main internal()
我看不到依赖项已初始化
public class SpringNoConextDataReaderClient {
    @Autowired
    protected TextFileReaderProvider readerProvider;

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