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
查找注解及其元注解,并合并其属性。