前言
在最近开发财务系统的时候,遇到一个问题:因为集团财务数据库的权限控制,导致测试和预发布环境只有部分机器有访问集团财务数据库的权限,而代码中又不区分机器,导致开发测试过程中因为没有环境导致验证相关功能阻塞.本着不侵入业务代码的原则,尝试依赖spring去解决在特定环境下直接跳过访问集团财务数据库的方式来解决该问题.
既然要依赖spring来解决,那么就需要先了解下spring bean和mybatis自动注入的一些基本机制,顺便也为了以后写类似的装X代码做点准备.
1.spring-bean的生命周期
2.mybaitis注解自动扫描注入
目标方案
- 使用代理类替换mybatis自动生成的类
- 不影响spring本身的加载机制
- 可根据环境配置进行生效
实现方案
增加标记注解,用于标记可被代理的mapper类
@Retention(RetentionPolicy.RUNTIME)
public @interface MapperMock {
}
实现BeanPostProcessor,当存在环境变量为mapperMock=true时,用自己的代理类替换掉mapper自动生成的代理类
@ConditionalOnProperty(name = "mapperMock", havingValue = "true")
public class MapperMockBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof MapperFactoryBean) {
MapperFactoryBean mapperFactoryBean = (MapperFactoryBean) bean;
Class clazz = mapperFactoryBean.getMapperInterface();
MapperMock mock = (MapperMock) clazz.getAnnotation(MapperMock.class);
Class[] superClasses = clazz.getInterfaces();
Class[] finalClasses = new Class[superClasses.length + 1];
System.arraycopy(superClasses, 0, finalClasses, 1, superClasses.length);
finalClasses[0] = clazz;
if (mock == null) {
return bean;
} else {
bean = new FactoryBean() {
@Override
public Object getObject() throws Exception {
return Proxy.newProxyInstance(clazz.getClassLoader(), finalClasses, new InvocationHandler() {
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
Class returnType = method.getReturnType();
if (int.class.equals(returnType)) {
return 0;
}
if (byte.class.equals(returnType)) {
return 0;
}
if (float.class.equals(returnType)) {
return 0f;
}
if (long.class.equals(returnType)) {
return 0l;
}
if (double.class.equals(returnType)) {
return 0d;
}
if (char.class.equals(returnType)) {
return 0;
}
if (boolean.class.equals(returnType)) {
return false;
}
if (short.class.equals(returnType)) {
return 0;
}
return null;
}
});
}
@Override
public Class getObjectType() {
return clazz;
}
@Override
public boolean isSingleton() {
return true;
}
};
}
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
发散:简单装X指南
如果想依赖spring写一个通用代码框架
1.通过实现ImportBeanDefinitionRegistrar扫描特定注解
2.通过实现FactoryBean来定制需要生成的对象
3.通过实现BeanPostProcessor来对bean做特殊处理
例如可以写代码简化类似kqueue的配置工作~
范世龙