需求
车辙在平时的工作,经常碰到需要对前端上传数据进行处理的问题,例如元转分,去空,脱敏等。 在GitHub上转了半天,都是自己定义好的,完全没有扩展点。
在车辙的一顿操作下,参考了Mybatis MapperScan的源码,车辙在此基础上写了个小框架,代码侵入性低的同时用户可以自定义注解和实现类满足需求。
如何使用
1. 项目打包,并依赖(地址在最下方)
<dependency>
<groupId>cn.dface</groupId>
<artifactId>anno</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
2.启动类添加扫描注解实现类包路径
SpringBoot
@HandleBeanScanAnno(basePackages = "cn.dface.annocustom.web.back.anno.implHandler")
SpringMVC
<context:component-scan base-package="com.xxx.**.cotroller"/> 我也没试过😄
3.定义注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TrimBlankAnno {
String value() default "" ;
String methodName() default "" ;
int level() default 1;
}
4.写实现类在(2的包路径下)
@Slf4j
public class TrimBlankAnnoServiceImpl extends AbstractHandleAnno implements HandleBeanParentService {
@Override
public void setAnnotation() {
this.annotation = TrimBlankAnno.class;
}
@Override
public Object doParam(Object param) {
String str = ((String)param).replaceAll("1","333");
return str;
}
}
5.在Controller上添加包自带注解
@PostMapping("/testTrimAnno")
@HandleBeanAnno
public Object testTrimAnno( @RequestBody TestBean setBankInfoBean) {
System.out.println(JSON.toJSONString(setBankInfoBean));
return ResultVO.newSuccess(setBankInfoBean);
}
6.在入参Bean和出参Bean上添加注解
@TrimBlankAnno(level = 2)
@AddSpaceAnno
private String bankCardNo;
@YuanToFenAnno
private Integer amount;
7. 其他功能
- 参数业务检查,不符合业务要求直接抛出异常
- 不用再参数上写多个注解,一个注解满足多个条件,如先判断非空,在判断格式
8. 效果
实现类
@Slf4j
public class YuanToFenServiceImpl extends AbstractHandleAnno implements HandleBeanParentService {
@Override
public void setAnnotation() {
this.annotation = YuanToFenAnno.class;
}
@Override
public Object doParam(Object param) {
Integer originParam = (Integer) param;
return originParam * 100;
}
}
入参
{
"bankCardNo": " 123 4 5 6 ", // 去空格处理 然后加上空格
"amount": 10 // 返回值元转分
}
出参
{
"status": 0,
"errorCode": null,
"errorMsg": null,
"object": {
"bankCardNo": "123456 ",
"amount": 1000
}
}
源码分析
定义注册扫描包的注解
/**
* 处理bean的注解
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import(HandleBeanScanRegister.class)
public @interface HandleBeanScanAnno {
String basePackages() default "com";
}
扫描包,注册bean, 保存类(参考Mybatis和Dubbo)
public class HandleBeanScanRegister implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 通过注解map获取注解
AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(HandleBeanScanAnno.class.getName()));
// 设置register用于set beanDefinition
HandleBeanParentScanner scanner = new HandleBeanParentScanner(registry,false);
//设置资源加载器
if (resourceLoader != null) {
scanner.setResourceLoader(resourceLoader);
}
//获取注解中的包
List<String> basePackages = new ArrayList<String>();
for (String pkg : annoAttrs.getStringArray("basePackages")) {
if (StringUtils.hasText(pkg)) {
basePackages.add(pkg);
}
}
//接受所有,不设置过滤Filter
scanner.acceptAll();
//获取创建bean后的BeanDefinitionHolder SET,并设置到map,用于后期通过名称获取bean
Set<BeanDefinitionHolder> beanDefinitionHolderSet = scanner.doScan(StringUtils.toStringArray(basePackages));
beanDefinitionHolderSet.forEach(beanDefinitionHolder -> {
HandleBeanAnnoProperty.registerClassNames.add(beanDefinitionHolder.getBeanName());
});
}
}
刷新上下文时获取ServiceBean并保存
@Component
public class HandleBeanAnnoProperty implements ApplicationListener<ContextRefreshedEvent> {
// 已经注册的所有的类名
public static List<String> registerClassNames = new ArrayList<>();
public static List<HandleBeanParentService> beanServiceList = new ArrayList<>();
public static List<HandleParamParentService> paramServiceList = new ArrayList<>();
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
//所有的bean 放入list中
registerClassNames.forEach(beanName ->{
Object bean = SpringContext.getBean(beanName);
if(bean instanceof HandleBeanParentService){
beanServiceList.add((HandleBeanParentService) bean);
}
// 如果是param的参数,放入resolvers中
if(bean instanceof HandleParamParentService){
paramServiceList.add((HandleParamParentService) bean);
}
});
}
}
AOP 处理入参和出参
@Aspect
@Slf4j
public class HandleBeanAnnoHandler {
private String RESULT_OBJECT_NAME = "object";
/**
* point 扩展点
* around 通知
*/
@Around(value = "@annotation(around)")
public Object initBean(ProceedingJoinPoint point, HandleBeanAnno around) throws Throwable {
// 获取方法入参的参数
Object[] submitBeans = point.getArgs();
// 每个service执行参数 如某个bean,返回整个bean,并执行数组赋值,
// 用于防止字符串传递引用
for(int i = 0; i < submitBeans.length; i++){
Object submitBean = submitBeans[i];
submitBeans[i] = handleParam(submitBean);
}
// 执行被代理类方法
Object object = point.proceed(point.getArgs());
//获取返回的ResultVO 的 Field(即object) 的参数
Field field = object.getClass().getDeclaredField(RESULT_OBJECT_NAME);
field.setAccessible(true);
Object param = field.get(object);
// 处理返回的参数
handleParam(param);
log.info("Handle End");
return object;
}
/**
* 很多情况下是bean,少数情况下是某个RequestParam
* @param param
* @return
*/
private Object handleParam(Object param) throws Exception{
List<HandleBeanParentService> beanServiceList = HandleBeanAnnoProperty.beanServiceList;
List<HandleParamParentService> paramServiceList = HandleBeanAnnoProperty.paramServiceList;
Map<String, HandleService> handleBeanServiceMap = HandleBeanAnnoProperty.annoConnectMap;
if(null != param){
if(checkNotBean(param)){
for(HandleParamParentService service : paramServiceList){
param = service.doParam(param);
}
}else{
Field[] fields = param.getClass().getDeclaredFields();
for(Field field : fields){
// 获取field上的多个注解
Annotation[] annotations = field.getDeclaredAnnotations();
// 判断是否注解,没有注解直接返回
if(null == annotations || annotations.length == 0){
continue;
}
field.setAccessible(true);
//注解排序
AnnoCommonUtil.sort(annotations);
for(Annotation annotation : annotations){
// bean 参数上的注解名称
String annotationName = annotation.annotationType().getName();
// 判断注解是否有实现类,存在去执行
HandleService handleService = handleBeanServiceMap.get(annotationName);
Object beExecuteObject = null;
try {
beExecuteObject = field.get(param);
} catch (IllegalAccessException e) {
log.error("不能获取改field的值, field{}",field.getName());
continue;
}
// 启动时定义完成的service bean 在初始化时就存入map中
if(null != handleService){
Object resultParam = handleService.doParam(beExecuteObject);
// 判断返回值是否相同,不相同再赋值,减少反射消耗
if(!beExecuteObject.equals(resultParam)) {
field.set(param, resultParam);
}
}else{
// 没有实现类 就去所有的实现类中试一次(防止通过代码手动生成bean的情况)
for(HandleBeanParentService service : beanServiceList){
AbstractHandleAnno abstractHandleAnno = (AbstractHandleAnno)service;
// 每个service的注解名称
String serviceAnnotationName = abstractHandleAnno.getAnnotation().getName();
if(annotationName.equals(serviceAnnotationName)){
handleBeanServiceMap.put(annotationName, service);
Object resultParam = service.doParam(beExecuteObject);
if(!beExecuteObject.equals(resultParam)) {
field.set(param, resultParam);
}
}
}
}
}
}
}
}
return param;
}
}
结语
项目目前还不完善,欢迎下方留言。目前只支持RequestBody的bean支持,而RequestParam尚未支持,这些问题应该会在之后的版本中支持。 完整代码在GitHub上