Configuração de uma aplicação Spring através de código Java (sem XML)

Nesse artigo, veremos como criar uma aplicação Spring sem precisar criar os tradicionais arquivos XML web.xml e spring-servlet.xml. Usaremos aqui apenas classes Java com as devidas anotações.

O artefato central do suporte do Spring para configuração baseado em código Java é a anotação de classe @Configuration. Classes anotadas dessa forma consistem basicamente de métodos anotados com  @Bean que definem a instancialização, configuração e lógica para os objetos gerenciados pelo Spring.

Anotar uma classe com @Configuration indica que essa classe pode ser usada pelo Spring como fonte de definições de beans. A classe mais simples a fazer uso de @Configuration seria a seguinte:

@Configuration
public class AppConfig {
}

Uma aplicação pode fazer uso de um ou várias classes anotadas com @Configuration@Configuration é meta-anotada como @Component. Dessa forma, classes anotadas com @Configuration são candidatas à escaneamento de componentes e podem tirar vantagem da anotação  @Autowired em atributos e métodos, mas não em construtores.

Desde a versão 3.0 da specificação do ervlet, podemos eliminar o uso do venerável arquivo web.xml. Para podermos fazer isso, basta criar uma classe que implemente ServletContainerInitializer e um arquivo META-INF/services/javax.servlet.ServletContainerInitializer contendo o nome completo de sua implementação.

Spring

Desde a versão 3.1, o Spring fornece uma implementação da interface ServletContainerInitializer com o nome SpringServletContainerInitializer. Se você der uma olhada no arquivo spring-web-[versão 3.1 ou mais recente].jar verá o arquivo META-INF/services mencionada acima.
A classe SpringServletContainerInitializer delega a sua implementação de
org.springframework.web.WebApplicationInitializer o controle do fluxo de ação. Existe apenas um método que você precisa implementar:
WebApplicationInitializer#onStartup(ServletContext)
Onde você deve especificar o ServletContext que precisa inicializar.

public class WebAppInitializer implements WebApplicationInitializer {
 @Override
 public void onStartup(ServletContext servletContext) {
  WebApplicationContext appContext = ?; //set up the context
  ServletRegistration.Dynamic dispatcher =   servletContext.addServlet("dispatcher", new DispatcherServlet(appContext));
  dispatcher.setLoadOnStartup(1);
  dispatcher.addMapping("/");
}
}

Configurando os contextos

Como estamos evitando escrever arquivos XML, sugiro que em vez de usar a classe XmlWebApplicationContext, você use AnnotationConfigWebApplicationContext que suporta o escaneamento do classpath por configurações baseadas em anotações do Spring. Você pode explicitamente adicionar classes de configuração também.
Para iniciar ou desligar o contexto, devemos adicionar um ContextLoaderListener.

@Override
public void onStartup(ServletContext servletContext) throws ServletException {
 // Create the root appcontext
 AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
 rootContext.register(RootConfig.class);
 // since we registered RootConfig instead of passing it to the constructor
 rootContext.refresh();
 // Manage the lifecycle of the root appcontext
servletContext.addListener(new ContextLoaderListener(rootContext));
 servletContext.setInitParameter("defaultHtmlEscape", "true");
 // now the config for the Dispatcher servlet
AnnotationConfigWebApplicationContext mvcContext = new AnnotationConfigWebApplicationContext();
 mvcContext.register(WebMvcConfig.class);
 // The main Spring MVC servlet.
ServletRegistration.Dynamic appServlet = servletContext.addServlet(
"appServlet", new DispatcherServlet(mvcContext));
appServlet.setLoadOnStartup(1);
 Set mappingConflicts =  appServlet.addMapping("/");
 if (!mappingConflicts.isEmpty()) {
for (String s : mappingConflicts) {
logger.error("Mapping conflict: " + s);
 }
 throw new IllegalStateException(
"'appServlet' cannot be mapped to '/' under Tomcat versions <= 7.0.14");
 }
}

Arquivos de configuração Java

A classe Java RootConfig que especificamos no exemplo anterior precisa usar a anotação @Configuration. Essencialmente essa classe corresponde ao elemento <beans> dos arquivos de configuração do Spring. Os elementos <bean> agora são método com anotação @Bean que retornam uma instância do bean. O elemento <context:component-scan> passa a ser a anotação de classe @ComponentScan.

@Configuration
@ComponentScan(basePackages = { "com.rockhoppertech.mvc.service", "com.rockhoppertech.mvc.repositories.internal" })
public class RootConfig {
 @Bean
 public SomeClass someClass() {
  return someInstance;
 }
}

Filtros

Você pode criar qualquer Filtro de Servlets aqui. Nesse link você pode encontrar alguns filtros fornecidos pelo Spring.
No exemplo a seguir, configuramos o filtro CharacterEncodingFilter.

@Override
public void onStartup(ServletContext servletContext) throws ServletException {
 ...
 FilterRegistration.Dynamic fr = servletContext.addFilter("encodingFilter",
new CharacterEncodingFilter());
 fr.setInitParameter("encoding", "UTF-8");
 fr.setInitParameter("forceEncoding", "true");
 fr.addMappingForUrlPatterns(null, true, "/*");
 ...
}

@Enable*

Os namespaces XML são substituídos pelas anotações de classe que começam com  @Enable.

  • @EnableWebMvc
  • @EnableAsync
  • @EnableScheduling
  • @EnableLoadTimeWeaving
  • @EnableTransactionManagement

Configuração para o Spring MVC

Aqui, criamos uma classe de configuração que contém a anotação @EnableWebMvc, que é definida por DelegatingWebMvcConfiguration. Além disso, você pode querer fazer um escaneamento de componentes por controllers.
Para personalizar os padrões, implemente a interface  WebMvcConfigurer ou estenda a classe base WebMvcConfigurerAdapter. Qualquer método sobrecarregado que não retorne NULL será usado no lugar do método padrão.

@Configuration
@EnableWebMvc
// scan for controllers
@ComponentScan(basePackages = { "com.rockhoppertech.mvc.web" })
public class WebMvcConfig extends WebMvcConfigurerAdapter {
 @Bean
 ViewResolver viewResolver() { ... }
 @Bean
 MessageSource messageSource() { ... }
etc.

Agora, iremos mostrar três implementações básicas para classes de Configuração, que são o mínimo necessário para poder executar a sua aplicação sem a necessidade de arquivos XML.

WebAppInitializer.java

@Order(value=1)
public class WebAppInitializer implements WebApplicationInitializer {
 public void onStartup(ServletContext arg0) throws ServletException {
  // Create the 'root' Spring application context
AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
  rootContext.register(WebAppConfig.class);
  // Manage the lifecycle of the root application context
  //container.addListener(new ContextLoaderListener(rootContext));
  // Create the dispatcher servlet's Spring application context
  AnnotationConfigWebApplicationContext dispatcherContext = new AnnotationConfigWebApplicationContext();
  dispatcherContext.register(DispatcherConfig.class);
  // Register and map the dispatcher servlet
ServletRegistration.Dynamic dispatcher = arg0.addServlet("dispatcher", new DispatcherServlet(dispatcherContext));
  dispatcher.setLoadOnStartup(1);
  dispatcher.addMapping("/");
 }
}

WebAppConfig.java

@EnableWebMvc
@ComponentScan(value="spring.example")
@Configuration
public class WebAppConfig extends WebMvcConfigurerAdapter {
 @Override
 public void addResourceHandlers(ResourceHandlerRegistry registry) {
  registry.addResourceHandler("/jquery-ui/**").addResourceLocations("/resources/jquery-ui/").setCachePeriod(31556926);
  registry.addResourceHandler("/bootstrap/**").addResourceLocations("/resources/bootstrap/").setCachePeriod(31556926);
  registry.addResourceHandler("/extras/**").addResourceLocations("/resources/extras/").setCachePeriod(31556926);
 }
 @Override
 public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
  configurer.enable();
 }
}

DispatcherConfig.java

@Configuration
@Import(WebAppConfig.class)
public class DispatcherConfig {
 @Bean
 public ViewResolver viewResolver() {
  InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
  viewResolver.setPrefix("/WEB-INF/jsp/");
  viewResolver.setSuffix(".jsp");
  return viewResolver;
 }
}