ApplicationObjectSupport深入解析+实战

ApplicationObjectSupport深入解析+实战

ApplicationObjectSupport是什么

ApplicationObjectSupport 是 Spring Framework 中的一个工具类,主要用于为自定义的 Spring Bean 提供便捷的 应用上下文(ApplicationContext)访问能力。它通常用于需要与 Spring 容器交互的场景。用于策略设计模式,定义一个包含公共接口的基类,一系列解析并列源的处理实现类。使用时调用基类基类接口方法,自由切换对应实现类。

核心作用

  • 访问 ApplicationContext
    内部实现了ApplicationContextAware 上下文接口,所以继承该类的 Bean 可以直接通过 getApplicationContext() 方法获取当前 Spring 容器的上下文,无需手动实现 ApplicationContextAware 接口。
private ApplicationContext applicationContext;
public final ApplicationContext getApplicationContext() throws IllegalStateException {
		if (this.applicationContext == null && isContextRequired()) {
			throw new IllegalStateException(
					"ApplicationObjectSupport instance [" + this + "] does not run in an ApplicationContext");
		}
		return this.applicationContext;
	}
  • 消息国际化支持
    提供 getMessageSourceAccessor() 方法,简化从 MessageSource 获取国际化消息的操作。
private MessageSourceAccessor messageSourceAccessor;
protected final MessageSourceAccessor getMessageSourceAccessor() throws IllegalStateException {
		if (this.messageSourceAccessor == null && isContextRequired()) {
			throw new IllegalStateException(
					"ApplicationObjectSupport instance [" + this + "] does not run in an ApplicationContext");
		}
		return this.messageSourceAccessor;
	}

适用场景:

  • 需要访问 Spring 容器管理的其他 Bean。
  • 需要实现国际化消息解析。
  • 需要在 Bean 内部动态获取资源或配置。

使用实例

public class MyCustomBean extends ApplicationObjectSupport {

    public void doSomething() {
        // 1. 获取 ApplicationContext
        ApplicationContext ctx = getApplicationContext();

        // 2. 获取其他 Bean
        SomeService service = Objects.requireNonNull(ctx).getBean("someService", SomeService.class);

        // 3. 获取国际化消息
        String message = getMessageSourceAccessor().getMessage("msg.key");

        // 4. 检查上下文状态
        if (isContextActive()) {
            // 执行逻辑
        }
    }
}

ApplicationObjectSupport实战之数据源自动切换

案例分析

需求背景:当前有基础数据服务A和B两个系统,都有订单状态字典数据。为统一管理,需要数据整合,由api统一接口服务接管,对外提供标准接口
方案分析
这是一个接口合并问题,一般涉及多数据源的问题都会必传一个目标数据源标识key,比如appId。传统方式基本是api服务接口根据appId标识,转接对应的feign接口。如果再来一个C系统,需要在原方法里扩展分支,if else again and again。如果接口返回的数据接口与标准不一致,又要加一坨字段映射解析【吐槽一下,最恶心的就是要处理数据映射】。总之就是很不优雅有木有,强迫症不能忍啊。
不经意间发现了ApplicationObjectSupport,这个策略模式,类似插件式的自动扩展方式,优雅,完美解决了强迫症。
方案实现

  1. 定义策略接口
public interface DictStrategy {
    Dict processDict(String key);
}
  1. 实现策略
@Component
public class sysADictHandler implements DictStrategy{
    @Override
    public Dict processDict(String key) {
        // A系统字典数据处理
        
    }
}
@Component
public class sysBDictHandler implements DictStrategy{
    @Override
    public Dict processDict(String key) {
        // B系统字典数据处理
        
    }
}
  1. 策略注册,spring托管
@Component
public class SpringContextHolder extends ApplicationObjectSupport {

    public DictStrategy getBean(String beanName){
        return Objects.requireNonNull(super.getApplicationContext()).getBean(beanName , DictStrategy .class);
    }
}
  1. 策略使用
    定义标准对外api接口
  • service接口
public interface DictService {
	/**
     * 查询字典列表
     * @param key 字典key
     * @param source 字典数据源
     * @return Dict 
     */
    Dict getDict(String key,String source);
}
  • service实现
    根据数据源标识自动切换策略实现
public interface DictServiceImpl implements DictService {
	@Resource
    private SpringContextHolder holder;
	/**
     * 查询字典列表
     * @param key 字典key
     * @param source 字典数据源
     * @return Dict 
     */
    public Dict getDict(String key,String appId){
			DictStrategy bean = getBeanImpl(String appId);
			if(bean==null){
			throw new RuntimeException("数据源策略未配置");
			}
			reurn bean.processDict(key);
     }
     /**
     * 获取映射类
     * @param sysCode
     * @return
     */
    private DictStrategy getBeanImpl(String appId){
        if(StringUtils.isEmpty(appId)){
            return null;
        }
        String sourceType = sourceService.getSourceType(appId);
        if(StringUtils.isEmpty(sourceType)){
            throw new RuntimeException("数据源未配置");
        }
        return holder.getBean(sourceType+"DictHandler");
    }
}

sourceService.getSourceType方法是从配置表查询数据源标识。简单定义一个配置表即可,方便以后扩展。参考字段,appId:数据源参数标识,prefix:策略类名称前缀

appIdprefix
codeAsysA
codeBsysB
  • controller

@RestController
@RequestMapping("/api")
public class DictController {
	@Resource
    private DictService dictService;

    @GetMapping(value="/dict")
    public R<Object> getDict(@RequestParam("key") String key,@RequestParam("appId") String appId) {
        try {
            return  R.ok(dictService.getDict(key,appId));
        } catch (Exception e) {
            e.printStackTrace();
            return  R.fail("查询失败");
        }
    }
}

到此字典多数据源查询基本实现。数据源对接部分没有展开说明。无非是对象字段映射处理,实体类转换。这款工具类一大把,hutool,ObjectMapper等。hutool很好用,方法齐全,太完美,很周到,但是实际常用的工具类主要就是StrUtl,DateUtil,偶尔用下ArrayUtil。所以本人更喜欢ObjectMapper。s送上我自己Cover

public class CovertUtil {

    private static ObjectMapper objectMapper;

    static {
        initObjectMapper();
    }

    /**
     * 初始化ObjectMapper
     */
    private static void initObjectMapper(){
        ObjectMapper mapper = new ObjectMapper();
        // 反序列化忽略不存在的属性
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,false);
        objectMapper = mapper;
    }

    /**
     * linkedHashmap 转实体类
     * @param obj
     * @param tClass
     * @param <T>
     * @return
     */
    public static <T> T toObject(Object obj,Class<T> tClass){
        return objectMapper.convertValue(obj, tClass);
    }
    /**
     * linkedHashmap 转实体类
     * @param obj
     * @param tClass
     * @param <T>
     * @param curField 原字段
     * @param tar 目标字段
     * @return
     */
    public static <T> T toObject(Object obj,Class<T> tClass,String curField,String tar){
        Object val = BeanUtil.getFieldValue(obj, curField);
        T obj2 = objectMapper.convertValue(obj, tClass);
        BeanUtil.setFieldValue(obj2,tar,val);
        return obj2;
    }
    /**
     * linkedHashmap 转实体类
     * @param obj
     * @param tClass
     * @param <T>
     * @param tars 指定字段集合 原字段:目标字段
     * @return
     */
    public static <T> T toObject(Object obj,Class<T> tClass,Map<String,String> tars){
        T obj2 = objectMapper.convertValue(obj, tClass);
        for (Map.Entry<String, String> tar : tars.entrySet()) {
            Object val = BeanUtil.getFieldValue(obj, tar.getKey());
            BeanUtil.setFieldValue(obj2,tar.getValue(),val);
        }
        return obj2;
    }
    /**
     * linkedHashmap 转实体类
     * @param obj
     * @param tClass
     * @param <T>
     * @return
     */
    public static <T> List<T> toObjectList(Object obj, Class<T> tClass){
        List<T> retList = new ArrayList<>();
        if(obj instanceof Collection){
            Collection<?> list = (Collection<?>)obj;
            for (Object o : list) {
                retList.add(toObject(o,tClass));
            }
        }
        return retList;
    }
    /**
     * linkedHashmap 转实体类
     * @param obj
     * @param tClass
     * @param <T>
     * @param curField 原字段
     * @param tar 目标字段
     * @return
     */
    public static <T> List<T> toObjectList(Object obj, Class<T> tClass,String curField,String tar){
        List<T> retList = new ArrayList<>();
        if(obj instanceof Collection){
            Collection<?> list = (Collection<?>)obj;
            for (Object o : list) {
                retList.add(toObject(o,tClass,curField,tar));
            }
        }
        return retList;
    }
    /**
     * linkedHashmap 转实体类
     * @param obj
     * @param tClass
     * @param <T>
     * @param tars 指定字段集合 原字段:目标字段
     * @return
     */
    public static <T> List<T> toObjectList(Object obj, Class<T> tClass, Map<String,String> tars){
        List<T> retList = new ArrayList<>();
        if(obj instanceof Collection){
            Collection<?> list = (Collection<?>)obj;
            for (Object o : list) {
                retList.add(toObject(o,tClass,tars));
            }
        }
        return retList;
    }
}

小结

基于ApplicationObjectSupport策略实现字典数据源自由切换查询。优点是实现了业务闭环,后续新的数据源需要对接,只需要定义xxxDictStrategy,插件式扩展。撒花鞠躬,欢迎大佬指正😎

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

烟雨の落浮生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值