modelandview - spring mvc jsp
Spring Boot crea aplicaciones webmvc múltiples(en funcionamiento) usando la configuración automática (4)
Actualizado
Mi pregunta es cómo inicializar una aplicación webmvc de primavera aislada en el arranque de primavera. La aplicación web aislada debería:
- No debe inicializarse en la clase de aplicación. Queremos hacer esto en un pom de arranque a través de la configuración automática. Tenemos varias de estas aplicaciones web y necesitamos la flexibilidad de la configuración automática.
- Tener la capacidad de personalizarse utilizando interfaces como:
WebSecurityConfigurer
(tenemos varias aplicaciones web, cada una tiene seguridad a su manera) yEmbeddedServletContainerCustomizer
(para establecer la ruta de acceso al contexto del servlet). - Necesitamos aislar beans específicos para ciertas aplicaciones web y no queremos que ingresen al contexto principal.
Progreso
La clase de configuración a continuación se detalla en mis META-INF / spring.factories.
La siguiente estrategia no conduce a un servlet web-mvc en funcionamiento. La ruta del contexto no está configurada y tampoco la seguridad personalizada. Mi corazonada es que tengo que incluir ciertos beans webmvc que procesan el contexto y que se configuran automáticamente en función de qué beans están presentes, similar a cómo obtuve la configuración del marcador de posición de la propiedad basada en el arranque al incluir 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;
}
}
Esta podría ser una forma de hacerlo (está en nuestro código de producción). Apuntamos a la configuración XML, así que quizás en lugar de dispatcherServlet.setContextConfigLocation()
podría usar 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;
}
}
Lamentablemente, no pude encontrar una forma de usar la configuración automática para múltiples servlets.
Sin embargo, puede usar ServletRegistrationBean
para registrar múltiples servlets para su aplicación. Le recomendaría usar AnnotationConfigWebApplicationContext
para iniciar el contexto porque de esta manera puede usar las herramientas de configuración de Spring predeterminadas (no el de arranque de primavera) para configurar sus servlets. Con este tipo de contexto, solo tiene que registrar una clase de configuración.
@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;
}
Si desea manejar solicitudes multiparte, debe establecer la configuración multiparte para el bean de registro. Esta configuración se puede autoconectar para el registro y se resolverá desde el contexto principal.
public ServletRegistrationBean servletRegistration(MultipartConfigElement mutlipart) ...
registration.setMultipartConfig(mutlipartConfig);
He creado un pequeño proyecto de ejemplo de github al que puedes acceder here . Tenga en cuenta que configuré las configuraciones de servlet por paquete de Java pero también puede definir anotaciones personalizadas para este propósito.
Me las arreglé para crear un jar independiente que hace el seguimiento en mi aplicación web y se inicia dependiendo del valor de una propiedad en un archivo spring.factories en resources / META-INF en la aplicación principal:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=my package.tracking.TrackerConfig
Tal vez, podrías intentar tener una guerra independiente, comenzar con este mecanismo y luego inyectar valores en los archivos de propiedades con el mecanismo / complemento Maven (Solo una teoría, nunca probé, pero basándome en varios proyectos en los que trabajé)
Tuvimos un problema similar al usar Boot (crear una aplicación de múltiples servlets con el contexto principal) y lo resolvimos de la siguiente manera:
1. Cree la configuración primaria de Spring, que consistirá en todos los beans de los padres que desee compartir. Algo como esto:
@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. Cree el tipo que determinará el tipo de su módulo de aplicación específico (por ejemplo, si su caso es REST o SOAP). También aquí puede especificar su ruta de contexto requerida u otra información específica de la aplicación (mostraré a continuación cómo se usará):
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. Cree el inicializador de la aplicación Boot para toda su aplicación:
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. Cree clases de configuración abstracta que contengan beans específicos para niños y todo lo que no puede o no quiere compartir a través del contexto primario. Aquí puede especificar todas las interfaces requeridas, como WebSecurityConfigurer
o EmbeddedServletContainerCustomizer
para su módulo de aplicación en particular:
/*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. Cree una clase de punto de entrada que compilará toda nuestra aplicación:
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);
}
}
Ahora todo está listo para lanzar nuestro súper inicio multi-app en dos pasos:
1.Inicie las aplicaciones de su hijo, por ejemplo, REST y 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. Prepare el punto de entrada y ejecute como aplicación de arranque: aplicación de clase pública {
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)
);
}
}
disfrutar ^ _ ^
Enlaces útiles: