我刚刚开始了一个新的春季项目,这次我想做“正确”的事情。在上一个项目中,由于多个@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
类吗?哪一个应该扫描@Service
,@Configuration
和@Component
?
第二个问题是: 或者我应该只使用软件包缩小扫描路径?
示例:
rootpackage
rootpackage.config.RootConfig
rootpackage.config.DatabaseConfig
rootpackage.mvc.WebMvcConfig
然后将所有@Controller
个类放在rootpackage.mvc.*
下?
第三个问题是: 让RootConfig
扫描DatabaseConfig
更常见吗?或者我应该将DatabaseConfig
放在getRootConfigClasses
类的WebAppInitializer
方法中吗?
最后一个问题是: 在多模块项目中:您如何组织这些事情?
示例:如果我选择问题二中描述的方式,我可以说,应用程序的每个模块实际上都包含几个不同的模块。比方说,我想创建一个模块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
如果您建议这样做,那么:放置模型和存储库(用于访问数据库)的位置?我应该为每个模块创建一个新模块吗?
提前致谢!
答案 0 :(得分: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 {
/* ... */
}
<强> WebMvcConfig:强>
@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 {
/* ... */
}
<强> SecurityConfig:强>
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
/* ... */
}
<强> ServiceConfig:强>
@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”,以便查看它们注册/加载的频率。每个构造函数都执行一次!
您如何看待这个?有什么改进吗?
答案 1 :(得分:1)
使用基于XML的配置,您通常会有两个上下文,一个父上下文将加载所有业务服务,数据库配置,存储库,域对象等,以及用于加载控制器等的Web上下文。
两者都应该使用包来确保它们不会尝试两次加载相同的bean。您同时指定ContextLoaderListener
以创建ApplicationContext。
Web应用程序上下文知道父级(而不是相反的方式),并将在父级中搜索在其自己的上下文中找不到的任何bean。这意味着您的控制器可以访问您的服务。
我在Java配置中没有这样做,但我认为方法是相同的