Spring Injections/annotation

需依來源/條件產出bean時, 讓物件implements相同interface, 之後可用@Autowired List<物件>設定, 取用所有物件

依來源/條件產出bean, 並將來源/條件設為enum, 關聯物件及enum值存在map以取用

bean建構子傳不特定參數寫法

bean裡需依情況在建構子inject不同設定(e.g.api代碼,來源別...)寫法

types of Dependency Injections: field, setter, constructor.

spring configuration: XML, Java code or annotations.

Instantiating beans.

Bean Lifecycle.

常用annotations

@ConfigurationProperties	引用application.properties的特定前綴屬性

需依來源/條件產出bean時, 讓物件implements相同interface, 之後可用@Autowired List<物件>設定, 取用所有物件

public interface apnoGet {
	apnoEnum getApno();
	String getRequestCode();
	String getSysCode();
}
@Component("sbroker")
public class ApnoSbrokerConfig implements apnoGet {	... }
@Component("Upddta")
public class ApnoUpddtaConfig implements apnoGet { ... }
@Configuration
public class ApnoMapConfig {
	
    @Autowired
    List<apnoGet> apnoGets;	//可取用所有apnoGet物件
	...
}

依來源/條件產出bean, 並將來源/條件設為enum, 關聯物件及enum值存在map以取用

@Configuration
public class ApnoMapConfig {
	
    @Autowired
    List<apnoGet> apnoGets;
    
    @Bean
    public Map<apnoEnum, apnoGet> apnoConfigMap() {
        Map<apnoEnum, apnoGet> map = new HashMap<>();
        apnoGets.forEach(s -> {
        	if ("Upddta")
        		map.put(apnoEnum.upddta, s);
        	
        	if ("Sbroker")
        		map.put(apnoEnum.sbroker, s);
        });
        return map;
    } 	
    
    public enum apnoEnum {
   
    	upddta("upddta","變更"), 
    	sbroker("sbroker","開戶"),
    	none("none","無此apno");
		...
    }
}

bean建構子傳不特定參數寫法

https://nullbeans.com/how-to-define-and-declare-spring-beans-using-java-config-and-constructor-injection/

bean裡需依情況在建構子inject不同設定(e.g.api代碼,來源別...)寫法

@Component
public class LoginServiceBean implements LoginService {
   
private SourceService sourceService;

public LoginServiceBean(SourceService sourceService) {	//constructor inject
 this.sourceService = sourceService;
}
SourceService通常會有很多資料源物件, 像

@Component("fileSourceService")
public class FileSourceService implements SourceService {...}

@Component("apiSourceService")
public class APISourceService implements SourceService {...}
在APPLICATION START時就會出現訊息, 說建構子參數需指定用哪個bean

***************************
APPLICATION FAILED TO START
***************************

Description:

Parameter 0 of constructor in com.tss.config.LoginServiceBean required a single bean, but 2 were found:
- fileSourceService: defined in file [D:\java\workspace\demo4\target\classes\com\tss\config\FileSourceService.class]
- apiSourceService: defined in file [D:\java\workspace\demo4\target\classes\com\tss\config\ApiSourceService.class]
目前覺得較佳做法: 
	建構子引用SourceService的LoginServiceBean.class不要做成spring bean, 另外建LoginServiceBean.class的factory class, 在factory class裡依SourceService回傳需要的LoginServiceBean, 並集中管理
//@Component -->移除class裡相關annotation設定, 移到factory class處理
public class LoginServiceBean implements LoginService {
   
private SourceService sourceService;

public LoginServiceBean(SourceService sourceService) {
 this.sourceService = sourceService;
}
...

@Component
public class LoginSpringConfigFactory {

	private SourceService fileSourceService;
	private SourceService apiSourceService;
	private LoginConfig loginConfig;

	@Autowired
    public LoginSpringConfig(
     	@Qualifier("apiSourceService") SourceService apiSourceService, 
     	@Qualifier("fileSourceService") SourceService fileSourceService,
     	LoginConfig loginConfig) {
 		super();
 		this.apnoUpddtaService = apnoUpddtaService;
 		this.apnoSbrokerService = apnoSbrokerService;
 		this.loginConfig = loginConfig;
	}

   public LoginService loginService(String source) {
    
    if (source.equals("file"))
     return new LoginServiceBean(fileSourceService);    
    else if (source.equals("api"))
     return new LoginServiceBean(apiSourceService);
    
    return null;
   }
   
}

types of Dependency Injections example

Field Injection(not recommanded anymore)

@Component
public class MyClass {
    @Autowired 
    private CarsController controller;

	//...
}

Setter Injection

@Component
public class MyClass {
    private CarsController controller;
    private CarsService service;

    @Autowired
    public void setController(CarsController controller) {
        this.controller = controller;
    }

    @Autowired
    public void setService(CarsService service) {
        this.service = service;
    }
	//...
}

...

<bean id="myClass" class="x.y.MyClass">
	<property name="controller" ref="anotherController"/>
	<property name="service" ref="anotherService"/>
</bean>

<bean id="anotherController" class="x.y.AnotherController"/>
<bean id="anotherService" class="x.y.AnotherService"/>

Constructor Injection.

@Component
public class MyClass {
    private CarsController controller;
    private CarsService service;
    private CarsDao dao;
    private ApplicationProperties properties;

    @Autowired
    public MyClass(CarsController controller, 
    		CarsService service, 
    		CarsDao dao, 
    		ApplicationProperties properties) {
        this.controller = controller;
        this.service = service;
        this.dao = dao;
        this.properties = properties;
    }
    //...
}
...
<beans>
  <bean id="MyClass" class="x.y.Foo">
      <constructor-arg ref="carsController"/>
      <constructor-arg ref="carsService"/>
      <constructor-arg ref="carsDao"/>
      <constructor-arg ref="applicationProperties"/>      
  </bean>

  <bean id="carsController" class="x.y.CarsController"/>
  <bean id="carsService" class="x.y.CarsService"/>

  <bean id="carsDao" class="x.y.CarsDao"/>
  <bean id="applicationProperties" class="x.y.ApplicationProperties"/>
</beans>
Circular dependencies

If you use predominantly constructor injection, it is possible to create an unresolvable circular dependency scenario.

For example: Class A requires an instance of class B through constructor injection, and class B requires an instance of class A through constructor injection. If you configure beans for classes A and B to be injected into each other, the Spring IoC container detects this circular reference at runtime, and throws a BeanCurrentlyInCreationException.

Using the container - ApplicationContext

ClassPathXmlApplicationContext:
	spring config file.(applicationContext.xml)
FileSystemXmlApplicationContext:
	spring config file.(applicationContext.xml)
AnnotationConfigApplicationContext:
	spring-javaconfig, without xml

Instantiating beans

Instantiation with a constructor.

<bean id="exampleBean" class="examples.ExampleBean"/>

Instantiation with a static factory method.

<bean id="clientService"
     class="examples.ClientService"
     factory-method="createInstance"/>
... 
public class ClientService {
  private static ClientService clientService = new ClientService();
  private ClientService() {}
  	public static ClientService createInstance() {
	return clientService;
  }
}

Instantiation using an instance factory method.

<bean id="serviceLocator" class="examples.DefaultServiceLocator">
  <!-- inject any dependencies required by this locator bean -->
</bean>
<bean id="clientService"
   factory-bean="serviceLocator"
   factory-method="createClientServiceInstance"/>
<bean id="accountService"
   factory-bean="serviceLocator"
   factory-method="createAccountServiceInstance"/>
...
public class DefaultServiceLocator {
  private static ClientService clientService = new ClientServiceImpl();
  private static AccountService accountService = new AccountServiceImpl();
  private DefaultServiceLocator() {}
  public ClientService createClientServiceInstance() {
	 return clientService;
  }
  public AccountService createAccountServiceInstance() {
	 return accountService;
  }
} 

Bean Lifecycle.

//bean lifecycle.

container started 
-> bean initantiated
-> dependencies injected
-> internal spring preocessing
-> custom initialization code
-> (bean is ready to use)
-> custom destruction code
-> stopped.

//add custom code during bean initialization and destruction. like.

1.calling custom business logic methods.
2.setup handles to resource: db, sockets, file, etc.

<bean id="exampleInitBean" 
	class="examples.ExampleBean" 
	init-method="methodName"
	destroy-method="methodName"
 />