7

我刚刚开始了一个新的春季项目,这次我想做“正确”的事情。在上一个项目中,由于多个@ComponentScan注释,我遇到了某些类的多次注册问题。(即所有服务类都注册了两次)

基本上我使用以下布局:

WebAppInitializer

public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[] { RootConfig.class };
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[] { WebMvcConfig.class };
    }

    @Override
    protected String[] getServletMappings() {
        return new String[] { "/" };
    }

}

RootConfig

@Configuration
@ComponentScan
public class RootConfig {
    /* ... */
}

WebMvcConfig

@EnableWebMvc
@ComponentScan
public class WebMvcConfig extends WebMvcConfigurerAdapter {
    /* ... */
}

DatabaseConfig

@Configuration
@EnableJpaRepositories("my.base.class.path")
public class DataConfig {
    /* ... */
}

第一个基本问题是: 哪个类应该扫描哪些类/注释?

应该只WebMvcConfig扫描@Controller类吗?应该扫描哪一个@Servicefor@Configuration和 for @Component

第二个问题是: 还是我应该简单地使用包来缩小扫描路径?

例子:

rootpackage
rootpackage.config.RootConfig
rootpackage.config.DatabaseConfig
rootpackage.mvc.WebMvcConfig

然后将所有@Controller课程置于rootpackage.mvc.*?

第三个问题是: let scan for更常见吗?或者我应该放在类的方法中?RootConfigDatabaseConfigDatabaseConfiggetRootConfigClassesWebAppInitializer

最后一个问题是: 在一个多模块项目中:你如何组织这些东西?

示例:如果我选择我在问题二中描述的方式,我可以说,应用程序的每个模块实际上都由几个不同的模块组成。比方说,我想创建一个X包含一个@Service类和几个@Controller类的模块,我可以将它们放在不同的包中。像这样:

Maven Module X Service

rootpackage.services.x.XService
rootpackage.services.x.XServiceImpl

Maven Module X Controller

rootpackage.mvc.controller.x.X1Controller
rootpackage.mvc.controller.x.X2Controller
rootpackage.mvc.controller.x.X3Controller

如果您建议这种方式,那么:在哪里放置模型和存储库(用于访问数据库)?我应该为每个模块创建一个新模块吗?

提前致谢!

4

2 回答 2

2

我想我现在找到了一个非常不错的项目布局:

rootpackage.web.WebAppInitializer (see below)
rootpackage.web.SecurityWebAppInitializer (creates "springSecurityFilterChain")
rootpackage.web.WebMvcConfig (scans for everything in its own package and subpackages)
rootpackage.web.SecurityConfig (Spring Security config)

rootpackage.web.moduleA.SomeAController
rootpackage.web.moduleB.SomeBController

rootpackage.service.ServiceConfig (scans for everything in its own package and subpackages)
rootpackage.service.moduleA.AService
rootpackage.service.moduleA.AServiceImpl
rootpackage.service.moduleB.BService
rootpackage.service.moduleB.BServiceImpl
rootpackage.service.security.UserDetailsServiceImpl (for Spring Security)

rootpackage.model.DatabaseConfig (scans for everything in its own package and subpackages)
rootpackage.model.moduleA.SomeADomainObject
rootpackage.model.moduleB.SomeBDomainObject

WebAppInitializer:

@Order(2)
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[] {
            SecurityConfig.class,
            ServiceConfig.class,
            DatabaseConfig.class
        };
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[] { WebMvcConfig.class };
    }

    @Override
    protected String[] getServletMappings() {
        return new String[] { "/" };
    }

}

SecurityWebAppInitializer:

@Order(1) // should always be registered in first place (= before WebAppInitializer)
public class SecurityWebAppInitializer extends AbstractSecurityWebApplicationInitializer {
    /* ... */
}

WebMvc 配置:

@Configuration
@EnableWebMvc
@ComponentScan // scans for everything in its own package and subpackages
               // so it only finds SomeAController.class and SomeBController.class
public class WebMvcConfig extends WebMvcConfigurerAdapter {
    /* ... */
}

安全配置:

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    /* ... */
}

服务配置:

@Configuration
@ComponentScan // scans for everything in its own package and subpackages
               // so it only finds AServiceImpl.class and BServiceImpl.class
public class ServiceConfig {
    /* ... */   
}

我在所有这些类的构造函数中放置了一些“System.out.println”,以查看它们注册/加载的频率。每个构造函数只执行一次!

你怎么看待这件事?有什么改进吗?

于 2014-01-14T23:05:30.047 回答
1

使用基于 XML 的配置,您通常会有 2 个上下文,一个父上下文将加载您的所有业务服务、数据库配置、存储库、域对象等,还有一个用于加载控制器等的 Web 上下文。

两者都应该使用包来确保它们不会尝试两次加载相同的 bean。您同时指定两者以ContextLoaderListener创建 ApplicationContext。

Web 应用程序上下文知道父级(而不是相反),并将搜索父级以查找在它自己的上下文中找不到的任何 bean。这意味着您的控制器可以访问您的服务。

我没有在 Java 配置中这样做,但我假设方法是相同的

于 2014-01-14T06:12:10.167 回答