需依來源/條件產出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"
/>