参考:https://fangshixiang.blog.csdn.net/article/details/88765470、https://blog.csdn.net/weixin_42189048/article/details/108288213
ClassMetaData
// @since 2.5
public interface ClassMetadata {
// 返回类名(注意返回的是最原始的那个className)
String getClassName();
boolean isInterface();
// 是否是注解
boolean isAnnotation();
boolean isAbstract();
// 是否允许创建 不是接口且不是抽象类 这里就返回true了
boolean isConcrete();
boolean isFinal();
// 是否是独立的(能够创建对象的) 比如是Class、或者内部类、静态内部类
boolean isIndependent();
// 是否有内部类之类的东东
boolean hasEnclosingClass();
@Nullable
String getEnclosingClassName();
boolean hasSuperClass();
@Nullable
String getSuperClassName();
// 会把实现的所有接口名称都返回 具体依赖于Class#getSuperclass
String[] getInterfaceNames();
// 基于:Class#getDeclaredClasses 返回类中定义的公共、私有、保护的内部类
String[] getMemberClassNames();
}
MethodsMetadata
// @since 2.1 可以看到它出现得更早一些
public interface MethodsMetadata extends ClassMetadata {
// 返回该class所有的方法
Set<MethodMetadata> getMethods();
// 方法指定方法名的方法们(因为有重载嘛~)
Set<MethodMetadata> getMethods(String name);
}
AnnotatedTypeMetadata
// @since 4.0
public interface AnnotatedTypeMetadata {
// 此元素是否标注有此注解~~~~
// annotationName:注解全类名
boolean isAnnotated(String annotationName);
// 这个就厉害了:取得指定类型注解的所有的属性 - 值(k-v)
// annotationName:注解全类名
// classValuesAsString:若是true表示 Class用它的字符串的全类名来表示。这样可以避免Class被提前加载
@Nullable
Map<String, Object> getAnnotationAttributes(String annotationName);
@Nullable
Map<String, Object> getAnnotationAttributes(String annotationName, boolean classValuesAsString);
// 参见这个方法的含义:AnnotatedElementUtils.getAllAnnotationAttributes
@Nullable
MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationName);
@Nullable
MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationName, boolean classValuesAsString);
}
MethodMetadata
// @since 3.0
public interface MethodMetadata extends AnnotatedTypeMetadata {
String getMethodName();
String getDeclaringClassName();
String getReturnTypeName();
boolean isAbstract();
boolean isStatic();
boolean isFinal();
boolean isOverridable();
}
AnnotationMetadata
// @since 2.5
public interface AnnotationMetadata extends ClassMetadata, AnnotatedTypeMetadata {
//拿到当前类上所有的注解的全类名(注意是全类名)
Set<String> getAnnotationTypes();
// 拿到指定的注解类型
//annotationName:注解类型的全类名
Set<String> getMetaAnnotationTypes(String annotationName);
// 是否包含指定注解 (annotationName:全类名)
boolean hasAnnotation(String annotationName);
//这个厉害了,用于判断注解类型自己是否被某个元注解类型所标注
//依赖于AnnotatedElementUtils#hasMetaAnnotationTypes
boolean hasMetaAnnotation(String metaAnnotationName);
/*上面hasAnnotation与hasM*/
// 类里面只有有一个方法标注有指定注解,就返回true
//getDeclaredMethods获得所有方法, AnnotatedElementUtils.isAnnotated是否标注有指定注解
boolean hasAnnotatedMethods(String annotationName);
// 返回所有的标注有指定注解的方法元信息。注意返回的是MethodMetadata 原理基本同上
Set<MethodMetadata> getAnnotatedMethods(String annotationName);
}
StandardAnnotationMetadata
// @since 2.5
public class StandardAnnotationMetadata extends StandardClassMetadata implements AnnotationMetadata {
// 很显然它是基于标准反射类型:java.lang.annotation.Annotation
// this.annotations = introspectedClass.getAnnotations()
private final Annotation[] annotations;
private final boolean nestedAnnotationsAsMap;
...
// 获取本Class类上的注解的元注解们
@Override
public Set<String> getMetaAnnotationTypes(String annotationName) {
return (this.annotations.length > 0 ?
AnnotatedElementUtils.getMetaAnnotationTypes(getIntrospectedClass(), annotationName) : Collections.emptySet());
}
@Override
public boolean hasAnnotation(String annotationName) {
for (Annotation ann : this.annotations) {
if (ann.annotationType().getName().equals(annotationName)) {
return true;
}
}
return false;
}
...
}
MetadataReader
MetadataReader 接口抽象元数据的读取,其实现基于 ASM 直接扫描对应文件字节码实现
// @since 2.5
public interface MetadataReader {
// 返回此Class文件的来源(资源)
Resource getResource();
// 返回此Class的元数据信息
ClassMetadata getClassMetadata();
// 返回此类的注解元信息(包括方法的)
AnnotationMetadata getAnnotationMetadata();
}
SimpleMetadataReader
final class SimpleMetadataReader implements MetadataReader {
private final Resource resource;
private final ClassMetadata classMetadata;
private final AnnotationMetadata annotationMetadata;
// 唯一构造函数:给上面三个私有属性赋值,下面就只需提供get方法即可
SimpleMetadataReader(Resource resource, @Nullable ClassLoader classLoader) throws IOException {
InputStream is = new BufferedInputStream(resource.getInputStream());
ClassReader classReader;
try {
classReader = new ClassReader(is);
} catch (IllegalArgumentException ex) {
throw new NestedIOException("ASM ClassReader failed to parse class file - " + "probably due to a new Java class file version that isn't supported yet: " + resource, ex);
} finally {
is.close();
}
//通过流构建出一个AnnotationMetadataReadingVisitor,咀咒读取从而获取到各种信息
// 它实现了ClassVisitor,所以可以作为入参传给ClassReader ASM去解析
AnnotationMetadataReadingVisitor visitor = new AnnotationMetadataReadingVisitor(classLoader);
classReader.accept(visitor, ClassReader.SKIP_DEBUG);
this.annotationMetadata = visitor;
// (since AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor)
this.classMetadata = visitor;
this.resource = resource;
}
... // 省略三个get方法
}
MethodsMetadataReader
public interface MethodsMetadataReader extends MetadataReader {
MethodsMetadata getMethodsMetadata();
...
}
// 它所有的实现都是委托给静态内部类MethodsMetadataReadingVisitor去做的
MetadataReaderFactory
// @since 2.5
public interface MetadataReaderFactory {
//className: the class name (to be resolved to a ".class" file)
MetadataReader getMetadataReader(String className) throws IOException;
MetadataReader getMetadataReader(Resource resource) throws IOException;
}
SimpleMetadataReaderFactory
public class SimpleMetadataReaderFactory implements MetadataReaderFactory {
// ResourceLoader这个资源加载类应该不陌生了吧
// 默认使用的是DefaultResourceLoader,当然你可以通过构造器指定
private final ResourceLoader resourceLoader;
// 根据类名找到一个Resource
@Override
public MetadataReader getMetadataReader(String className) throws IOException {
try {
// 把..形式换成//.class形式。使用前缀是:classpath: 在类路径里找哦
String resourcePath = ResourceLoader.CLASSPATH_URL_PREFIX + ClassUtils.convertClassNameToResourcePath(className) + ClassUtils.CLASS_FILE_SUFFIX;
Resource resource = this.resourceLoader.getResource(resourcePath);
return getMetadataReader(resource); // 调用重载方法
} catch (FileNotFoundException ex) {
// Maybe an inner class name using the dot name syntax? Need to use the dollar syntax here...
// ClassUtils.forName has an equivalent check for resolution into Class references later on.
... // 此处是兼容内部类形式,代码略
}
}
// 默认使用的是SimpleMetadataReader哦~~~
@Override
public MetadataReader getMetadataReader(Resource resource) throws IOException {
return new SimpleMetadataReader(resource, this.resourceLoader.getClassLoader());
}
}
CachingMetadataReaderFactory
它继承自SimpleMetadataReaderFactory,没有其它特殊的,就是提供了缓存能力private Map<Resource, MetadataReader> metadataReaderCache,提高访问效率。
因为有了它,所以SimpleMetadataReaderFactory就不需要被直接使用了,用它代替。Spring内自然也使用的便是效率更高的它喽~
MethodsMetadataReaderFactory
它继承自SimpleMetadataReaderFactory,唯一区别是它生产的是一个MethodsMetadataReader(DefaultMethodsMetadataReader),从而具有了读取MethodsMetadata的能力。
此类可认为从没有被Spring内部使用过,暂且可忽略(spring-data工程有用)
Factory工厂的实现都是非常简单的,毕竟只是为了生产一个实例而已。
Spring注解编程中AnnotationMetadata的使用
Spring从3.0开始就大量的使用到了注解编程模式,所以可想而知它对元数据(特别是注解元数据)的使用是非常多的,此处我只给出非常简单的总结。
对于MetadataReaderFactory的应用主要体现在几个地方:
ConfigurationClassPostProcessor
该属性值最终会传给ConfigurationClassParser,用于@EnableXXX / @Import等注解的解析上~
// 私有成员变量,默认使用的CachingMetadataReaderFactory
private MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory();
ClassPathScanningCandidateComponentProvider
它用于@ComponentScan的时候解析,拿到元数据判断是否是@Component的派生注解
public final MetadataReaderFactory getMetadataReaderFactory() {
if (this.metadataReaderFactory == null) {
this.metadataReaderFactory = new CachingMetadataReaderFactory();
}
return this.metadataReaderFactory;
}
Mybatis的SqlSessionFactoryBean
它在使用上非常简单,只是为了从Resouece里拿到ClassName而已。classMetadata.getClassName()
private static final MetadataReaderFactory METADATA_READER_FACTORY = new CachingMetadataReaderFactory();
private Set<Class<?>> scanClasses(String packagePatterns, Class<?> assignableType) {
...
ClassMetadata classMetadata = METADATA_READER_FACTORY.getMetadataReader(resource).getClassMetadata();
Class<?> clazz = Resources.classForName(classMetadata.getClassName());
...
}
SourceClass
它是对source对象一个轻量级的包装,持有AnnotationMetadata 元数据,如下一般实际为一个StandardAnnotationMetadata,比如@EnableTransactionManagement用的就是它
private class SourceClass implements Ordered {
private final Object source; // Class or MetadataReader
private final AnnotationMetadata metadata;
public SourceClass(Object source) {
this.source = source;
if (source instanceof Class) {
this.metadata = new StandardAnnotationMetadata((Class<?>) source, true);
} else {
this.metadata = ((MetadataReader) source).getAnnotationMetadata();
}
}
}
说明:Spring的@EnableXXX模块注解很多都使用到了ImportSelector这个接口,此接口的回调方法参数第一个便是AnnotationMetadata代表着@Import所在类的注解的一些元数据们。通常我们会这样使用它:
// 1、转换成AnnotationAttributes(LinkedHashMap),模糊掉注解类型(常用)
AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(importingClassMetadata, annType);
// 2、拿到指定类型注解的元数据信息(也较为常用)
AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(name, true))
// 3、直接使用MetaData
MultiValueMap<String, Object> attributes = metadata.getAllAnnotationAttributes(EnableConfigurationProperties.class.getName(), false);
使用示例
ClassMetadata&AnnotatedTypeMetadata&AnnotationMetadata
// 准备一个Class类 作为Demo演示
@Repository("repositoryName")
@Service("serviceName")
@EnableAsync
class MetaDemo extends HashMap<String, String> implements Serializable {
private static class InnerClass {
}
@Autowired
private String getName() {
return "demo";
}
}
public static void main(String[] args) {
StandardAnnotationMetadata metadata = new StandardAnnotationMetadata(MetaDemo.class, true);
// 演示ClassMetadata的效果
System.out.println("==============ClassMetadata==============");
ClassMetadata classMetadata = metadata;
System.out.println(classMetadata.getClassName()); //com.fsx.maintest.MetaDemo
System.out.println(classMetadata.getEnclosingClassName()); //null 如果自己是内部类此处就有值了
System.out.println(StringUtils.arrayToCommaDelimitedString(classMetadata.getMemberClassNames())); //com.fsx.maintest.MetaDemo$InnerClass 若木有内部类返回空数组[]
System.out.println(StringUtils.arrayToCommaDelimitedString(classMetadata.getInterfaceNames())); // java.io.Serializable
System.out.println(classMetadata.hasSuperClass()); // true(只有Object这里是false)
System.out.println(classMetadata.getSuperClassName()); // java.util.HashMap
System.out.println(classMetadata.isAnnotation()); // false(是否是注解类型的Class,这里显然是false)
System.out.println(classMetadata.isFinal()); // false
System.out.println(classMetadata.isIndependent()); // true(top class或者static inner class,就是独立可new的)
// 演示AnnotatedTypeMetadata的效果
System.out.println("==============AnnotatedTypeMetadata==============");
AnnotatedTypeMetadata annotatedTypeMetadata = metadata;
System.out.println(annotatedTypeMetadata.isAnnotated(Service.class.getName())); // true(依赖的AnnotatedElementUtils.isAnnotated这个方法)
System.out.println(annotatedTypeMetadata.isAnnotated(Component.class.getName())); // true
System.out.println(annotatedTypeMetadata.getAnnotationAttributes(Service.class.getName())); //{value=serviceName}
System.out.println(annotatedTypeMetadata.getAnnotationAttributes(Component.class.getName())); // {value=repositoryName}(@Repository的value值覆盖了@Service的)
System.out.println(annotatedTypeMetadata.getAnnotationAttributes(EnableAsync.class.getName())); // {order=2147483647, annotation=interface java.lang.annotation.Annotation, proxyTargetClass=false, mode=PROXY}
// 看看getAll的区别:value都是数组的形式
System.out.println(annotatedTypeMetadata.getAllAnnotationAttributes(Service.class.getName())); // {value=[serviceName]}
System.out.println(annotatedTypeMetadata.getAllAnnotationAttributes(Component.class.getName())); // {value=[, ]} --> 两个Component的value值都拿到了,只是都是空串而已
System.out.println(annotatedTypeMetadata.getAllAnnotationAttributes(EnableAsync.class.getName())); //{order=[2147483647], annotation=[interface java.lang.annotation.Annotation], proxyTargetClass=[false], mode=[PROXY]}
// 演示AnnotationMetadata子接口的效果(重要)
System.out.println("==============AnnotationMetadata==============");
AnnotationMetadata annotationMetadata = metadata;
System.out.println(annotationMetadata.getAnnotationTypes()); // [org.springframework.stereotype.Repository, org.springframework.stereotype.Service, org.springframework.scheduling.annotation.EnableAsync]
System.out.println(annotationMetadata.getMetaAnnotationTypes(Service.class.getName())); // [org.springframework.stereotype.Component, org.springframework.stereotype.Indexed]
System.out.println(annotationMetadata.getMetaAnnotationTypes(Component.class.getName())); // [](meta就是获取注解上面的注解,会排除掉java.lang这些注解们)
System.out.println(annotationMetadata.hasAnnotation(Service.class.getName())); // true
System.out.println(annotationMetadata.hasAnnotation(Component.class.getName())); // false(注意这里返回的是false)
System.out.println(annotationMetadata.hasMetaAnnotation(Service.class.getName())); // false(注意这一组的结果和上面相反,因为它看的是meta)
System.out.println(annotationMetadata.hasMetaAnnotation(Component.class.getName())); // true
System.out.println(annotationMetadata.hasAnnotatedMethods(Autowired.class.getName())); // true
annotationMetadata.getAnnotatedMethods(Autowired.class.getName()).forEach(methodMetadata -> {
System.out.println(methodMetadata.getClass()); // class org.springframework.core.type.StandardMethodMetadata
System.out.println(methodMetadata.getMethodName()); // getName
System.out.println(methodMetadata.getReturnTypeName()); // java.lang.String
});
}
MetadataReaderFactory->MetadataReader->AnnotationMetadata
public static void main(String[] args) throws IOException {
CachingMetadataReaderFactory readerFactory = new CachingMetadataReaderFactory();
// 下面两种初始化方式都可,效果一样
//MetadataReader metadataReader = readerFactory.getMetadataReader(MetaDemo.class.getName());
MetadataReader metadataReader = readerFactory.getMetadataReader(new ClassPathResource("com/fsx/maintest/MetaDemo.class"));
ClassMetadata classMetadata = metadataReader.getClassMetadata();
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
Resource resource = metadataReader.getResource();
System.out.println(classMetadata); // org.springframework.core.type.classreading.AnnotationMetadataReadingVisitor@79079097
System.out.println(annotationMetadata); // org.springframework.core.type.classreading.AnnotationMetadataReadingVisitor@79079097
System.out.println(resource); // class path resource [com/fsx/maintest/MetaDemo.class]
}
Spring扫描包的大致逻辑
public class ScanTest {
@Test
public void test_01() throws IOException {
// 详细的扫包逻辑可参见:ClassPathScanningCandidateComponentProvider,
// 里面有扩展出TypeFilter接口,过来排除(excludeFilters)、匹配(includeFilters)、条件(conditionEvaluator.shouldSkip(metaReader))(先排除,再匹配,最后看条件是否满足)
PathMatchingResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
Resource[] resources = resourcePatternResolver.getResources("classpath*:com/zzhua/test/"+"**/*.class");
CachingMetadataReaderFactory metaDataReaderFactory = new CachingMetadataReaderFactory();
for (Resource resource : resources) {
MetadataReader metadataReader = metaDataReaderFactory.getMetadataReader(resource);
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
System.out.println(annotationMetadata.isAnnotated(Component.class.getName()) + "->"
+ metadataReader.getClassMetadata().getClassName());
}
/*
输出:
false->com.zzhua.test.CommaTest
true->com.zzhua.test.Hat
true->com.zzhua.test.Person
false->com.zzhua.test.Weapon
*/
}
@Test
public void test_02() {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// ClassPathBeanDefinitionScanner有个配置问要不要注册注解配置相关的后置处理器,默认true,
// 就会调用:AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
// 所以后面有另外几个beanDefinition
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(beanFactory);
scanner.addExcludeFilter( // 不处理Controller注解的bd
(metadataReader,metadataReaderFactory)->
metadataReader.getAnnotationMetadata()
.isAnnotated(Controller.class.getName())
);
int size = scanner.scan("com.zzhua.test");
System.out.println(size);
Stream.of(beanFactory.getBeanDefinitionNames()).forEach(System.out::println);
}
/*
输出:
8
hat
person
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
*/
}
ConfigurationClassUtils
/*
* Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.context.annotation;
import java.io.IOException;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.core.Conventions;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.StandardAnnotationMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.stereotype.Component;
/**
* Utilities for processing @{@link Configuration} classes.
*
* @author Chris Beams
* @author Juergen Hoeller
* @since 3.1
*/
abstract class ConfigurationClassUtils {
private static final String CONFIGURATION_CLASS_FULL = "full";
private static final String CONFIGURATION_CLASS_LITE = "lite";
private static final String CONFIGURATION_CLASS_ATTRIBUTE =
Conventions.getQualifiedAttributeName(ConfigurationClassPostProcessor.class, "configurationClass");
private static final String ORDER_ATTRIBUTE =
Conventions.getQualifiedAttributeName(ConfigurationClassPostProcessor.class, "order");
private static final Log logger = LogFactory.getLog(ConfigurationClassUtils.class);
private static final Set<String> candidateIndicators = new HashSet<String>(4);
static {
candidateIndicators.add(Component.class.getName());
candidateIndicators.add(ComponentScan.class.getName());
candidateIndicators.add(Import.class.getName());
candidateIndicators.add(ImportResource.class.getName());
}
/**
* Check whether the given bean definition is a candidate for a configuration class
* (or a nested component class declared within a configuration/component class,
* to be auto-registered as well), and mark it accordingly.
* @param beanDef the bean definition to check
* @param metadataReaderFactory the current factory in use by the caller
* @return whether the candidate qualifies as (any kind of) configuration class
*/
public static boolean checkConfigurationClassCandidate(BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {
String className = beanDef.getBeanClassName();
if (className == null || beanDef.getFactoryMethodName() != null) {
return false;
}
AnnotationMetadata metadata;
if (beanDef instanceof AnnotatedBeanDefinition &&
className.equals(((AnnotatedBeanDefinition) beanDef).getMetadata().getClassName())) {
// Can reuse the pre-parsed metadata from the given BeanDefinition...
metadata = ((AnnotatedBeanDefinition) beanDef).getMetadata();
}
else if (beanDef instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) beanDef).hasBeanClass()) {
// Check already loaded Class if present...
// since we possibly can't even load the class file for this Class.
Class<?> beanClass = ((AbstractBeanDefinition) beanDef).getBeanClass();
metadata = new StandardAnnotationMetadata(beanClass, true);
}
else {
try {
MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(className);
metadata = metadataReader.getAnnotationMetadata();
}
catch (IOException ex) {
if (logger.isDebugEnabled()) {
logger.debug("Could not find class file for introspecting configuration annotations: " + className, ex);
}
return false;
}
}
if (isFullConfigurationCandidate(metadata)) {
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
}
else if (isLiteConfigurationCandidate(metadata)) {
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
}
else {
return false;
}
// It's a full or lite configuration candidate... Let's determine the order value, if any.
Map<String, Object> orderAttributes = metadata.getAnnotationAttributes(Order.class.getName());
if (orderAttributes != null) {
beanDef.setAttribute(ORDER_ATTRIBUTE, orderAttributes.get(AnnotationUtils.VALUE));
}
return true;
}
/**
* Check the given metadata for a configuration class candidate
* (or nested component class declared within a configuration/component class).
* @param metadata the metadata of the annotated class
* @return {@code true} if the given class is to be registered as a
* reflection-detected bean definition; {@code false} otherwise
*/
public static boolean isConfigurationCandidate(AnnotationMetadata metadata) {
return (isFullConfigurationCandidate(metadata) || isLiteConfigurationCandidate(metadata));
}
/**
* Check the given metadata for a full configuration class candidate
* (i.e. a class annotated with {@code @Configuration}).
* @param metadata the metadata of the annotated class
* @return {@code true} if the given class is to be processed as a full
* configuration class, including cross-method call interception
*/
public static boolean isFullConfigurationCandidate(AnnotationMetadata metadata) {
return metadata.isAnnotated(Configuration.class.getName());
}
/**
* Check the given metadata for a lite configuration class candidate
* (e.g. a class annotated with {@code @Component} or just having
* {@code @Import} declarations or {@code @Bean methods}).
* @param metadata the metadata of the annotated class
* @return {@code true} if the given class is to be processed as a lite
* configuration class, just registering it and scanning it for {@code @Bean} methods
*/
public static boolean isLiteConfigurationCandidate(AnnotationMetadata metadata) {
// Do not consider an interface or an annotation...
if (metadata.isInterface()) {
return false;
}
// Any of the typical annotations found?
for (String indicator : candidateIndicators) {
if (metadata.isAnnotated(indicator)) {
return true;
}
}
// Finally, let's look for @Bean methods...
try {
return metadata.hasAnnotatedMethods(Bean.class.getName());
}
catch (Throwable ex) {
if (logger.isDebugEnabled()) {
logger.debug("Failed to introspect @Bean methods on class [" + metadata.getClassName() + "]: " + ex);
}
return false;
}
}
/**
* Determine whether the given bean definition indicates a full {@code @Configuration}
* class, through checking {@link #checkConfigurationClassCandidate}'s metadata marker.
*/
public static boolean isFullConfigurationClass(BeanDefinition beanDef) {
return CONFIGURATION_CLASS_FULL.equals(beanDef.getAttribute(CONFIGURATION_CLASS_ATTRIBUTE));
}
/**
* Determine whether the given bean definition indicates a lite {@code @Configuration}
* class, through checking {@link #checkConfigurationClassCandidate}'s metadata marker.
*/
public static boolean isLiteConfigurationClass(BeanDefinition beanDef) {
return CONFIGURATION_CLASS_LITE.equals(beanDef.getAttribute(CONFIGURATION_CLASS_ATTRIBUTE));
}
/**
* Determine the order for the given configuration class bean definition,
* as set by {@link #checkConfigurationClassCandidate}.
* @param beanDef the bean definition to check
* @return the {@link @Order} annotation value on the configuration class,
* or {@link Ordered#LOWEST_PRECEDENCE} if none declared
* @since 4.2
*/
public static int getOrder(BeanDefinition beanDef) {
Integer order = (Integer) beanDef.getAttribute(ORDER_ATTRIBUTE);
return (order != null ? order : Ordered.LOWEST_PRECEDENCE);
}
}
实现mybatis扫包大致逻辑
@Dao
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Target(ElementType.TYPE)
public @interface Dao {
String value() default "";
}
EnableDao
@Inherited
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(MyDaoRegistrar.class)
public @interface EnableDao {
String value();
}
MyDaoRegistrar
public class MyDaoRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata anoMetaData, BeanDefinitionRegistry registry) {
Map<String, Object> attrs = anoMetaData.getAnnotationAttributes(EnableDao.class.getName());
Object value = attrs.get("value");
Assert.notNull(value, "value can't be null");
DaoScanner daoScanner = new DaoScanner(registry);
daoScanner.setBeanNameGenerator(new MyDaoBeanNameGenerator());
daoScanner.addIncludeFilter((a,b)->true);
Set<BeanDefinitionHolder> bdfs = daoScanner.doScan(value.toString());
for (BeanDefinitionHolder bdh : bdfs) {
ScannedGenericBeanDefinition sbd = (ScannedGenericBeanDefinition) bdh.getBeanDefinition();
String rawClassName = sbd.getBeanClassName();
sbd.setBeanClass(DaoFactoryBean.class);
AnnotationMetadata metadata = sbd.getMetadata();
try {
sbd.getConstructorArgumentValues().addGenericArgumentValue(Class.forName(rawClassName));
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
}
DaoScanner
public class DaoScanner extends ClassPathBeanDefinitionScanner {
public DaoScanner(BeanDefinitionRegistry registry) {
super(registry);
}
@Override
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
return super.doScan(basePackages);
}
@Override
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
return beanDefinition.getMetadata().isAnnotated(Dao.class.getName());
}
}
DaoFactoryBean
public class DaoFactoryBean<T> implements FactoryBean {
private Class<T> intf;
public DaoFactoryBean(Class<T> intf) {
Assert.notNull(intf,"intf can't be null");
Assert.isTrue(intf.isInterface(),"intf must be defined as a interface");
this.intf = intf;
}
@Override
public T getObject() {
// 返回接口实例
return (T) Proxy.newProxyInstance(DaoFactoryBean.class.getClassLoader(), new Class[]{intf},
(proxy, method, args) -> {
// 这里可置入sqlSession来作查询(mybatis-spring中为实现事务同步有作特别处理),这里仅简单构造返回
Constructor<?> cons = method.getReturnType().getConstructor();
return cons.newInstance();
}
);
}
@Override
public Class<?> getObjectType() {
return intf;
}
@Override
public boolean isSingleton() {
return true;
}
}
MyDaoBeanNameGenerator
public class MyDaoBeanNameGenerator extends AnnotationBeanNameGenerator {
protected boolean isStereotypeWithNameValue(String annotationType,
Set<String> metaAnnotationTypes, @Nullable Map<String, Object> attributes){
return super.isStereotypeWithNameValue(annotationType, metaAnnotationTypes, attributes) || Dao.class.getName().equals(annotationType);
}
}
EmployeeDao
@Dao("myEmpDao")
public interface EmployeeDao {
Employee getEmpById(Integer id);
}
DaoConfig
@EnableDao("com.zzhua.mapper")
@Configuration
public class DaoConfig {
}
测试
@Test
public void test_04() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(DaoConfig.class);
EmployeeMapper empMapper = (EmployeeMapper) context.getBean("myEmpDao");
Employee emp = empMapper.getEmpById(1);
System.out.println(emp);
// 输出:Employee(id=null, lastName=null, email=null, gender=null, hireDate=null)
}e'mem