使用java 的annotation和反射的小例子,记录下来以供参考,
实现的功能是 配置页面可以动态选择一个实现检核接口的类,保存输入的参数,在真正检核时传入保存的参数
配置页面如下:第一行选择了检核1 有三个参数需要输入,第二行可以看到下拉选单,内容是根据annotation标注的class生成的
下拉选单是根据annotation标注的class生成的,目前是有3个测试class,选择了以后可以填入需要的参数进行保存,参数也是定义在class中,目前先讲一讲这部分配置的实现。
自定义一个annotation
<span style="font-size:14px;">@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface AnnotBpbAssertionClass {
public String descr() default "";
}</span>
其中的一个检核class,实现的接口是为了实作assertData方法,检核调用的真正方法,
@AnnotBpbAssertionArg标注输入的参数,可以看到和上面页面上的匹配
@AnnotBpbAssertionClass(descr="检核1") public class TestAssert1 implements AssertionClassHandler{ @AnnotBpbAssertionArg private String arg1; @AnnotBpbAssertionArg private String arg2; @AnnotBpbAssertionArg private String arg3; public String getArg1() { return arg1; } public void setArg1(String arg1) { this.arg1 = arg1; } public String getArg2() { return arg2; } public void setArg2(String arg2) { this.arg2 = arg2; } public String getArg3() { return arg3; } public void setArg3(String arg3) { this.arg3 = arg3; } @Override public AssertionResult assertData() { } }
为了效率比较高,标注有annotation的class是在spring加载bean的时候写入内存的,service需要实现InitializingBean,初始化方法中用到了类扫描
首先获得Reflections,PKG_NAME 为扫描其实包路径,如果觉得没有用也可以在调用时抓取一次
获得所有标注annotation的class集合。private static Reflections getRef(String pkgName) { String sPkgName = pkgName == null ? PKG_NAME : pkgName; Set<URL> clzLoaderUrls = ClasspathHelper.forPackage(sPkgName); Set<URL> webInfoLibUrls = Collections.emptySet(); Set<URL> scanUrls = new LinkedHashSet<URL>(); try { ServletContext servletContext = WebContextInfo.getInstance().getParentServletContext(); webInfoLibUrls = ClasspathHelper.forWebInfLib(servletContext); URL webInfoClassUrls = ClasspathHelper.forWebInfClasses(servletContext); if(webInfoClassUrls != null){ scanUrls.add(webInfoClassUrls); } } catch (Exception e) { LG.warn(ClassScanner.class.getSimpleName(), "get webinf lib/class failed., err: {0}", e.getMessage()); } scanUrls.addAll(webInfoLibUrls); scanUrls.addAll(clzLoaderUrls); ConfigurationBuilder cfgBuilder = new ConfigurationBuilder(); cfgBuilder.filterInputsBy(new FilterBuilder.Include(FilterBuilder.prefix(sPkgName))); cfgBuilder.addScanners( new SubTypesScanner(), new TypeAnnotationsScanner(), new MethodAnnotationsScanner(), new FieldAnnotationsScanner()); cfgBuilder.setUrls(scanUrls); Reflections refs = new Reflections(cfgBuilder); return refs; }
将class和annotation的信息封装在 BpbAssertionClassMeta 这个类中,可以看到class和annotation的一些方法。<span style="white-space:pre"> </span>Reflections ref = getRef(); Set<Class<?>> ret = ref.getTypesAnnotatedWith(annotation);
页面循环BpbAssertionClassMeta 就可以渲染此页面了。if (CollectionUtils.isNotEmpty(clazzSet)) { for (Class<?> clazz : clazzSet) { BpbAssertionClassMeta meta = new BpbAssertionClassMeta(); List<ArgMeta> argMetas = new ArrayList<BpbAssertionClassMeta.ArgMeta>(); meta.setArgs(argMetas); meta.setClassName(clazz.getCanonicalName()); AnnotBpbAssertionClass annotation = clazz.getAnnotation(AnnotBpbAssertionClass.class);//获取annotation meta.setDesc(annotation.descr()); metas.add(meta); Field[] declaredFields = clazz.getDeclaredFields();//获取类的所有字段 if (declaredFields != null) { for (Field field : declaredFields) { if (field.isAnnotationPresent(AnnotBpbAssertionArg.class)) {//是否标注的此annotation if (String.class.isAssignableFrom(field.getType()) == false) {//是否string类型 throw new IllegalArgumentException( String.format( "unsupported type!, expected %s but %s found. @field: %s, @class: %s", String.class.getSimpleName(), field.getType() .getSimpleName(), field.getName(), clazz .getName())); } ArgMeta argMeta = meta.new ArgMeta(); argMeta.setField(field); argMeta.setDesc(field.getName()); argMetas.add(argMeta); } } } } }
BpbAssertionClassMeta 类
检核的调用,获取之前保存的数据就省略掉了public class BpbAssertionClassMeta { public String className; public String desc; public List<ArgMeta> args; public String getDesc() { return desc; } public void setDesc(String desc) { this.desc = desc; } public String getClassName() { return className; } public void setClassName(String className) { this.className = className; } public List<ArgMeta> getArgs() { return args; } public void setArgs(List<ArgMeta> args) { this.args = args; } public class ArgMeta{ private Field field; private String desc; public Field getField() { return field; } public void setField(Field field) { this.field = field; } public String getDesc() { return desc; } public void setDesc(String desc) { this.desc = desc; } } }
Class<? extends AssertionClassHandler> forName = (Class<? extends AssertionClassHandler>) Class
.forName(assertionClassName);
AssertionClassHandler newInstance = forName.newInstance();//获取实例
// 将保存的参数填入实例中
if (metaArgs != null && CollectionUtils.isNotEmpty(args)) {
for (ArgMeta argMeta : metaArgs) {
for (ClassArg arg : args) {
if (arg.getKey().equals(argMeta.getDesc())) {
Field field = argMeta.getField();
field.setAccessible(true);
field.set(newInstance, arg.getValue());
break;
}
}
}
}
//调用检核方法
<span style="white-space:pre"> </span>assertData = newInstance.assertData();