当一个接口有多个实现的业务场景,我们为了代码更清晰,为了满足开闭原则,巧妙的结合注解和反射实现动态调用实现类。
1.自定义注解,用于在多实现类上增加这个注解(这块最好和定义的枚举值相对应)。
//功能 : 一个接口多个实现类,只需要传入value 即可获取对应的实现类
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface YangImplement {
String value() default "";
}
2.获取bean工具类
@Component
public class SpringContextHolder implements ApplicationContextAware, DisposableBean {
private static ApplicationContext applicationContext = null;
private static Logger logger =LogManager.getLogger(SpringContextHolder.class);
private static final Map<String, Object> YANG_IMPLEMENT_CACHE = new ConcurrentHashMap<String, Object>();
/**
* 取得存储在静态变量中的ApplicationContext.
*/
public static ApplicationContext getApplicationContext() {
assertContextInjected();
return applicationContext;
}
/**
* 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
*/
@SuppressWarnings("unchecked")
public static <T> T getBean(String name) {
assertContextInjected();
return (T) applicationContext.getBean(name);
}
/**
* 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
*/
public static <T> T getBean(Class<T> requiredType){
assertContextInjected();
return applicationContext.getBean(requiredType);
}
/**
* 根据传入的value,返回对应类的实例
*/
@SuppressWarnings("unchecked")
public static <T> T getBeanByAnnotation(Class<T> clazz, String type){
if (clazz == null || type == null) {
return null;
}
assertContextInjected();
// 优先从缓存中取出对象
final String yangKey = clazz.getName() + type;
final Object cache = YANG_IMPLEMENT_CACHE.get(yangKey );
if (cache != null) {
return (T) cache;
}
// 未命中缓存,去ApplicationContext内部寻找
Map<String, T> beans = applicationContext.getBeansOfType(clazz);
for (Map.Entry<String, T> entry : beans.entrySet()) {
// 通过工具类获取原生类
@SuppressWarnings("rawtypes")
Class nativeClazz = AopTargetUtils.getTarget(entry.getValue()).getClass();
YangImplement yangImp = AnnotationUtils.findAnnotation(nativeClazz, YangImplement.class);
if (yangImp != null && yangImp .value().equals(type)){
YANG_IMPLEMENT_CACHE.put(yangKey, entry.getValue());
return entry.getValue();
}
}
logger.warn("获取不到BaokuImplement注解标记的实例,clazz:{},type:{}",clazz,type);
return null;
}
/**
* 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true
*
* @param name
* @return boolean
*/
public static boolean containsBean(String name) {
return applicationContext.containsBean(name);
}
/**
* 判断以给定名字注册的bean定义是一个singleton还是一个prototype。
* 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException)
*
* @param name
* @return boolean
* @throws NoSuchBeanDefinitionException
*/
public static boolean isSingleton(String name)
throws NoSuchBeanDefinitionException {
return applicationContext.isSingleton(name);
}
/**
* 如果给定的bean名字在bean定义中有别名,则返回这些别名
*
* @param name
* @return
* @throws NoSuchBeanDefinitionException
*/
public static String[] getAliases(String name)
throws NoSuchBeanDefinitionException {
return applicationContext.getAliases(name);
}
/**
* 清除SpringContextHolder中的ApplicationContext为Null.
*/
public static void clearHolder() {
logger.debug("清除SpringContextHolder中的ApplicationContext:" + applicationContext);
applicationContext = null;
}
/**
* 实现ApplicationContextAware接口, 注入Context到静态变量中.
*/
public void setApplicationContext(ApplicationContext applicationContext) {
logger.debug("注入ApplicationContext到SpringContextHolder:{}"+applicationContext);
if (SpringContextHolder.applicationContext != null) {
logger.warn("SpringContextHolder中的ApplicationContext被覆盖, 原有ApplicationContext为:"
+ SpringContextHolder.applicationContext);
}
SpringContextHolder.applicationContext = applicationContext; //NOSONAR
}
/**
* 实现DisposableBean接口, 在Context关闭时清理静态变量.
*/
public void destroy() throws Exception {
SpringContextHolder.clearHolder();
}
/**
* 检查ApplicationContext不为空.
*/
private static void assertContextInjected() {
Validate.validState(applicationContext != null,
"applicaitonContext属性未注入, 请在applicationContext.xml中定义SpringContextHolder.");
}
}
3.上面用到的Aop工具类(获取原生对象)
public class AopTargetUtils {
private static Logger logger = LogManager.getLogger(AopTargetUtils.class);
/**
* 获取目标对象
* @param proxy 代理对象
* @return
* @throws Exception
*/
public static Object getTarget(Object proxy) {
// 不是代理对象
if (!AopUtils.isAopProxy(proxy)) {
return proxy;
}
if (AopUtils.isJdkDynamicProxy(proxy)) {
// jdk代理
return getJdkDynamicProxyTargetObject(proxy);
} else { // cglib
return getCglibProxyTargetObject(proxy);
}
}
/**
* 获取Cglib代理的目标对象
* @param proxy 代理对象
* @return
*/
private static Object getCglibProxyTargetObject(Object proxy) {
Object target = null;
try {
// 根据关键字获取类中MethodInterceptor的属性
Field h = proxy.getClass().getDeclaredField("CGLIB$CALLBACK_0");
// 取消 Java 语言访问检查
h.setAccessible(true);
Object dynamicAdvisedInterceptor = h.get(proxy);
Field advised = dynamicAdvisedInterceptor.getClass().getDeclaredField("advised");
advised.setAccessible(true);
// 获取目标对象
target = ((AdvisedSupport) advised.get(dynamicAdvisedInterceptor)).getTargetSource().getTarget();
} catch (Exception e) {
logger.error("getCglibProxyTargetObject", e);
}
return target;
}
/**
* 获取JDK代理的目标对象
* @param proxy 代理对象
* @return
*/
private static Object getJdkDynamicProxyTargetObject(Object proxy) {
Object target = null;
try {
// 根据关键字获取类中的属性
Field h = proxy.getClass().getSuperclass().getDeclaredField("h");
// 取消 Java 语言访问检查
h.setAccessible(true);
AopProxy aopProxy = (AopProxy) h.get(proxy);
Field advised = aopProxy.getClass().getDeclaredField("advised");
advised.setAccessible(true);
// 根据代理对象获取原生类
target = ((AdvisedSupport) advised.get(aopProxy)).getTargetSource().getTarget();
} catch (Exception e) {
logger.error("getJdkDynamicProxyTargetObject", e);
}
return target;
}
使用:通过SpringContextHolder.getBeanByAnnotation(参数1,参数2)动态获取实现类。
参数1:调用的接口的class,例如接口名为MyInterface,则传MyInterface.class
参数2:不同实现类自定义注解上的value值,这块可以使用枚举对应,防止出错。
这样就是实现了动态的调用不同的接口实现,增加新的实现也不会影响以前的代码,做到了松耦合。