Small Spring系列六:annotation Injection(二)
we never know, we just believe it.
概述
在 Small Spring系列五:annotation Injection(一) 中,我們已經通過 PackageResourceLoader
將指定包下面的 class
檔案轉變為 Resource
資源。本章我們實現通過 ASM
讀取 Resource
中的註解資訊並建立 BeanDefinition
。關於 ASM
讀取類資訊可參考 連結 ,由於 spring
已經封裝好讀取操作,我們就不重複造輪子了。
修改pom.xml檔案
在 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>
使用ASM讀取類資訊
ClassMetadataReadingVisitor
讀取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。
AnnotationMetadataReadingVisitor
讀取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 * AnnotationAttributesvalue->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
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
AnnotationAttributes
擴充套件了 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; } }
ClassReaderTest
測試類,測試上述讀取類和註解的資訊
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")); } }
類圖
時序圖
程式碼下載
優化ASM讀取類資訊
我們現在想要讀取類資訊和註解資訊需要使用 ClassReader
和 AnnotationMetadataReadingVisitor
, ClassReader
還是在 ASM
包下。這樣使用起來不太方便,耦合性很高。因此我們準備在 spring
的基礎上再次封裝一下,類圖如下:
封裝MetadataReader介面封裝讀取類資訊和註解資訊的操作,預設實現類為SimpleMetadataReader
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; /** * 封裝 ClassMetadataReadingVisitor和AnnotationMetadataReadingVisitor * * @author zhenglongfei */ public interface MetadataReader { /** * 獲取資源資訊 * * @return */ Resource getResource(); /** * 獲取類資訊 * * @return */ ClassMetadata getClassMetadata(); /** * 獲取註解資訊 * * @return */ AnnotationMetadata getAnnotationMetadata(); }
ClassMetadata
封裝獲取類資訊的介面由 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(); }
AnnotationMetadata
封裝獲取註解資訊的介面,繼承 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); }
SimpleMetadataReader
實現了 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; } }
ClassMetadataReadingVisitor
實現 ClassMetadata
public class ClassMetadataReadingVisitor extends ClassVisitor implements ClassMetadata { ...... }
AnnotationMetadataReadingVisitor
實現 AnnotationMetadata
public class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor implements AnnotationMetadata { ...... }
測試類MetadataReaderTest
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()); } }