mini-ssm Lab2 - BeanDefinition
在 Lab1 中,我们通过 ClassUtil 实现了包扫描,获取了所有的 Class 对象。接下来,我们将根据这些 Class 对象生成 BeanDefinition 集合。
为什么需要 BeanDefinition
在 Spring 框架中,BeanDefinition 是用来描述 Bean 配置信息的类。我们需要 BeanDefinition 的原因有:
- 并非所有的
Class都要被 Spring 管理。 - 可以通过
BeanDefinition封装 Bean 的元数据信息,如Class对象、是否懒加载、作用域(scope)、Bean 名称等。
类定义
以下是 BeanDefinition 的定义:
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class BeanDefinition {
private String beanClassName;
private Class<?> beanClass;
private boolean lazyInit = false;
private String factoryBeanName;
private String scope = "SINGLETON";
}
在 BeanDefinition 中,beanClassName 保存类的全限定名,factoryBeanName 保存 Bean 的名称(默认首字母小写)。lazyInit 用于表示是否懒加载,scope 定义了 Bean 的作用域(默认为 SINGLETON)。
选择哪些 Bean 需要被管理
通常,在 Spring 中被 @Component 注解标记的类需要被管理,如 @Controller、@Service 和 @Configuration 这些注解类,它们都标记了 @Component。为此,我们定义了一个 BeanLoader 接口,用于确定哪些类需要加载:
public interface BeanLoader {
boolean isLoad(Class<?> clz);
default BeanDefinition loadBean(Class<?> clz){
return BeanDefinition.builder()
.beanClassName(clz.getName())
.beanClass(clz)
.factoryBeanName(StringUtil.toLowerFirstCase(clz.getSimpleName()))
.build();
}
}
ComponentBeanLoader 实现
为了标识需要加载的 @Component Bean,我们实现了 ComponentBeanLoader:
public class ComponentBeanLoader implements BeanLoader {
@Override
public boolean isLoad(Class<?> clz) {
for (Annotation annotation : clz.getAnnotations()) {
Class<? extends Annotation> annotationType = annotation.annotationType();
if (annotationType.isAnnotationPresent(Component.class) || annotation instanceof Component) {
return true;
}
}
return false;
}
@Override
public BeanDefinition loadBean(Class<?> clz) {
BeanDefinition beanDefinition = BeanLoader.super.loadBean(clz);
Component component = AnnotationUtil.getMergedAnnotation(clz, Component.class);
if (component == null || StringUtil.isEmpty(component.value())) {
return beanDefinition;
}
beanDefinition.setFactoryBeanName(component.value());
return beanDefinition;
}
}
通过实现 BeanLoader,我们可以控制哪些类需要被加载,并将符合条件的类转换为 BeanDefinition。
使用 SPI 实现解耦
在 Spring 中,可以使用服务提供者接口 (SPI) 加载自定义的 BeanLoader,实现解耦和自定义扩展。
BeanDefinitionReader
BeanDefinitionReader 负责加载 BeanLoader,并将 Class 集合转换为 BeanDefinition 集合。
public class BeanDefinitionReader {
@Getter
private final Properties config = new Properties();
private final List<BeanLoader> beanLoaders = new ArrayList<>();
@Getter
private final Set<Class<?>> registerBeanClasses;
public BeanDefinitionReader(String scanPackage) {
registerBeanClasses = ClassUtil.getClassSet(scanPackage);
ServiceLoader<BeanLoader> loaders = ServiceLoader.load(BeanLoader.class);
for (BeanLoader loader : loaders) {
beanLoaders.add(loader);
}
}
public List<BeanDefinition> loadBeanDefinitions() {
List<BeanDefinition> result = new ArrayList<>();
for (Class<?> beanClass : registerBeanClasses) {
for (BeanLoader beanLoader : beanLoaders) {
if (beanLoader.isLoad(beanClass)) {
BeanDefinition beanDefinition = beanLoader.loadBean(beanClass);
result.add(beanDefinition);
}
}
}
return result;
}
public void addBeanLoader(BeanLoader beanLoader) {
beanLoaders.add(beanLoader);
}
}
BeanDefinitionReader 会扫描配置文件中指定的包路径,利用 BeanLoader 加载相应的 BeanDefinition。通过 ServiceLoader 机制,我们可以动态加载 BeanLoader,实现了更灵活的扩展。
AliasFor 注解处理
在 ComponentBeanLoader 中,我们使用 AnnotationUtil.getMergedAnnotation() 处理注解。这是因为直接使用 clz.getAnnotation(Component.class) 无法获取 @Service 等注解标记的 @Component。
以下是 AnnotationUtil 实现:
public abstract class AnnotationUtil {
@SuppressWarnings("unchecked")
public static <A extends Annotation> A getMergedAnnotation(Class<?> element, Class<A> annotationType) {
Annotation annotation = findAnnotation(element, annotationType);
if (annotation == null || annotation.annotationType() == annotationType) {
return (A) annotation;
}
return mergeAttributes(annotation, annotationType);
}
private static <A extends Annotation> Annotation findAnnotation(Class<?> element, Class<A> annotationType) {
A annotation = element.getAnnotation(annotationType);
if (annotation != null) {
return annotation;
}
for (Annotation declaredAnnotation : element.getAnnotations()) {
Annotation metaAnnotation = findAnnotation(declaredAnnotation.annotationType(), annotationType);
if (metaAnnotation != null) {
return declaredAnnotation;
}
}
return null;
}
@SuppressWarnings("unchecked")
private static <A extends Annotation> A mergeAttributes(Annotation annotation, Class<A> annotationType) {
Map<String, Object> attributes = new HashMap<>();
for (Method method : annotation.getClass().getInterfaces()[0].getDeclaredMethods()) {
try {
AliasFor aliasFor = method.getAnnotation(AliasFor.class);
if (aliasFor != null) {
Object value = method.invoke(annotation);
String aliasName = "".equals(aliasFor.value()) ? method.getName() : aliasFor.value();
attributes.put(aliasName, value);
}
} catch (Exception e) {
e.printStackTrace();
}
}
if (attributes.isEmpty()) {
return null;
}
return (A) Proxy.newProxyInstance(
annotationType.getClassLoader(),
new Class<?>[]{annotationType},
(proxy, method, args) -> attributes.get(method.getName())
);
}
}
注解的原理
注解是特殊接口,继承 Annotation,实例是由 JDK 动态代理生成的。AnnotationUtil 使用 getMergedAnnotation 查找注解及其元注解,并合并其属性。