上一篇博客写了用AOP去完成参数校验的功能,但是作为一个比较通用的解决方案,如果要添加新的校验规则,最好是不需要修改原有的代码。前一篇博客在注解里把校验规则给写死了,这是不灵活的。拦截到了请求之后我们可以定制具体的校验方案。想想之前学的设计模式之策略模式不就是在一系列相同步骤当中封装了容易变化的部分么。因此这次我们采用策略模式来设计校验工具。
首先定义一个策略接口:
最开始的思路是这样的,用一个拦截器去扫描注解,这些注解标记了使用的校验器的bean名称,这个拦截器增加实现接口ApplicationContextAware可以通过扫描到方法的字段并在切面里从工厂获取对应的校验器完成校验工作。这样是可以实现功能的,但是这里有两个问题,第一个问题是这里跟Spring容器耦合了。无法当作独立组件使用。第二个问题,不能自定义校验器的名字,还要注入到容器,然后再使用的时候还是标记的bean的名字。说到底第二条也使由于跟框架耦合了。
改造后我通过注解标记到校验器的实现类标记校验器的名称。获取校验器也通过反射机制而不依赖容器。
代码如下:
策略接口
public interface ParamsValidator {
boolean checkParams(Object param);
}
比较简单,传入参数,返回是否通过校验的结果即可。可以实现一个具体的校验器:
@ValidatorName("notNull")
public class NotNullValidator implements ParamsValidator {
@Override
public boolean checkParams(Object param) {
return param == null ? false : true;
}
}
这个就做了一个非空的校验。需要自定义校验直接实现此接口即可。
这里的注解validatorName定义:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface ValidatorName {
String value();
}
环境类,封装了策略接口,加载校验器等初始化工作。
public class ValidatorContext {
private final static String VALIDATOR_PATH = ParamsValidator.class.getName();
private static String ROOT_PACKAGE_PATH;
private static Map<String,ParamsValidator> validatorMap;
private void initValidator() {
if (validatorMap == null) {
validatorMap = new HashMap<String, ParamsValidator>();
loadValidator();
}
return;
}
public boolean isOk(Method method, Object[] args) throws Exception {
initValidator();
if (validatorMap == null || validatorMap.keySet().size() == 0) {
return false;
}
Parameter[] params = method.getParameters();
boolean isOk = true;
int index = 0;
for (Parameter param : params) {
ParamsCheck paramsCheck = param.getAnnotation(ParamsCheck.class);
if (paramsCheck == null) {
continue;
}
// 具体策略名称
String[] validatorNames = paramsCheck.validators();
// 根据策略名称获取具体策略
for (String validatorName : validatorNames) {
ParamsValidator paramsValidator = validatorMap.get(validatorName);
if (paramsValidator != null) {
isOk = paramsValidator.checkParams(args[index]);
if (!isOk) {
break;
}
} else {
throw new Exception("not find validator : " + validatorName);
}
}
index ++;
}
return isOk;
}
private void loadValidator() {
// 获取所有策略
try {
Class<?> clazz = Class.forName(VALIDATOR_PATH);
if (clazz != null && clazz.isInterface()) {
// 根包路径
ROOT_PACKAGE_PATH = clazz.getResource("/").getPath();
loadValidatorByFile(new File(ROOT_PACKAGE_PATH));
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public void loadValidatorByFile(File file) {
if (!file.isDirectory()) {
try {
if (file.getName().endsWith(".class")) {
if (file.getAbsolutePath().replaceAll(ROOT_PACKAGE_PATH, "").length() < file.getAbsolutePath().length()) {
String classPath = file.getAbsolutePath().replaceAll(ROOT_PACKAGE_PATH, "").replace("/", ".");
Class<?> clazz = Class.forName(classPath.substring(0, classPath.length() - 6));
if (!clazz.isInterface() && isImplementor(ParamsValidator.class, clazz)) {
ParamsValidator paramsValidator = (ParamsValidator) clazz.newInstance();
String validatorName = paramsValidator.getClass().getAnnotation(ValidatorName.class).value();
validatorMap.put(validatorName, paramsValidator);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
} else {
for (File f : file.listFiles()) {
loadValidatorByFile(f);
}
}
}
private boolean isImplementor(Class<ParamsValidator> inter, Class<?> implementor) {
Class<?>[] classes = implementor.getInterfaces();
for (Class<?> c : classes) {
if (c.equals(inter)) {
return true;
}
}
return false;
}
public static void main(String[] args) {
ValidatorContext validatorContext = new ValidatorContext();
validatorContext.initValidator();
System.out.println("validators implementor is : " + ValidatorContext.validatorMap);
}
}
切面类:
@Component
public class CheckParamsIntercepor extends BaseResourceApi implements MethodInterceptor{
@Autowired
private ValidatorContext validatorContext;
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
boolean isOk = true;
Method method = invocation.getMethod();
Object[] args = invocation.getArguments();
isOk = validatorContext.isOk(method, args);
if (isOk) {
return invocation.proceed();
}
return response(ResultEnum.PARAM_ERROR);
}
}
ParamsCheck注解:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD})
public @interface ParamsCheck {
String[] validators();
}
使用的时候只要在字段前加@ParamsCheck(“notNull”)即可, 这个”notNull”就是前面校验器实现类定义的名称。