Pages

Monday, July 6, 2020

@EnableAutoConfiguration Annotation in Spring Boot

Spring Boot @EnableAutoConfiguration

1. Introduction


In this article, We'll be learning how to use @EnableAutoConfiguration annotation in spring boot.

@EnableAutoConfiguration is an interface as part of org.springframework.boot.autoconfigure package.

@EnableAutoConfiguration Annotation in Spring Boot




2. @EnableAutoConfiguration 


This enables auto-configuration of your application context that is based on our needs and tries to attempt to configure beans. Mainly this autoconfiguration is decided based on your classpath and what are the jars added to the application.

If you have added tomcat-embedded.jar then it tries to intellectually configure TomcatServletWebServerFactory if you do not have specified explicitly as ServletWebServerFactory bean.

@Retention(value=RUNTIME)
 @Documented
 @Inherited
 @SpringBootConfiguration
 @EnableAutoConfiguration
 @ComponentScan(excludeFilters={@ComponentScan.Filter(type=CUSTOM,classes=TypeExcludeFilter.class),})
public @interface SpringBootApplication

Note:

If you are using @SpringBootApplication then by default auto-configure feature is enabled automatically. So, adding explicitly @EnableAutoConfiguration annotation has no value added.

Read more on @ComponentScan annotation.

Below are the example classes created to simulate auto configurations. In real-time applications, manually created classes do not need to use this annotation. In this example, trying to add custom classes to be auto-configured by spring with @EnableAutoConfiguration.

[post_ads]

class Message {

 public String getMessage() {
  return "New message generated";
 }

}

@Controller
class MessageController {
 Logger logger = LoggerFactory.getLogger(getClass());

 @Autowired
 private Message msg;

 @PostMapping("/send")
 public void messageSender() {

  String messag = msg.getMessage();
  logger.info("Message : " + messag);
  logger.info("Message sent....");
 }
}


package com.javaprogram.auto.configurations;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;

@EnableAutoConfiguration
public class AutoConfigureApplication {
 public static void main(String[] args) {
  SpringApplication.run(AutoConfigureApplication.class, args);

 }

}

After creating all of these classes, we are able to start the server but not able to find the endpoint for /send.

.   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.2.6.RELEASE)

2020-04-19 12:44:00.498  INFO 78470 --- [           main] c.j.a.c.AutoConfigureApplication         : Starting AutoConfigureApplication on.local with PID 78470 (/Users/Spring-Boot-Tutorials/target/classes started by  in /Users/workspace/Spring-Boot-Tutorials)
2020-04-19 12:44:00.501  INFO 78470 --- [           main] c.j.a.c.AutoConfigureApplication         : No active profile set, falling back to default profiles: default
2020-04-19 12:44:01.425  INFO 78470 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2020-04-19 12:44:01.441  INFO 78470 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2020-04-19 12:44:01.441  INFO 78470 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.33]
2020-04-19 12:44:01.527  INFO 78470 --- [           main] o.a.c.c.C.[.[localhost].[/api/v1]        : Initializing Spring embedded WebApplicationContext
2020-04-19 12:44:01.528  INFO 78470 --- [           main] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 996 ms
2020-04-19 12:44:01.711  INFO 78470 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2020-04-19 12:44:01.796  INFO 78470 --- [           main] o.s.b.a.w.s.WelcomePageHandlerMapping    : Adding welcome page template: index
2020-04-19 12:44:01.964  INFO 78470 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path '/api/v1'
2020-04-19 12:44:01.967  INFO 78470 --- [           main] c.j.a.c.AutoConfigureApplication         : Started AutoConfigureApplication in 2.135 seconds (JVM running for 2.708)
2020-04-19 12:44:32.079  INFO 78470 --- [nio-8080-exec-1] o.a.c.c.C.[.[localhost].[/api/v1]        : Initializing Spring DispatcherServlet 'dispatcherServlet'
2020-04-19 12:44:32.079  INFO 78470 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'

2020-04-19 12:44:32.085  INFO 78470 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 6 ms

When I hit localhost:8080/api/v1/send api, it returns 404 always.

Because created controller and beans are not registered with spring because these are not auto-configured by spring even though @EnableAutoConfiguration is enabled.

Now, we have to tell spring to configure all these as below. Let us see if the below configuration works or not?

@EnableAutoConfiguration
public class AutoConfigureApplication {

 @Bean
 public Message getMessage() {
  return new Message();
 }

 @Bean
 public MessageController getMessageController() {
  return new MessageController();
 }

 public static void main(String[] args) {
  SpringApplication.run(AutoConfigureApplication.class, args);

 }

}

Yes now, all of these are registered and after hitting the request got the response as a success.

2020-04-19 13:04:49.728  INFO 79367 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
2020-04-19 13:04:49.751  INFO 79367 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 23 ms
2020-04-19 13:04:49.808  INFO 79367 --- [nio-8080-exec-1] c.j.a.configurations.MessageController   : Message : New message generated
2020-04-19 13:04:49.809  INFO 79367 --- [nio-8080-exec-1] c.j.a.configurations.MessageController   : Message sent...

autio configure message

3. Exclude Auto-Configuration Classes


If you do not want to include some of the classes then we can exclude them using exclude and excludeName attributes.

excludeName - which takes class names that should be ignored by auto configurations.

public abstract String[] excludeName

exclude - Which takes the classes that are never applied to auto configurations.

public abstract Class[] exclude


Now, Let us try to exclude the classes that we have created earlier in this article.

@EnableAutoConfiguration(exclude = MessageController.class, excludeName = "Message")
public class AutoConfigureApplication {

 public static void main(String[] args) {
  SpringApplication.run(AutoConfigureApplication.class, args);

 }
}

Output:

[post_ads_2]

This code produces the error because these two create by us and they are not auto configured classes. Only autoconfigured classes that are managed by application context will only be excluded. Even though excluding user-defined classes will not work with exclude attributes.


2020-04-19 13:13:13.051 ERROR 79761 --- [           main] o.s.boot.SpringApplication               : Application run failed

java.lang.IllegalStateException: The following classes could not be excluded because they are not auto-configuration classes:
 - com.javaprogram.auto.configurations.MessageController

 at org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.handleInvalidExcludes(AutoConfigurationImportSelector.java:209) ~[spring-boot-autoconfigure-2.2.6.RELEASE.jar:2.2.6.RELEASE]
 at org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.checkExcludedClasses(AutoConfigurationImportSelector.java:195) ~[spring-boot-autoconfigure-2.2.6.RELEASE.jar:2.2.6.RELEASE]
 at org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.getAutoConfigurationEntry(AutoConfigurationImportSelector.java:119) ~[spring-boot-autoconfigure-2.2.6.RELEASE.jar:2.2.6.RELEASE]
 at org.springframework.boot.autoconfigure.AutoConfigurationImportSelector$AutoConfigurationGroup.process(AutoConfigurationImportSelector.java:396) ~[spring-boot-autoconfigure-2.2.6.RELEASE.jar:2.2.6.RELEASE]
 at org.springframework.context.annotation.ConfigurationClassParser$DeferredImportSelectorGrouping.getImports(ConfigurationClassParser.java:878) ~[spring-context-5.2.5.RELEASE.jar:5.2.5.RELEASE]
 at org.springframework.context.annotation.ConfigurationClassParser$DeferredImportSelectorGroupingHandler.processGroupImports(ConfigurationClassParser.java:808) ~[spring-context-5.2.5.RELEASE.jar:5.2.5.RELEASE]
 at org.springframework.context.annotation.ConfigurationClassParser$DeferredImportSelectorHandler.process(ConfigurationClassParser.java:779) ~[spring-context-5.2.5.RELEASE.jar:5.2.5.RELEASE]
 at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:192) ~[spring-context-5.2.5.RELEASE.jar:5.2.5.RELEASE]
 at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:319) ~[spring-context-5.2.5.RELEASE.jar:5.2.5.RELEASE]
 at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:236) ~[spring-context-5.2.5.RELEASE.jar:5.2.5.RELEASE]
 at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:275) ~[spring-context-5.2.5.RELEASE.jar:5.2.5.RELEASE]
 at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:95) ~[spring-context-5.2.5.RELEASE.jar:5.2.5.RELEASE]
 at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:706) ~[spring-context-5.2.5.RELEASE.jar:5.2.5.RELEASE]
 at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:532) ~[spring-context-5.2.5.RELEASE.jar:5.2.5.RELEASE]
 at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:141) ~[spring-boot-2.2.6.RELEASE.jar:2.2.6.RELEASE]
 at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:747) [spring-boot-2.2.6.RELEASE.jar:2.2.6.RELEASE]
 at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397) [spring-boot-2.2.6.RELEASE.jar:2.2.6.RELEASE]
 at org.springframework.boot.SpringApplication.run(SpringApplication.java:315) [spring-boot-2.2.6.RELEASE.jar:2.2.6.RELEASE]
 at org.springframework.boot.SpringApplication.run(SpringApplication.java:1226) [spring-boot-2.2.6.RELEASE.jar:2.2.6.RELEASE]
 at org.springframework.boot.SpringApplication.run(SpringApplication.java:1215) [spring-boot-2.2.6.RELEASE.jar:2.2.6.RELEASE]
 at com.javaprogram.auto.configurations.AutoConfigureApplication.main(AutoConfigureApplication.java:28) [classes/:na]


Exclude JmsAutoConfiguration and SecurityAutoConfiguration:


import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;

@EnableAutoConfiguration(exclude = JmsAutoConfiguration.class, excludeName = "SecurityAutoConfiguration")
public class AutoConfigureApplication {

}

Pass the class names to either exclude or excludenames attributes. This tells to spring to ignore these two classes when doing auto configuration step. Here, it won't throw an exception because these are auto configured by spring boot.

@EnableAutoConfiguration(exclude = { SecurityAutoConfiguration.class, SecurityAutoConfiguration.class })

[post_ads_2]

4. Conclusion


In this article, We've seen how to use @EnableAutoConfiguration in spring boot and its attributes. If you are using spring boot, you need not mention this annotation.

Alternatively, we can use ComponetScan to register with Spring.

Leave your questions in the comments section.

All the examples shown in this article are over GitHub.

GitHub Code

EnableAutoConfiguration API

Stackoverflow

No comments:

Post a Comment

Please do not add any spam links in the comments section.