we never know, we just believe it.
在 Small Spring系列五:annotation Injection(一) 中,我们已经通过 PackageResourceLoader 将指定包下面的 class 文件转变为 Resource 资源。本章我们实现通过 ASM 读取 Resource 中的注解信息并创建 BeanDefinition 。关于 ASM 读取类信息可参考 链接 ,由于 spring 已经封装好读取操作,我们就不重复造轮子了。
在 pom.xml 中增加 spring-core-asm-3.2.18.RELEASE.jar 的依赖。
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core-asm</artifactId>
<version>3.2.18.RELEASE</version>
<scope>system</scope>
<systemPath>${project.basedir}/libs/spring-core-asm-3.2.18.RELEASE.jar</systemPath>
</dependency>
读取class文件,获取类名,判断是否为接口,是否抽象,是否final,父类,实现接口数量等,继承 ClassVisitor 。关于 ClassVisitor 可参考 链接 。
package com.niocoder.core.type.classreading;
import com.niocoder.util.ClassUtils;
import org.springframework.asm.ClassVisitor;
import org.springframework.asm.Opcodes;
import org.springframework.asm.SpringAsmInfo;
/**
* 读取class文件,获取类名,判断是否为接口,是否抽象,是否final,父类,实现接口数量等
*
* @author zhenglongfei
*/
public class ClassMetadataReadingVisitor extends ClassVisitor {
private String className;
private boolean isInterface;
private boolean isAbstract;
private boolean isFinal;
private String superClassName;
private String[] interfaces;
public ClassMetadataReadingVisitor() {
super(SpringAsmInfo.ASM_VERSION);
}
@Override
public void visit(int version, int access, String name, String signature, String supername, String[] interfaces) {
this.className = ClassUtils.convertResourcePathToClassName(name);
this.isInterface = ((access & Opcodes.ACC_INTERFACE) != 0);
this.isAbstract = ((access & Opcodes.ACC_ABSTRACT) != 0);
this.isFinal = ((access & Opcodes.ACC_FINAL) != 0);
if (supername != null) {
this.superClassName = ClassUtils.convertResourcePathToClassName(supername);
}
this.interfaces = new String[interfaces.length];
for (int i = 0; i < interfaces.length; i++) {
this.interfaces[i] = ClassUtils.convertResourcePathToClassName(interfaces[i]);
}
}
public String getClassName() {
return className;
}
public boolean isInterface() {
return isInterface;
}
public boolean isAbstract() {
return isAbstract;
}
public boolean isFinal() {
return isFinal;
}
public boolean hasSuperClass() {
return this.superClassName != null;
}
public String getSuperClassName() {
return superClassName;
}
public String[] getInterfacesNames() {
return this.interfaces;
}
public String[] getInterfaces() {
return interfaces;
}
}
注意 ClassVisitor引用的包名,是org.springframework.asm.ClassVisitor。
读取class文件,注解信息如: @Component
package com.niocoder.core.type.classreading;
import com.niocoder.core.annotation.AnnotationAttributes;
import jdk.internal.org.objectweb.asm.Type;
import org.springframework.asm.AnnotationVisitor;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
/**
* 读取class文件,注解信息
*
* @author zhenglongfei
*/
public class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor {
/**
* com.niocoder.stereotype.Component
*/
private final Set<String> annotationSet = new LinkedHashSet<>(4);
/**
* com.niocoder.stereotype.Component
* AnnotationAttributes value->nioCoder
*/
private final Map<String, AnnotationAttributes> attributeMap = new LinkedHashMap<>(4);
public AnnotationMetadataReadingVisitor() {
}
@Override
public AnnotationVisitor visitAnnotation(final String desc, boolean visible) {
String className = Type.getType(desc).getClassName();
this.annotationSet.add(className);
return new AnnotationAttributesReadingVisitor(className, this.attributeMap);
}
public Set<String> getAnnotationTypes() {
return this.annotationSet;
}
public boolean hasAnnotation(String annotationType) {
return this.annotationSet.contains(annotationType);
}
public AnnotationAttributes getAnnotationAttributes(String annotationType) {
return this.attributeMap.get(annotationType);
}
}
注意,该类继承了ClassMetadataReadingVisitor,并且在访问注解信息时构建了AnnotationAttributesReadingVisitor
读取注解的属性信息,如: @Component(value = "nioCoder") ,使用 AnnotationAttributes 保存
package com.niocoder.core.type.classreading;
import com.niocoder.core.annotation.AnnotationAttributes;
import org.springframework.asm.AnnotationVisitor;
import org.springframework.asm.SpringAsmInfo;
import java.util.Map;
/**
* 注解中定义的方法信息
*
* @author zhenglongfei
*/
public class AnnotationAttributesReadingVisitor extends AnnotationVisitor {
private final String annotationType;
private final Map<String, AnnotationAttributes> attributesMap;
AnnotationAttributes attributes = new AnnotationAttributes();
public AnnotationAttributesReadingVisitor(
String annotationType, Map<String, AnnotationAttributes> attributesMap) {
super(SpringAsmInfo.ASM_VERSION);
this.annotationType = annotationType;
this.attributesMap = attributesMap;
}
@Override
public final void visitEnd() {
this.attributesMap.put(this.annotationType, this.attributes);
}
@Override
public void visit(String attributeName, Object attributeValue) {
this.attributes.put(attributeName, attributeValue);
}
}
该类继承了AnnotationVisitor
扩展了 LinkedHashMap ,保存注解属性信息,如: @Component(value = "nioCoder")
package com.niocoder.core.annotation;
import com.niocoder.util.Assert;
import java.util.LinkedHashMap;
import static java.lang.String.format;
/**
* 扩展LinkedHashMap 记录注解信息
*
* @author zhenglongfei
*/
public class AnnotationAttributes extends LinkedHashMap<String, Object> {
public AnnotationAttributes() {
}
public String getString(String attributeName) {
return doGet(attributeName, String.class);
}
@SuppressWarnings("unchecked")
private <T> T doGet(String attributeName, Class<T> expectedType) {
Object value = this.get(attributeName);
Assert.notNull(value, format("Attribute '%s' not found", attributeName));
return (T) value;
}
}
测试类,测试上述读取类和注解的信息
public class ClassReaderTest {
@Test
public void testGetClassMetaData() throws Exception {
ClassPathResource resource = new ClassPathResource("com/niocoder/service/v4/NioCoderService.class");
ClassReader reader = new ClassReader(resource.getInputStream());
ClassMetadataReadingVisitor visitor = new ClassMetadataReadingVisitor();
reader.accept(visitor, ClassReader.SKIP_DEBUG);
Assert.assertFalse(visitor.isAbstract());
Assert.assertFalse(visitor.isInterface());
Assert.assertFalse(visitor.isFinal());
Assert.assertEquals("com.niocoder.service.v4.NioCoderService", visitor.getClassName());
Assert.assertEquals("java.lang.Object", visitor.getSuperClassName());
Assert.assertEquals(0, visitor.getInterfaces().length);
}
@Test
public void testGetAnnotation() throws Exception {
ClassPathResource resource = new ClassPathResource("com/niocoder/service/v4/NioCoderService.class");
ClassReader reader = new ClassReader(resource.getInputStream());
AnnotationMetadataReadingVisitor visitor = new AnnotationMetadataReadingVisitor();
reader.accept(visitor,ClassReader.SKIP_DEBUG);
String annotation = "com.niocoder.stereotype.Component";
Assert.assertTrue(visitor.hasAnnotation(annotation));
AnnotationAttributes attributes = visitor.getAnnotationAttributes(annotation);
Assert.assertEquals("nioCoder",attributes.get("value"));
}
}
我们现在想要读取类信息和注解信息需要使用 ClassReader 和 AnnotationMetadataReadingVisitor , ClassReader 还是在 ASM 包下。这样使用起来不太方便,耦合性很高。因此我们准备在 spring 的基础上再次封装一下,类图如下:
封装MetadataReader接口封装读取类信息和注解信息的操作,默认实现类为SimpleMetadataReader
封装读取类信息和注解信息的所有操作
package com.niocoder.core.type.classreading;
import com.niocoder.core.io.Resource;
import com.niocoder.core.type.AnnotationMetadata;
import com.niocoder.core.type.ClassMetadata;
/**
* 封装 ClassMetadataReadingVisitor和AnnotationMetadataReadingVisitor
*
* @author zhenglongfei
*/
public interface MetadataReader {
/**
* 获取资源信息
*
* @return
*/
Resource getResource();
/**
* 获取类信息
*
* @return
*/
ClassMetadata getClassMetadata();
/**
* 获取注解信息
*
* @return
*/
AnnotationMetadata getAnnotationMetadata();
}
封装获取类信息的接口由 ClassMetadataReadingVisitor 实现
package com.niocoder.core.type;
/**
* 封装获取类信息的接口
*
* @author zhenglongfei
*/
public interface ClassMetadata {
/**
* 获取类名称
*
* @return
*/
String getClassName();
/**
* 判断是否是接口
*
* @return
*/
boolean isInterface();
/**
* 判断是否抽象
*
* @return
*/
boolean isAbstract();
/**
* 判断是否类是否final
*
* @return
*/
boolean isFinal();
/**
* 是否父类
*
* @return
*/
boolean hasSuperClass();
/**
* 获取父类名称
*
* @return
*/
String getSuperClassName();
/**
* 获取所有的接口名称
*
* @return
*/
String[] getInterfaceNames();
}
封装获取注解信息的接口,继承 ClassMetadata ,由 AnnotationMetadataReadingVisitor 负责实现
package com.niocoder.core.type;
import com.niocoder.core.annotation.AnnotationAttributes;
import java.util.Set;
/**
* 封装获取接口信息的接口
*
* @author zhenglongfei
*/
public interface AnnotationMetadata extends ClassMetadata {
/**
* 获取注解信息
*
* @return
*/
Set<String> getAnnotationTypes();
/**
* 判断是否有该注解信息
*
* @param annotationType
* @return
*/
boolean hasAnnotation(String annotationType);
/**
* 获取注解的内容信息
*
* @param annotationType
* @return
*/
AnnotationAttributes getAnnotationAttributes(String annotationType);
}
实现了 MetadataReader
package com.niocoder.core.type.classreading;
import com.niocoder.core.io.Resource;
import com.niocoder.core.type.AnnotationMetadata;
import com.niocoder.core.type.ClassMetadata;
import org.springframework.asm.ClassReader;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* 实现MetadataReader
*
* @author zhenglongfei
*/
public class SimpleMetadataReader implements MetadataReader {
private final Resource resource;
private final ClassMetadata classMetadata;
private final AnnotationMetadata annotationMetadata;
public SimpleMetadataReader(Resource resource) throws IOException {
InputStream is = new BufferedInputStream(resource.getInputStream());
org.springframework.asm.ClassReader classReader;
try {
classReader = new ClassReader(is);
} finally {
is.close();
}
AnnotationMetadataReadingVisitor visitor = new AnnotationMetadataReadingVisitor();
classReader.accept(visitor, ClassReader.SKIP_DEBUG);
this.annotationMetadata = visitor;
this.classMetadata = visitor;
this.resource = resource;
}
@Override
public Resource getResource() {
return resource;
}
@Override
public ClassMetadata getClassMetadata() {
return classMetadata;
}
@Override
public AnnotationMetadata getAnnotationMetadata() {
return annotationMetadata;
}
}
实现 ClassMetadata
public class ClassMetadataReadingVisitor extends ClassVisitor implements ClassMetadata {
......
}
实现 AnnotationMetadata
public class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor implements AnnotationMetadata {
......
}
public class MetadataReaderTest {
@Test
public void testGetMetadata() throws Exception{
ClassPathResource resource = new ClassPathResource("com/niocoder/service/v4/NioCoderService.class");
MetadataReader reader = new SimpleMetadataReader(resource);
AnnotationMetadata amd = reader.getAnnotationMetadata();
String annotation = Component.class.getName();
Assert.assertTrue(amd.hasAnnotation(annotation));
AnnotationAttributes attributes = amd.getAnnotationAttributes(annotation);
Assert.assertEquals("nioCoder",attributes.get("value"));
Assert.assertFalse(amd.isAbstract());
Assert.assertFalse(amd.isInterface());
Assert.assertFalse(amd.isFinal());
}
}
从零开始造Spring