Spring框架——初学IOC容器

Spring框架——初学IOC容器,第1张

Spring框架——初学IOC容器 注:作者本人也是初学者,所以本文有些总结性见解可能存在问题,但是多数问题都是在上网查询过资料后总结的,如果有逻辑或者原理上的错误,或者见解不同,欢迎在评论区讨论!!!

目录:

IOC

1.什么是IOC

1.1什么是控制反转

1.2什么是耦合性

1.2.1有关耦合性在开发中常见的问题

2.IOC底层原理

2.1使用工厂模式解耦

2.2使用IOC容器解耦

3.IOC(接口)

IOC的几个体系 

IOC具体 *** 作:Bean管理

  1.什么是Bean管理

  2.Bean管理 *** 作又两种方式

1)基于xml方式创建对象

2)基于xml方式注入属性

2.1)第一种注入方式:使用set方法注入属性

2.2)第二种注入方式:通过含参构造方法注入属性

2.3)第三种注入方式:p名称空间注入(set方法注入优化)

tips1:注入空值和特殊符号

        注入属性-外部bean

        注入属性-内部注入

        注入属性-级联赋值

2.4)注入集合属性

        细节问题1:集合存储的是一个对象

        细节问题2:把集合注入部分提取出来,而不是普通的标签内置

2.5)自动装配

2.6)引入其他配置文件(分模块开发)

Bean补充1:   FactoryBean

Bean补充2:   Bean作用域

Bean补充3:  Bean的生命周期

        注:关于后置处理器

3)使用注解进行配置

1. 获取类对象(在实现类上配置)

2.在字段上注入属性


IOC

IOC底层原理

IOC接口(BeanFactory)

IOC *** 作Bean管理(基于xml)

IOC *** 作Bean管理(基于注释)

1.什么是IOC

(1)控制反转,把对象创建和对象之间的调用过程,交给Spring进行管理

(2)使用IOC目的:为了耦合度降低

(3)做入门案例就是IOC的实现

1.1什么是控制反转

                (1)控制反转并不是什么技术,而是一种设计思想,就是将原本在程序中手动创建对象的控制权,交由Spring框架(第三方)来管理。

                (2)正控制:需要使用某个对象时,需要自己去负责对象的创建

                (3)反控制:需要使用某个对象时,只需要从Spring容器中获取需要使用的对象,不关心对象的创建过程,将创建对象的控制权反转给Spring框架。

                (4)那这样需要对象的时候就不需要面向其他对象,而是找IOC去要就行了,具体有没有不管我们事(这样就把开发的各个领域区分开来)。负责开发各个领域的人就不需要去找其他类获取对象,各个领域的职能被分割开了。

1.2什么是耦合性

        耦合性(英语:Coupling,dependency,或称耦合力或耦合度)是一种软件度量,是指一程序中,模块及模块之间信息或参数依赖的程度。

        内聚性是一个和耦合性相对的概念,一般而言低耦合性代表高内聚性,反之亦然。耦合性和内聚性都是由提出结构化设计概念的赖瑞·康斯坦丁所提出[1]。低耦合性是结构良好程序的特性,低耦合性程序的可读性及可维护性会比较好。

        例子:两个类分别为学生类和计算器类,学生类中有计算方法,其方法体中创建了计算器类并调用了他的计算方法,这样学生类和计算机类中耦合度就太高了。当计算机类的位置改变时,学生类调用计算机类的位置也要跟着改变,当计算器类方法名发生改变时,学生类调用的计算器类方法名也要跟着改变。

1.2.1有关耦合性在开发中常见的问题

         如果我们在开发中,负责后端的人员需要完成Service层的编写,而队友负责UserDao实现类的编写,我们在编写Service层实现类的时候不可避免的会调用UserDao类方法,因此就不可避免的需要创建对象,导入对象所在包。而如果UserDao相应的实现类还没有编写,我们负责Service层的人员无法创建对象,因此就无法继续向下编写,这样的代码结构耦合性极高。这里就诞生了IOC容器,Service把创建对象交给IOC容器,而UserDao把写好的对象配置给IOC,这样编写不同层代码的人就无需关注对方的进度,只需要关注自己的进度即可。

        

2.IOC底层原理

xml解析,工厂模式,反射(反射的目的:通过得到类的字节码文件,可以 *** 作类中的所有内容)

2.1使用工厂模式解耦

        在我们学习完javaweb我们都知道开发时所用到的三层架构,即负责处理请求信息的web层、处理业务的service层和处理数据的dao层。其中Service层需要调用dao层的一个或者多个数据处理方法,这样就不可避免的需要创建dao对象,导入dao包,那么在开发中如果负责编写dao层的人还没有完成代码,那么我们也就无法获取dao对象。

        这时,我们就可以将创建对象的权利交给“别人”,也就是工厂类。

        工厂类最开始无需把真正带有对象的返回值返回回去,只需要返回null(毕竟负责编写dao层的人还没有编写好,既然没有编写好,那么怎么用不存在的dao接口实现类创建对象?),那么编译器就不会报错,service层的人就拿到了dao对象(其实是null),调用接口中已经约束好功能的方法就可以完成service层的逻辑实现。当dao层的人写好代码开始整合的时候,我们再把Factory加工的类方法返回值全部改成实际返回值,这样一来大大加快了开发速度。

                                        Service ——>Factory——>Dao

 

        上述说法多少有些抽象,所以我们来举个生活中很常见的例子吧:

        手机,我们日常中最常见的东西。一部手机有很多部分组成:

        其中芯片的设计需要一个公司负责完成,比如华为海斯的麒麟芯片设计,美国高通的骁龙芯片设计,他们负责的是芯片设计职能,具体怎么做他们是不需要会的,他们只需要按照光刻机厂商提供的芯片制作规格去设计芯片即可。

        而芯片的制作也无需考虑芯片如何设计,他们需要做的就是提高光刻机的精度,如何让光刻机可以制作7nm,5nm甚至4nm芯片,而这些芯片结构如何设计不管他们事。

        芯片设计厂和芯片加工厂之间是各司其职,他们只需要在双方规定的协议之下做他们做的事即可,无需等你突破4nm制作工艺我再去设计4nm芯片,也无需等你会设计4nm芯片我再去突破4nm的制作工艺。这个协议就是负责两方交互的“Factory类”,他将两个本是一个职能的芯片服务分裂为多个,每个方面各司其职。

        但是这样我们发现一个问题:虽然Service和Dao各司其职了,但是Factorty与Dao层的耦合性提高了,Factory需要根据Dao进行反复修改,于是就有了后面的IOC容器来进一步降低耦合性。

2.2使用IOC容器解耦

        IOC容器解耦实际上是工厂类的再升级版本,因为他底层任然使用了工厂类进行相关 *** 作

        1.首先我们把需要创建类的路径都配置在一个文件中

        2.在spring框架下,底层调用方法去读取配置文件中的每个地址,并利用Class进行对象的反射 *** 作,创建对象,并将其安置在工厂类中

        3.需要创建对象的时候,从IOC容器内部的工厂类去取即可

        这样这个配置文件就成为了缓解耦合性的载体,编写程序的时我们也可以给配置文件直接留白,等到所有成员负责的领域都完工后,完善配置文件。

3.IOC(接口)

IOC思想基于IOC容器完成,IOC容器底层就是对象工厂

Spring提供IOC容器实现两种方式:(两个接口)

1). BeanFactory:IOC容器基本实现方式,是Spring内部的使用接口,不提供开发人员进行使用 —— 加载配置文件时不会创建对象,在获取对象(使用)才去创建对象

2). ApplicationContext:BeanFactory接口的子接口,提供更多更强大的功能,一般由开发人员进行使用——加载配置文件时,就会把 你在配置文件中配置的对象 进行创建(可以结合tomcat使用,在服务器的init方法中,服务器启动时,就自动加载该配置文件)

 ApplicationContext的两个后代实现类

FileSystemXmlApplicationContext类是通过盘符路径加载配置文件

ClassPathXmlApplicationContext类是通过类路径加载配置文件

IOC的几个体系 

    Resource体系:对资源的抽象,它的每一个实现类都代表了一种资源访问的策略。有了策略那就有加载资源的方法,Spring利用ResourceLoader来进行统一资源加载。

    BeanFactory体系:BeanFactory是一个非常纯粹的bean容器,它是IOC必备的数据结构,其中BeanDefinition是他的基本结构,他内部维护着一个BeanDefinition map,并且可以根据BeanDefinition的描述进行bean的创建和管理。

    BeanDefinition体系:BeanDefinition用来描述Spring中的Bean对象。

    BeanDefinitionReader体系:BeanDefinitionReader的作用是读取Spring的配置文件的内容,并将其转换成ioc容器内部数据结构:BeanDefinition。

    ApplicationContext体系:这也就是传说中的Spring容器,它叫做应用上下文,与我们应用息息相关,他继承BeanFactory,所以他是BeanFactory的拓展升级版:

    继承MessageSource,提供国际化的标准访问策略

    继承ApplicationEvenPublisher,提供强大的事件机制

    扩展ResourceLoader,可以用来加载多个Resource,可以灵活访问不同的资源

    对Web应用的支持。

IOC具体 *** 作:Bean管理 1.什么是Bean管理

Bean管理指的是两个 *** 作

Spring创建对象

Spring注入属性

2.Bean管理 *** 作又两种方式

基于xml配置文件方式实现

基于注解方式实现

1)基于xml方式创建对象

    在spring配置文件中,使用bean标签,标签里面添加对应属性,就可以实现对象创建

    在bean标签有很多属性,介绍常用的属性

    id属性:唯一标识

    class属性:类全路径(包类路径)

    创建对象时,默认也是使用对象的无参构造法(当不含无参构造法时会报错)

2)基于xml方式注入属性

    DI:依赖注入,就是注入属性

    注入时寻找参数:

    name属性:通过构造参数名称找到对应参数

    type属性:通过元素类型注入(同类型字段注入按照注入值顺序注入),type可以省略

    index属性:通过参数下标找到对应参数

    value属性:参数值--只包括基本数据类型和String类型

    ref属性:在bean容器中查找id,将bean容器中的值注入到当前参数

2.1)第一种注入方式:使用set方法注入属性

设置类,设置属性以及set方法

public class Student {
    private String name;
    private int age;
    private long id;
​
    public void setName(String name) {
        this.name = name;
    }
​
    public void setAge(int age) {
        this.age = age;
    }
​
    public void setId(long id) {
        this.id = id;
    }
    
    public void show(){
        System.out.println(name+"  "+age+"  "+ id);
    }
}

在配置文件中bean标签内使用property标签进行设置(name和value)


    
    
    

最后在测试类中通过IOC容器提供的ApplicationContext接口加载配置文件,获取加载器对象,最后通过getBean方法获取对象

public class Demo {
    @Test
    public void DemoStudent(){
        //获取配置文件
        ApplicationContext context = new ClassPathXmlApplicationContext("springUserBean.xml");
        //获取通过加载器对象获得学生对象
        Student st = context.getBean("student",Student.class);
        //使用学生类中的show方法查看属性值是否改变
        st.show();
    }
}
2.2)第二种注入方式:通过含参构造方法注入属性

创建类,定义属性,创建属性对应有参数构造方法

public class Book {
    private String name;
    private double price;
​
    public Book(String name, double price) {
        this.name = name;
        this.price = price;
    }
​
    public void show(){
        System.out.println(name+"  "+price);
    }
}

在配置文件中进行文件配置这里在bean标签中使用constructor-arg标签

这里name的设置即可以根据属性名设置值,也可以根据他在构造器中的形参排序顺序设置值


    
    

最终在测试类中测试代码运行结果

public class Demo02 {
    @Test
    public void TestBook(){
        ApplicationContext context = new ClassPathXmlApplicationContext("springUserBean.xml");
        Book book = context.getBean("book", Book.class);
        book.show();
    }
}
2.3)第三种注入方式:p名称空间注入(set方法注入优化)

第一步:修改xml文件配置(将第一行复制,键值后加:p,获取一个名称为p的命名空间,然后在后面的路径中把beans修改为p)


    
       

第二步:配置文件对象(使用bean标签创建对象,使用p:属性 = 值注入属性。)

注意:该方法只能对使用set注入的JavaBean对象生效,使用含参构造器的JavaBean对象无法使用

tips1:注入空值和特殊符号

在设置属性的时候不设置value值,而是在标签体内加上标签


    
    
        
    

设置value值时有特殊符号,这时采用xml特有的CDATA语句

首先把value值单独拿到property内部作为一个标签 ,标签内写上,注意value内不要随意加回车,如果添加了,那么回车也会被当做值的一部分


    
        ]]>
    
    
注入属性-外部bean

注:之前的注入方法都是注入java已有类,例如String,而对于其他自定义的外部类注入方法我们还不曾知晓,所以此处讲解外部类的注入

创建两个类service类和dao类

在service类中调用dao里面的方法

在spring的配置文件中配置


    
    

在外部注入中,两个bean是各自各家的两个JavaBean对象配置,只是后者通过id从外部引用了前者,这就是外部注入,外部可以理解为“非包含”。

注入属性-内部注入

和外部注入相似

不同点:主要用于处理一对多的关系,标签中配置属性时就配置其他对象


    
        
    
注入属性-级联赋值

其实就是在一个类A中如果有另外一个类a作为自己的属性,那么在配置本类A时,引入的类a就已经给自己的字段附上值,这就是级联赋值。

级联赋值方法

外部注入时,把将要引入的id对应的配置对象设置好字段值,然后在引入,这样在对象创建后,字段对象就已经被初始化,而非一个没设置值的对象。



    
    

内部注入时,把中新定义的配置文件字段对象赋好值,这样在对象创建后,对象的字段就已经被初始化,而非一个没设置值的对象。


    
        
        
    

外部注入时,不初始化将要注入的内容,而是直接赋值




    
    
    
    
    

注意:这里通过引用来的dept(第一行配置的id),需要直接设置字段,那么就需要拥有他的对象,所以这里需要在源文件中配置好他的get方法。

2.4)注入集合属性

数组配置:配置好对象后配置数组属性,value值用array标签包裹

    
    
        
            张三
            李四
        
    

list集合配置:配置好对象后配置list集合属性,value值用list标签包裹

        
        
            
                张三
                李四
            
        

set集合配置:配置好对象后配置set集合属性,value值用set标签包裹

        
        
            
                张三
                李四
            
        

map集合配置:配置好对象后配置map集合属性,与其他三个不一样,map集合改用entry标签,放在map标签内部,key和value值分别在内部赋值

    
    
        
            
            
            
        
    
properties属性集配置:配置好对象后配置props属性,与其他四个集合不一样,properties标签改用props标签,在props标签内部依次使用prop标签进行键值对配置,key通过属性赋值,而value在标签之间赋值
 

    
        
            张三
            李四
            19
        
    

细节问题1:集合存储的是一个对象

其他的一切按照原list集合配置方式配置,但是当写到value标签时,改用ref标签,将外部的对象id写到ref标签的bean属性中。


        
    
​
    
        
    
​
    
        
            
                
                
            
        
    

细节问题2:把集合注入部分提取出来,而不是普通的标签内置

1.首先配置xml引入的命名空间


2.然后时配置list对象,整体思路和外部注入类似,首先定义两个user,留出id,定义list时导入id,然后定义对象,把list集合id导入对象属性中。这里糅合了“集合注入自定义的对象”和“外部注入”两个地方


    


    




    
    




    

2.5)自动装配

    什么是自动装配

    根据指定装配规则(属性名称或者属性类型),Spring自动将匹配的属性值进行注入

    演示自动装配过程

    第一种:根据属性名称自动注入

    
    
    

    第二种:根据类型注入

    
    
    
2.6)引入其他配置文件(分模块开发)

        Spring的配置内容较多,体积较大,可以将Spring根据业务或者模块进行拆分,而在Spring的主配置文件(ApplicationContext对象中传入的文件)中通过import标签进行加载。

Bean补充1:   FactoryBean

Spring有两种类型的bean,一种是普通bean,另外一种是工厂bean(FactoryBean)

普通bean:在配置文件中定义bean类型就是返回类型

以上学习的都是普通bean创建方式

工厂bean:在配置文件中定于bean类型可以和返回类型不一样

定义一个Java类,实现FactoryBean接口,重写三个方法,其中getObject方法用于确定该类真正的返回值:

public class MyBean implements FactoryBean {
    
    @Override
    public User getObject() throws Exception {
        User user = new User();
        user.setName("张三");
        return user;
    }
​
    @Override
    public Class getObjectType() {
        return null;
    }
​
    @Override
    public boolean isSingleton() {
        return false;
    }
}

xml文件正常书写即可:

Bean补充2:   Bean作用域

在Spring里面,设置创建bean实例是单实例还是多实例

在Spring里面,默认情况下,bean是单实例对象,即返回的对象都是同一个对象(地址相同)

如何设置单实例还是多实例

    在spring配置文件bean标签里面有属性(scope),用于设置单实例还是多实例

    scope属性值

    第一个值 默认值,singleton,表示是单实例对象

    第二个值 prototype,表示是多实例对象,返回的对象地址也就不相同

singleton和prototype区别

    singleton单实例,prototype多实例

    scope设置为singleton时,加载spring配置文件时候就会创建单实例对象

    scope设置为prototype时,不是在加载spring配置文件的时候创建对象,而是在调用getBean方法时创建多实例对象

Bean补充3:  Bean的生命周期

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

    根据配置文件调用对象的无参构造器,创建对象

    根据配置文件调用对象的set方法设置对象属性

    执行后置处理器BeanPostProcessor接口中的postProcessBeforeInitialization前置初始化方法(会对所有配置文件实施)

    执行自己配置的初始化方法,此处在xml需要配置init-method

    执行后置处理器BeanPostProcessor接口中的postProcessAfterInitialization后置初始化方法(会对所有配置文件实施)

    通过ApplicationContext对对象进行 *** 作

    通过ClassPathXmlApplicationContext的close方法关闭容器,执行自己配置的销毁方法,此处在xml需要配置destory-method

注:关于后置处理器

    执行顺序:构造方法—>BeanPostProcessor的before—>init-method—>BeanPostProcessor的after。

    实际运用中,可以配置多个BeanFactoryPostProcessor和BeanPostProcessor

    应用场景

    解析bean的注解,将注解中的字段转化为属性

    统一将属性在执行前,注入bean中,如数据库访问的sqlMap,如严重服务,这样不需要每个bean都配置属性

    打印日志,记录时间等。

 

3)使用注解进行配置 1. 获取类对象(在实现类上配置)

作用在类上,相当于xml配制中添加id,并将该对象放在Bean容器中

    @Component:在类上用于实例化Bean

    @Controller:使用在web层类上用于实例化Bean

    @Service:使用在service层类上用于实例化Bean

    @Repository:使用在dao层类上用于实例化Bean

步骤:

1.配置xml注解:使用注解开发,需要在核心配置文件中,指定那个包下的Bean需要进行扫描以识别使用注解配置的类和字段。



​
    

2.在实现类上使用注解

@Component("userDao")
public class UserDaoImpl implements UserDao {}

3.加载配置文件,从IOC容器中获取对象

public static void main(String[] args) {
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("anno.xml");
    UserDao userDao = applicationContext.getBean("userDao", UserDao.class);
    System.out.println(userDao);
}
2.在字段上注入属性

    @Autowired使用在字段上用于根据类型依赖注入:相当于xml标签中的autowire属性,用于注入属性

    使用多态的形式创建对象时,当该字段是自己写的类或接口,且该类的子类或者接口的实现类只有一个被注解过,那么系统就会直接查找到被注解的类,将其注入到其中

    使用多态的形式创建对象时,当该字段是自己写的类或接口,且该类的子类或者接口的实现类有多个被注解,那么系统就会查找到所有该接口下被注解的类,根据属性名称比对注解设置的id,并将其注入到bean对象中

    @Qualifier配置,结合@Autowired一起用于根据名称进行依赖注入,当遇到多个类注解且id与属性没有一个相同时,使用@Qualifier注解,来指定获取哪个类对象

    @Resource,相当于@Autowired和@Qualifier结合,但是此处注解的value属性不等于name属性(name属性并不是最重要的属性),所以需要对name属性进行赋值:@Resource(name = "") ,按名称(id)进行依赖注入,相当于xml标签中的ref属性,用于注入属性

    @Scope,作用在类上,标注Bean的作用范围,相当于xml中的scope属性(指定单例singleton还是多例prototype)

    @PostConstruct:标注初始化方法,相当于xml中的init-method属性,用于定义初始化方法,在配置好对象之后执行

    @PreDestory:标注销毁方法,相当于xml中的destroy-method属性,用于定义销毁方法,在ApplicationContext *** 作完对象后执行

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5706720.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存