首页天道酬勤,

,

张世龙 05-04 21:00 8次浏览

最近刚通过视频学习了Spring的基础原理,为了便于理解记忆,按照代码整理笔记是见解片面的,希望大家一起交流,有错误的地方指出来,一起进步。 (学习地址:图灵学院自由心灵的钥匙老师) ) ) ) )。

1.Spring是什么

2.Spring Bean的创建周期

3.IOC

4.AOP

1.Spring是什么

首先,谈谈什么是Spring。 我相信网上也有很多Spring的定义。 Spring是J2EE轻量级的开源框架,他是用于放入我们javaBaen对象的容器框架,是中间层框架,它起到一个连接的作用。 例如,可以将不同的框架粘合起来使用,使我们的企业开发更快。

2.SpringBean的创建周期

首先,我们知道,以前创建对象时,我们需要去新对象,如图所示

但是,使用Spring框架时,不需要用这种方法注入所需的对象。 只要对需要注入的类添加@Component注释,就可以在其他类中利用@Autowired注释实现对象的注入。

众所周知,我们创建新对象时是通过构建类的方法创建的。 例如,如果我要创建新的new对象,就需要如何构建他。 orderserviceorderservice=new order service (实际上,Spring也不例外,它通过类的构建方法创建和使用对象。 但是,它在Spring内部执行逻辑处理(源代码中的doCreateBean方法)。

在这里发现了几个问题。 当一个类有很多构建方法时,我们可以在新建一个对象时指定如何构建他。 如图所示:

这是我们在new对象的时候,可以手动指定我的结构方法,但是Spring怎么判断应该使用哪个结构方法? 在此,引入了估计结构方法这一概念。 Spring扫描@Compoent注释,以确定当前类是Bean,并确定是否有缺省的生成方法。 如果没有指定其他生成方法,Spring将基于缺省生成方法创建Bean;如果指定了特定的生成方法,Spring可能还不知道如何通过扫描注释来指定此构造方法还是用@Autowired评论。 当然,如果类没有缺省的生成方法,并且存在许多其他生成方法,并且未指定,则会报告错误,并且Spring不知道该如何创建Bean。

下图显示了具有默认构建方法时的打印结果。

但是,当指定生成方法时,Spring会基于指定的生成方法创建Bean对象,

如果没有缺省的生成方法,并且有多种未指定的生成方法,Spring将报告错误

向包含@Component的类添加@Autowired注释时,Spring为我们创建了Bean对象(例如,UserService的Bean对象),然后Spring将命令序列在UserService的Bean对象中创建了OrderService对象,然后在调试过程中可以看到该OrderService中有一个值。 这就是我们常说的依赖注入。

如上所述,我们通过无参与的构建方法创建了USerService的Bean对象。 我们这个对象按理说应该没什么,但当我们在调试程序中看到时,我们发现orderService确实有价值。 这就是依赖注入,Spring通过依赖注入是我们用户服务中的顺序注入

Spring如何确定类是否包含@Autowired注释? 很多人认为反射是可能的,但必须通过反射来判断是否包含在该类中,如果包含,则进行依赖注入

到此为止,实际上Spring可以总结创建bean的步骤

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

但是,这并不完全。 因为还有很多步骤,包括初始化前、初始化、初始化后、有无Aop、是否是代理对象等,都没有涉及。 所以稍微全面的是下面的步骤,正如我之前说的,接下来是初始化前,初始化和初始化后。

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

t;初始化前---->初始化---->初始化后---->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名字(name),Bean对象(type)>,对应的就是: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

 

iaware,getannotation方法