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,这个策略模式,类似插件式的自动扩展方式,优雅,完美解决了强迫症。
方案实现:
- 定义策略接口
public interface DictStrategy {
Dict processDict(String key);
}
- 实现策略
@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系统字典数据处理
}
}
- 策略注册,spring托管
@Component
public class SpringContextHolder extends ApplicationObjectSupport {
public DictStrategy getBean(String beanName){
return Objects.requireNonNull(super.getApplicationContext()).getBean(beanName , DictStrategy .class);
}
}
- 策略使用
定义标准对外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:策略类名称前缀
appId | prefix |
---|---|
codeA | sysA |
codeB | sysB |
- 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,插件式扩展。撒花鞠躬,欢迎大佬指正😎