Spring: Make your java-based configuration more elegant
Hi everyone, I haven’t written new articles a long period of time. A lot of materials were accumulated which need to be posted in my blog in nearest future. But now I want to talk about Spring MVC application configurations. If to be more precisely, I want to talk about java based Spring configurations.
Despite that Spring java based configuration was introduced in 3.0 version, many developers still use XML-based approach. Personally I use annotation based configurations, because they are more convenient in management, in development and in maintenance. If you have read my blog before you could noticed that in all code examples I used exactly java based configurations.
Not so long time ago I have made a code review of one my project. I noticed that something is wrong with a structure of the configurations. Two aspects were bad as for me:
- All beans were configured in a single class
- Initialization on web app context was too complex
You can witness these two drawbacks in all my examples of Spring MVC applications. For example you can open one of my last tutorials about Spring REST services with CNVR. Take a look there on two classes: WebAppConfig and Initializer.
WebAppConfig
The first one represents the first point in this article. Definitely I need to do something to split beans configuration logically. To resolve this issue I decided to make two steps:
- Move DB configurations in a separate class0102030405060708091011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories
(
"com.mobapp.repository"
)
public
class
DataBaseConfig {
private
static
final
String PROPERTY_NAME_DATABASE_DRIVER =
"db.driver"
;
private
static
final
String PROPERTY_NAME_DATABASE_PASSWORD =
"db.password"
;
private
static
final
String PROPERTY_NAME_DATABASE_URL =
"db.url"
;
private
static
final
String PROPERTY_NAME_DATABASE_USERNAME =
"db.username"
;
private
static
final
String PROPERTY_NAME_HIBERNATE_DIALECT =
"hibernate.dialect"
;
private
static
final
String PROPERTY_NAME_HIBERNATE_SHOW_SQL =
"hibernate.show_sql"
;
private
static
final
String PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN =
"entitymanager.packages.to.scan"
;
@Resource
private
Environment env;
@Bean
public
DataSource dataSource() {
DriverManagerDataSource dataSource =
new
DriverManagerDataSource();
dataSource.setDriverClassName(env.getRequiredProperty(PROPERTY_NAME_DATABASE_DRIVER));
dataSource.setUrl(env.getRequiredProperty(PROPERTY_NAME_DATABASE_URL));
dataSource.setUsername(env.getRequiredProperty(PROPERTY_NAME_DATABASE_USERNAME));
dataSource.setPassword(env.getRequiredProperty(PROPERTY_NAME_DATABASE_PASSWORD));
return
dataSource;
}
@Bean
public
LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean =
new
LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(dataSource());
entityManagerFactoryBean.setPersistenceProviderClass(HibernatePersistence.
class
);
entityManagerFactoryBean.setPackagesToScan(env.getRequiredProperty(PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN));
entityManagerFactoryBean.setJpaProperties(hibProperties());
return
entityManagerFactoryBean;
}
private
Properties hibProperties() {
Properties properties =
new
Properties();
properties.put(PROPERTY_NAME_HIBERNATE_DIALECT, env.getRequiredProperty(PROPERTY_NAME_HIBERNATE_DIALECT));
properties.put(PROPERTY_NAME_HIBERNATE_SHOW_SQL, env.getRequiredProperty(PROPERTY_NAME_HIBERNATE_SHOW_SQL));
return
properties;
}
@Bean
public
JpaTransactionManager transactionManager() {
JpaTransactionManager transactionManager =
new
JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
return
transactionManager;
}
}
- Convert WebAppConfig class in a main configuration class and assign to it the rest of configuration (in current case it would be just DataBaseConfig class) classes via @Import annotation.01020304050607080910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061
@Configuration
@EnableWebMvc
@Import
({DataBaseConfig.
class
})
@ComponentScan
(
"com.mobapp"
)
@PropertySource
(
"classpath:application.properties"
)
public
class
WebAppConfig
extends
WebMvcConfigurerAdapter {
@Resource
private
Environment env;
@Override
public
void
addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler(
"/resources/**"
).addResourceLocations(
"/resources/"
);
}
@Override
public
void
configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.favorPathExtension(
true
)
.useJaf(
false
)
.ignoreAcceptHeader(
true
)
.mediaType(
"html"
, MediaType.TEXT_HTML)
.mediaType(
"json"
, MediaType.APPLICATION_JSON)
.defaultContentType(MediaType.TEXT_HTML);
}
@Bean
public
ViewResolver contentNegotiatingViewResolver(
ContentNegotiationManager manager) {
List< ViewResolver > resolvers =
new
ArrayList< ViewResolver >();
InternalResourceViewResolver r1 =
new
InternalResourceViewResolver();
r1.setPrefix(
"/WEB-INF/pages/"
);
r1.setSuffix(
".jsp"
);
r1.setViewClass(JstlView.
class
);
resolvers.add(r1);
JsonViewResolver r2 =
new
JsonViewResolver();
resolvers.add(r2);
ContentNegotiatingViewResolver resolver =
new
ContentNegotiatingViewResolver();
resolver.setViewResolvers(resolvers);
resolver.setContentNegotiationManager(manager);
return
resolver;
}
/**
* View resolver
for
returning JSON in a view-based system. Always returns a
* {
@link
MappingJacksonJsonView}.
*/
public
class
JsonViewResolver
implements
ViewResolver {
public
View resolveViewName(String viewName, Locale locale)
throws
Exception {
MappingJacksonJsonView view =
new
MappingJacksonJsonView();
view.setPrettyPrint(
true
);
return
view;
}
}
}
In this way you can separate single big configuration class in several smaller which will contain specific configs for them.
Initializer
Code of Initializer class is too verbose in the example which I mentioned above and provided the link to it. I registered there the web application root configurations, mapping and filter. How I can decrease a number of code lines? The answer I got looking on AbstractAnnotationConfigDispatcherServletInitializer class. Look on the class and you will notice that it implements WebApplicationInitializer interface, which I have implmented in my previous version of the Initializer class. So here is a new version of the Initializer:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | public class Initializer extends AbstractAnnotationConfigDispatcherServletInitializer { @Override protected Class< ? >[] getRootConfigClasses() { return null ; } @Override protected Class< ? >[] getServletConfigClasses() { return new Class< ? >[] { WebAppConfig. class }; } @Override protected String[] getServletMappings() { return new String[] { "/" }; } @Override protected Filter[] getServletFilters() { return new Filter[] { new HiddenHttpMethodFilter() }; } } |
Thank to these two steps I have made my configurations for Spring MVC application more elegant. Now you can do it too. Good luck