ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Dependency Injection (DI)
    Framework/SPRING 2020. 3. 5. 19:24

    1. Overview

    for dependency injection, Spring can automatically wire up your objects together. So basically what'll happen is that Spring will look for a class that matches a given property. And it'll actually match by type, so the type could be either the class or the interface. Once Spring finds a match, then it'll automatically inject it. Hence it's called Autowired.

    2. Dependency Injection with Annotations

    3.1 Construction Injection

    • Injecting FortuneService into a Coach Implementation
    • Spring will scan @Components
    • Anyone implements FortuneService interface. If so, injecting them. For example, HappyFortuneService
    • Forcing that the object is created with the required dependency

    3.1.1 What if there are multiple FortuneService implementations?

    Spring has special support to handle this case. Use the @Qualifier annotation.

    interface Coach {
    	public String getDailyWorkout();
    	public String getDailyFortune();
    }
    // helper. This is a dependency of Coach
    interface FortuneService {
    	public String getFortune();
    
    }
    @Component
    class HappyFortuneService implements FortuneService {
    	@Override
        // Spring will find this bean that implements FortuneService based on Component annotation
    	public String getFortune() {
    		return "Today is your lucky day!";
    	}
    }
    @Component
    class TennisCoach implements Coach {
    	private FortuneService fortuneService;
    	@Autowired
        //	auto injection will occur in this constructor based on autowired annotation
    	public TennisCoach(FortuneService theFortuneService) {
    		fortuneService = theFortuneService;
    	}
    	@Override
    	public String getDailyWorkout() {
    		return "Practice your backhand volley";
    	}
    
    	@Override
    	public String getDailyFortune() {
    		return fortuneService.getFortune();
    	}
    }
    
    public class AnnotationDemoApp {
    
    	public static void main(String[] args) {
    
    		// read spring config file
    		ClassPathXmlApplicationContext context = 
    				new ClassPathXmlApplicationContext("applicationContext.xml");
    		
    		// get the bean from spring container
    		Coach theCoach = context.getBean("tennisCoach", Coach.class);
    		
    		// call a method on the bean
    		System.out.println(theCoach.getDailyWorkout());
    
    		// call method to get daily fortune
    		System.out.println(theCoach.getDailyFortune());
    				
    		// close the context
    		context.close();
    		
    	}
    }
    <!-- add entry to enable component scanning -->
    <context:component-scan base-package="com.luv2code.springdemo" />

    3.2 Method Injection (including Setter Injection)

    interface Coach {
    	public String getDailyWorkout();
    	public String getDailyFortune();
    }
    interface FortuneService {
    	public String getFortune();
    }
    @Component
    class HappyFortuneService implements FortuneService {
    	@Override
    	public String getFortune() {
    		return "Today is your lucky day!";
    	}
    
    }
    @Component
    class TennisCoach implements Coach {
    	private FortuneService fortuneService;
    	// define a default constructor
    	public TennisCoach() {
    		System.out.println(">> TennisCoach: inside default constructor");
    	}
    
    	// define a setter method
    	@Autowired
    	public void setFortuneService(FortuneService theFortuneService) {
    		System.out.println(">> TennisCoach: inside setFortuneService() method");
    		this.fortuneService = theFortuneService;
    	}
    
    	/*
    	@Autowired
    	public TennisCoach(FortuneService theFortuneService) {
    		fortuneService = theFortuneService;
    	}
    	*/
    
    	@Override
    	public String getDailyWorkout() {
    		return "Practice your backhand volley";
    	}
    
    	@Override
    	public String getDailyFortune() {
    		return fortuneService.getFortune();
    	}
    
    }
    public class AnnotationDemoApp {
    
    	public static void main(String[] args) {
    
    		// read spring config file
    		ClassPathXmlApplicationContext context = 
    				new ClassPathXmlApplicationContext("applicationContext.xml");
    		
    		// get the bean from spring container
    		Coach theCoach = context.getBean("tennisCoach", Coach.class);
    		
    		// call a method on the bean
    		System.out.println(theCoach.getDailyWorkout());
    
    		// call method to get daily fortune
    		System.out.println(theCoach.getDailyFortune());
    				
    		// close the context
    		context.close();	
    	}
    }

    3.3 Field Injection

    Inject dependencies by setting field values on your class directly even private fields through using Java Reflection. It is applied directly to the field and no need for setter methods.

    interface Coach {
    	public String getDailyWorkout();
    	public String getDailyFortune();
    }
    interface FortuneService {
    	public String getFortune();
    }
    @Component
    class HappyFortuneService implements FortuneService {
    
    	@Override
    	public String getFortune() {
    		return "Today is your lucky day!";
    	}
    
    }
    @Component
    class TennisCoach implements Coach {
    
    	@Autowired
    	private FortuneService fortuneService;
    
    	// define a default constructor
    	public TennisCoach() {
    		System.out.println(">> TennisCoach: inside default constructor");
    	}
    
    	// define a setter method
    	/*
    	@Autowired
    	public void setFortuneService(FortuneService theFortuneService) {
    		System.out.println(">> TennisCoach: inside setFortuneService() method");
    		this.fortuneService = theFortuneService;
    	}
    	*/
    
    	/*
    	@Autowired
    	public TennisCoach(FortuneService theFortuneService) {
    		fortuneService = theFortuneService;
    	}
    	*/
    
    	@Override
    	public String getDailyWorkout() {
    		return "Practice your backhand volley";
    	}
    
    	@Override
    	public String getDailyFortune() {
    		return fortuneService.getFortune();
    	}
    
    }
    public class AnnotationDemoApp {
    
    	public static void main(String[] args) {
    
    		// read spring config file
    		ClassPathXmlApplicationContext context = 
    				new ClassPathXmlApplicationContext("applicationContext.xml");
    		
    		// get the bean from spring container
    		Coach theCoach = context.getBean("tennisCoach", Coach.class);
    		
    		// call a method on the bean
    		System.out.println(theCoach.getDailyWorkout());
    
    		// call method to get daily fortune
    		System.out.println(theCoach.getDailyFortune());
    				
    		// close the context
    		context.close();	
    	}
    
    }

     

    3.4 Qualifiers

    How will Spring know which one to pick? You have to kind of be very specific, and you have to qualify or tell Spring which specific bean you want them to use. So what you'll do is actually make use of this annotation called Qualifier.

    The default name of bean id of a Class name is the first letter lower case. For example, HapptyFortuneService will make a bean id named happyFortuneService.

    interface Coach {
    	public String getDailyWorkout();
    	public String getDailyFortune();
    }
    interface FortuneService {
    	public String getFortune();
    }
    @Component("dfs1")
    class DatabaseFortuneService implements FortuneService {
    	@Override
    	public String getFortune() {
    		// TODO Auto-generated method stub
    		return null;
    	}
    }
    @Component("hfs5")
    class HappyFortuneService implements FortuneService {
    	@Override
    	public String getFortune() {
    		return "Today is your lucky day!";
    	}
    }
    @Component("rfs")
    class RandomFortuneService implements FortuneService {
    	// create an array of strings
    	private String[] data = {
    			"Beware of the wolf in sheep's clothing",
    			"Diligence is the mother of good luck",
    			"The journey is the reward"
    	};
    
    	// create a random number generator
    	private Random myRandom = new Random();
    	@Override
    	public String getFortune() {
    		// pick a random string from the array
    		int index = myRandom.nextInt(data.length);
    
    		String theFortune = data[index];
    
    		return theFortune;
    	}
    }
    @Component("RESTFS")
    class RESTFortuneService implements FortuneService {
    	@Override
    	public String getFortune() {
    		// TODO Auto-generated method stub
    		return null;
    	}
    }
    class SadFortuneService implements FortuneService {
    	@Override
    	public String getFortune() {
    		return "Today is a sad day";
    	}
    
    }
    @Component("tc5")
    class TennisCoach implements Coach {
    
    	@Autowired
    	@Qualifier("hfs5")
    	private FortuneService fortuneService;
    
    	// define a default constructor
    	public TennisCoach() {
    		System.out.println(">> TennisCoach: inside default constructor");
    	}
    
    	// define a setter method
    	/*
    	@Autowired
    	public void setFortuneService(FortuneService theFortuneService) {
    		System.out.println(">> TennisCoach: inside setFortuneService() method");
    		this.fortuneService = theFortuneService;
    	}
    	*/
    
    	/*
    	@Autowired
    	public TennisCoach(FortuneService theFortuneService) {
    		fortuneService = theFortuneService;
    	}
    	*/
    
    	@Override
    	public String getDailyWorkout() {
    		return "Practice your backhand volley";
    	}
    
    	@Override
    	public String getDailyFortune() {
    		return fortuneService.getFortune();
    	}
    
    }
    public class AnnotationDemoApp {
    	public static void main(String[] args) {
    		// read spring config file
    		ClassPathXmlApplicationContext context = 
    				new ClassPathXmlApplicationContext("applicationContext.xml");
    		
    		// get the bean from spring container
    		Coach theCoach = context.getBean("tc5", Coach.class);
    		
    		// call a method on the bean
    		System.out.println(theCoach.getDailyWorkout());
    
    		// call method to get daily fortune
    		System.out.println(theCoach.getDailyFortune());
    				
    		// close the context
    		context.close();	
    	}
    }

    3.4.1 Qualifier on Constructor

    public class MovieRecommender {
    
        private MovieCatalog movieCatalog;
    
        private CustomerPreferenceDao customerPreferenceDao;
    
        @Autowired
        public void prepare(@Qualifier("main") MovieCatalog movieCatalog,
                CustomerPreferenceDao customerPreferenceDao) {
            this.movieCatalog = movieCatalog;
            this.customerPreferenceDao = customerPreferenceDao;
        }
    
        // ...
    }

    3.5 Injection Guidelines

    • For mandatory dependencies or when aiming for immutability, use constructor injection
    • For optional or changeable dependencies, use setter injection
    • Avoid field injection in most cases

    3.5.1 Field Injection Drawbacks

    • You cannot create immutable objects, as you can with constructor injection
    • Your classes have tight coupling with your DI container and cannot be used outside of it
    • Your classes cannot be instantiated (for example in unit tests) without reflection. You need the DI container to instantiate them, which makes your tests more like integration tests. Also, Reflection makes the application code run slower since it involves types that are dynamically resolved at runtime
    • Your real dependencies are hidden from the outside and are not reflected in your interface (either constructors or methods)
    • It is really easy to have like ten dependencies. If you were using constructor injection, you would have a constructor with ten arguments, which would signal that something is fishy. But you can add injected fields using field injection indefinitely. Having too many dependencies is a red flag that the class usually does more than one thing and that it may violate the Single Responsibility Principle.  

    3.5.2 Constructor Injection Pros

    • Cannot Instantiation without clear dependency 
      • Prevent NullPointerException(NPE)
      • Can be recognized at compile time
    • Can declare fields as final
      • Immutable
    • Can be recognized cycle reference
    • Easy for test code

    4. Reference

    https://stackoverflow.com/questions/39890849/what-exactly-is-field-injection-and-how-to-avoid-it#:~:text=The%20reasons%20why%20field%20injection,in%20unit%20tests)%20without%20reflection.

    https://yaboong.github.io/spring/2019/08/29/why-field-injection-is-bad/

    http://www.luv2code.com/

    https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#beans-autowired-annotation

    https://docs.oracle.com/javase/8/docs/api/java/beans/Introspector.html#decapitalize(java.lang.String)

    https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#beans-autowired-annotation-qualifiers

    'Framework > SPRING' 카테고리의 다른 글

    Application Context  (0) 2020.04.10
    Portable Service Abstraction (PSA)  (0) 2020.03.06
    Spring Inversion of Control (IoC)  (0) 2020.02.06
    Spring Framework  (0) 2020.02.06
    Spring MVC and Request Life Cycle  (0) 2020.02.03

    댓글

Designed by Tistory.