在独立的Java Spring spring应用程序中删除getBean(非Web应用程序,没有容器)

Him*_*dar 5 java spring

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

ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-context.xml");
ctx.getBean("beanId");
Run Code Online (Sandbox Code Playgroud)

因为一般的做法是加载上下文文件并使用webL中的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>
--> 
Run Code Online (Sandbox Code Playgroud)

然而,在没有容器的独立Java应用程序中,我最终做了ctx.getBean("xyz"); .有没有干净的方法来做到这一点,无法在网上找到一个例子.

我检查过.. Simple Spring,使用ClasspathApplicationContext进行独立应用,如何重用?,讨论使用SingletonBeanFactoryLocator,但最终使用context.getBean().

我还查看了ServiceLocatorFactoryBean,但这又是通过使用代理来获取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);
    }

}
Run Code Online (Sandbox Code Playgroud)

弹簧的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>
Run Code Online (Sandbox Code Playgroud)

谢谢.

Pet*_*lák 8

在独立应用程序中,您需要创建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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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


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

这里的要点是,如果将所有代码从中移动mainmainInternal,则将初始化所有必需的依赖项.不仅如此Main,还有它的依赖关系,它们的依赖关系等等.所以如果你的应用程序是使用Spring正确构建的,如果它只使用Spring功能(例如@Autowired)来管理依赖关系,那么你将得到一个类似于你所拥有的环境的环境.一个Web应用程序.

因此,在这种情况下,正确的过程是:创建应用程序启动依赖项所需的所有bean Main.它们将与所有依赖项一起初始化,您可以安全地使用它们mainInternal或任何它所调用的.


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

SpringNoConextDataReaderClient client = new SpringNoConextDataReaderClient();
Run Code Online (Sandbox Code Playgroud)

所以client不会被Spring管理,也不会设置或解决它的依赖关系.可以这样想:如果你自己创造一个物体,春天怎么知道呢?

此外,您的示例设计不合理.Spring的主要思想是管理程序组件并将它们连接在一起使用Inversion of control principle.在大多数情况下,这样的程序组件应该是在应用程序的整个生命周期中存在的单例对象.(也可以使用寿命较短的组件,例如一个HTTP请求或一个HTTP会话范围,但这超出了本问题的范围.)重要的是,这些单例组件一旦发生就不应改变其内部状态'初始化.

另一方面,Spring并不是要管理您的数据对象,例如IReader.IReader它不是程序的一个组件,它是您创建的对象,从文件中读取并在之后进行处置.更好的设计是:

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

    public class TextFileReaderProvider {
        public IReader createReader() { ... }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  • 将此提供商连接到SpringNoConextDataReaderClient类似的

    public class SpringNoConextDataReaderClient {
        @Autowired
        protected TextFileReaderProvider readerProvider;
    
        public SomeResult doMyComputation() {
            IReader r = readerProvider.createReader();
            try {
                // compute the result
                return theResult;
            } finally {
                r.close();
            }
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)

    (或者代替@Autowired在XML中手动配置依赖关系).

  • main,让Spring为您提供一个实例SpringNoConextDataReaderClient并调用doMyComputation()它.

这种设计使您可以将软件分离为可在整个应用程序中重用的不同组件,而不会出现并发问题.