使用Spring设计一个Java库

27

我正在将一个现有程序中的某些功能提取到一个单独的库中。

这个程序使用Spring进行依赖注入和其他任务,我希望在库中也继续使用它。

这个库需要监视文件系统中的更改,因此它会启动一些独立的线程来完成这个任务。

我不太清楚初始化这个库的选项:

  • 如何初始化这个库的上下文?我不能假设库的用户也会使用Spring,但我可以将Spring与库一起分发。

  • 如何管理我的文件系统监视线程?期望程序实例化库的主类并调用init或类似的方法是好的设计吗?

4个回答

11
我如何初始化库的上下文?我不能假设库用户也会使用Spring,但我可以将Spring与库一起分发。
我正在编写使用Spring上下文的库,类似于这样做法。假设你的库名为 FooLib,拥有两个服务: FooServiceBarService,以及一个名为 SpringContext 的类来通过Java配置配置你的Spring上下文:
public final class FooLib {

    private static ApplicationContext applicationContext;

    private FooLib() {
    }

    public static FooService getFooService() {
        return getApplicationContext().getBean(FooService.class);
    }

    public static BarService getBarService() {
        return getApplicationContext().getBean(BarService.class);
    }

    private static ApplicationContext getApplicationContext() {
        if (applicationContext == null) {
            applicationContext = new AnnotationConfigApplicationContext(SpringContext.class);
        }
        return applicationContext;
    }
}

那么客户端可以以以下方式使用BarService

BarService barService = FooLib.getBarService();

我该如何管理文件系统监控线程?期望程序实例化库的主类并调用init或者类似的方法是个好的设计吗?

你可以在Spring上下文中静态地启动你的监控子系统,比如在SpringContext类中。

@Configuration
@ComponentScan(basePackages = "com.yourgroupid.foolib")
public class SpringContext {

    @Bean
    public MonitoringSystem monitoringSystem() {
        MonitoringSystem monitoringSystem = new MonitoringSystem();
        monitoringSystem.start();
        return monitoringSystem;
    }

}

这应该足够了,因为Spring默认会急切地创建它的bean。

干杯


9
如何初始化库的上下文?我不能假设库的用户也会使用Spring,但我可以将Spring与库一起分发。
这取决于你的库以你需要的方式实例化Spring。通常在你的接口入口点中完成此操作,该入口点委托给一个使用例如ClassPathXmlApplicationContext来配置Spring的例程。以下是一个示例:
public class SpringContextLoader {
   private static ApplicationContext ctx = null;
   public static void init() {
       if (ctx == null) {
          ctx = ClassPathXmlApplicationContext("classpath:/applicatonContext.xml");
       }
   }
}

如何管理我的文件系统监控线程?期望程序实例化库的主类并调用 init 或类似方法是好的设计吗?
在这种情况下,您可能会提供一个非守护线程,例如,必须手动终止线程才能使应用程序正常退出的线程。因此,您应该提供 start 和 stop 机制。在您的情况下,这些机制可能更好地被称为 registerEventListener 和 unregisterAllEventListener(因为我猜想您想将文件系统事件传递给客户端...)。另一种选择可能是使用 Spring 的 quartz 调度。

实际上我不需要将FS事件传递给客户端。这只是为了让库知道可用的文件,因为它需要访问某些在随机时间更改的文件的不同版本。因此,这是库的内部使用,我目前将其设计为守护线程,但我不知道从Spring处理守护线程的最佳方法是什么。 - Iker Jimenez
@Jimenez,最简单的方法并不总是最糟糕的:new Thread(new Runnable()).setDaemon(true),除非你想采用优美的@Scheduled方法。 - Johan Sjöberg
我知道,现在就是这样 :) ,但我只是好奇是否有其他更好的选择。 - Iker Jimenez
@Jimenez,Spring是核心中的一个IoC容器,它不涉及线程模型,这些模型已经由Java语言提供。 - Johan Sjöberg

4

如何初始化库的上下文?我不能假设库用户也会使用Spring,但我可以将Spring与库一起分发。

您可以使用PropertyPlaceholderConfigurer从(可能是外部的)属性文件中读取配置设置,用户可以编辑该文件。这样,用户就不会暴露给Spring的细节,甚至不需要使用XML配置文件。

如何管理我的文件系统监控线程?期望程序实例化库的主类并调用init或类似方法是好的设计吗?

我建议使用Java并发框架,特别是ScheduledExecutorService在指定的时间间隔内运行监控任务。但是,您可能希望隐藏此实现细节,只公开某些init方法以传递所需的计时器间隔。


关于第一部分,我认为Java库应该通过Java API进行配置,而不是属性文件。 - Katona

1
据我所知,无法配置库以自动启动线程,您必须定义一个类作为起始点。使用Maven,您可以创建一个可执行的jar包: http://maven.apache.org/plugins/maven-shade-plugin/examples/executable-jar.html 在您的主类中,只需简单地使用:
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:myspring-context.xml");      
    context.registerShutdownHook();

对于线程,您可以尝试实现可运行的接口,并初始化一个使用Spring任务执行器启动线程的bean。但是,我可以建议的更优雅的解决方案是将您的线程创建为POJO,然后使用Spring任务调度程序,方法如下:

<bean id="threadPojo" class="com.mydomain.ThreadPojo">
</bean>
<task:scheduled-tasks scheduler="mydomainTaskScheduler">
    <task:scheduled ref="threadPojo" method="process" fixed-delay="${delay-pool}"/>
</task:scheduled-tasks>
<task:scheduler id="mydomainTaskScheduler" pool-size="${my-pool-size}" />

希望这对你有所帮助。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接