Over a million developers have joined DZone.

Spring component scan for beans with no no-args constructor

· Java Zone

Microservices! They are everywhere, or at least, the term is. When should you use a microservice architecture? What factors should be considered when making that decision? Do the benefits outweigh the costs? Why is everyone so excited about them, anyway?  Brought to you in partnership with IBM.

Suppose you have the following spring set-up 

  • component scan enabled
  • a class (MailServer) with
    • some constructors (no no-args constructor)
    • annotated with @Component (or @Service / @Named)
  • declaration of above class in the spring-config
    • (Even though you have component-scan enabled, suppose you need to configure the default mail server with a name "defaultMailServer" )
  • injecting the above "defaultMailServer" to a property of another class (i.e. MailSender)


Spring Configuration

 <bean id="defaultMailServer" class="me.fahimfarook.mail.MailServer"  scope="singleton">  
     <constructor-arg name="host" value="smtp.gmail.com" />  
     <constructor-arg name="port" value="587" />  
     <constructor-arg name="protocol" value="smtp" />  
 </bean>  
 <context:component-scan base-package="me.fahimfarook.mail" />  


MailServer.java

 @Component  
 public class MailServer {  
    private String host;   
    private int port;   
    private String protocol;  

    public MailServer(final String host, final int port, final String protocol) {  
       this.host = host;  
       this.port = port;  
       this.protocol = protocol;  
    }  

    //getters  
 }  


MailSender.java

@Service  
 public class MailSenderImpl implements MailSender {  

    @Autowired  
    @Qualifier("defaultMailServer")  
    private MailServer mailServer;  

    @Override  
    public void send() {  
      // TODO  
    }  
 }  


Main.java

public class Main {  
    private static final String CONFIG_PATH = "classpath*:application-config.xml";  

    public static void main(final String[] args) {  
       final ApplicationContext context = new ClassPathXmlApplicationContext(CONFIG_PATH);  
       final MailSender mailSender = context.getBean(MailSenderImpl.class);  
       mailSender.send();  
    }  
 }  


Nothing looks wrong. But when you execute the program you will get the following exception.

org.springframework.beans.BeanInstantiationException: 
Could not instantiate bean class [me.fahimfarook.mail.MailServer]: No default constructor found;  


This error message is misleading because we are not accessing MailServer with default constructor anywhere in our code. We have injected "defaultMailServer" which is properly defined with constructor-args.

However, if you add a no-args constructor to MailServer this exception will go away. That means spring context is trying to create a MailServer object using no-args constructor somewhere even though we have defined a MailServer with constructor-args. But we have defined a MailServer bean with constructor-args and accessing that bean only?

The reason for that is - we have defined a bean explicitly in the spring config xml while we have enabled component scanning. Spring will create the "defaultMailServer" bean successfully since constructor-args have been defined in the spring config xml. However, since component scanning has been enabled, spring will try to automatically discover and register another MailServer bean. However, this will fail because spring can't create an object using the defined constructor as we have not auto-wired the constructor / parameters in the constructor. As the second step, spring will try to create a MailServer with no-args constructor.

Therefore you need to have auto-wired the constructor which you defined in the spring-config file in order to get rid of this error.

MailServer.java - Fixed

@Component  
 public class MailServer {  
      private String host;  
      private int port;  
      private String protocol;

      @Autowired  
      public MailServer(@Value("")final String host, @Value("#{new Integer(-1)}")final int port, @Value("")final String protocol) {   
           this.host = host;  
           this.port = port;  
           this.protocol = protocol;  
      }  
 }  


Now that you have auto-wired the constructor which is defined with "defaultMailServer", spring can create this bean and it will not demand a default constructor. However, if spring had thrown BeanInstantiationException with a message like "Constructor for defaultMailServer could not be found" instead of "No default constructor found", the exact issue with the code could have been identified easily.


In summary:

  • If component scanning is enabled, spring will try to create a bean even though a bean of that class has already been defined in the spring config xml.
  • However if the bean defined in the spring config file and the auto-discovered bean have the same name, spring will not to create a new bean while it does component scanning.
  • If a bean does not have a no-args constructor, at-least one of the constructors must be auto-wired.
  • If no constructor is auto-wired, spring will try to create an object using default no-args constructor.
Original post here 

Discover how the Watson team is further developing SDKs in Java, Node.js, Python, iOS, and Android to access these services and make programming easy. Brought to you in partnership with IBM.

Topics:
java ,high-perf ,spring ,tips and tricks ,component-scan

Published at DZone with permission of Fahim Farook. See the original article here.

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}