Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring & Servlet 3.0 Java Configuration Mess

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!

like image 945
nyxz Avatar asked Nov 01 '22 19:11

nyxz


1 Answers

  • 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

like image 193
Marios Avatar answered Nov 08 '22 04:11

Marios