常用注解结构

当前IOC容器设计结构
可以看到分为了注解、核心、包扫描器3部分。
注解部分最基础的bean注解我们实现了Component,3层架构中的注解我们实现了Controller,Service,Repository,这3者均“继承”了Component(其实是在其注解定义中加入了注解@Component).
待补充:Autowired,Qualifier,Scope

BeanDefinition#
核心部分,我们先介绍BeanDefinition,该类作为Bean元数据类,主要包含4个属性:用于反射类型,用于实例化的对象名,是否单例(2个属性用于描述)。
那么为何使用两个属性来表示是否单例?
reply: 无需多余的字符串比较,可直接由其中一个字段的bool值直接得到是否单例。这在判断性能上提升多倍。
1
2
3
4
5
6
7
8
9
| @Data
@NoArgsConstructor
@AllArgsConstructor
public class BeanDefinition {
private Class<?> beanClass; // Bean的Class对象(用于反射创建实例)
private String beanName; // Bean的唯一标识符
private String scope; // 作用域(singleton/prototype)
private boolean singleton; // 是否单例的快速标志位
}
|
两个字段表示是否单例,也带来了新问题:我们设置信息时需要保证二者的一致性
1
2
3
4
5
6
7
8
9
| public void setScope(String scope) {
this.scope = scope;
this.singleton = "singleton".equals(scope); // 自动同步
}
public void setSingleton(boolean singleton) {
this.singleton = singleton;
this.scope = singleton ? "singleton" : "prototype"; // 自动同步
}
|
构造器方面,我们提供2个不同参数的构造器,区别是后者可以传入scope(即是否单例)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| /**
*两参数的构造
*/
public BeanDefinition(Class<?> beanClass, String beanName) {
this.beanClass = beanClass;
this.beanName = beanName;
}
/**
* 三个参数的构造,设置scope,singleton也要设置
*/
public BeanDefinition(Class<?> beanClass, String beanName, String scope) {
this.beanClass = beanClass;
this.beanName = beanName;
this.scope = scope;
this.singleton = "singleton".equals(scope);
}
|
BeanFactory接口与DefaultBeanFactory实现类#
BeanFactory方面,我们实现了一个接口和一个实现类,主要功能包含:注册、获取bean(根据类型或者名称)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
| package cn.amebob.ioc.core;
public interface BeanFactory {
/**
* 注册bean
*/
void registerBeanDefinition(String beanName, BeanDefinition beanDefinition);
/**
* 根据名称获取bean
*/
Object getBean(String beanName);
/**
* 根据类型获取bean
*/
<T> T getBean(Class<T> beanClass);
/**
* 是否包含指定bean
*/
boolean containsBean(String beanName);
/**
* 获取存放bean信息的BeanDefinition
*/
BeanDefinition getBeanDefinition(String beanName);
}
|
说完接口,就要开始说其实现类,这里是DefaultBeanFactory,用于bean的管理,包含3个属性
1
2
3
4
5
6
7
8
| //用map分别存储完全实例化的单例bean,早期暴露的单例bean,bean定义
//这种3级缓存机制,有效避免了循环依赖注入的问题
//1.存储bean定义
private final Map<String,BeanDefinition> beanDefinitionMap = new HashMap<>();
//2.存储完全实例化的单例bean(一种缓存池)
private final Map<String,Object> singletonBeanMap = new HashMap<>();
//3.存储早期暴露的bean
private final Map<String,Object> earlyExposedBeanMap = new HashMap<>();
|
用来返回bean的方法,主要针对单例bean进行缓存查找,非单例则直接创建并返回
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
| /**
* 是单例bean,则先检查缓存,无则创建后返回bean;
* 非单例,则直接创建后返回bean
*/
@Override
public Object getBean(String beanName) {
//1.检查有无bean定义
if(!beanDefinitionMap.containsKey(beanName)){
throw new RuntimeException("No bean definition found for " + beanName);
}
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
//是单例才执行2和3
if(beanDefinition.isSingleton()){
//2.检查单例缓存池
if(singletonBeanMap.containsKey(beanName)){
return singletonBeanMap.get(beanName);
}
//3.检查早期暴露缓存池
if(earlyExposedBeanMap.containsKey(beanName)){
return earlyExposedBeanMap.get(beanName);
}
}
//4.创建bean
Object bean = createBean(beanName,beanDefinition);
//// 如果是非单例Bean,立即完成依赖注入
if(!beanDefinition.isSingleton()){
autowireFields(bean);
}
return bean;
}
|
这里值得一提的是,创建单例bean时采用提前暴露,来杜绝循环注入问题,具体来说就是 ApplicationContext中先统一初始化单例bean,再统一注入单例bean,这分离了实例对象创建和注入,例如Bean A,B已经完成初始化创建,无论哪个先进行注入,都可以成功注入(注入的是半成品)。
在实例化完成后移动该bean到单例缓存池。即在注入完成后会调用promoteBean方法,其中非单例bean,在创建后就进行依赖注入,单例bean则统一进行依赖注入(这发生在所有单例bean对象初始化创建完成后)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| protected Object createBean(String beanName, BeanDefinition beanDefinition) {
// 步骤1:反射创建Bean实例
Object bean = beanDefinition.getBeanClass()
.getDeclaredConstructor()
.newInstance();
// 步骤2:单例Bean提前暴露(关键设计!)
if (beanDefinition.isSingleton()) {
earlySingletonObjects.put(beanName, bean); // 暴露半成品
}
// 步骤3:返回半成品Bean
return bean; // 注意:不放入singletonObjects
}
|
1
2
3
4
| public void promoteBean(String beanName, Object bean) {
earlySingletonObjects.remove(beanName); // 从半成品池移除
singletonObjects.put(beanName, bean); // 放入成品池
}
|
依赖注入时,优先按名注入
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
| private void autowireFields(Object bean){
//1.获取注解信息
Field[] fields = bean.getClass().getDeclaredFields();
for(Field field : fields){
if(field.isAnnotationPresent(Autowired.class)){
try {
field.setAccessible( true); //允许访问其他类的成员变量
//优先按名注入
Object dependentBean = null;
if(field.isAnnotationPresent(Qualifier.class)){
Qualifier qualifier = field.getAnnotation(Qualifier.class);
String beanName = qualifier.value();
dependentBean = getBean(beanName);
}else{
//其次根据类型注入
dependentBean = getBean(field.getType());
}
field.set(bean,dependentBean);
} catch (Exception e) {
Autowired autowired = field.getAnnotation(Autowired.class);
if(autowired.required()){
throw new RuntimeException("依赖注入失败: " + bean.getClass().getName() + "." + field.getName(), e);
}
}
}
}
}
|
ApplicationContext#
下面要说的类呢,ApplicationContext目前是负责单例的初始化(之前,需要先扫描包)和依赖注入。
1
2
3
4
5
6
7
8
9
10
11
12
13
| //1.引入bean工场,对bean进行管理
private final DefaultBeanFactory beanFactory;
public ApplicationContext(String basePackage) {
//a.创建bean工厂
beanFactory = new DefaultBeanFactory();
//b.扫描包,将扫描到的bean定义注册到bean工厂中
scanAndRegisterBean(basePackage);
//c.初始化bean
initSingletonsBean();
//d.注入依赖
autowireBean();
}
|
首先调用ClassScanner的方法扫描包,再调用bean工厂的方法进行bean注册
1
2
3
4
5
6
7
8
9
10
| //2.扫描包,注册bean
private void scanAndRegisterBean(String basePackage) {
//a.扫描包
List<BeanDefinition> beanDefinitions = ClassScanner.scan(basePackage);
//b.注册bean
for(BeanDefinition beanDefinition : beanDefinitions){
beanFactory.registerBeanDefinition(beanDefinition.getBeanName(),beanDefinition);
}
}
|
初始化bean,是初始化单例bean,非单例bean,则采取懒加载的方式,在获取时直接创建并返回;初始化后接着进行依赖注入
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
| //3.初始化单例bean
private void initSingletonsBean() {
Map<String,BeanDefinition> beanDefinitionMap = beanFactory.getBeanDefinitionMap();
for(Map.Entry<String,BeanDefinition> entry : beanDefinitionMap.entrySet()){
//获取键值对Map.Entry<String,BeanDefinition> entry
//返回全部键值对beanDefinitionMap.entrySet()
//增加两个变量提升可读性
String beanName = entry.getKey();
BeanDefinition beanDefinition = entry.getValue();
if(beanDefinition.isSingleton()){
beanFactory.getBean(beanName);
}
}
}
//4.自动注入bean
private void autowireBean() {
//这里也是只处理单例(非单例创建后直接返回即可)
Map<String,Object> earlyExposedBeanMap = beanFactory.getEarlyExposedBeanMap();
Map<String,Object> beansToBuild = new HashMap<>(earlyExposedBeanMap);
for(Map.Entry<String,Object> entry : beansToBuild.entrySet()){
String beanName = entry.getKey();
Object bean = entry.getValue();
//a.注入
autowireFields(bean);
//b.提升到完成的bean,即提升到singletonBeanMap
beanFactory.promoteBean(beanName,bean);
}
}
|
非单例bean,不进行初始化,直接调用bean工厂的getbean方法,该方法会直接返回一个创建好的非单例bean,且在返回前完成依赖注入(注入的依赖是单例的哦,并未能解决多例和多例间的依赖注入)
1
2
3
| public Object getBean(String beanName) {
return beanFactory.getBean(beanName);
}
|
ClassScanner#
这个类用于包扫描,并返回beanDefinition类型的列表(记录包下所以类的信息)
主要涉及,递归遍历文件夹,扫描记录类上的注解(bean名称、Class、Scope),
得到包名
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| /**
* 扫描指定包下的类
*/
public static List<BeanDefinition> scan(String basePackage) {
List<BeanDefinition> beanDefinitions = new ArrayList<>();
try {
String path = basePackage.replace(".", "/");
//URL对象用于文件定位
URL url = Thread.currentThread().getContextClassLoader().getResource(path);
if(url == null) {
System.err.println("未找到包: " + basePackage);
return beanDefinitions;
}
//File 对象用于文件操作
File packageDir = new File(url.getFile());
//递归扫描该文件夹下所有的类
scanClasses(packageDir,basePackage,beanDefinitions);
} catch (Exception e) {
e.printStackTrace();
}
return beanDefinitions;
}
|
得到Class
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
| private static void scanClasses(File packageDir, String packageName, List<BeanDefinition> beanDefinitions) {
//1.检查文件夹是否存在
if(!packageDir.exists() || !packageDir.isDirectory()){
return;
}
//2.获取文件夹下的所有文件
File[] files = packageDir.listFiles();
//3.递归扫描,获取所有类
for(File file : files){
//分别处理目录和类文件,目录递归遍历,类文件进行处理
if(file.isDirectory()){
scanClasses(file,packageName+"."+file.getName(), beanDefinitions);
}else if(file.getName().endsWith(".class")){
//格式化类名(带包格式)
String className = packageName + "." + file.getName().replace(".class", "");
//利用反射机制,检测当前类是否被@Component注解,即是否需要实例化
try {
Class< ?>clazz = Class.forName(className); //获取Class对象
//进行注解解析
BeanDefinition beanDefinition = parseBeanDefinition(clazz);
if(beanDefinition != null){
beanDefinitions.add(beanDefinition);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
}
|
解析注解信息,与ag-0-1j7lvvlo4ag-1-1j7lvvlo4生成默认bean名称
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
| /**
* 解析类文件中的注解信息,生成BeanDefinition
*/
private static BeanDefinition parseBeanDefinition (Class<?> clazz){
//获取注解,无注解返回null,有注解,获取注解上的信息(bean名称,scope ..),
// 如果没有指定beanName,则调用方法生成,获取Scope注解,以生产单例或者多例
String beanName = null;
String scope = "singleton";
//1.获取注解
if(clazz.isAnnotationPresent(Component.class)){
Component component = clazz.getAnnotation(Component.class);
beanName = component.value(); //获取注解上的beanName
}// 检查@Service注解
else if (clazz.isAnnotationPresent(Service.class)) {
Service service = clazz.getAnnotation(Service.class);
beanName = service.value();
}
// 检查@Repository注解
else if (clazz.isAnnotationPresent(Repository.class)) {
Repository repository = clazz.getAnnotation(Repository.class);
beanName = repository.value();
}
// 检查@Controller注解
else if (clazz.isAnnotationPresent(Controller.class)) {
Controller controller = clazz.getAnnotation(Controller.class);
beanName = controller.value();
}else{
return null;
}
//2.没有指定名称,生成默认beanName
if(beanName == null || beanName.isEmpty()){
beanName = getBeanName(clazz);
}
//3.获取Scope注解
if(clazz.isAnnotationPresent(Scope.class)){
Scope scopeAnnotation = clazz.getAnnotation(Scope.class);
scope = scopeAnnotation.value();
}
return new BeanDefinition(clazz,beanName,scope);
}
/**
* 生成Bean的名称,以小写字母开头
*/
private static String getBeanName(Class<?> clazz){
String simpleName =clazz.getSimpleName();
return simpleName.substring(0,1).toLowerCase() + simpleName.substring(1);
}
|