springIOC 概述理解

什么是Spring?

In a word:Spring是一个轻量级的控制反转(IoC-Inversion of Control)

                                               面向切面(AOP-Aspect Oriented Programming)容器(框架)。

   特点
(1)方便解耦,简化开发
  (2)Aop 编程支持
    (3)方便程序测试
      (4)方便和其他框架进行整合
         (5)方便进行事务操作
           (6)降低 API 开发

1. 体系结构

 1.1 体系结构图

Spring框架至今已集成了20多个模块,这些模块分布在以下模块中:

  • 核心容器(Core Container)
  • 数据访问/集成(Data Access/Integration)层
  • Web层
  • AOP(Aspect Oriented Programming)模块
  • 植入(Instrumentation)模块
  • 消息传输(Messaging)
  • 测试(Test)模块

Spring 5 的模块结构图1.1

1.2 核心容器(Core Container)

Spring的核心容器是其他模块建立的基础,有Spring-core、Spring-beans、Spring-context、Spring-context-support和Spring-expression(String表达式语言)等模块组成。

  • Spring-core模块:提供了框架的基本组成部分,包括控制反转(Inversion of Control,IoC)和依赖注入(Dependency Injection,DI)功能。
  • Spring-beans模块:提供了BeanFactory,是工厂模式的一个经典实现,Spring将管理对象称为Bean它移除了编码式单例的需要,并且可以把配置和依赖从实际编码逻辑中解耦。
  • Spring-context模块:建立在Core和Beans模块的基础之上,提供一个框架式的对象访问方式它以一种类似于 JNDI 注册的方式访问对象。是访问定义和配置的任何对象的媒介。ApplicationContext接口是Context模块的焦点。
  • Spring-context-support模块:支持整合第三方库到Spring应用程序上下文,特别是用于高速缓存(EhCache、JCache)和任务调度(CommonJ、Quartz)、邮件(JavaMail)、调度(CommonJ, Quartz)、模板引擎(FreeMarker, JasperReports, Velocity)的支持。
  • Spring-expression模块:提供了强大的表达式语言去支持运行时查询和操作对象图。这是对JSP2.1规范中规定的统一表达式语言(Unified EL)的扩展。该语言支持set和get属性值、属性分配、方法调用、访问数组、集合和索引器的内容、逻辑和算术运算、变量命名以及通过Spring的IOC容器中以名称检索对象。它还支持列表投影、选择以及常用的列表聚合。

依赖图

                                                                         依赖图1.2.1

 

                                                                 依赖图1.2.2                 

        

                                                             实例依赖图1.2.3

注:其中spring框架不知道aop是注解方式中必须加的一个依赖

 1.3 Spring 配置文件

--》applicationContext.xml 

1.2.1 控制反转和依赖注入(IOC-DI) 

控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。DL

依赖注入(DI),是指程序运行过程中,如果需要调用另一个对象协助时,无须在代码中创建被调用者,而是依赖于外部的注入。Spring的依赖注入对调用者和被调用者几乎没有任何要求,完全支持对POJO之间依赖关系的管理。

  • 依赖 : 指Bean对象的创建依赖于Spring容器
  • 注入 : 指Bean对象的属性(例如pojo类对象属性、其它需要依赖的Bean对象) , 由容器来设置装配.

传统Java SE程序设计,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象

IOC是由专门一个容器来创建这些对象,即由Ioc容器来控制对象的创建,程序被动接受对象,这就叫控制反转:将对象的创建权反转给(交给)Spring

在Spring中,我们可以把IOC容器与Spring容器等同于一个东西,及IOC容器=Spring容器,我们也可以把Spring容器看做配置文件xxx.xml来简单记忆

容器:一个Java 所编写的程序,可以管理对象的生命周期、对象与对象之间的依赖关系,在启动容器之后,所有的对象都可以直接取用,可以直接产生对象,或是建立对象与对象之间的依赖关系(不用编写任何程序代码)。

(注::框架:是开发程序的一部分,没有框架是必须存在的。)

  • IOC -- Inverse of Control,   控制反转,将对象的创建权反转给Spring!!
  • DI -- Dependency Injection,依赖注入,在Spring框架负责创建Bean对象时,动态的将依赖对象注入到Bean组件中!!IoC的技术实现

DI和IOC的关系: DI不能单独存在,DI需要在IOC的基础上来完成. 

这样做得好处做到了单一职责,并且提高了复用性,解耦了之后,任你如何实现,使用接口的引用调用的方法,永远不需要改变

 解耦

 以下是一个使用xml配置文件实现IOC的图例:


1.2.2 DI:给属性赋值

spring调用类的无参构造方法,创建对象。对象创建后给属性赋值。

给属性赋值可以使用

1)xml配置文件中的标签和属性;

2)使用注解

DI分类 :1 set注入,也叫设值注入;

                2 构造注入。

package com.bjpowernode;

import com.bjpowernode.service.SomeService;
import com.bjpowernode.service.impl.SomeServiceImpl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AppMain {
    public static void main(String[] args) {
//        SomeService service = new SomeServiceImpl();
//        service.doSome();
        //1.指定spring配置文件:从类路径(classpath)之下开始的路径
        String config="beans.xml";
        //2.创建容器对象。 ApplicationContext 表示spring容器对象。通过ctx来获取某个java对象
        ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
        //这是个接口 然后通过这个实现类ClassPathXmlApplicationContext的构造方法中负责读这个配置文件 当遇到bean标签后 就创建对象 从这个类路径下寻找这个内容
        //3.从容器中获取指定名称的对象 使用getBean方法 //又因为是SomeService接口类型的对象所以还要转一下
        SomeService service = (SomeService) ctx.getBean("someService");
        //拿到这个someservice对象然后用其方法
        //4.调用对象的方法,接口的方法
        service.doSome();

    }
}
package com.bjpowernode.service;

public interface SomeService {
    void doSome();
}

package com.bjpowernode.service;

public class OtherService {
    public void doOther(){
        System.out.println("执行otherService的doOther()");
    }
}

resources下配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--    声明对象
       id:自定义对象名称,唯一值。(可以没有,Spring可提供默认名称)
       class:类的全限名称:spring 通过反射机制创建对象,不能是接口

       spring根据id,class创建对象,把对象放入到spring的一个map对象
       map.put(id,对象)   map(key,value)
       -->
    <bean id="someService" class="com.bjpowernode.service.impl.SomeServiceImpl"></bean>
    <bean id="someService1" class="com.bjpowernode.service.impl.SomeServiceImpl"></bean>

    <bean id="otherService" class="com.bjpowernode.service.OtherService"></bean>
    <!--创建非自定义对象 -->
    <bean id="mydate" class="java.util.Date"></bean>

</beans>

测试文件 

package com.bjpowernode;

import com.bjpowernode.service.OtherService;
import com.bjpowernode.service.SomeService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.util.Date;


public class Mytest {
    /**
     * spring创建对象,调用的是类的哪个方法?
     *     答:默认是调用了无参数构造方法 。
     */
    @Test
    public void test01(){
        //1.先指定配置文件路径
        String config="beans.xml";
        //2.创建容器对象
        //ApplicationContext是个接口  用他的这个ClassPathXmlApplicationContext这个实现类  从类路径中来读取加载配置文件
        ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
//        SomeService service = ctx.getBean(SomeService.class);
//        service.doSome();
        SomeService service=(SomeService)ctx.getBean("someService");
        service.doSome();
    }




    /**
     *  spring在什么时候创建对象?
     *   答:创建spring容器对象的时候创建的,会读取配置文件,创建文件中声明的java对象。
     *    优点:获取对象速度快,因为对象已经创建好了
     *    缺点:占用内存
     */
    @Test
    public void test02(){
        //1.先指定配置文件路径
        String config="beans.xml";
        //2.创建容器对象
        //ApplicationContext是个接口  用他的这个ClassPathXmlApplicationContext这个实现类  从类路径中来读取加载配置文件
        ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
//        SomeService service=(SomeService)ctx.getBean("someService");
//        service.doSome();
    }
/**
 * spring容器创建对象 一次创建几个?
 *  在创建容器对象时,会把配置文件中所有对象创建出来(spring默认规则)
 */
    @Test
    public void test03(){
         //1.先指定配置文件路径
         String config="beans.xml";
        //2.创建容器对象
        //ApplicationContext是个接口  用他的这个ClassPathXmlApplicationContext这个实现类  从类路径中来读取加载配置文件
        ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
//         SomeService service=(SomeService)ctx.getBean("someService");
//        service.doSome();
}
    /**
     * 获取容器中对象的信息
     */
    @Test
    public void test04(){
        String config="beans.xml";
        ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
        //获取容器中定义对象的数量
        int nums = ctx.getBeanDefinitionCount();
        System.out.println("容器中定义对象的数量=="+nums);

        //获取容器中定义的对象名称
        String names[] = ctx.getBeanDefinitionNames();
        for(String name:names){
            System.out.println("容器中对象的名称=="+name);
        }
//         new java.util.Date();
    }
//    让spring创建非自定义类的对象
//    有class就能让spring创建对象
    @Test
    public void test05() {
        //1.先指定配置文件路径
        String config = "beans.xml";
        //2.创建容器对象
        //ApplicationContext是个接口  用他的这个ClassPathXmlApplicationContext这个实现类  从类路径中来读取加载配置文件
        ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
       Date date =(Date) ctx.getBean("mydate");
        System.out.println("date=="+date);

//        从容器中拿对象

       OtherService service =(OtherService) ctx.getBean("otherService");
       service.doOther();
    }
    }

测试结果 解释 如下

 解释:spring默认使用无参构造方法,创建对象 若定义了有参一定要定义无参

        因为配置文件中有两个bean标签 

<bean id="someService" class="com.bjpowernode.service.impl.SomeServiceImpl"></bean>
<bean id="someService1" class="com.bjpowernode.service.impl.SomeServiceImpl"></bean>

所以创建了两个对象

//2.创建容器对象
//ApplicationContext是个接口  用他的这个ClassPathXmlApplicationContext这个实现类  从类路径中来读取加载配置文件 把对象创建出来
ApplicationContext ctx = new ClassPathXmlApplicationContext(config);

实现类 

package com.bjpowernode.service.impl;

import com.bjpowernode.service.SomeService;

public class SomeServiceImpl implements SomeService {
    /**
     * spring默认使用无参构造方法,创建对象
     * 如果定义了有参 一定要调用无参
     */
    public SomeServiceImpl(){
        System.out.println("SomeServiceImpl的无参数构造方法");
        //无参构造执行了说明对象创建了 意味着spring框架默认执行的是无参构造方法
    }
    @Override
    public void doSome(){
        System.out.println("执行了业务方法doSome");
    }
}

看注释无参构造执行了说明对象创建了  意味着spring框架默认执行的是无参构造方法

SomeService service=(SomeService)ctx.getBean("someService");
service.doSome();

从容器中获取指定名称的对象,使用getBean()

getBean(括号里是"引号中的是对象的名字 也是就<Bean id="对象的名字">")

 spring在什么时候创建对象?Test02

创建spring容器对象的时候创建的,会读取配置文件,创建文件中声明的java对象。
*    优点:获取对象速度快,因为对象已经创建好了
*    缺点:占用内存

 spring容器创建对象 一次创建几个? Test03

* 在创建容器对象时,会把配置文件中所有对象创建出来(spring默认规则)

获取容器中对象的信息Test04  如图
 

 /**
     * 获取容器中对象的信息
     */
    @Test
    public void test04(){
        String config="beans.xml";
        ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
        //获取容器中定义对象的数量
        int nums = ctx.getBeanDefinitionCount();
        System.out.println("容器中定义对象的数量=="+nums);

        //获取容器中定义的对象名称
        String names[] = ctx.getBeanDefinitionNames();
        for(String name:names){
            System.out.println("容器中对象的名称=="+name);
        }
//         new java.util.Date();
    }

         控制台: 为什么输出前两行看实现类写了一个无参构造里sout

                 

spring创建非自定义类的对象

//    让spring创建非自定义类的对象
//    有class就能让spring创建对象
    @Test
    public void test05() {
        //1.先指定配置文件路径
        String config = "beans.xml";
        //2.创建容器对象
        //ApplicationContext是个接口  用他的这个ClassPathXmlApplicationContext这个实现类  从类路径中来读取加载配置文件
        ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
       Date date =(Date) ctx.getBean("mydate");
        System.out.println("date=="+date);

//        从容器中拿对象

       OtherService service =(OtherService) ctx.getBean("otherService");
       service.doOther();
    }

Spring属性赋值的分类

 pojo类

因为是set注入所以只要set方法就行

package com.bjpowernode.ba01;

public class Student {
    private String name;
    private int age;

    public Student() {
        System.out.println("Student无参数构造方法");
    }



    public void setName(String name) {
        System.out.println("setName=="+name);
        this.name ="Hello"+name;
    }



    public void setAge(int age) {
        System.out.println("setAge=="+age+"也就是先创建对象再进行set注入");
        this.age = age;
    }
    public void setEmail(String email){
//        email属性 <property name="email" value="list@qq.com"/>
        System.out.println("setEmail==="+email);
    }
    public void setGender(String gender){
        System.out.println("setGender=="+gender);
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

配置文件 

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--声明bean    -->
    <!--
        DI:给属性赋值
        简单类型:java中的基本数据类型和String

        1.set注入:spring调用类的set方法,通过set方法完成属性赋值
          简单类型的set注入:
          语法:<bean id="xxx" class="yyy">
              <property name="pojo属性名" value="简单类型属性值"/>
              ...
              </bean>

        -->
    <!--简单类型set注入-->
    <bean id="myStudent" class="com.bjpowernode.ba01.Student">
        <property name="name" value="李四"></property><!--setName("李四") -->
        <property name="age" value="22"></property><!-- setAge("20")-->
        <property name="email"  value="list@qq.com"></property><!-- setEmail()-->
        <property name="gender" value="男"></property>
    </bean>
    <!--声明日期类    -->
    <bean id="mydate" class="java.util.Date">
        <property name="time" value="9990000000000"></property><!--setTime() -->
    </bean>
</beans>

Test测试类

package com.bjpowernode.ba01;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.util.Date;

public class MyTest01 {
    @Test
    public void test01(){
        String config = "ba01/applicationContext.xml";
        ApplicationContext ctx = new ClassPathXmlApplicationContext(config);

        Student student = (Student) ctx.getBean("myStudent");
        System.out.println("student=="+student);
        Date date = (Date) ctx.getBean("mydate");
        System.out.println("date=="+date);
    }
}

运行结果

 解释分析

第一行是因为spring默认使用无参构造方法,创建对象

第三行是因为spring和咱们约定的是只调用set方法 而set方法里由自己决定 我在里面加了一条sout如图b

2-5行都是set注入

第六行 在测试类中进行的输出 Hello李四是我在setName()方法中this.name=name;改成了

                                                                                                      this.name="Hello"+name;

spring创建对象自动调用set方法 set方法里面的内容加不加都可以  在set代码里面可以对赋值进行改变加东西

给属性赋值看得是set方法和有没有属性无关

第七行是 给非自定义类属性赋值     只要能找到该class就可以对其在配置文件中配置然后进行赋值

                                                         

前三行是因为 配置文件中有三个Bean标签需要创建三个对象 

 

  <!-- 引用类型自动注入:spring根据byName,byType规则给引用类型赋值
    1.byName(按名称注入):java类中引用类型的属性名称和spring容器中bena的id名称一样,
    且数据类型一样的,这样的bean能够赋值给引用类型。
    语法:
    <bean id="xxx" class="yyy" autowire="byName">
        简单类型属性赋值
    </bean>
    2.1.byType(按类型注入):java类中引用类型的数据类型和bean的class是同源的,
    这些bean能够赋值给引用类型
    同源关系:
    1.java中引用类型的数据类型和bean的class值是一样的。
    2.java中引用类型的数据类型和bean的class值是父子类关系的。
    3.java中引用类型的数据类型和bean的class值是接口和实现类关系的。
    语法:
    <bean id="xxx" class="yyy" autowire="byType">
        简单类型属性赋值
    </bean>
    注意:在xml配置文件中,符合条件的对象,只能有一个。
    多余一个是报错的。
    -->
    <!--School相关类声明-->
    <bean id="mySchool" class="com.ziyang.diEnd.School">
        <property name="name" value="哈尔滨工程大学"></property><!---->
        <property name="address" value="哈尔滨的学院路"></property><!---->
    </bean>
 <!--  学生模块的bean -->
    <bean id="myStudent" class="com.ziyang.diEnd.Student" autowire="byType">
    <property name="name" value="张冉"></property><!--setName("李四") -->
    <property name="age" value="666"></property><!-- setAge("20")-->
    </bean>

                                                                给引用类型赋值

 测试类

byName()自动注入

怎么实现的              按名称来进行比较来完成属性赋值

String config="applicationContext,xml"

当我们执行ApplicationContext ctx = new ClassPathXmlApplicationContext(config);时

会去识别这个配置文件

读到<bean >标签时先去创建这个对象 当看见autowire="byName"时会自动把 Student类中所有引用类型赋值 那么他就找这个类 找引用类型找School school 这个对象名字 找到之后 拿着这个名字和配置文件中的每一个<bean >标签的id比

然后再给两个属性赋值

 byType()自动注入 

怎么实现的              按类型来进行比较来完成属性赋值

java类中引用类型的数据类型和spring容器中的bean的class值是同源关系的,这样的bean赋值给引用类型。                                 

   同源关系:
    1.java中引用类型的数据类型和bean的class值是一样的。
    2.java中引用类型的数据类型和bean的class值是父子类关系的。
    3.java中引用类型的数据类型和bean的class值是接口和实现类关系的。
       语法:
 <bean id="xxx" class="yyy" autowire="byType">
         简单类型属性赋值
         </bean>
注意:在xml配置文件中,符合条件的对象,只能有一个。
多余一个是报错的。

然后再给两个属性赋值

缺点:(引入注解的原因)

一个大型项目中 应用多个配置文件 主要目的是 能够减小单一文件大小 让编写配置文件更多一点更方便

2.2 基于注解的DI

基于注解的DI:使用spring提供的注解,完成java对象创建,属性赋值。

注解使用的核心步骤:

1)在源代码中加入注解,eg:Component

2)在spring的配置文件,加入组件扫描器的标签

注解是对程序代码的一个说明

1.创建对象的注解

@Component

@Respository

@Service

@Controller

2.简单类型属性赋值

@Value

3.引用类型赋值

@Autowired:Spring提供的注解,支持byName,byType

@Autowired:默认是byType

或者

@AutoWired

@Qualifier:使用byName

@Resource:来自jdk中的注解,给引用类型赋值的,默认是byName

@Resource:先使用byName,在byType

若@Resource(name="bean的名称"):只能使用byName注入

2.3IoC总结

IoC:管理对象的,把对象放在容器中,创建,赋值,管理依赖关系。

IoC:通过管理对象,实现解耦合。IoC解决处理业务逻辑对象之间的解耦合关系,也就是Servlet和dao之间的解耦合。

spring作为容器管理什么对象?

1) service对象,dao对象。

2)工具类对象。

不适合交给spring的对象?

1)实体类

2)servlet,listener,filter等web中的对象。他们上tomcat创建和管理的。

代码和配置文件分离 若对象要调整只需该配置文件

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值