实现IOC
实现ResourceResolver
Java的ClassLoader机制可以在指定的Classpath中根据类名加载指定的Class,但遗憾的是,给出一个包名,例如,org.example,它并不能获取到该包下的所有Class,也不能获取子包。要在Classpath中扫描指定包名下的所有Class,包括子包,实际上是在Classpath中搜索所有文件,找出文件名匹配的.class文件。例如,Classpath中搜索的文件org/example/Hello.class就符合包名org.example,我们需要根据文件路径把它变为org.example.Hello,就相当于获得了类名。因此,搜索Class变成了搜索文件。
提供一个ResourceResolver,定义scan()方法来获取扫描到的Resource:
public class ResourceResolver {
String basePackage;
public ResourceResolver(String basePackage) {
this.basePackage = basePackage;
}
public <R> List<R> scan(Function<Resource, R> mapper) {
...
}
}
过滤Class:
// 定义一个扫描器:
ResourceResolver rr = new ResourceResolver("org.example");
List<String> classList = rr.scan(res -> {
String name = res.name(); // 资源名称"org/example/Hello.class"
if (name.endsWith(".class")) { // 如果以.class结尾
// 把"org/example/Hello.class"变为"org.example.Hello":
return name.substring(0, name.length() - 6).replace("/", ".").replace("\\", ".");
}
// 否则返回null表示不是有效的Class Name:
return null;
});
实现PropertyResolver
Spring框架中,注入分为@Autowired和@Value两种,第一种依赖bean注入,而第二中只是简单
的属性注入,所以,我们可以创建一个类。存放配置的属性,并提供方法来获取属性
Java中提供了按照key查询的Properties,我们可以将其传入
PropertyResolver
public PropertyResolver(Properties props) {
//获取环境变量
this.properties.putAll(System.getenv());
Set<String> names = props.stringPropertyNames();
for (String name : names) {
this.properties.put(name, props.getProperty(name));
}
if (logger.isDebugEnabled()) {
List<String> keys = new ArrayList<>(this.properties.keySet());
Collections.sort(keys);
for (Iterator<String> it = keys.iterator(); it.hasNext();) {
String key = it.next();
logger.debug("PropertyResolver: {} = {}", key, this.properties.get(key));
}
}
converters.put(String.class, s -> s);
converters.put(boolean.class, s -> Boolean.parseBoolean(s));
converters.put(Boolean.class, s -> Boolean.valueOf(s));
converters.put(byte.class, s -> Byte.parseByte(s));
converters.put(Byte.class, s -> Byte.valueOf(s));
converters.put(short.class, s -> Short.parseShort(s));
converters.put(Short.class, s -> Short.valueOf(s));
converters.put(int.class, s -> Integer.parseInt(s));
converters.put(Integer.class, s -> Integer.valueOf(s));
converters.put(long.class, s -> Long.parseLong(s));
converters.put(Long.class, s -> Long.valueOf(s));
converters.put(float.class, s -> Float.parseFloat(s));
converters.put(Float.class, s -> Float.valueOf(s));
converters.put(double.class, s -> Double.parseDouble(s));
converters.put(Double.class, s -> Double.valueOf(s));
converters.put(LocalDate.class, s -> LocalDate.parse(s));
converters.put(LocalTime.class, s -> LocalTime.parse(s));
converters.put(LocalDateTime.class, s -> LocalDateTime.parse(s));
converters.put(ZonedDateTime.class, s -> ZonedDateTime.parse(s));
converters.put(Duration.class, s -> Duration.parse(s));
converters.put(ZoneId.class, s -> ZoneId.of(s));
}
获取键值
解析${abc.xyz:defaultValue}这样的key,我们需要定义一个实体类,传入key和默认值,所以在获取键值(getProperty(String key))的方法中,我们需要判断是否存在默认值(parsePropertyExpr(String key)),如果不存在默认值,则将属性defaultValue置为NULL
转换类型
方法
<T> T convert(Class<?> clazz, String value) {
Function<String, Object> fn = this.converters.get(clazz);
if (fn == null) {
throw new IllegalArgumentException("Unsupported value type: " + clazz.getName());
}
return (T) fn.apply(value);
}
存储
Map<Class<?>, Function<String, Object>> converters = new HashMap<>();
键为要转化的对象,所以我们可以通过Function类型来实现注入类型的转换
传入key的思考
如果key所对应的值也存在占位符的使用,那么应该如何处理:
定义一个函数专门进行处理占位符,如果存在占位符,递归重新获取键值,没有则返回原值
函数调用总览
getProperty(String key)
├── parsePropertyExpr(key)
├── getRequiredProperty(key)
│ └── getProperty(key)
├── parseValue(value)
│ ├── parsePropertyExpr(value)
│ ├── getProperty(key, defaultValue)
│ └── getRequiredProperty(key)
└── 返回结果
getProperty(String key, String defaultValue)
├── getProperty(key)
└── parseValue(defaultValue)
getProperty(String key, Class<T> targetType)
├── getProperty(key)
└── convert(targetType, value)
getRequiredProperty(String key, Class<T> targetType)
├── getProperty(key, targetType)
└── Objects.requireNonNull()
核心:getProperty(String key) 调用后先判断是否是占位符格式,如果是,将其转换成指定的实体 类,再次进行判断:如果有默认值->获取默认值,没有则获取属性值
getProperty(String key, Class<> targetType)和getRequiredProperty(String key, Class<> targetType)是对注入的属性值进行类型转换
创建BeanDefinition
我们可以定义BeanDefinitiin这样一个类来管理Bean的信息,包括:名称,声明类型,构造器方法,工厂方法名称,工厂方法,声明类型,Bean的实例,Bean的顺序,对外提供工厂方法和构造器方法的有参构造
定义好类型以后,我们可以设置一个Map<>类型的变量,按照名字存储Bean信息
public class AnnotationConfigApplicationContext {
Map<String, BeanDefinition> beans;
}
但是如果我们按照名字查找Bean的话,只能返回一个实例或者无实例,使用上存在一定的局限,但如果按照类查询的话,无法定义以class为键的Map类型,因为可能声明类型和实际类型不一定相符,如果不进行标注,可能返回多个实例
@Configuration
public class AppConfig {
@Bean
AtomicInteger counter() {
return new AtomicInteger();
}
@Bean
Number bigInt() {
return new BigInteger("1000000000");
}
}
所以我们需要定义一个方法,根据类型查找出所有满足条件的BeanDefinition,然后再定义一个方法,如果有唯一标识,则返回特定BeanDefinition
// 根据Type查找若干个BeanDefinition,返回0个或多个:
List<BeanDefinition> findBeanDefinitions(Class<?> type) {
return this.beans.values().stream()
// 按类型过滤:
.filter(def -> type.isAssignableFrom(def.getBeanClass()))
// 排序:
.sorted().collect(Collectors.toList());
}
}
// 根据Type查找某个BeanDefinition,如果不存在返回null,如果存在多个返回@Primary标注的一个:
@Nullable
public BeanDefinition findBeanDefinition(Class<?> type) {
List<BeanDefinition> defs = findBeanDefinitions(type);
if (defs.isEmpty()) { // 没有找到任何BeanDefinition
return null;
}
if (defs.size() == 1) { // 找到唯一一个
return defs.get(0);
}
// 多于一个时,查找@Primary:
List<BeanDefinition> primaryDefs = defs.stream().filter(def -> def.isPrimary()).collect(Collectors.toList());
if (primaryDefs.size() == 1) { // @Primary唯一
return primaryDefs.get(0);
}
if (primaryDefs.isEmpty()) { // 不存在@Primary
throw new NoUniqueBeanDefinitionException(String.format("Multiple bean with type '%s' found, but no @Primary specified.", type.getName()));
} else { // @Primary不唯一
throw new NoUniqueBeanDefinitionException(String.format("Multiple bean with type '%s' found, and multiple @Primary specified.", type.getName()));
}
}
所以主要流程:
1. 构造函数调用
↓
2. 扫描配置类及其关联的 Bean 类名
→ 获取 @ComponentScan 指定的包(默认为 configClass 所在包)
→ 使用 ResourceResolver 扫描这些包下的所有 .class 文件 → 提取全限定类名
→ 同时收集 @Import 导入的配置类名
↓
3. 得到一组候选类名 Set<String> classNameSet
↓
4. 遍历每个类名,加载 Class 对象
→ Class.forName(className)
↓
5. 判断该 Class 是否是“组件”(即是否间接或直接标注了 @Component)
→ 通过 ClassUtils.findAnnotation(clazz, Component.class) 递归查找元注解
↓
6. 若是组件:
→ 提取 Bean 名称(默认为首字母小写的类名)
→ 获取合适的构造方法(用于后续实例化)
→ 查找 @Order、@Primary、@PostConstruct、@PreDestroy 等注解信息
→ 创建 BeanDefinition(类型为 clazz,无工厂方法)
→ 注册到 Map<String, BeanDefinition> beans
↓
7. 若该组件同时是 @Configuration(即工厂类):
→ 遍历其所有方法
→ 查找带有 @Bean 注解的方法
→ 提取 Bean 名称(默认为方法名)
→ 声明类型 = 方法返回类型(注意:不一定是实际运行时类型!)
→ 工厂方法 = 该 Method 对象
→ 工厂 Bean 名 = 配置类的 Bean 名(用于后续调用实例方法)
→ 读取 @Bean(initMethod="...", destroyMethod="...")
→ 创建 BeanDefinition(带 factoryName + factoryMethod)
→ 注册到 beans Map
↓
8. 最终得到完整的 Map<String, BeanDefinition> beans
↓
9. (后续步骤,虽未实现但隐含):
→ 按依赖顺序实例化 Bean(先实例化 @Configuration 工厂类)
→ 调用构造方法 或 工厂方法 创建 instance
→ 注入依赖(字段/方法/setter)
→ 调用初始化方法(@PostConstruct / init-method)
创建Bean实例
Spring提供了四种注入方法:构造方法注入,工厂方法注入,Setter注入,字段注入
前两种方法Bean的创建和注入是不可分离的,后两种可以先创建,后注入
因为创建和注入是不可分离的,所以如果遇到循环注入就只能抛出异常
检测循环依赖
if (!this.creatingBeanNames.add(def.getName())) {
throw new UnsatisfiedDependencyException(String.format("Circular dependency detected when create bean '%s'", def.getName()));
}
尝试将当前创建的Bean姓名进行存放,如果已经创建,说明存在循环依赖,抛出异常
逻辑:
createBeanAsSingleton 有三种入口
-
创建@Configuration类型的Bean
-
创建普通的Bean
-
递归执行
第一种方式必然不会循环依赖
第二种方式在执行前会检查instance是否已赋值,所以不会让creatingBeanNames重复
只有第三者情况,即在递归执行createBeanAsSingleton时,才可能存在重复;如果遇到重复的Bean,说明之前的Bean还没创建完成,也就说明有了循环依赖
创建时先创建配置类,再创建普通Bean
核心逻辑:通过递归循环调用依赖创建进行注入,如果构造函数或者工厂方法的参数又进行了依赖
注入,可以再次调用方法创建实例返回,通过反射获取构造函数及其参数,创建实例,完成依赖注
入
private Object createBeanAsEarlySingleton(BeanDefinition def) {
log.debug("Try create bean '{}' as early singleton: {}", def.getName(), def.getBeanClass().getName());
//检测循环依赖
if (!this.creatingBeanNames.add(def.getName())) {
throw new UnsatisfiedDependencyException(String.format("Circular dependency detected when create bean '%s'", def.getName()));
}
//创建方式:构造函数或工厂方法
//Executable:统一处理构造函数和工厂方法
Executable createFn=null;
if (def.getFactoryName()== null){
createFn=def.getConstructor();
}else {
createFn=def.getFactoryMethod();
}
// 创建参数:
final Parameter[] parameters = createFn.getParameters();
final Annotation[][] parametersAnnos = createFn.getParameterAnnotations();
Object[] args = new Object[parameters.length];
for (int i = 0; i < parameters.length; i++) {
final Parameter param = parameters[i];
final Annotation[] paramAnnos = parametersAnnos[i];
final Value value = ClassUtils.getAnnotation(paramAnnos, Value.class);
final Autowired autowired = ClassUtils.getAnnotation(paramAnnos, Autowired.class);
// @Configuration类型的Bean是工厂,不允许使用@Autowired创建:
final boolean isConfiguration = isConfigurationDefinition(def);
if (isConfiguration && autowired != null) {
throw new BeanCreationException(
String.format("Cannot specify @Autowired when create @Configuration bean '%s': %s.", def.getName(), def.getBeanClass().getName()));
}
// 参数需要@Value或@Autowired两者之一:
if (value != null && autowired != null) {
throw new BeanCreationException(
String.format("Cannot specify both @Autowired and @Value when create bean '%s': %s.", def.getName(), def.getBeanClass().getName()));
}
if (value == null && autowired == null) {
throw new BeanCreationException(
String.format("Must specify @Autowired or @Value when create bean '%s': %s.", def.getName(), def.getBeanClass().getName()));
}
// 参数类型:
final Class<?> type = param.getType();
if (value != null) {
// 参数是@Value:
args[i] = this.propertyResolver.getRequiredProperty(value.value(), type);
} else {
// 参数是@Autowired:
String name = autowired.name();
boolean required = autowired.value();
// 依赖的BeanDefinition:
BeanDefinition dependsOnDef = name.isEmpty() ? findBeanDefinition(type) : findBeanDefinition(name, type);
// 检测required==true?
if (required && dependsOnDef == null) {
throw new BeanCreationException(String.format("Missing autowired bean with type '%s' when create bean '%s': %s.", type.getName(),
def.getName(), def.getBeanClass().getName()));
}
if (dependsOnDef != null) {
// 获取依赖Bean:
Object autowiredBeanInstance = dependsOnDef.getInstance();
if (autowiredBeanInstance == null && !isConfiguration) {
// 当前依赖Bean尚未初始化,递归调用初始化该依赖Bean:
autowiredBeanInstance = createBeanAsEarlySingleton(dependsOnDef);
}
args[i] = autowiredBeanInstance;
} else {
args[i] = null;
}
}
}
//创建Bean实例
Object instance=null;
//构造函数创建
if(def.getFactoryName()== null){
try {
//注入过程:创建Bean实例
instance = def.getConstructor().newInstance(args);
}catch (Exception e){
throw new BeanCreationException(e);
}
}else {
// 获取配置类实例:通过 getBean(def.getFactoryName()) 获取定义了 @Bean 方法的配置类实例
// 调用工厂方法:使用 def.getFactoryMethod().invoke(configInstance, args) 调用 @Bean 注解的方法来创建 Bean 实例
// 异常处理:如果调用过程中发生异常,包装成 BeanCreationException 抛出
// 设置实例:通过 def.setInstance(instance) 将创建好的实例设置到 BeanDefinition 中
// 返回实例:最终返回创建的 Bean 实例
// 用@Bean方法创建:
Object configInstance = getBean(def.getFactoryName());
try {
instance = def.getFactoryMethod().invoke(configInstance, args);
} catch (Exception e) {
throw new BeanCreationException(String.format("Exception when create bean '%s': %s", def.getName(), def.getBeanClass().getName()), e);
}
}
//设置实例
def.setInstance(instance);
return def.getInstance();
}
初始化Bean
在创建Bean过程中,我们实现了强依赖注入,接下来实现字段和Setter注入
使用Setter方法和字段注入时,要注意一点,就是不仅要在当前类查找,还要在父类查找,因为有些@Autowired写在父类,所有子类都可使用,这样更方便。注入弱依赖代码如下:
// 在当前类及父类进行字段和方法注入:
void injectProperties(BeanDefinition def, Class<?> clazz, Object bean) {
// 在当前类查找Field和Method并注入:
for (Field f : clazz.getDeclaredFields()) {
tryInjectProperties(def, clazz, bean, f);
}
for (Method m : clazz.getDeclaredMethods()) {
tryInjectProperties(def, clazz, bean, m);
}
// 在父类查找Field和Method并注入:
Class<?> superClazz = clazz.getSuperclass();
if (superClazz != null) {
// 递归调用:
injectProperties(def, superClazz, bean);
}
}
// 注入单个属性
void tryInjectProperties(BeanDefinition def, Class<?> clazz, Object bean, AccessibleObject acc) {
...
}
注入完依赖之后,在对BeanDifinition进行遍历,执行init方法
实现BeanPostProcessor
BeanPostProcessor的出现改变了这一切。Spring允许用户自定义一种特殊的Bean,即实现了BeanPostProcessor接口,它有什么用呢?其实就是替换Bean。
例子:
@Configuration
@ComponentScan
public class AppConfig {
public static void main(String[] args) {
var ctx = new AnnotationConfigApplicationContext(AppConfig.class);
// 可以获取到ZonedDateTime:
ZonedDateTime dt = ctx.getBean(ZonedDateTime.class);
System.out.println(dt);
// 错误:NoSuchBeanDefinitionException:
System.out.println(ctx.getBean(LocalDateTime.class));
}
// 创建LocalDateTime实例
@Bean
public LocalDateTime localDateTime() {
return LocalDateTime.now();
}
// 实现一个BeanPostProcessor
@Bean
BeanPostProcessor replaceLocalDateTime() {
return new BeanPostProcessor() {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
// 将LocalDateTime类型实例替换为ZonedDateTime类型实例:
if (bean instanceof LocalDateTime) {
return ZonedDateTime.now();
}
return bean;
}
};
}
}
定义一个代理类继承原始Bean,通过BeanPostProcessor的postProcessBeforeInitialization()
方法将原始Bean替换为代理类,并将BeanDenifition中对应的实现类进行替换
但是由此引发了一个问题,当我们想对这个Bean注入依赖的时候,是注入到原始Bean还是代理Bean上呢,举个例子
@Configuration
@ComponentScan
public class AppConfig {
public static void main(String[] args) {
var ctx = new AnnotationConfigApplicationContext(AppConfig.class);
UserService u = ctx.getBean(UserService.class);
System.out.println(u.getClass().getSimpleName()); // UserServiceProxy
u.register("bob@example.com", "bob12345");
}
@Bean
BeanPostProcessor createProxy() {
return new BeanPostProcessor() {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
// 实现事务功能:
if (bean instanceof UserService u) {
return new UserServiceProxy(u);
}
return bean;
}
};
}
}
@Component
class UserService {
public void register(String email, String password) {
System.out.println("INSERT INTO ...");
}
}
// 代理类:
class UserServiceProxy extends UserService {
UserService target;
public UserServiceProxy(UserService target) {
this.target = target;
}
@Override
public void register(String email, String password) {
System.out.println("begin tx");
target.register(email, password);
System.out.println("commit tx");
}
}
我们创建的代理类实现了事务功能,但是方法还是调用的原始Bean,也就是说必须对原始Bean进行
注入,否则在调用原始方法时会进行报错
两条原则:
- 一个Bean如果被Proxy替换,则依赖它的Bean应注入Proxy,即上图的
MvcController应注入UserServiceProxy; - 一个Bean如果被Proxy替换,如果要注入依赖,则应该注入到原始对象,即上图的
JdbcTemplate应注入到原始的UserService。
基于这个原则,要满足条件1是很容易的,因为只要创建Bean完成后,立刻调用BeanPostProcessor就实现了替换,后续其他Bean引用的肯定就是Proxy了。先改造创建Bean的流程,在创建@Configuration后,接着创建BeanPostProcessor,再创建其他普通Bean:
// 创建BeanPostProcessor类型的Bean:
List<BeanPostProcessor> processors = this.beans.values().stream()
// 过滤出BeanPostProcessor:
.filter(this::isBeanPostProcessorDefinition)
// 排序:
.sorted()
// 创建BeanPostProcessor实例:
.map(def -> {
return (BeanPostProcessor) createBeanAsEarlySingleton(def);
}).collect(Collectors.toList());
this.beanPostProcessors.addAll(processors);
替换实例
public Object createBeanAsEarlySingleton(BeanDefinition def) {
...
// 创建Bean实例:
Object instance = ...;
def.setInstance(instance);
// 调用BeanPostProcessor处理Bean:
for (BeanPostProcessor processor : beanPostProcessors) {
Object processed = processor.postProcessBeforeInitialization(def.getInstance(), def.getName());
// 如果一个BeanPostProcessor替换了原始Bean,则更新Bean的引用:
if (def.getInstance() != processed) {
def.setInstance(processed);
}
}
return def.getInstance();
}
这时,对这个Bean进行依赖注入会有问题,因为注入的是Proxy而不是原始Bean,怎么办?
这时我们要思考原始Bean去哪了?原始Bean实际上是被BeanPostProcessor给丢了!如果BeanPostProcessor能保存原始Bean,那么,注入前先找到原始Bean,就可以把依赖正确地注入给原始Bean。我们给BeanPostProcessor加一个postProcessOnSetProperty()方法,让它返回原始Bean:
我们可以重写BeanPostProcessor中的 postProcessOnSetProperty()方法,定义一个Map集合保留原始Bean,并返回原始Bean
例如:
// 在AroundProxyBeanPostProcessor中的实现:
public class AroundProxyBeanPostProcessor implements BeanPostProcessor {
Map<String, Object> originBeans = new HashMap<>(); // 保存了原始Bean的引用
@Override
public Object postProcessOnSetProperty(Object bean, String beanName) {
Object origin = this.originBeans.get(beanName); // 查找原始Bean
return origin != null ? origin : bean; // 返回原始Bean或保持当前Bean
}
}