2

【Spring系列】- Bean生命周期底层原理 - 怒放吧德德

 1 year ago
source link: https://www.cnblogs.com/lyd-code/p/16907518.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Bean生命周期底层原理

😄生命不息,写作不止
🔥 继续踏上学习之路,学之分享笔记
👊 总有一天我也能像各位大佬一样
🏆 一个有梦有戏的人 @怒放吧德德
🌝分享学习心得,欢迎指正,大家一起学习成长!

bean.jpg

前言

上次学到动手模拟Spring底层实现,简单学习了一下Spring,对spring有所了解,接着就来分析spring中bean的生命周期的步步流程。

流程

接下来会根据Bean生命周期一步一步去学习,spring在创建bean对象的过程中,还是做了许多的操作,从依赖注入,通过初始化以及前后操作,最后创建了bean对象放入Map单例池,对于多例是不放进去的。

image.png


本次实验使用的pom依赖坐标如下



<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.3.15</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.3.15</version> </dependency>

依赖注入

首先是根据无参构造方法去获取对象,通过这个类获取所有字段,在来判断是否有Autowired注解,在给这个属性去赋值。



UserService userService1 = new UserService(); for (Field field : userService1.getClass().getDeclaredFields()) { if (field.isAnnotationPresent(Autowired.class)) { field.set(userService1, ???); } }

初始化前执行方法

通过对象获取所有方法,我们在需要执行的方法上使用PostConstruct注解,然后就只需要遍历这些方法,去判断是否含有这个注解,在使用invoke去执行方法。



for (Method method : userService1.getClass().getMethods()) { if (method.isAnnotationPresent(PostConstruct.class)) { method.invoke(userService1, null); } }

初始化

       除了使用PostConstruct注解去执行方法,还有一种方法是通过去实现InitializingBean接口,并且需要实现其未实现的方法afterPropertiesSet。
       那么对象是如何知道在spring中会有afterPropertiesSet这个方法呢?可以通过反射判断是否有这个方法,有的话就直接执行。在spring中,他是采用去判断对象是否有实现InitializingBean这个类,有的话会强转成这个类,再去执行这个类的方法

推断构造方法底层原理

       如果有多个构造方法,回去寻找是否有无参的,找到了,就直接使用,没找到就会报错。如果是使用了多个构造方法,可以使用Autowired去告诉spring需要使用那个构造方法。如果在构造方法里需要一个bean对象,那么spring会去map单例池中去查找相应的bean对象,如果没找到,就会去创建bean对象,但如果是多例bean的话,就不需要查找,直接创建一个对象。
当我们使用构造方法获取bean对象时,一般是通过类上使用Component注解去定义一个首位字母小写的bean对象,也可以是通过Bean注解,去创建不同bean名的相同类型的bean对象。
        如下代码,在配置中添加两个bean对象,包括类上自己生成的一共三个bean对象。分别为{roleServiceroleService1roleService2}。这三个的类型一样,但是对象是不同的,名字不同,bean对象就不同。



@ComponentScan("com.lyd") public class ApplicationConfig {

@Bean public RoleService roleService1() { return new RoleService(); }

@Bean public RoleService roleService2() { return new RoleService(); } }

当使用其中一个beanName都是可以的



@Component public class UserService { private RoleService roleService; public UserService(RoleService roleService1) { this.roleService = roleService1; } public void test(){ System.out.println(roleService); } }

但是如果使用的不是上面三个其中之一,就会报错。但是能看到他找到了三个。

image.png

AOP - 动态代理

使用AOP就是需要使用代理对象,然而代理对象与第一次的对象是不一样的。
首先定义一个aop切面



@Aspect @Component public class AopAspect { @Before("execution(public void com.lyd.service.UserService.test())") public void beanBefore(JoinPoint joinPoint) { System.out.println("before"); } }

在配置类中标上注解 @EnableAspectJAutoProxy 开启切面,这样切面就会实现了。再来debug调用userService的test方法,可以观察到,获得的对象是CGLIB代理的对象

image.png


并且能看到里面的roleService是没有值的。但是从运行结果来看,先走了切面,最后的roleService是有值的。

image.png


然而这都是因为代理类是父子级关系来实现的。接下来一步一步分析。

image.png


       spring通过代理对象,就是为了能够先执行切面方法,在来执行原本对象的方法。首先,spring会生成一个对象:UserServiceProxy,这个就是UserService对象的代理对象。我们用java面向对象思想来思考,代理对象会继承UserService类,他内部重写了父类的test方法,在里面去执行切面的逻辑,接着通过super.test调用父类方法。这个方法虽然是可以实现,但是在spring中却不是这样的。

image.png


在Spring中,代理对象里面还会定义一个父类对象UserService  target,这个对象最终会赋值这个类生成的对象,也就是bean生命周期最开始遇到的那个对象。Spring通过调用target.test()实现。说白了还是使用了最原来的那个对象去执行的方法。

image.png

👍创作不易,如有错误请指正,感谢观看!记得点赞哦!👍

__EOF__


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK