前段时间写了两版mybatis拦截器实现公共字段自动注入,感觉第二版还是限制的过多了。用户基本无法扩展字段,也无法自定义方法,作为一个公共的基础框架工具类是不够的,所以又根据以上做了一些修改。
@Target({
ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface AutoStuff {
/**
* 新增时是否需要自动注入
* @return
*/
boolean addFlag() default false;
/**
* 新增时的注入方法
* @return
*/
String addMethod() default "";
/**
* 修改时是否需要自动注入
* @return
*/
boolean updateFlag() default false;
/**
* 修改时的注入方法
* @return
*/
String updateMethod() default "";
}
说明:
addFlag:如果该字段在新增时需要自动注入,则给true
addMethod:当addFlag为true时,会从用户对IOperInfo
接口的实现类中,以该值为方法名查找方法,获取数据注入
updateFlag:如果该字段在修改时需要自动注入,则给true
updateMethod:当updateFlag为true时,会从用户对IOperInfo
接口的实现类中,以该值为方法名查找方法,获取数据注入
public interface IOperInfoService {
}
作用:@AutoStuff
自动注入的内容都是根据接口获取的,用户必须实现该接口,并在自己的实现类中结合@AutoStuff
注解中的参数完善方法
该拦截器引入后默认启用,提供开关也使用户在不需要使用注入时,不需要更改代码,只需要修改配置文件即可。
/**
* 通过@ConfigurationProperties根据prefix从配置文件中读取配置块
* ignoreInvalidFields: 为了避免消费者填的字段不符合我们规定的字段,无法解析而导致启动报错
* 比如enabled消费者填的是abc,不是boolean类型。
* 设置了ignoreInvalidFields,就会忽略配置文件中填的错误内容,而使用我们规定的默认值,如果没有默认值则为null
*/
@ConfigurationProperties(
prefix = "my.interceptor", ignoreInvalidFields = true
)
public class MybatisInterceptorProperties {
// 默认为true
private Boolean enabled = Boolean.TRUE;
public Boolean getEnabled() {
return enabled;
}
public void setEnabled(Boolean enabled) {
this.enabled = enabled;
}
}
@Configuration
@EnableConfigurationProperties({
MybatisInterceptorProperties.class})// 激活配置模块
/**
* 控制Configuration是否生效
* name: 数组,property完整名称或部分名称
* matchIfMissing:缺少该property时是否可以加载。如果为true,没有该property也会正常加载;反之报错
* havingValue: 比较获取到的属性值与havingValue给定的值是否相同,相同才加载配置
*/
@ConditionalOnProperty(
name = {
"my.interceptor.enabled"},
matchIfMissing = true,
havingValue = "true"
)
public class InterceptorConfig {
}
当不需要使用拦截器时,只需在配置文件中设为false
即可关闭
my:
interceptor:
enabled: false
public class SpringBeanUtil implements ApplicationContextAware {
@Autowired
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext ctx) throws BeansException {
this.applicationContext = ctx;
}
public ApplicationContext getApplicationContext() {
return this.applicationContext;
}
}
public class ReflectUtil {
private static Logger logger = LoggerFactory.getLogger(ReflectUtil.class);
private ReflectUtil(){
}
/**
* 获取该类的所有字段
* @param object
* @return
*/
public static Field[] getAllFields(Object object){
Class clazz = object.getClass();
List<Field> fieldList = new ArrayList<>();
while (clazz != null){
fieldList.addAll(new ArrayList<>(Arrays.asList(clazz.getDeclaredFields())));
clazz = clazz.getSuperclass();
}
Field[] fields = new Field[fieldList.size()];
fieldList.toArray(fields);
return fields;
}
/**
* 根据类名,方法名,反射调用类的方法
* @param className
* @param methodName
* @return
*/
public static Object getResult(String className,String methodName){
try{
Object obj = Class.forName(className).newInstance();
Class clazz = obj.getClass();
Method mothod = clazz.getDeclaredMethod(methodName, null);
return mothod.invoke(obj, null);
} catch (NoSuchMethodException e){
logger.error("errCode: {},errMsg: {}", ErrCons.REFLECT_METHOD_NOT_FOUND, "【"+className+ ErrCons.REFLECT_METHOD_NOT_FOUND_MSG+methodName);
throw new BusinessException(ErrCons.REFLECT_METHOD_NOT_FOUND, "【"+className+ ErrCons.REFLECT_METHOD_NOT_FOUND_MSG+methodName);
} catch (Exception e) {
logger.error("errCode: {},errMsg: {}", ErrCons.REFLECT_INVOKE_METHOD_ERR, "调用"+methodName+"异常");
throw new BusinessException(ErrCons.REFLECT_INVOKE_METHOD_ERR, "调用"+methodName+"异常");
}
}
}
@Intercepts({
@Signature(type = Executor.class, method = "update"
, args = {
MappedStatement.class, Object.class})})
public class StuffInterceptor implements Interceptor {
private static Logger logger = LoggerFactory.getLogger(StuffInterceptor.class);
@Autowired
private SpringBeanUtil springBeanUtil;
@Override
public Object intercept(Invocation invocation) throws Throwable {
if (invocation.getTarget() instanceof Executor && invocation.getArgs().length == 2) {
final Executor executor = (Executor) invocation.getTarget();
// 获取第一个参数
final MappedStatement ms = (MappedStatement) invocation.getArgs()[0];
final Object paramObj = invocation.getArgs()[1];
if (ms.getSqlCommandType() == SqlCommandType.INSERT) {
return this.executeInsert(executor, ms, paramObj);
} else if (ms.getSqlCommandType() == SqlCommandType.UPDATE) {
return this.executeUpdate(executor, ms, paramObj);
}
}
return invocation.proceed();
}
@Override
public Object plugin(final Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
//
}
/**
* 新增操作
*
* @param executor executor
* @param ms ms
* @param paramObj 参数
* @return 返回执行结果
*/
private Object executeInsert(final Executor executor, final MappedStatement ms, final Object paramObj) throws Exception {
// 获取IOperInfoService接口的实现类名
String implClassName = buildImplClassName();
// 获取实体类的所有字段
final Field[] fields = ReflectUtil.getAllFields(paramObj);
for(final Field field : fields){
field.setAccessible(true);
// 判断是否有@AutoStuff注解
if(field.isAnnotationPresent(AutoStuff.class)){
AutoStuff autoStuff = field.getAnnotation(AutoStuff.class);
// addFlag是否为true
if(autoStuff != null && autoStuff.addFlag()){
// addMethod为空则抛出异常
if(StringUtils.isEmpty(autoStuff.addMethod())){
logger.error(ErrCons.ERR_MSG , ErrCons.ADDMETHOD_IS_EMPTY, ErrCons.ADDMETHOD_IS_EMPTY_MSG);
throw new BusinessException(ErrCons.ADDMETHOD_IS_EMPTY, ErrCons.ADDMETHOD_IS_EMPTY_MSG);
}
// 根据addMethod构建方法名
String methodName = buildMethodName(autoStuff.addMethod());
// 设置值
field.set(paramObj, ReflectUtil.getResult(implClassName, methodName));
}
}
}
return executor.update(ms, paramObj);
}
/**
* 修改操作
*
* @param executor executor
* @param ms ms
* @param paramObj 参数
* @return 返回执行结果
*/
private Object executeUpdate(final Executor executor, final MappedStatement ms, final Object paramObj) throws Exception {
// 获取IOperInfoService接口的实现类名
String implClassName = buildImplClassName();
// 获取实体类的所有字段
final Field[] fields = ReflectUtil.getAllFields(paramObj);
for(final Field field : fields){
field.setAccessible(true);
// 判断是否有@AutoStuff注解
AutoStuff autoStuff = field.getAnnotation(AutoStuff.class);
// updateFlag是否为true
if(autoStuff != null && autoStuff.updateFlag()){
// updateMethod为空则抛出异常
if(StringUtils.isEmpty(autoStuff.updateMethod())){
logger.error(ErrCons.ERR_MSG , ErrCons.UPDATE_METHOD_IS_EMPTY, ErrCons.UPDATE_METHOD_IS_EMPTY_MSG);
throw new BusinessException(ErrCons.UPDATE_METHOD_IS_EMPTY, ErrCons.UPDATE_METHOD_IS_EMPTY_MSG);
}
// 根据updateMethod构建方法名
String methodName = buildMethodName(autoStuff.updateMethod());
// 设置值
field.set(paramObj, ReflectUtil.getResult(implClassName, methodName));
}
}
return executor.update(ms, paramObj);
}
/**
* 获取接口IOperInfoService的实现类
* @return
*/
private String buildImplClassName(){
String implClassName = "";
// 通过Spring容器获取
Map<String, IOperInfoService> result = springBeanUtil.getApplicationContext().getBeansOfType(IOperInfoService.class);
// 未找到实现类,抛出异常
if(result.isEmpty()){
logger.error(ErrCons.ERR_MSG , ErrCons.IMPL_CLASS_NOT_FOUND, ErrCons.IMPL_CLASS_NOT_FOUND_MSG);
throw new BusinessException(ErrCons.IMPL_CLASS_NOT_FOUND, ErrCons.IMPL_CLASS_NOT_FOUND_MSG);
}
// 实现类大于1,抛出异常
if(result.size() > 1){
logger.error(ErrCons.ERR_MSG , ErrCons.IMPL_CLASS_TO_MUCH, ErrCons.IMPL_CLASS_TO_MUCH_MSG);
throw new BusinessException(ErrCons.IMPL_CLASS_TO_MUCH, ErrCons.IMPL_CLASS_TO_MUCH_MSG);
}
// 返回第一个实现类的类名
for (Map.Entry<String, IOperInfoService> entry : result.entrySet()) {
String name = entry.getValue().getClass().getName();
if(StringUtils.isNotEmpty(name)){
implClassName = name;
break;
}
}
return implClassName;
}
/**
* 构建方法名
* @param methodName 注解的addMethod或updateMethod
* @return
*/
private String buildMethodName(String methodName){
return "get"+upperFirstLatter(methodName);
}
/**
* 字符串首字母大写
* @param letter
* @return
*/
private String upperFirstLatter(String letter){
char[] chars = letter.toCharArray();
if(chars[0]>='a' && chars[0]<='z'){
chars[0] = (char) (chars[0]-32);
return new String(chars);
}
return letter.substring(0, 1).toUpperCase()+letter.substring(1);
}
}
方法名设计与构建根据各自需求自行修改
@Configuration
@EnableConfigurationProperties({
MybatisInterceptorProperties.class})
@ConditionalOnProperty(
name = {
"microservices.framework.mybaits.autostuff.interceptor.enabled"},
matchIfMissing = true,
havingValue = "true"
)
public class InterceptorConfig {
@Bean
public SpringBeanUtil springBeanUtil() {
return new SpringBeanUtil();
}
@Bean
@DependsOn("springBeanUtil")
public Interceptor interceptor() {
return new StuffInterceptor();
}
}
@Data
public class AHello implements Serializable {
private String id;
private String name;
@AutoStuff(addFlag = true, addMethod = "aUser")
private String createUser;
@AutoStuff(addFlag = true, addMethod = "nowDate")
private Date createTime;
@AutoStuff(addFlag = true, addMethod = "operUser", updateFlag = true, updateMethod = "newUser")
private String lastModifyUser;
@AutoStuff(addFlag = true, addMethod = "nowTime", updateFlag = true, updateMethod = "nowTime")
private Date lastModifyTime;
}
@Service
public class OperInfoImpl implements IOperInfoService {
public String getAUser(){
return "aUser";
}
public String getOperUser() {
return "operUser";
}
public String getNewUser(){
return "newUser";
}
public Date getNowDate() {
Date date = new Date();
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.add(Calendar.DAY_OF_MONTH, +1);//+1今天的时间加一天
date = calendar.getTime();
return date;
}
public Timestamp getNowTime() {
Date date = new Date();
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.add(Calendar.DAY_OF_MONTH, +1);//+1今天的时间加一天
date = calendar.getTime();
return new Timestamp(date.getTime());
}
}
@AutoStuff(addFlag = true, addMethod = "nowDate")
private Date createTime;
已知注解中addFlag为true
,addMethod为nowdate
将addMethod首字母大写并在开头加上get
组成方法名:getNowDate
拦截器就会从实现类OperInfoImpl
中调用getNowDate()
方法,将该方法的返回值注入到该字段中。
若方法不存在则会抛出异常提示方法不存在。
注意,必须实现且只能实现一次
IOperInfoService
接口,否则将抛出异常。
addMethod与updateMethod只能由字母、数字、下划线组成