自定义注解动态解耦调用实现类

       当一个接口有多个实现的业务场景,我们为了代码更清晰,为了满足开闭原则,巧妙的结合注解和反射实现动态调用实现类。

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值,这块可以使用枚举对应,防止出错。

这样就是实现了动态的调用不同的接口实现,增加新的实现也不会影响以前的代码,做到了松耦合。

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值