자동 구성을 사용하여 여러 개의 (작동 중인) Webmvc 응용 프로그램을 생성하는 스프링 부트
갱신필
질문입니다.스프링 부트에서는 격리된 스프링 webmvc 웹 앱을 어떻게 초기화합니까?격리된 웹 애플리케이션은 다음과 같습니다.
- 응용 프로그램 클래스에서 자신을 초기화하지 마십시오.이 작업은 자동 설정을 통해 스타터 폼에서 수행하고자 합니다.이러한 웹 앱이 여러 개 있기 때문에 자동 설정의 유연성이 필요합니다.
- 같은 수 .
WebSecurityConfigurer
웹 가 있으며 각각 합니다.) 및 (의 웹.)EmbeddedServletContainerCustomizer
(어느 쪽인가 하면) - 특정 웹 앱에 고유한 콩을 분리하여 부모 컨텍스트에 들어가지 않도록 해야 합니다.
진보.
다음 구성 클래스는 META-INF/spring.factories에 나와 있습니다.
web-mvc를 사용하다콘텍스트 패스는 설정되어 있지 않고, 시큐러티도 커스터마이즈 되어 있지 않습니다.은 부트 플레이스 이 webmvc를 .이것은, 다음과 같이 동작하는 부트 베이스의 속성 플레이스 홀더 설정 방법과 비슷합니다.PropertySourcesPlaceholderConfigurer.class
.
@Configuration
@AutoConfigureAfter(DaoServicesConfiguration.class)
public class MyServletConfiguration {
@Autowired
ApplicationContext parentApplicationContext;
@Bean
public ServletRegistrationBean myApi() {
AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();
applicationContext.setParent(parentApplicationContext);
applicationContext.register(PropertySourcesPlaceholderConfigurer.class);
// a few more classes registered. These classes cannot be added to
// the parent application context.
// includes implementations of
// WebSecurityConfigurerAdapter
// EmbeddedServletContainerCustomizer
applicationContext.scan(
// a few packages
);
DispatcherServlet ds = new DispatcherServlet();
ds.setApplicationContext(applicationContext);
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(ds, true, "/my_api/*");
servletRegistrationBean.setName("my_api");
servletRegistrationBean.setLoadOnStartup(1);
return servletRegistrationBean;
}
}
Boot(부모 컨텍스트에서 멀티 서블릿 앱 만들기)에서도 같은 문제가 발생하여 다음과 같이 해결했습니다.
1. Spring 구성을 만듭니다.Spring 구성은 공유하는 모든 부모의 빈으로 구성됩니다.다음과 같은 경우:
@EnableAutoConfiguration(
exclude = {
//use this section if your want to exclude some autoconfigs (from Boot) for example MongoDB if you already have your own
}
)
@Import(ParentConfig.class)//You can use here many clasess from you parent context
@PropertySource({"classpath:/properties/application.properties"})
@EnableDiscoveryClient
public class BootConfiguration {
}
2. 특정 앱 모듈의 유형을 결정하는 유형을 만듭니다(예: REST 또는 SOAP).또한 여기서 필요한 컨텍스트 경로 또는 다른 앱별 데이터를 지정할 수 있습니다(사용 방법을 아래에 나타냅니다).
public final class AppModule {
private AppType type;
private String name;
private String contextPath;
private String rootPath;
private Class<?> configurationClass;
public AppModule() {
}
public AppModule(AppType type, String name, String contextPath, Class<?> configurationClass) {
this.type = type;
this.name = name;
this.contextPath = contextPath;
this.configurationClass = configurationClass;
}
public AppType getType() {
return type;
}
public void setType(AppType type) {
this.type = type;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getRootPath() {
return rootPath;
}
public AppModule withRootPath(String rootPath) {
this.rootPath = rootPath;
return this;
}
public String getContextPath() {
return contextPath;
}
public void setContextPath(String contextPath) {
this.contextPath = contextPath;
}
public Class<?> getConfigurationClass() {
return configurationClass;
}
public void setConfigurationClass(Class<?> configurationClass) {
this.configurationClass = configurationClass;
}
public enum AppType {
REST,
SOAP
}
}
3. 전체 앱을 위한 부팅 앱 이니셜라이저를 만듭니다.
public class BootAppContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
private List<AppModule> modules = new ArrayList<>();
BootAppContextInitializer(List<AppModule> modules) {
this.modules = modules;
}
@Override
public void initialize(ConfigurableApplicationContext ctx) {
for (ServletRegistrationBean bean : servletRegs(ctx)) {
ctx.getBeanFactory()
.registerSingleton(bean.getServletName() + "Bean", bean);
}
}
private List<ServletRegistrationBean> servletRegs(ApplicationContext parentContext) {
List<ServletRegistrationBean> beans = new ArrayList<>();
for (AppModule module: modules) {
ServletRegistrationBean regBean;
switch (module.getType()) {
case REST:
regBean = createRestServlet(parentContext, module);
break;
case SOAP:
regBean = createSoapServlet(parentContext, module);
break;
default:
throw new RuntimeException("Not supported AppType");
}
beans.add(regBean);
}
return beans;
}
private ServletRegistrationBean createRestServlet(ApplicationContext parentContext, AppModule module) {
WebApplicationContext ctx = createChildContext(parentContext, module.getName(), module.getConfigurationClass());
//Create and init MessageDispatcherServlet for REST
//Also here you can init app specific data from AppModule, for example,
//you can specify context path in the follwing way
//servletRegistrationBean.addUrlMappings(module.getContextPath() + module.getRootPath());
}
private ServletRegistrationBean createSoapServlet(ApplicationContext parentContext, AppModule module) {
WebApplicationContext ctx = createChildContext(parentContext, module.getName(), module.getConfigurationClass());
//Create and init MessageDispatcherServlet for SOAP
//Also here you can init app specific data from AppModule, for example,
//you can specify context path in the follwing way
//servletRegistrationBean.addUrlMappings(module.getContextPath() + module.getRootPath());
}
private WebApplicationContext createChildContext(ApplicationContext parentContext, String name,
Class<?> configuration) {
AnnotationConfigEmbeddedWebApplicationContext ctx = new AnnotationConfigEmbeddedWebApplicationContext();
ctx.setDisplayName(name + "Context");
ctx.setParent(parentContext);
ctx.register(configuration);
Properties source = new Properties();
source.setProperty("APP_SERVLET_NAME", name);
PropertiesPropertySource ps = new PropertiesPropertySource("MC_ENV_PROPS", source);
ctx.getEnvironment()
.getPropertySources()
.addLast(ps);
return ctx;
}
}
4.컨텍스트를 할 수 없거나 을 포함하는 .4 . 자녀 고유의 콩과 부모 컨텍스트를 통해 공유할 수 없거나 원하지 않는 모든 것을 포함하는 추상 구성 클래스를 만듭니다.에서는, 한 모든 인터페이스(예: 「」, 「」등)를할 수 있습니다.WebSecurityConfigurer
★★★★★★★★★★★★★★★★★」EmbeddedServletContainerCustomizer
[ ] [ 정 、 [ 。
/*Example for REST app*/
@EnableWebMvc
@ComponentScan(basePackages = {
"com.company.package1",
"com.company.web.rest"})
@Import(SomeCommonButChildSpecificConfiguration.class)
public abstract class RestAppConfiguration extends WebMvcConfigurationSupport {
//Some custom logic for your all REST apps
@Autowired
private LogRawRequestInterceptor logRawRequestInterceptor;
@Autowired
private LogInterceptor logInterceptor;
@Autowired
private ErrorRegister errorRegister;
@Autowired
private Sender sender;
@PostConstruct
public void setup() {
errorRegister.setSender(sender);
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(logRawRequestInterceptor);
registry.addInterceptor(scopeInterceptor);
}
@Override
public void setServletContext(ServletContext servletContext) {
super.setServletContext(servletContext);
}
}
/*Example for SOAP app*/
@EnableWs
@ComponentScan(basePackages = {"com.company.web.soap"})
@Import(SomeCommonButChildSpecificConfiguration.class)
public abstract class SoapAppConfiguration implements ApplicationContextAware {
//Some custom logic for your all SOAP apps
private boolean logGateWay = false;
protected ApplicationContext applicationContext;
@Autowired
private Sender sender;
@Autowired
private ErrorRegister errorRegister;
@Autowired
protected WsActivityIdInterceptor activityIdInterceptor;
@Autowired
protected WsAuthenticationInterceptor authenticationInterceptor;
@PostConstruct
public void setup() {
errorRegister.setSender(sender);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
/**
* Setup preconditions e.g. interceptor deactivation
*/
protected void setupPrecondition() {
}
public boolean isLogGateWay() {
return logGateWay;
}
public void setLogGateWay(boolean logGateWay) {
this.logGateWay = logGateWay;
}
public abstract Wsdl11Definition defaultWsdl11Definition();
}
5. 앱 전체를 컴파일하는 엔트리 포인트 클래스를 만듭니다.
public final class Entrypoint {
public static void start(String applicationName, String[] args, AppModule... modules) {
System.setProperty("spring.application.name", applicationName);
build(new SpringApplicationBuilder(), modules).run(args);
}
private static SpringApplicationBuilder build(SpringApplicationBuilder builder, AppModule[] modules) {
return builder
.initializers(
new LoggingContextInitializer(),
new BootAppContextInitializer(Arrays.asList(modules))
)
.sources(BootConfiguration.class)
.web(true)
.bannerMode(Banner.Mode.OFF)
.logStartupInfo(true);
}
}
이제 슈퍼 멀티앱 부팅을 두 단계로 진행할 준비가 되었습니다.
1. 자녀 앱을 초기화합니다(예: REST 및 SOAP:
//REST module
@ComponentScan(basePackages = {"com.module1.package.*"})
public class Module1Config extends RestAppConfiguration {
//here you can specify all your child's Beans and etc
}
//SOAP module
@ComponentScan(
basePackages = {"com.module2.package.*"})
public class Module2Configuration extends SoapAppConfiguration {
@Override
@Bean(name = "service")
public Wsdl11Definition defaultWsdl11Definition() {
ClassPathResource wsdlRes = new ClassPathResource("wsdl/Your_WSDL.wsdl");
return new SimpleWsdl11Definition(wsdlRes);
}
@Override
protected void setupPrecondition() {
super.setupPrecondition();
setLogGateWay(true);
activityIdInterceptor.setEnabled(true);
}
}
2. 엔트리 포인트를 준비하고 Boot App: public class App {로 실행
public static void main(String[] args) throws Exception {
Entrypoint.start("module1",args,
new AppModule(AppModule.AppType.REST, "module1", "/module1/*", Module1Configuration.class),
new AppModule(AppModule.AppType.SOAP, "module2", "module2", Module2Configuration.class)
);
}
}
^_^를 즐기다
유용한 링크:
- https://dzone.com/articles/what-servlet-container
- 스프링: "루트" 애플리케이션 컨텍스트와 "서블릿" 애플리케이션 컨텍스트가 서로 다른 당사자에 의해 생성되는 이유는 무엇입니까?
- 봄철 ContextLoaderListener의 역할/목적
이것은 (제품 코드에 기재되어 있는) 하나의 방법일 수 있습니다.XML 구성을 가리키기 때문에dispatcherServlet.setContextConfigLocation()
사용할 수 있다dispatcherServlet.setContextClass()
@Configuration
public class JettyConfiguration {
@Autowired
private ApplicationContext applicationContext;
@Bean
public ServletHolder dispatcherServlet() {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(MvcConfiguration.class);//CUSTOM MVC @Configuration
DispatcherServlet servlet = new DispatcherServlet(ctx);
ServletHolder holder = new ServletHolder("dispatcher-servlet", servlet);
holder.setInitOrder(1);
return holder;
}
@Bean
public ServletContextHandler servletContext() throws IOException {
ServletContextHandler handler =
new ServletContextHandler(ServletContextHandler.SESSIONS);
AnnotationConfigWebApplicationContext rootWebApplicationContext =
new AnnotationConfigWebApplicationContext();
rootWebApplicationContext.setParent(applicationContext);
rootWebApplicationContext.refresh();
rootWebApplicationContext.getEnvironment().setActiveProfiles(applicationContext.getEnvironment().getActiveProfiles());
handler.setAttribute(
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,
rootWebApplicationContext);
handler.setContextPath("/my-root");
handler.setResourceBase(new ClassPathResource("webapp").getURI().toString());
handler.addServlet(AdminServlet.class, "/metrics/*");//DROPWIZARD
handler.addServlet(dispatcherServlet(), "/");
/*Web context 1*/
DispatcherServlet webMvcDispatcherServlet1 = new DispatcherServlet();
webMvcDispatcherServlet1.setContextConfigLocation("classpath*:/META-INF/spring/webmvc-config1.xml");
webMvcDispatcherServlet1.setDetectAllHandlerAdapters(true);
webMvcDispatcherServlet1.setDetectAllHandlerMappings(true);
webMvcDispatcherServlet1.setDetectAllViewResolvers(true);
webMvcDispatcherServlet1.setEnvironment(applicationContext.getEnvironment());
handler.addServlet(new ServletHolder("webMvcDispatcherServlet1",webMvcDispatcherServlet1), "/web1/*");
/*Web context 2*/
DispatcherServlet webMvcDispatcherServlet2 = new DispatcherServlet();
webMvcDispatcherServlet2.setContextConfigLocation("classpath*:/META-INF/spring/web-yp-config.xml");
webMvcDispatcherServlet2.setDetectAllHandlerAdapters(true);
webMvcDispatcherServlet2.setDetectAllHandlerMappings(true);
webMvcDispatcherServlet2.setDetectAllViewResolvers(false);
webMvcDispatcherServlet2.setEnvironment(applicationContext.getEnvironment());
handler.addServlet(new ServletHolder("webMvcDispatcherServlet2",webMvcDispatcherServlet2), "/web2/*");
/* Web Serices context 1 */
MessageDispatcherServlet wsDispatcherServlet1 = new MessageDispatcherServlet();
wsDispatcherServlet1.setContextConfigLocation("classpath*:/META-INF/spring/ws-config1.xml");
wsDispatcherServlet1.setEnvironment(applicationContext.getEnvironment());
handler.addServlet(new ServletHolder("wsDispatcherServlet1", wsDispatcherServlet1), "/ws1/*");
/* Web Serices context 2 */
MessageDispatcherServlet wsDispatcherServlet2 = new MessageDispatcherServlet();
wsDispatcherServlet2.setContextConfigLocation("classpath*:/META-INF/spring/ws-siteconnect-config.xml");
wsDispatcherServlet2.setEnvironment(applicationContext.getEnvironment());
handler.addServlet(new ServletHolder("wsDispatcherServlet2", wsDispatcherServlet2), "/ws2/*");
/*Spring Security filter*/
handler.addFilter(new FilterHolder(
new DelegatingFilterProxy("springSecurityFilterChain")), "/*",
null);
return handler;
}
@Bean
public CharacterEncodingFilter characterEncodingFilter() {
CharacterEncodingFilter bean = new CharacterEncodingFilter();
bean.setEncoding("UTF-8");
bean.setForceEncoding(true);
return bean;
}
@Bean
public HiddenHttpMethodFilter hiddenHttpMethodFilter() {
HiddenHttpMethodFilter filter = new HiddenHttpMethodFilter();
return filter;
}
/**
* Jetty Server bean.
* <p/>
* Instantiate the Jetty server.
*/
@Bean(initMethod = "start", destroyMethod = "stop")
public Server jettyServer() throws IOException {
/* Create the server. */
Server server = new Server();
/* Create a basic connector. */
ServerConnector httpConnector = new ServerConnector(server);
httpConnector.setPort(9083);
server.addConnector(httpConnector);
server.setHandler(servletContext());
return server;
}
}
유감스럽게도 여러 서블릿에 대해 자동 구성을 사용하는 방법을 찾을 수 없었습니다.
단,ServletRegistrationBean
응용 프로그램에 여러 서블릿을 등록합니다.를 사용하는 것을 추천합니다.AnnotationConfigWebApplicationContext
그러면 기본 스프링 설정 도구(스프링 부트 도구가 아님)를 사용하여 서블릿을 설정할 수 있기 때문에 컨텍스트를 시작합니다.이런 유형의 컨텍스트에서는 컨피규레이션클래스를 등록하기만 하면 됩니다.
@Bean
public ServletRegistrationBean servletRegistration() {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(YourConfig.class);
DispatcherServlet servlet = new DispatcherServlet();
servlet.setApplicationContext(context);
ServletRegistrationBean registration = new ServletRegistrationBean(servlet, "/servletX");
registration.setLoadOnStartup(1);
registration.setName("servlet-X");
return registration;
}
멀티파트 요구를 처리할 경우 등록빈의 멀티파트 구성을 설정해야 합니다.이 설정은 등록용으로 자동 전력을 공급할 수 있으며 부모 컨텍스트에서 해결됩니다.
public ServletRegistrationBean servletRegistration(MultipartConfigElement mutlipart) ...
registration.setMultipartConfig(mutlipartConfig);
여기서 보실 수 있는 작은 github 예시 프로젝트를 만들었습니다.Java 패키지별로 서블릿 구성을 설정하지만 이 목적을 위해 사용자 지정 주석을 정의할 수도 있습니다.
웹 앱에서 추적하는 독립형 jar를 만들 수 있습니다.메인 앱의 Resources/META-INF의 properties 파일에 있는 spring 속성의 값에 따라 실행됩니다.
org.springframework.boot.autoconfigure.EnableAutoConfiguration=my package.tracking.TrackerConfig
아마도 당신은 이 메커니즘에서 시작하여 maven 메커니즘/플러그인을 사용하여 속성 파일에 값을 주입할 수 있을 것이다(그냥 이론일 뿐, 시도한 적은 없지만, 내가 작업한 여러 프로젝트에 기초하고 있다).
언급URL : https://stackoverflow.com/questions/35095326/spring-boot-creating-multiple-functioning-webmvc-applications-using-auto-confi
'programing' 카테고리의 다른 글
(JSON:ParserError) "{N}: "alihack <%eval request(\"alihack.com\)"에서 예기치 않은 토큰"%> (0) | 2023.03.11 |
---|---|
ES6 Import 스테이트먼트에서 곱슬곱슬한 중괄호의 사용법은 무엇입니까? (0) | 2023.03.11 |
Postgre란SQL은 Oracle의 SYSDATE와 동등합니까? (0) | 2023.03.11 |
"유형 'EventEmitter'가 일반적이지 않습니다." 각도 오류 (0) | 2023.03.11 |
스프링 부트를 사용하지 않고 스프링 휴식 서비스 생성 (0) | 2023.03.06 |