spring boot,Where's the Root WebApplicationContext and Servlet WebApplicationContext - spring

I use spring-boot-starter-web 1.5.17.I can't find any auto configuration about build Root WebApplicationContext and Servlet WebApplicationContext.
When not use spring boot,we need do this
public class WebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[]{SecurityAdminConfig.class, DataSourceConfig.class, JavaConfig.class};
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[]{WebMvcConfig.class};
}
//controller、service
I know WebMvcAutoConfiguration do a lot of spring MVC configuration,but i can't find about auto build Root WebApplicationContext and Servlet WebApplicationContext.
public abstract class AbstractAnnotationConfigDispatcherServletInitializer
extends AbstractDispatcherServletInitializer {
#Override
protected WebApplicationContext createRootApplicationContext() {
Class<?>[] configClasses = getRootConfigClasses();
if (!ObjectUtils.isEmpty(configClasses)) {
AnnotationConfigWebApplicationContext rootAppContext = new AnnotationConfigWebApplicationContext();
rootAppContext.register(configClasses);
return rootAppContext;
}
else {
return null;
}
}
#Override
protected WebApplicationContext createServletApplicationContext() {
AnnotationConfigWebApplicationContext servletAppContext = new AnnotationConfigWebApplicationContext();
Class<?>[] configClasses = getServletConfigClasses();
if (!ObjectUtils.isEmpty(configClasses)) {
servletAppContext.register(configClasses);
}
return servletAppContext;
}
protected abstract Class<?>[] getRootConfigClasses();
protected abstract Class<?>[] getServletConfigClasses();
}
How can know this beans go Root WebApplicationContext or Servlet WebApplicationContext?
image here

There is, by default, a single application context in a Spring Boot web (or otherwise) application. It is created by SpringApplication when your application starts.

The right way to do it would be this:
public class App {
public static void main(String[] args) {
new SpringApplicationBuilder()
.parent(ParentConfig.class).web(WebApplicationType.NONE)
.child(WebConfig.class).web(WebApplicationType.SERVLET)
.sibling(RestConfig.class).web(WebApplicationType.SERVLET)
.run(args);
}
}
I have taken it from here:
However, it is still not clear why by default everything is in the same context..
Also, I am not sure if that's fine that other beans created by WebMvcAutoconfiguration will be shared across all child contexts.

Related

Spring Boot Tomcat deployment

I am having issue with deploying spring boot application in Tomcat 8. I am getting the error below while starting application.
java.lang.IllegalStateException: Cannot initialize context because there is already a root application context present - check whether you have multiple ContextLoader* definitions in your web.xml!
our webinitializer
public class SpringWebMvcInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class[]{SecurityConfiguration.class};
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class[] {WebMvcConfiguration.class};
}
#Override
protected String[] getServletMappings() {
return new String[]{"/"};
}}
I fixed my own problem by changing my web fertilizer to
public class SpringWebMvcInitializer extends AbstractSecurityWebApplicationInitializer {
// nothing here
}
reference -
http://www.mkyong.com/spring-security/spring-security-hello-world-annotation-example/

How can I run jobs after spring beans is initialized?

I am working on Spring 4 mvc and hibernate
I want to run code on the server startup that will use the get data from the database then do some business logic
where can I put my code I tried to put the code
org.springframework.web.servlet.support.AbstractDispatcherServletInitializer.onStartup(ServletContext)
but I was not able to use #Autowired variables
public class WebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Autowired
TaskDAO task;
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { SpringRootConfig.class };
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class[] { SpringWebConfig.class };
}
#Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
task.getAllTasks()
// TODO Auto-generated method stub
super.onStartup(servletContext);
}
}
You are not able to autowire variables because your class is not managed by spring. So annotate your class with #Component annotation.
Then you can define a method that will do your logic (for example onStartup method) and annotate it with the #PostConstruct annotation as explained in this answers.
How to call a method after bean initialization is complete?
It will execute the method after the beans initialization.
This could be your class:
#Component
public class WebInitializer{
#Autowire
TaskDAO task;
#PostConstruct
private void onStartup(){
task.getAllTasks();
// Do whatever you want
}
}

can not start spring mvc 4 application without web.xml

I am trying to deploy spring mvc 4 web application without web.xml file using #Configuration annotation only.
I have
public class WebAppInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext servletContext)
throws ServletException {
WebApplicationContext context = getContext();
servletContext.addListener(new ContextLoaderListener(context));
ServletRegistration.Dynamic dispatcher = servletContext.addServlet(
"DispatcherServlet", new DispatcherServlet(context));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("*.html");
}
private AnnotationConfigWebApplicationContext getContext() {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.setConfigLocation("ge.dm.icmc.config.WebConfig");
return context;
}
}
and my WebConfig.java class looks like :
#Configuration
#EnableWebMvc
#ComponentScan(basePackages="ge.dm.icmc")
public class WebConfig{
}
But when I try to start the application, I see in log :
14:49:12.275 [localhost-startStop-1] DEBUG o.s.w.c.s.AnnotationConfigWebApplicationContext - Could not load class for config location [] - trying package scan. java.lang.ClassNotFoundException:
If I try to add web.xml file, then it is started normally.
You are using the method setConfigLocation which, in this case, is wrong. You should use the register method instead.
private AnnotationConfigWebApplicationContext getContext() {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(ge.dm.icmc.config.WebConfig.class);
return context;
}
However instead of implementing the WebApplicationInitializer I strongly suggest using one of the convenience classes of Spring for this. In your case the AbstractAnnotationConfigDispatcherServletInitializer would come in handy.
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
protected Class<?>[] getRootConfigClasses() { return null;}
protected Class<?>[] getServletConfigClasses() {
return new Class[] { WebConfig.class};
}
protected String[] getServletMappings() {
return new String[] {"*.html"};
}
}

Spring Profiles not recognized in a Web Application

I'm trying to implement Spring Profiles to load a specific class implementation based on the profile specified. The profile to use is specified as a property (spring.profiles.active) within a properties file included in the classpath.
Source here: https://github.com/overattribution/spring-profile-test
public interface MyService {
public void doSomething();
}
#Service
#Profile("preprod")
public class MyServicePreProdImpl implements MyService {
private final Logger LOG = LoggerFactory.getLogger(MyServicePreProdImpl.class);
#Override
public void doSomething() {
LOG.debug("Doing something in " + MyServicePreProdImpl.class.getName());
}
}
#Service
#Profile("prod")
public class MyServiceProdImpl implements MyService {
private final Logger LOG = LoggerFactory.getLogger(MyServiceProdImpl.class);
#Override
public void doSomething() {
LOG.debug("Doing something in " + MyServiceProdImpl.class.getName());
}
}
However, I'm getting the following error:
No qualifying bean of type [com.example.profileservice.MyService] found for dependency.
What am I missing?
UPDATE 1:
I'm manually setting the active profile during web application initialization, but I'm still getting the "No qualifying bean found" error. Changes can be seen here: https://github.com/overattribution/spring-profile-test/commit/09175a10b28ea8e5a08b43ad1416431bcf094c9d
Ok I got it to work. Profiles need to be set within the root context (as opposed to servlet context) during web application initialization. I have done so in my WebAppInitializer class like so:
https://github.com/overattribution/spring-profile-test/blob/f895a8bc67dc1f6ba2fcedb58b73a19cc5cf8cf7/src/main/java/com/example/config/WebAppInitializer.java
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
/**
* Overriding to include setting active profile.
*/
#Override
protected WebApplicationContext createRootApplicationContext() {
Class<?>[] configClasses = getRootConfigClasses();
if (!ObjectUtils.isEmpty(configClasses)) {
AnnotationConfigWebApplicationContext rootAppContext = new AnnotationConfigWebApplicationContext();
String[] activeProfiles = getActiveProfiles();
rootAppContext.getEnvironment().setActiveProfiles(activeProfiles);
rootAppContext.register(configClasses);
return rootAppContext;
}
else {
return null;
}
}
#Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] {
ApplicationConfig.class
};
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[] {
WebMvcConfig.class
};
}
#Override
protected Filter[] getServletFilters() {
CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
characterEncodingFilter.setEncoding("UTF-8");
characterEncodingFilter.setForceEncoding(true);
return new Filter[] {characterEncodingFilter};
}
protected String[] getActiveProfiles() {
PropertySource propertySource = null;
try {
propertySource = new ResourcePropertySource("classpath:application.properties");
String profilesString = (String) propertySource.getProperty(AbstractEnvironment.ACTIVE_PROFILES_PROPERTY_NAME);
return profilesString.split(",");
} catch (IOException e) {
throw new ResourceAccessException("application.properties is not available on the classpath");
}
}
}
A profile is a named logical grouping that may be activated programmatically via ConfigurableEnvironment.setActiveProfiles(java.lang.String...) or declaratively through setting the spring.profiles.active property, usually through JVM system properties, as an environment variable, or for web applications as a Servlet context parameter in web.xml.
I would say that it is not possible to specify active profile as a property within your properties file unless you use Spring Boot that also enables you to set the active profile in application.properties file.
Try to use one of the options above.

Change main endpoint in Spring Data Rest (usind Spring Boot)

Im building a small application using Spring (Boot, Data, Data Rest).
I have some JpaRepositories that aumotatically are exported as Rest endpoints.
What i want to do is to change the base path from / to /api.
Now to list all people for example i do a GET to http://localhost:8080/people and i want the url to be http://localhost:8080/api/people.
I tried adding this config class but nothing happened (it seems that Spring Boot overrides this config):
public class SpringWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer{
#Override
protected Class<?>[] getRootConfigClasses()
{
return new Class<?>[] { Application.class};
}
#Override
protected Class<?>[] getServletConfigClasses()
{
return new Class<?>[] { RestExporterRestConfig.class, RepositoryRestMvcConfiguration.class };
}
#Override
protected String[] getServletMappings()
{
return new String[] { "/api/*" };
}
}
My Application.java:
#Configuration
#ComponentScan
#Import(RestExporterRestConfig.class)
#EnableJpaRepositories
#EnableAutoConfiguration
public class Application extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
and RestExporterRestConfig:
#Configuration
public class RestExporterRestConfig extends RepositoryRestMvcConfiguration {
#Bean
public Validator validator() {
return new LocalValidatorFactoryBean();
}
#Override
protected void configureValidatingRepositoryEventListener(ValidatingRepositoryEventListener v) {
v.addValidator("beforeCreate", validator());
}
#Bean
#Qualifier
public DefaultFormattingConversionService defaultConversionService() {
DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService();
conversionService.addConverter(StringToDate.INSTANCE);
return conversionService;
}
#Bean
public DomainClassConverter<?> domainClassConverter() {
return new DomainClassConverter<DefaultFormattingConversionService>(defaultConversionService());
}
}
Well i figured it out. SpringWebAppInitializer is not necesary in this case. I just added this code to Application.java:
#Bean
public ServletRegistrationBean dispatcherRegistration(DispatcherServlet dispatcherServlet) {
ServletRegistrationBean reg = new ServletRegistrationBean(dispatcherServlet);
reg.addUrlMappings("/api/*");
return reg;
}
I think this is the correct way to modify (add, change mappings, etc) servlets using Spring Boot.

Resources