手写Spring框架Day2

ylnxwlp 菲比啾比!

进入第二天了,今天要学的东西就比较深入一些了。

第一部分 依赖注入

在昨天我们已经实现了bean的创建和管理。既然创建了bean,那就需要使用,也就是当一个bean要使用到别的bean,就得把这个bean给他放入需要bean的类中吧。

怎么实现呢?首先肯定想到怎么标识需要注入的地方,用过Spring的都知道有一个@Autowired注解。那我们就把他创建出来。

1
2
3
4
5
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Autowired {
String value() default "";
}

我们再创建一个新的OrderService,把他也注册成bean。然后,在原先的UserService中去注入这个OrderService的bean。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Component
public class OrderService {
}

@Component
public class UserService {

@Autowired
private OrderService orderService;

public void test(){
System.out.println(orderService);
}
}

好了,现在标识已经做好了。那不能只有表面功夫吧,加个注解就完事;肯定要为他去实现一个依赖注入的功能。要在哪里实现呢?

想想昨天写过的东西,构造函数?似乎只是用来扫描和把创建出来的bean加入单例池;getBean?更不对了,只是用来获取bean的,都不知道要注入到哪里……

对了!createBean可太适合不过了!在昨天的代码里,这里面可是把每一个bean进行实例化的地方,那实例化之后,跟着把里面的内容一起给他放进去,不是妙哉?

所以,我们先获取实例化的实例所有内部字段,再一个个遍历判断是否有加上注解,如果加上了@Autowired,那就进行注入就好了。

那怎么注入呢?首先需要拿到注入的bean实例,getBean方法就有用了,把字段的名字传给getBean,这样就拿到了需要注入的一个实例,我们写好的getBean会帮我们解决单多例的问题。
然后就可以通过反射把这个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
private Object createBean(String beanName, BeanDefinition beanDefinition) {
Class clazz = beanDefinition.getType();

try {
Object instance = clazz.getConstructor().newInstance();
//依赖注入
for (Field field : clazz.getDeclaredFields()) {
if(field.isAnnotationPresent(Autowired.class)){
//允许修改
field.setAccessible(true);
//将这个bean实例注入需要注入的实例里
field.set(instance,getBean(field.getName()));
}
}
return instance;
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}

这样我们就实现bean的DI了!用测试方法看看bean的打印情况:

1
2
3
4
5
6
7
8
9
public class Test {
public static void main(String[] args) {

SwissApplicationContext swissApplicationContext = new SwissApplicationContext(AppConfig.class);

UserService userService = (UserService) swissApplicationContext.getBean("userService");
userService.test();
}
}
1
com.swiss.service.OrderService@6f94fa3e

依赖注入就实现了。

第二部分 Aware回调

在有的时候,我们在bean实例化和依赖注入后,想要得到一些注册出的bean内容,比如我并不知道bean名字在Spring容器中的默认命名规则,但我想要知道当前这个bean在IOC中以什么名字存储,这个时候就需要一个回调,我们可以根据这个回调,拿到bean的一些有关信息,这就是Aware回调。

这里我们模拟一个bean名字获取的回调,创建回调接口:

1
2
3
4
public interface BeanNameAware {

void setBeanName(String beanName);
}

然后我们就可以去实现这个接口,得到beanName。注意,这个回调函数是在Spring中自动被调用的,也就是Spring会自动把bean名字传进来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Component
public class UserService implements BeanNameAware {

@Autowired
private OrderService orderService;

private String beanName;

@Override
public void setBeanName(String beanName) {
this.beanName = beanName;
}

public void test(){
System.out.println(orderService);
}
}

这样我们通过beanName的Aware回调,拿到了想要的bean的名字。

当然实际上还有好多回调:BeanClassLoaderAware,BeanFactoryAware,ApplicationContextAware……但是都和这个原理相同。

第三部分 初始化回调

在bean处理好了之后,我们想要对其进行一些预先的操作,比如给一些东西赋值啊之类的,这个时候就是属于初始化的内容了。

Spring为初始化回调提供了一个接口,和上面的Aware一样,我们需要自己去实现,然后进行自己想要的操作。注意,这里面你想做什么,Spring都不会管,也不会传来任何参数,完全是你自己的逻辑。

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
public interface InitializingBean {
void afterPropertiesSet();
}

@Component
public class UserService implements BeanNameAware, InitializingBean {

@Autowired
private OrderService orderService;

private String beanName;

private String INeed;

@Override
public void setBeanName(String beanName) {
this.beanName = beanName;
}

@Override
public void afterPropertiesSet() {
//很多很多想要在初始化执行的都可以写在这里
this.INeed = "Mika";
}

public void test(){
System.out.println(orderService);
}
}

比如在这里,我为我的一个成员变量赋了一个值,我还可以输出,存储……干什么都可以。这就是初始化的回调作用。

第四部分 BeanPostProcessor Bean的后置处理器

为了更好、更灵活的管理bean对象,Spring还在初始化的前后提供了处理的方法。BeanPostProcessor接口就是用来让插入在初始化前后的一些想要执行的逻辑。

1
2
3
4
5
6
public interface BeanPostProcessor {

void postProcessBeforeInitialization(String beanName, Object bean);

void postProcessAfterInitialization(String beanName, Object bean);
}

这两个方法分别在初始化前、后调用。

我们可以试着实现以下这两个方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Component
public class SwissBeanPostProcessor implements BeanPostProcessor {
@Override
public void postProcessBeforeInitialization(String beanName, Object bean) {
if(beanName.equals("userService")){
System.out.println("1111");
}
}

@Override
public void postProcessAfterInitialization(String beanName, Object bean) {
if(beanName.equals("userService")){
System.out.println("2222");
}
}
}

我们实现的逻辑是:bean的名字是userService的时候,在初始化前打印一个1111,在初始化后打印一个2222。

这些调用在我们写好逻辑后,Spring会帮我们自动在初始化前后调用。那Spring是怎么知道有哪些后置处理器的?

这个需要在Spring里面进行处理。在注册完所有BeanDefinition后,专门处理BeanPostProcessor的创建,使其被初始化成一个成熟的bean,然后加入List进行管理。

在处理其他正常bean的时候,遇到BeanPostProcessor子类就跳过即可。

提示!

但是!这里为了简单方便,我们在构造函数里面扫描的时候就直接创建BeanPostProcessor实例!实际上不应该是这样,会导致这样创建出来的实例没有被DI,是个残缺的bean,所以下面这一段代码仅供参考。

1
2
3
4
5
6
7
8
9
if (clazz.isAnnotationPresent(Component.class)) {
//说明这是一个bean,开始创建beanDefinition对象

//先把BeanPostProcessor的实现子类存起来
if (BeanPostProcessor.class.isAssignableFrom(clazz)) {
BeanPostProcessor instance = (BeanPostProcessor) clazz.newInstance();
beanPostProcessorList.add(instance);
}
……

然后,在初始化前后,遍历bean后置处理器的List,执行定义的方法即可。

1
2
3
4
5
6
7
8
9
10
11
12
for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
instance = beanPostProcessor.postProcessBeforeInitialization(beanName,instance);
}

//初始化
if (instance instanceof InitializingBean) {
((InitializingBean) instance).afterPropertiesSet();
}

for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
instance = beanPostProcessor.postProcessAfterInitialization(beanName,instance);
}

我们试着运行一下:

1
2
3
1111
Mika(afterPropertiesSet,初始化方法打印)
2222

确实是在初始化的前后插入了逻辑。

这就是bean的后置处理器BeanPostProcessor。在Spring里面实现了很多BeanPostProcessor,如下面的AOP。当然我们也可以自己实现BeanPostProcessor,执行想要的逻辑。

第五部分 AOP

在上一个部分中,讲到了Bean的后置处理器,可以在Bean被初始化的前后执行一些逻辑。

但我们知道,我们有时候可能对于某个bean需要增强,如AOP的切面。这个时候,我们就需要代理对象来实现增强的内容,而不是原生的bean实例。

在Spring中,有一个AnnotationAwareAspectJAutoProxyCreator,会解析@Aspect里面的内容,根据匹配的切点表达式得到是哪个bean,然后进行动态代理创建,构造一个拦截器链,按顺序执行前置通知、目标方法、后置通知等等。
但我们这里同样是一个模拟,简单的处理。

首先我们需要把上一部分的postProcessBeforeInitialization和postProcessAfterInitialization返回值改为Object。因为需要返回一个代理对象了。

然后在postProcessAfterInitialization里面,也就是初始化完成后,我们将进行动态代理的创建。这里为userService增强,创建一个JDK动态代理,首先需要创建一个interface(JDK只支持接口代理):

1
2
3
public interface UserInterface {
void test();
}

然后我们让UserService去实现这个接口,就可以为UserService创建动态代理了。

1
2
3
4
5
6
7
8
9
10
11
12
13
@Override
public Object postProcessAfterInitialization(String beanName, Object bean) {
if (beanName.equals("userService")) {
return Proxy.newProxyInstance(SwissApplicationContext.class.getClassLoader(), bean.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("切面逻辑");
return method.invoke(bean, args);
}
});
}
return bean;
}

可以看到,这里userService的bean已经将变成代理对象返回。那我们尝试使用getBean得到的bean去进行原先的test方法:

1
2
切面逻辑
com.swiss.service.OrderService@355da254

成功了!增强逻辑被实现了,也就是我们的AOP动态代理增强实现了。

第二天的学习内容结束了!

  • 标题: 手写Spring框架Day2
  • 作者: ylnxwlp
  • 创建于 : 2025-10-02 11:56:20
  • 更新于 : 2025-10-03 01:13:19
  • 链接: https://www.swissroll.today/2025/10/02/手写Spring框架Day2/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论