在项目中使用策略者模式、工厂模式等面向接口设计时,有时会遇到要获取实现某接口所有实现类的bean实例。
实现方式
有三种可实现的方法,分别是:
- 将所有的bean实例注入在List中
@Autowired
private List<T> serverList;
- 将所有的bean实例以beanName:bean的形式注入在Map中
@Autowired
private Map<String, T> serverMap
- 从ApplicationContext中获取
@Autowired
ApplicationContext applicationContext;
// beanName:bean
applicationContext.getBeansOfType(T.class)
测试代码:
接口:
public interface InterfaceDemo {
}
注解:
import java.lang.annotation.*;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface InterfaceDemoAn {
String[] values();
}
接口的实现类们:
interfaceA
import org.springframework.stereotype.Service;
@Service(value = "interfaceDemoA")
@InterfaceDemoAn(values = {"A", "a"})
public class InterfaceDemoA implements InterfaceDemo{
}
intetfaceB
import org.springframework.stereotype.Service;
@Service(value = "interfaceDemoB")
@InterfaceDemoAn(values = {"B", "b"})
public class InterfaceDemoB implements InterfaceDemo{
}
interfaceC
import org.springframework.stereotype.Service;
@Service(value = "interfaceDemoC")
@InterfaceDemoAn(values = {"C", "c"})
public class InterfaceDemoC implements InterfaceDemo{
}
通用抽象工厂:
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.lang.reflect.ParameterizedType;
import java.util.*;
/**
* 实现InitializingBean,会Spring启动后,初始化Bean时,会自动调用afterPropertiesSet方法
* 工具类不适合用@Resource注解注入,Spring在处理@Resource注解时,如果存在通用泛型,擦除不如@Autowired灵活
*/
@Slf4j
public abstract class CommonFactory<T> implements InitializingBean {
private static final Logger LOGGER = LoggerFactory.getLogger(CommonFactory.class);
@Autowired
private List<T> interfaceDemoList;
@Autowired
ApplicationContext applicationContext;
@Autowired
private Map<String, T> interfaceDemoMap;
/**
* 使用interfaceDemoList组装的只读MAP
*/
private Map<String, T> interfaceDemoMapFromList;
/**
* 使用applicationContext组装的只读MAP
*/
private Map<String, T> interfaceDemoMapFromAC;
public void getInstance(String beanName){
T t1 = interfaceDemoMap.get(beanName);
LOGGER.info("interfaceDemoMap : annoValues:{}",
Arrays.asList(t1.getClass().getAnnotation(InterfaceDemoAn.class).values()));
T t2 = interfaceDemoMapFromList.get(beanName);
LOGGER.info("interfaceDemoMapFromList : annoValues:{}",
Arrays.asList(t2.getClass().getAnnotation(InterfaceDemoAn.class).values()));
T t3 = interfaceDemoMapFromAC.get(beanName);
LOGGER.info("interfaceDemoMapFromAC : annoValues:{}",
Arrays.asList(t3.getClass().getAnnotation(InterfaceDemoAn.class).values()));
}
@Override
public void afterPropertiesSet() throws Exception {
// 使用applicationContext获取所有的bean实例
// 获取父类的ParameterizedType类型
ParameterizedType parameterizedType = (ParameterizedType) this.getClass().getGenericSuperclass();
// 获取父类上所有的泛型类型
Class tClass = (Class) parameterizedType.getActualTypeArguments()[0];
this.interfaceDemoMapFromAC = Collections.unmodifiableMap(applicationContext.getBeansOfType(tClass));
Map<String, T> tempMap = new HashMap<>();
// 使用List获取所有的bean实例
interfaceDemoList.forEach(t -> {
String beanName = t.getClass().getAnnotation(Service.class).value();
tempMap.put(beanName, t);
});
this.interfaceDemoMapFromList = Collections.unmodifiableMap(tempMap);
}
}
工厂实现类:
import org.springframework.stereotype.Component;
@Component
public class InterfaceFactory extends CommonFactory<InterfaceDemo> {
}
controller:
import com.example.springbootdemo.factory.InterfaceFactory;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/test")
@Slf4j
public class TestController {
private static final Logger LOGGER = LoggerFactory.getLogger(TestController.class);
@Autowired
private InterfaceFactory interfaceFactory;
@RequestMapping(value = "/interface", method = RequestMethod.POST)
public void getInterfaceEntity() {
interfaceFactory.getInstance("interfaceDemoB");
}
}
Console输出
INFO 5105 --- [nio-8080-exec-2] c.e.s.factory.CommonFactory : interfaceDemoMap : annoValues:[B, b]
INFO 5105 --- [nio-8080-exec-2] c.e.s.factory.CommonFactory : interfaceDemoMapFromList : annoValues:[B, b]
INFO 5105 --- [nio-8080-exec-2] c.e.s.factory.CommonFactory : interfaceDemoMapFromAC : annoValues:[B, b]