Java/Spring - Dependency injection

Published on

Basics

  • Spring has lot of modules
  • start.spring.io lists many spring modules which you can configure
  • spring boot is useful to build RESTfull web services. It makes developing spring applications easy by auto configuring default components. It is opinionated framework
  • fat jar -> also includes tomcat. better solution if you want to deploy on cloud platforms

Dependency Injection

  • Assume mail sender example, where we can have mailSender of type SMTP, POPM or MockMail.
  • We can have an interface MailSender which defines the common functionality and we implement the interface in SMTP, POPM or Mock class.
  • **Problem: object creation will be like: **
    • private MailSender = new SMTPMailSender()
    • private MailSender = new POPMMailSender()
    • private MailSender = new MockMailSender()
    • we need to hardcode the type while creating object
    • can we automate this?
    • Add @Component annotation to any class which implements MailSender
    • Add @Autowired annotation when creating object
    • how does it work:
      • When application starts all the beans gets setup
        • Beans: annotations at top level, whose object will be created and stored in Application Context
        • Beans examples: @Component, @RestController
      • When application is running and when we create objects variables for which we have annotations like @Autowired. Objects which were already created from application context are assigned to current variables.
      • i.e those objects from application context are injected into these variables and hence dependency injection
  • @Autowired also works on methods(setters, constructors, getters, any methods..)
  • Multiple beans problem: when we create two classes from same interface and add @component annotation to both classes, and create object using interface using @autowired annotation, spring will be confused to choose between beans
    • one solution is to use camel-case variable names while creating objects which resemble the name of the class you want (ex: smtpMailSender)
    • you can also add name param to @component -> @component(“mockMail”), and create object using the name mockMail
    • use extra @primary annotation, the bean which has this will get preference
    • you can also use @qualifier annotation to class, and also use @qualifier in setter or constructor, …etc
  • Prefer constructor injection
  • @resource and @inject are similar to @autowired, but @autowired is very powerful, so use @autowired
  • Specializations of @component annotations
    • @Controller
    • @Service
    • @Repository
    • @Configuration
    • @SpringBootApplication(scanBasePackageClasses = {default.class, abc.class}) - scans through the package and puts any dependency injection components into application context
    • @RestController
    • ….. many others
  • what if you import a dependency and you have to create object from there, you cannot add @component annotation dependency package
    • Create a new config class, which has methods to return objects from the external dependency
    • the new config class must have @Configuration annotation
    • and methods must have @Bean configuration
    • names of the methods can be used for object variable names
  • use @Value annotation to access variables from .properties or .yaml files
  • an external config file takes more preference over internal config(application.properties)
  • **Profiles: **A spring application can be configure to behave differently in different environments using profiles
    • ex environments: dev, test, prod
    • provide in application.properties:
      • spring.profiles.active: book, dev
    • you can also use particular beans in particular environments!
      • just use @Profile(“envName”), can accepts bool, !envName
    • @Conditional annotation, include or exclude beans based on arbitrary conditions
      • @ConditionalOnClass, OnProperty, OnMissingBean ..etc
      • configure bean based on presence of application property => @ConditionalOnProperty
  • In @Configuration classes, when we try to create same bean again from some method, spring still returns the cached bean only.
    • Caching does not happen when we use @Component annotation
  • Always use @Configuration Classes