美文网首页
3 IOC容器

3 IOC容器

作者: ca8519be679b | 来源:发表于2020-06-10 13:43 被阅读0次

    知识点

    将要讲解如下

    1 IOC底层原理

    2 IOC接口卡BeanFactory

    3 IOC操作Bean管理(基于xml)

    4 IOC操作Bean管理(基于注解)

    IOC原理和概念

    1什么是IOC?

    控制反转,用于降低你代码的耦合度。说的通俗点,我们原始创建对象是new对象,而IOC是解耦处理,将对象创建和调用过程交给spring管理,我们入门的案例就是IOC实现

    2 IOC底层原理

    底层主要用到3个,xml解析、工厂模式、反射

    1

    还是拿类似入门案例,我们比如有2个类如上图,一个类里调用另一个类,以前是new一个对象,然后调用方法,这种方法耦合度太高了,如果后者类变化,我们就影响很多(比如类路径变化)

    为了降低耦合,我们衍生出了工厂模式,当然这个解耦并不是最终方案,最终是IOC

    2

    如上图,工厂模式是额外创建一个工厂类,这个工厂类负责返回对象,然后我们直接在调用类里使用其静态方法即可,当然这种耦合度并不是完全解耦,其实只是2个类之间通过工厂类连接

    现在IOC过程如下图

    3

    第一步,配置xml文件,创建对象,bean标签,我们写过,id自己起一般相近,class使用类全路径

    第二步,使用工厂类,这里就不能直接返回new对象了,而是使用xml获得class值赋给字符串,就是类名,通过反射获得字节码对象,然后使用newInstance方法,这里结果做了强转,默认的应该是Object

    比如我们的路径有变化,那我们只需要修改xml配置文件,其他的都不用变,仍然可以创建实例,调用方法

    IOC接口

    IOC容器基于IOC思想(工厂模式)

    spring给IOC提供了2个实现方式(接口):

    1.BeanFactory:IOC容器的最基本实现,一般是其内部使用接口,一般不提供给开发人员,前者加载xml时不创建对象,而是到获得实例时创建

    2.ApplicationContext:BeanFactory子接口,比其提供了更多更强大的功能,面向开发人员,加载xml配置时已经创建对象(web开发项目启动时就已经准备好)

    4

    我们之前的代码是使用的后者,换成BeanFactory也能运行,但是一般推荐ApplicationContext

    5

    我们在idea里可以CTRL+H查看继承关系,我们来查看其接口和实现类,可以看到很多,我们重点学习选中的2个,FileSystemXmlApplicationContext,ClassPathXmlApplicationContext

    我们之前的案例使用的是后者,我们可以换成前者,但是前者需要传入系统盘符绝对路径

    6 7

    最后BeanFactory也看下层级关系,除了能看到ApplicationContext外,我们还能看到子接口ConfigurableListableBeanFactory这里简单了解下,其是用于功能扩展的接口

    IOC操作Bean管理

    什么是bean管理?

    通俗就是2个操作,1 spring创建对象2spring注入属性

    关于创建对象,之前讲过了,这里讲注入属性(我们以前定义类,比如设置个private的属性,然后要设置getter和setter,这里spring帮我们做了)

    spring对bean的操作,有2种实现,一种是基于xml解析实现,另一种是通过注解实现

    基于xml配置实现

    基于xml创建对象

    8

    创建对象演示过了,如上,就是xml文件使用bean标签

    这里同时介绍bean标签的常用几个属性

    id属性   起个别名,我们使用能得到标识

    class属性  创建的类所在的全路径

    name属性 和id比较相近,其可以用一些特殊字符,在structs里使用过,版本比较早,现在很少用

    另外spring创建对象默认是执行无参的构造方法

    9 10

    如果我们把构造方法改成有参的,而没写无参的,运行之前的程序就会报错

    基于xml注入属性

    spring里对于注入属性有DI缩写,也叫依赖注入(是IOC的一种具体实现)

    我们以前给属性设置值,就是设置个setter,调用方法就实现属性的设置,或者通过有参的构造方法初始化设置值,然后我们spring也是这么做的

    通过setter设置

    11

    为了加深印象,我们先创建个Book类,然后定义2个属性和setter,为了测试,定义show方法,

    12

    对于xml文件,我们当然还是写标签,但是赋值的部分写在xml配置文件里即可,如上,是在Bean标签里再加上Property标签,里面2个属性name和value,当然不用我说了,一个是我们定义的成员变量名,一个是我们设置的值,这里我们给2个类成员变量都赋值

    13

    这里还是修改测试类文件,我们这里注意了换了新类,对应位置要替换,运行show方法,可以看到属性赋值进去了

    通过有参构造设置

    14

    如上,我们给之前的Book类加上有参构造方法

    15

    这次我们把之前的property标签删掉,是在bean标签内部使用constructor-arg标签,里面还是使用name,value设置值

    16

    运行结果当然还是如我们所想

    17

    其中,constructor-arg标签还可以使用index标签,0,1表示其有参构造的参数顺序,不过一般还是使用name多些,防止写乱顺序

    p名称注入

    也是通过setter注入,其不需要bean内再使用额外标签,但是需要配置p名称空间

    18

    如上,最开始xml的错误果然就出现了,我们定义p名称空间,需要xml约束里添加,我们复制beans那行网址,然后粘贴一行,这里因为不能和之前的重复,左侧我们使用xmlns:p,网址里p替换beans,然而网页还是标红,视频里并没有报错

    19

    我们在file-settings-languages&frameworks->schemas and DTDs 右下的加号,添加忽略的网址,点Ok确定

    20

    确定后就不标红了,这里定义了p名称空间,只要bean标签里给p:属性名赋值即可,如上,

    21

    需要注意的是,这是通过setter方法设置,我们需要把之前定义的有参构造注释了,这个我们了解下就好,一般还是使用内部添加标签多一些

    xml注入其他属性及特殊符号

    字面量,什么是字面量,给属性赋的值就是字面量,我们之前都是给属性赋值,这里专门讲2个特殊情况

    1赋值null

    22

    xml配置文件里,使用property标签,比如我们想给bauthor赋空,如上,需要指定name属性,value属性不写,然后内部嵌套一个null标签

    23

    运行结果如上,可以看到实现了赋null

    24

    null标签除了<null></null>还可以写成如上的单标签,斜杠在右侧

    2赋值特殊符号

    25

    比如我们想给书使用<<>>英文的大小于符号,ide就会报错,为什么,是因为将其识别成了标签,而标签格式不正确,如果就是要使用呢,就需要其他办法

    办法1 转义&lt;   &gt;来替代小于,大于

    26

    如上,记得不要少了分号

    办法2,使用xml的原始内容展示

    27

    代码如上,我们可以把value单独作为property内部标签,里面使用<![CDATA[原始内容]]>的单标签,这个内容不用加引号,效果是一样的

    注入外部Bean

    28

    什么情况下使用呢,比如我们想实例的不是book了,而是User,里面的属性有Book类属性,我们定义方法等待测试调用

    29

    我们知道创建book对象的写法,如上下面的bean标签,创建user也是,这里设置属性,使用property,但是属性value就不用了,因为我们创建了Book对象,这个对象有id,我们只需要把id传给ref属性即可,如上,即完成了给User的book属性赋值,

    30

    最后测试调用方法,如上,这里的关键其实是设置ref属性,然后将id传入,其中记得给User设置setter方法

    注入属性内部Bean和级联赋值

    我们数据库表之间的关系,有时有1对多,如部门和员工,就是就考虑定义部门类Dep,员工类Emp

    31 32

    如上,因为员工只有一个部门,所以我们给其设置部门属性,并定义测试函数

    33

    当然我们要实例的是emp对象,我们可以像之前的使用外部bean,当然,这里是使用内部bean,即property设置value的标签部分我们使用新的bean标签替代,

    34

    最后运行结果如上,很简单

    级联赋值

    级联赋值分2种,第一种就是我们之前使用的外部Bean,不重复说了

    第二种如下

    35

    如上,xml里bean部门写在外边,其属性设置值了,但是我们在员工bean里使用外部引入后,可以在对其属性的name进行设置,这里需要注意的是,我们的Emp类需要给dep属性设置getter,否则会报错,

    36

    如上,设置getter及运行结果,此项相当于给初始的dep.name又进行了赋值

    xml注入集合属性

    比如类有数组、list,set,map集合等,多个元素,也是可以通过xml注入的

    37

    我们定义Stu类,定义属性为以上几种,并定义setter,和待测试的方法,层级关系如上

    38

    然后就是给这些属性赋值了,创建bean标签不用说了,property还是用来赋值的标签,这里多元素数组在property内使用array标签,然后用value标签包裹元素,List,Set分别使用list,set包裹,Map是使用map包裹,但是里面使用的是entry标签,里面的属性使用key,value给键值对赋值,(视频里说list和array是都可以的对于数组,List。。没去试)

    39

    最后就是运行测试了,可以看到集合都被成功赋值了

    集合元素为引用对象注入

    比如我们创建Course类,将其给Stu

    40 41

    2个类修改如上,之前定义的一大堆被精简了

    42

    在xml里,我们还是list内嵌property,但是元素不是使用value包裹,而是使用ref包裹,ref有属性bean,这个值就是我们需要创建bean标签对象的id,于是我们下面写了2个bean标签

    43

    运行结果如上

    xml集合抽取注入

    我们之前使用xml实现了注入集合属性,有时候需要把集合抽取出来,可能供别的bean多次调用,而不是仅类内部调用

    44

    我们对Stu修改,不用Course类,这里仅设置List即可,

    重点是xml进行名称空间设置

    45

    第一处设置,跟我们之前说的p名称空间一样,我们设置util名称空间,

    46

    第二个设置,对xsi:shemaLocation属性值的网址添加网址,就是把上面一行复制过来,带beans,都换成util即可,注意还是在双引号内

    47

    定义了名称空间,我们就能使用util标签,里面有:list可选,设置id属性为了别人调用,里面使用value设置集合,然后还是创建bean标签了,这里property就不用使用value了,使用ref属性,值为前面的id

    48

    运行结果如上


    IOC操作Bean管理

    IOC管理对象一种是通过Bean,我们之前讲过,一种是FactoryBean,注意和BeanFactory不一样,也叫工厂Bean。

    我们的普通Bean,在我们xml里定义了什么,context返回Bean使用什么字节码和id就返回什么类型的对象,而工厂Bean,返回可以不是同一个类型

    使用步骤:创建类实现FactoryBean接口,实现对应方法,返回指定类型

    49

    比如上面我们定义MyBean类,其要求复写3个方法,其实只修改第一个就行(我们暂时只用这个,用来获取对象的),还有获取对象类型和是否为单例,暂时也不关心这2个,第一个方法默认返回null,我们只要让其返回指定类型即可

    50

    我们给其返回Stu实例,为了测试,我们给其设置booklist,

    51

    在xml里,我们需要注入就是MyBean,这没有变,

    52

    但是测试里,我们不能用MyBean来接收了,因为返回类型是Stu,运行结果如上

    IOC管理----Bean的作用域

    spring创建bean默认是单例的,我们可以设置为多实例

    先看单例效果

    53

    我们不使用工厂类,就使用普通Bean,输出可以看到我们就得到一个对象

    我们可以在Bean标签里的scope属性设置值,常用的有1默认值singleton(单实例),2 prototype多实例

    54

    我们设置其为多实例时,可以看到内存地址不一样

    scope的2个属性值还有差别,当默认或者设置singleton时,我们读xml文件得到context对象时已经创建了实例(单例),但是prototype时,其到获得Bean才生成实例

    scope属性还有request和session,是请求和对话,设置后就会把实例放在request和session中,当然很少使用这两个值

    IOC管理----Bean的生命周期

    什么是周期,对象从创建到销毁的过程

    bean的生命周期:1通过构造器创建对象(无参或者constructor-arg有参)2设置属性值和其他bean的引用(使用setter),3 调用Bean的初始化方法(需要配置)  4使用Bean对象 5销毁Bean(也需要配置)

    55

    为了看得更清楚,我们给无参构造写出来,并setter打印,同时定义init,和end方法,如果只是这么写,并看不出会在什么时候调用,

    56

    在xml配置里,我们需要使用init-method属性,值为我们初始化函数名,destroy-method属性值为销毁函数名,

    57

    在测试类里,我们调用当然是第四步了,这里销毁需要我们执行context的close方法,注意这里我们context对象并没有使用多态写法,因为默认接口没有close方法,只有实现类有,所以直接2侧类一样,效果如上

    7步生命周期

    当然以上只是5步演示,其实spring对bean的生命周期控制一个有7步,就是在第3步初始化前后分别有2个后置处理器的配置方法

    58

    我们创建类MyBeanPost,让其继承BeanPostProcessor后置处理器接口,我们复写2个方法(只是加上打印)

    59

    在xml配置里,导入后置处理器到bean标签,然后不用写别的,因为我们继承了后置处理器后就会默认在所有bean的初始化方法前后执行对应方法(不管你写接Bean,每个Bean有初始化都会前后执行)

    60

    运行结果如上,可以看到后置处理器的方法

    XML自动装配

    我们之前给对象注入属性,是使用xml像property标签,使用name,value设置,这是手动装配,而自动装配我们就不需要使用property标签了,而是根据名称或类型自动装配

    我们先展示一个手动装配案例

    61 62 63 64

    如上,我们定义学生类,里面属性Book类,然后打印对象测试,我们之前使用外部Bean,ref属性如上实现,而现在我们就要使用自动装配了,涉及到Bean的autowire属性

    65

    autowire属性值可以是byName,byType,我们使用byName,就会按照名称自动查找,这里下面的bean的id必须和Stu成员变量名一样,否则Spring找不到,如上,运行结果是一样的,可以注入属性

    66

    byType是按类型自动装配,但是这会有问题,当然你会想到多个类型一样的Bean

    67

    比如多个一样类型的Bean,Spring就不知道装哪个了,所以还是通过id,byName准确些

    视频里也说了xml自动装配还是比较少


    XML管理Bean(外部属性文件)

    我们已经用了多次xml配置Bean了,如果Bean的设置property比较多,我们如果改动可能就要回xml里更改比较麻烦,一般把不变的属性放到一个外部的文件中,像数据库账户密码等

    配置Druid连接池

    Druid是数据库连接池,然而我此时还没用过,需要安装jar包,包在它资源里可以找到我们还是复制到lib下,然后file->project-structure里导入包,

    68

    我们直接xml配置,可能要如上图所写,4个属性分别为驱动,数据库地址,用户和密码如上

    现在我们进行外部文件的配置来实现功能,首先创建.properties文件

    69

    里面使用的是=连接的内容,我们把Key=value写上,这里加上prop.是为了防止冲突

    下一步是创建context名称空间,相信util名称空间你已经学会了,这里照猫画虎

    70

    如上,我们给util复制,同时替换为context即可

    71

    接下来我们就动value值,因为我们配置了外部文件,value就可以使用spring引用格式,${}内部使用我们配置文件的key即可

    基于注解方式创建对象

    我们之前已经讲解了基于xml创建对象,现在我们讲解基于注解创建对象

    注解:@注解名称(属性名=属性值,...属性名=属性值)

    我们使用注解可以简化配置

    spring对Bean管理提供了几个内置注解

    @Component

    @Service    一般用于业务逻辑层或者Service层上

    @Controller   一般用于Web层上

    @Repository   一般用于数据访问层

    上面4个注解功能是一样的,都可以创建Bean实例,但是名称使对象创建更有逻辑性

    使用步骤

    1引入依赖

    72

    除了之前的安装jar包,我们需要新的,这个是他们教学资料里的,安装完idea导入下,不重复说了

    2开启spring组件扫描

    首先要确保xml文件里已经引入context名称空间,我们之前的xml里引入过,所以这里以直接用,

    73

    我们给spring5包下新建2个包,如果我们组件扫描在package1中,我们使用context:component-scan标签(使用此标签,spring就知道我们要使用注解来生成对象),属性base-package传入包路径,如上,如果我们想使用多个包呢,比如package1,2都用

    74

    一种方法是如上,使用逗号分割,写入2个包,

    75

    另一种写法是直接使用上级目录,spring会自动扫描

    3 创建类,添加注解

    76

    比如我们在package1里创建类UserService,然后我们就可以类前面加上我们之前说的内置注解,这里给value赋值等同于之前bean标签的id值,这里value赋值是可以省略的,如果不写,自动设置value为类名称首字母小写

    77

    最后就是测试了,可以看到通过注解可以实现创建对象

    4组件扫描细节配置

    我们上节使用组件扫描context:component-scan标签规定扫描文件夹,则此文件夹里所有的类都会被扫描,我们可以细节配置哪些类被扫描到,哪些不被扫描

    78

    如上,我们要看懂如上的xml配置,这里我们还是使用组件扫描标签,里面使用了context:include-filter是包含的过滤器标签,类型是annotation注解,表达式,表示我们只扫描带Controller注解的类

    79

    上图和之前的又不一样,我们使用的是context:exclude-filter,没错,就是扫描不包含Controller注解的类

    5基于注解属性注入

    我们刚才只是对象创建,没有注入属性,这里我们学习使用注解注入属性,需要关系3个注解

    @AutoWired  根据属性类型自动注入

    80

    我们定义Book类,给其加上注解用于创建对象

    81

    创建Stu类,还是使用注解创建对象,这里属性Book我们使用@AutoWired,我们使用注解,就不用setter方法了,spring自动帮我们做

    82

    xml使用组件扫描,

    83

    测试结果如上,可以看到Stu实例的Book被自动创建了

    @Qualifier   根据属性名称进行注入,要和@AutoWired一起使用

    84

    我们知道注解可以设置value,否则是默认类名首字母小写,我们起名book111,

    85

    如上,我们可以使用@Qualifier注解,使用我们刚才设置的名称,有人也许会问,这么做有什么用?比如我们的属性是使用多态写法,那属性名就是接口名,而接口可以有多个实现类,每个实现类可能不一样,我们用哪个实现类就有讲究了,使用@Qualifier给实现类起id,就保证不会使用错

    @Resource   可以根据类型或名称注入

    86

    老师视频里的Java版本是8而我使用9,所以出错了,因为9没有Javax包,其实效果和之前的一样,不使用value,则按类型注入,使用value则按名称注入,视频里也说了Resource不是Spring包里,是扩展包,不推荐使用,反正9也用不了- -

    @Value   注入普通类型(如String)

    87

    比如我们修改Stu类,设置属性name,我们用Value标签设置其属性值,就不用像xml写bean->property标签了

    88

    测试结果如上

    纯注解开发

    我们之前已经演示了注解注入属性生成对象,注解目的是简化xml配置,而现在我们可以考虑使用纯注解(不使用xml配置文件)开发

    使用步骤:1创建配置类

    89

    我们把之前的乱七八糟全删了,建包config,里面创建SpringConfig类,然而不是这么建立就完了,需要添加注解@ComponentConfiguration让Spring知道是配置类

    90

    我们知道原来的基于xml写注解需要写上组件扫描,这里我们使用@ComponentScan,属性使用文件路径

    2 编写测试类

    对于配置注解创建对象和属性设置不用说了,这里测试类会发生变化,因为之前我们使用的是xml文件生成context对象

    91

    我们的待生成对象的User类

    92

    测试类如上,我们之前使用的xml相关的Context,这次使用AnnotationConfigApplicationContext,参数是配置类的字节码,然后其他使用就一样了

    实际使用中,一般是使用完全注解开发多一些

    相关文章

      网友评论

          本文标题:3 IOC容器

          本文链接:https://www.haomeiwen.com/subject/yzabzhtx.html