i am new to springboot application development and i generated my project with the help of this url https://start.spring.io/ and when i open this project in my IDE i had 2 classes generated
this is the first class
public class ServletInitializer extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(TravellingApplication.class);
}}
and this is the second class
#SpringBootApplication
public class TravellingApplication {
public static void main(String[] args) {
SpringApplication.run(TravellingApplication.class, args);
}}
i really don't get it whats happening inside the configure method in my Servletinitializer class.
i can write better code configuration if i delete both of the classes
and do something like this,
class simmilar to dispatcherservlet.xml
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = "com.travelliing")
public class WebConfig extends WebMvcConfigurerAdapter {
}
class simmilar to web.xml
public class WebAppInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext servletContext) throws ServletException { }
}
correct me if i am wrong. i think both ServletInitializer class and webAppInitializer is capable of same functionalities since the somehow implement WebApplicationInitializer.
except for the configure method in servletInitializer class.
whats happening with the travellingApplication class annotated with #SpringBootApplication is it simmilar to my webConfig Class which extends WebMvcConfigureAdapter
Both classes load the Spring application context.
The class with the main method (TravellingApplication) will be used if you run your application as normal java application. For example if you do Run As -> Java applciatnion from Eclipse or if you package the application as a jar and run java -jar myApp.jar from the command line.
SpringBootServletInitializer will be used to load the application context if you package the application as a war file and deploy it in Tomcat or another web server that supports Servlet 3.0+. It basically replaces the web.xml.
i really don't get it whats happening inside the configure method in
my Servletinitializer class.
TravellingApplication is a #Configuration class - it declares Spring beans and other Spring configuration, so this line - return application.sources(TravellingApplication.class); just loads this configuration (application context). The same thing that happens in the main method.
whats happening with the travellingApplication class annotated with
#SpringBootApplication is it simmilar to my webConfig Class which
extends WebMvcConfigureAdapter
#SpringBootApplication is just a shortcut to
#Configuration
#EnableAutoConfiguration
#ComponentScan
See here.
Related
I have a plugin for my application which has only one class:
#Configuration
public class AppConfig {
#Bean
public Supplier messageSupplier(){
return () -> "plugin";
}
}
I use it in other application but I want to test this class. I want to test it, but I don't know what is context of this application.
I'm working on a Spring project. The functionality is already done. What's missing is the security context.
As my project (maven) is separated in different sub projects (Services, RestControllers, Domain), I want the security configuration to be a separate sub project as well, which I only have to add as dependency to the main app to activate it.
I started with a very basic configuration, which is, by now, the only class in the security sub project:
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("admin")
.password("admin1")
.roles("ADMIN", "USER");
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeRequests().antMatchers("/").permitAll().and().authorizeRequests().anyRequest().authenticated();
}
}
As soon as I add this project as dependency to my main app, the security context is obviously getting activated, as the default spring login dialogue pops up. The thing is, that Spring ignores the configuration which I've defined in the SecurityConfiguration. It even won't let me access '/', or neither it let's me login with the defined user. Checking in debug mode, it never runs through the public void configure(AuthenticationManagerBuilder auth) method.
In a nut shell:
It activates the spring security context, but it does not apply my configuration. Why is that?
In case of Spring MVC project with Java based configuration import SecurityConfiguration to your ApplicationConfiguration
#EnableWebMvc
#Configuration
#ComponentScan({"xx.xxx.xx.*"})
#PropertySource("classpath:xxx.properties")
#Import(value = {SecurityConfiguration.class}) // <= like this
public class ApplicationConfiguration extends WebMvcConfigurerAdapter { ... }
You may also need SecurityInitializer. This class has to be present even though it's empty. This is a good place for certain filters that must be executed before security configuration.
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
public class SecurityInitializer extends AbstractSecurityWebApplicationInitializer { }
I saw a spring boot project which had #EnableAutoConfiguration defined in the file that main method similar to all Spring Boot projects that I have seen. However I saw this annotation being defined in other java files (file for including Swagger config) within the same project. Should this annotation be defined in multiple files? Will there be any adverse impact in doing this?
#EnableAutoComfiguration should only appear once.
In its simplest form, it won't cause any harm if it's declared multiple times, but there's no benefit. However, if you configure any excludes, those excludes will have to be configured on every occurrence of the annotation as they're not cumulative.
even it is allowed to have multiples #EnableAutoConfiguration I would recommend to have only one #EnableAutoConfiguration if it is possible. Just because to be able to exclude any configurations which you don't need in the single place. Otherwise the following issue could happen:
here is primary config of spring boot application:
#SpringBootApplication
#Import(value = {WebSecurityConfiguration.class})
public class Application {
public static void main(String... args) {
SpringApplication.run(Application.class, args);
}
}
here is content of WebSecurityConfiguration class
#Configuration
public class WebSecurityConfiguration {
#Configuration
#EnableAutoConfiguration(exclude ={SecurityAutoConfiguration.class,
SpringBootWebSecurityConfiguration.class})
#Profile("dev")
protected static class DefaultWebSecurityConfig {
}
#Configuration
#EnableAutoConfiguration
#EnableWebSecurity
protected static class LocalWebSecurityConfig extends WebSecurityConfigurerAdapter {
//implementation
}
}
so, I expected to have security auto configuration disabled for dev profile. But just because #SpringBootApplication defines #EnableAutoConfiguration implicitly appropriate configs were scanned as part of 'primary' #EnableAutoConfiguration and default security settings were applied. as result for dev profile security was enabled.
It would not happened if I defined #EnableAutoConfiguration with excludes just once for Application class.
I have a SpringBoot main application, as well as a separate Maven module project that compiles as a separate Jar. The module has a Spring config class annotated with #Configuration, which I want to get loaded, when the main app loads.
Apparently, this does not happen out of the box (by just including the module to the main app). What else do I need to do, to get the module configuration class also get loaded by the main app?
The easiest way is to scan the package that the #Configuration class is in.
#ComponentScan("com.acme.otherJar.config")
or to just load it as a spring bean:
#Bean
public MyConfig myConfig() {
MyConfig myConfig = new MyConfig ();
return myConfig;
}
Where MyConfig is something like:
#Configuration
public class MyConfig {
// various #Bean definitions ...
}
See docs
#ComponentScan annotation will scan all classes with #Compoment or #Configuration annotation.
Then spring ioc will add them all to spring controlled beans.
If you want to only add specific configurations, you can use #import annotation.
example:
#Configuration
#Import(NameOfTheConfigurationYouWantToImport.class)
public class Config {
}
#Import Annotation Doc
Context:
I switched from XML based to Java based Spring configuration. My application has a JSP based web layer, Spring MVC, Spring Security and Hibernate as persistence provider.
I managed to separate the whole XML configuration in different config classes:
WebConfig - for the Spring MVC configurations;
PersistenceConfig - as the name states - for the JPA configuration;
ServiceConfig - only for the #Service and #Component annotated classes;
SecurityConfig - for the Spring Security Configuration.
For application initialization I have the SecurityInitializer and WebAppInitializer classes.
Here is some code:
WebConfig
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = {"com.demo.app.web"})
public class WebConfig extends WebMvcConfigurerAdapter { /* Bean initialization */ }
PersistenceConfig
#Configuration
#ComponentScan(basePackages = {"com.demo.app.dao"})
#EnableTransactionManagement(mode = AdviceMode.PROXY, proxyTargetClass = true)
public class PersistenceConfig { /* Bean initialization */ }
ServiceConfig
#Configuration
#ComponentScan(basePackages = {"com.demo.app.service", "com.demo.app.component"})
public class ServiceConfig { /* Bean initialization */ }
SecurityConfig
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter { /* Bean initialization */ }
SecurityInitializer
#Order(1)
public class SecurityInitializer extends AbstractSecurityWebApplicationInitializer {
}
WebAppInitializer
#Order(2)
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] {SecurityConfig.class, PersistenceConfig.class,
ServiceConfig.class};
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[] {WebConfig.class};
}
#Override
protected String[] getServletMappings() {
return new String[] {"/"};
}
}
And having to test the whole thing I have:
TestContext - abstract class that I think it sets up the basic context;
TestWebContext - extends TestContext and adds the WebCOnfig context. It's extended by all Controller tests;
DaoTest - extends TestContext and adds transaction management. It's extended by all DAO tests;
TestContext
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {PersistenceConfig.class, ServiceConfig.class, SecurityConfig.class})
public abstract class TestContext {
}
TestWebContext
#ContextConfiguration(classes = {WebConfig.class})
#WebAppConfiguration
public abstract class TestWebContext extends TestContext {
}
DaoTest
#TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true)
#Transactional
public abstract class DaoTest extends TestContext {
}
Questions:
When should I put the WebConfig.class in the getServletConfigClasses() or in the getRootConfigClasses() or both? What is the difference?
Does it matter what is the order of the classes that are present in the getRootConfigClasses() and getServletConfigClasses() methods? I've seen somewhere that order matters for the initializers and people put #Order at them but what about the Config classes?
For the TestWebContext class I know that just adding #ContextConfiguration(classes = {WebConfig.class}) overrides the #ContextConfiguration from the base class but how can I achieve the context extension?
If I add another configuration class say CoreConfig (I had one). Then load spring application context from XML in it and add it to the classes in getRootConfigClasses():
Note: no duplicate beans with Config classes are present in the applicationContext.xml.
CoreConfig
#Configuration
#EnableScheduling
#ImportResource("classpath:applicationContext.xml")
public class CoreConfig { // No duplicate Beans load }
Which beans are loaded at first? The ones in applicationContext.xml or the ones from the Config classes?
Any other tips from practice that worked for you about the Java configuration are also highly appreciated!
The WebConfig is responsible for the servlet related beans and therefore loaded in the servlet context. Other beans that could be potentially shared from other servlets in the same application can go in the root context. A good start for me to learn about the difference between the contexts was What is the difference between ApplicationContext and WebApplicationContext in Spring MVC?
Practically it does not matter. Changing the order does not make any difference in my applications. I could not find any theoretical proof documented somewhere though
Have a look at
http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/test/context/ContextHierarchy.html which contains code examples on how to "merge" the context configurations between parent-children
It shouldn't really matter since spring first loads all bean definitions and only afterwards it instantiates them by performing the dependency-injection in an order where beans that are properties or constructor arguments of other beans are initialized first. In cases where a bean depends to another but there is no property or constructor dependency between them for spring to understand the order, you can make use of the "depends-on" attribute
Regarding the usage of the config files I actually use a similar approach with yours. A change that you may find useful could be to only have a RootConfig.class loaded from the getRootConfigClasses() , and that RootConfig may import the SecurityConfig.class, PersistenceConfig.class and ServiceConfig.class along with any other functionality it may have. For example in my case it also loads an application.properties file using #PropertySource("classpath:application.properties") annotation and containing a PropertySourcesPlaceholderConfigurer bean