栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

浅读Spring源码,说说我对Bean的生命周期,Aop,Ioc理解(学习笔记)

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

浅读Spring源码,说说我对Bean的生命周期,Aop,Ioc理解(学习笔记)

最近刚通过视频学习了Spring的一些底层原理,为了方便理解记忆,配合代码作为整理笔记,见解比较片面,希望和大家一起交流,有错误的地方希望大家指正,一起进步。( 学习地址:图灵学院周瑜老师)

1.Spring是什么

2.Spring Bean的创建周期

3.IOC

4.AOP

1.Spring是什么

首先谈一下Spring是什么,相信网上也有很多的对Spring的定义,Spring是一个J2EE的轻量级的开源的框架,他是一个容器框架,用来装我们的javaBaen对象,是一个中间层框架,它可以起到一个连接的作用,比如说可以把不同的框架粘合在一起进行使用,可以让我们的企业开发更快速,更简洁,所以也有人称它为万能胶。

2.SpringBean的创建周期

首先我们都知道,在以往创建对象的时候,我们是需要去new一个新的对象,就像图中一样

但是在我们使用Spring框架的时候是不需要这种方式来注入我们需要的对象的,我们只需要在需要被注入的类上加上@Component注解,就可以在其他类里边利用@Autowired注解实现对象的注入。

 众所周知我们创建一个新对象的时候是通过类的构造方法进行创建的,比如我新new一个对象的话就需要他的构造方法,OrderService orderService = new OrderService();其实Spring也不例外,也是通过类的构造方法去创建一个对象来给你使用,只不过在Spring的内部做了一些逻辑处理(源码中doCreateBean方法)。

这里我们就发现了一些问题,当一个类里边有很多个构造方法时,我们new一个对象的时候是可以指定他的构造方法的,如图所示:

 这是我们new对象的时候,可以手动的去指定我的构造方法,可是Spring又是怎么去判断他应该去使用哪个构造方法呢?这里就引入了一个概念,叫做推断构造方法。Spring会扫描@Compoent注解,判断你当前的类是一个Bean,然后去判断有没有默认的构造方法,如果有,在没有指定其他构造方法时,Spring就会根据默认构造方法去创建一个Bean,如果你指定了某个特定的构造方法,那么Spring也会通过扫描注解的形式去判断你是否进行了指定,如果指定了就会使用你指定的构造方法去创建Bean对像。可能有很多人还不知道怎么去指定这个构造方法,其实很简单还是使用@Autowired注解。当然你如果类里边没有默认的构造方法,又存在很多其他的构造方法,并且没有指定的话,就会报错,Spring就不知道该用哪个构造方法来给你创建Bean。

 这就是有默认构造方法的时候  打印结果见下图:

 

但是当我们指定了一个构造方法后,Spring就会根据我们指定的构造方法创建Bean对象、

 

 当我们没有默认构造方法,但是却有多个未指定的构造方法时,Spring就会报错

当我们在一个含有@Component的类里边加入@Autowired注解时,当Spring为我们创建了一个Bean对象之后,比如创建了UserService的Bean对象之后,Spring扫描到OrderSerivce的@Autowired注解后,就会在UserService的Bean对象里边创建一个OrderService对象,此时我们去debug就会发现这个orderService里边时有值的了。这就是我们常说的依赖注入。

上边也说到了我们通过无参的构造方法来创建了USerService的Bean对象,那么我们这个对象按道理来说应该什么都没有的,但是在我们debug看的时候发现,orderService确实是有值的,这就是依赖注入,Spring通过依赖注入的方式使得我们UserService里边的orderService有属性值,这就是Bean对象区别去普通对象的一个点。

Spring又是怎么判断类里边是否含有@Autowired注解的呢,相信很多人都能想到就是反射 ,用反射来判断这个类里边是否含有这个注解,有的话就要进行依赖注入

 到这里之后其实我们就能总结出来一个Spring创建bean的步骤

UserService---->推断构造方法---->普通对象---->依赖注入---->Bean

但是这并不全面,因为还有很多的步骤我们没有说到和涉及到,包括初始化前,初始化,初始化后,有没有Aop,是不是代理对象等。所以稍微全面一点的就是下边的步骤,我们说过了前面,接下来就是初始化前,初始化和初始化后。

UserService---->推断构造方法---->普通对象---->依赖注入---->初始化前---->初始化---->初始化后---->Bean

首先我们来看一个场景:我在新建一个User类,同时把他定义为一个Bean对象。

 然后我在UserService里边定义一个User的对象,把他命名为admin

UserSerivce是一个用户管理服务层,每一个系统里边都会有一个管理员,现在我想在Spring启动的时候admin里边就直接有值了,该怎么操作呢?通过上边我们说的,肯定很多人都说加一个@Autowired就可以了,这个当然是可以的,但是我现在的要求是要里边直接有值,这个值是存储在数据库里边真实的值(Id、用户名、密码、权限)。那这个该怎么操作呢?

首先数据是从数据库里边拿到的,我们肯定要自己写一个方法,去从数据库里边把数据拿到,然后封装成一个User类再把值给admin。

 现在这个getAdmin方法,就帮我们从数据取值,封装,赋值一系列操作都完成了,但是我们怎么能让Spring在启动时自动就去运行这个方法呢?其实只要Spring在初始化前可以自动调用我的getAdmin方法的话,我这个需求就可以实现了

UserService---->推断构造方法---->普通对象---->依赖注入---->初始化前(getAdmin)---->初始化---->初始化后---->Bean

让Spring在初始化前自动执行这个方法,其实也很简单只需要一个注解就搞定了

 但是Spring又是怎么来判断哪个类的哪个方法里边有这个注解呢,其实就和Spring判断@Autowired注解是一样的方法,利用反射拿到所有的方法,然后判断方法上是否含有该注解。(可以自行参考上边的代码)(getClass().getMethod)

UserService---->推断构造方法---->普通对象---->依赖注入---->初始化前(@PostConstruct)---->初始化---->初始化后---->Bean

这是初始化前Spring要做的一个操作,说完初始化前就说说初始化,其实初始化和初始化前很相似,同样是admin这个需求,我们换一种方式来实现,直接上代码。

 InitializingBean是Spring提供的一个接口方法。在初始化的过程中,Spring就会去判断你这个对象是否实现了这个接口,如果实现了,Spring会自动来调用你重写的这个afterPropertiesSet()方法。那么怎么判断是否实现了这个接口呢:对象  instance of  InitializingBean  (instance of  关键字)

然后Spring会把这个对象强制转换为InitializingBean对象,然后调用afterPropertiesSet()方法:

(InitializingBean(对象)).afterPropertiesSet();

Spring源码:

 

 这就是初始化所作的工作

UserService---->推断构造方法---->普通对象---->依赖注入---->初始化前(@PostConstruct)---->初始化(InitializingBean)---->初始化后---->Bean

接下来就是初始化后(Aop就是在这个阶段完成的):我们都知道Aop底层其实就是动态代理,既然是代理就会生成代理对象所以这个步骤就又变成了

UserService---->推断构造方法---->普通对象---->依赖注入---->初始化前(@PostConstruct)---->初始化---->初始化后(Aop)---->代理对象---->Bean

总体而言其实Bean对象全部都被存在Map里边  map,对应的就是:Bean名字:orderService,Bean对象:OrderService。这里就会出现一个情况,一个OrderService可能对应了很多个orderService、orderService1...,那么Spring又是怎么来区分这些orderService、orderService1...并把他注入到我们想注入的位置呢,这里就有一个  “先bytype后byname”  也就是先根据Bean对像来确定Map里边多少个value为OrderService的键值对,然后再去判断你要找的orderService与Bean名字做比较。从而为你的程序注入你想要注入的依赖。

 3.IOC 控制反转

 这个概念是我从别的博主那里截图来的,配合上边我们讲过的应该就会变得很容易理解了。

 4.Aop(面向切面编程)
系统是由许多不同的组件所组成的,每一个组件各负责一块特定功能。除了实现自身核心功能之外,这些组件还经常承担着额外的职责。例如日志、事务管理和安全这样的核心服务经常融入到自身具有核心业务逻辑的组件中去。这些系统服务经常被称为横切关注点。因为它们会跨越系统的多个组件。
当我们需要为分散的对象引入公共行为的时候,OOP则显得无能为力。也就是说,OOP允许你定义从上到下的关系,但并不适合定义从左到右的关系。例如日志功能。
日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系。在OOP设计中。它导致了大量代码的重复。而不利于各个模块的重用。
AOP:将程序中的交叉业务逻辑(比如安全,日志,事务等),封装成一个切面,然后注入到目标对象(具体业务逻辑)中去。AOP可以对某个对象或某些对象的功能进行增强,比如对象中的方法进行增强,可以在执行某个方法之前额外的做一些事情,在某个方法执行之后额外的做一些事情
 

在上边也说过了,初始化后的时候进行Aop,那么Spring怎么进行aop

 我们写一个类,然后通过@EnableAspectJAutoProxy注解,将aop打开,这个时候getAdmin方法就会被切到。这个时候我们得到的UserService的Bean对象,就是一个代理对象。(图片取自学习课程)

 

 这个时候的UserService里的OrderService里边是否有值呢,讲道理来说刚才说的这个OrderService应该就是有值,但是其实他是没有值的。(图片取自学习课程) 

那为什么会没有值呢,我们刚才不是说依赖注入了肯定有值啊,要是你看懂了应该就会明白了。aop得到的是一个代理对象。而我们做的依赖注入是注入普通对象,并没有在针对代理对象进行依赖注入。这么说可能有点难以理解,看一下下边的步骤就明白了。

UserService---->推断构造方法---->普通对象---->依赖注入---->初始化前(@PostConstruct)---->初始化(InitializingBean)---->初始化后(Aop)---->代理对象---->Bean

 那Spring是怎么进行的aop呢?

运用父子类(动态代理)

class UserServiceProxy extends UserService(){

//重写父类的方法getAdmin

public void getAdmin(){

//先执行切面的逻辑

//被代理的方法

//这里需要考虑的就是怎么去实现被代理的方法,因为是父类的方法 所以我们都知道super关键字

//是可以满足需求的(super.getAdmin)

//你是不是觉得很简单,但是真的可以这样吗,如果在getAdmin方法里调用了orderService呢就像代码中这样。

 //很明显,这是不行的,因为我们上边已经知道,代理对象UserService里的orderService是空值

//是null,那你在这里调用,就会报一个异常(空指针异常)

//我们可以看到Spring运行完他是有值的,所以Spring是怎么做的呢

}

}

public class UserServiceProxy extends UserService(){



//Spring是这样来解决这个问题的
private UserService target;


//重写父类的方法
public void getAdmin(){

//执行切面的逻辑
//执行父类的方法
userService.getAdmin;

    }


}



//代理对象:new UserServiceProxy ---> target(普通对象)---> Bean

因为在生成普通对象的时候orderService是有值的吗,所以Spring的处理方式就是引入这个对象,然后通过这个对象去调用他的方法,就完美的解决了这个问题。

UserService---->推断构造方法---->普通对象(target)---->依赖注入---->初始化前(@PostConstruct)---->初始化(InitializingBean)---->初始化后(Aop)---->代理对象---->Bean

 

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/389596.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号