Spring IOC

78 阅读8分钟

这是我参与11月更文挑战的第11天,活动详情查看:2021最后一次更文挑战

Spring

Spring简介

Spring就是一个java语言开发的轻量级的开源的框架,可以在java项目中是用来简化开发 spring的核心是IOC和AOP,控制反转和依赖注入,Spring作为容器装的是java对象,可以创建对象给对象赋值,实现解耦,解决java对象与对象之间,模块与模块之间的耦合

Spring体系结构

左上角数据访问的模块,右边是web开发模块,后边我写的SpringMvc就在这一块,下来时Aop,核心容器,再就是测试,核心的妹妹一个模块对应一个jar包。。

image.png

image.png

spring优点

  1. 轻量
  2. 解耦
  3. AOP
  4. 可以集成各种优秀框架

IoC控制反转

概述

控制反转用来指导开发人员如何管理对象,把对象的生成,赋值,全生命周期交给容器来管理。分为两大部分,控制和反转,,我们可以使用容器来使用容器中的对象。Spring就是个容器

  1. 控制是对象创建,属性赋值,对象的整个生命周期管理
  2. 反转就是把对象的管理权限给spring容器管理

IOC的技术实现

DI依赖注入,是IoC的一种技术实现,程序只需要提供需要使用的对象的名称就可以了,其余的都是帮我们实现好了。Spring就是使用DI实现的IOC,我们只需要提供名称就可以了,Spring底层通过反射来创建对象

Spring第一个程序

Spring的配置文件

Spring配置文件的跟标签是beans beans后面是是约束文件说明beans里边是用来声明被Spring管理的java对象 image.png 配置文件声明一个对象 声明对象

1) id:自定义对象名,唯一值,重复会报错
2) class:类的全限定名称Spring通过反射创建对象
3) Spring根据Id和Class创建对象放到Spring容器里
<bean id="UserServiceImpl" class="org.example.Service.impl.UserServiceImpl"></bean>

获取bean对象

public interface UserService {
    public void SelectUser();
}
public class UserServiceImpl implements org.example.Service.UserService {
    @Override
    public void SelectUser() {
        System.out.println("实现了查询");
    }
}

测试获取对象,跟我们之前new一个对象好像更复杂了过程是

public class appmain {
    public static void main(String[] args) {

//UserServiceImpl userService = new UserServiceImpl();
//userService.SelectUser();
//配置文件地址
String configPath="beans.xml";
//2创建对象容器
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(configPath);
//从容器里获取指定名称的对象
UserService userService = (UserService) applicationContext.getBean("UserServiceImpl");
//使用对象的方法
userService.SelectUser();
    }
}

结果 image.png

我们都走了哪些呢??

  1. 指定Spring文件的地址,
  2. 创建容器对象,读取配置文件,遇到bean标签,通过反射创建对象,把对象放到容器里
  3. 在容器里根据对象名获取对象
  4. 调用对象的方法

那么Spring创建对象调用的是哪个方法

我们写一个无参构造打印一句话

public  UserServiceImpl(){
    System.out.println("无参构造");
}

运行看一下结果可以看出Spring创建对象调用的是无参构造方法

image.png

Spring在什么时候创建对象的

创建容器的售后,读取配置,创建文件中的对象,获取对象的速度快,

容器创建对象一次创建几个对象

在创建容器时,把配置文件中的bean都创建出来

容器中bean个数和名字

@Test
public void Test(){
    //配置文件地址
    String configPath="beans.xml";
    //2创建对象容器
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext(configPath);
    //从容器里获取指定名称的对象
    UserService userService = (UserService) applicationContext.getBean("UserServiceImpl");
    int beanDefinitionCount = applicationContext.getBeanDefinitionCount();
    System.out.println("容器中对象的数量"+beanDefinitionCount);
    String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
    for (String beanDefinitionName : beanDefinitionNames) {

        System.out.println(beanDefinitionName);
    }

    //使用对象的方法
    //userService.SelectUser();
}

image.png

创建非自定义对象

我们来创建Util包里的Date对象,有class文件Spring就能创建对象

@Test
public void TestDate(){
    //配置文件地址
    String configPath="beans.xml";
    //2创建对象容器
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext(configPath);
    //从容器里获取指定名称的对象
    Date myDate = (Date)applicationContext.getBean("myDate");
    System.out.println(myDate);
}

image.png

无接口的对象创建

public class StudentService {
    public void SeleStudent(){
        System.out.println("学生类查询");
    }
}
<bean id="myStudent" class="org.example.Service.StudentService"></bean>
@Test
public void TestStudent(){
    //配置文件地址
    String configPath="beans.xml";
    //2创建对象容器
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext(configPath);
    //从容器里获取指定名称的对象
    StudentService myStudent = (StudentService)applicationContext.getBean("myStudent");
    myStudent.SeleStudent();
}

image.png

给属性赋值

Spring调用类的无参构造。创建对象,创建对象后给参数赋值。给属性赋值可以使用xml配置文件中的标签和属性还有使用注解

DI的分类

  1. set注入,也可以叫设值注入
  2. 构造注入

基于Xml的DI

在xml配置文件中使用标签和属性,完成对象创建和属性赋值

  1. set注入也叫设值注入,就是Spring调用类中的set方法,在set方法中完成属性赋值
简单类型
public class Student {
    private String name;
    private Integer age;
    //getset和toStirng自己写上
public void setAddress(String address) {
    System.out.println("setAddress"+ address);
}
    }
<!--
    DI 给属性赋值
    1.set注入: Spring调用类的set方法,通过se方法给属性赋值
     简单类型的set注入:
     语法<bean id ="xx" calss="xx>
        <property name="属性名" value="属性值"></property>
        。。。。。。。。。。。
     </bean>
-->

    <bean id="Mystudent" class="org.example.S01.Student">
        <property name="age" value="20"></property>
        <property name="name" value="张三"></property>
        <property name="address" value="科技城管委会"></property>
    </bean>

<bean id="MyDate" class="java.util.Date">
    <property name="time" value="23131231231"></property>
</bean>

运行结果,yi定要加set方法 ,否则报错无法赋值,有set方法就行,和属性名无关,还可以对非自定义属性赋值,只要set开头。

image.png

对象类型
public class Student {
    private String name;
    private Integer age;
    private Grade grade;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Grade getGrade() {
        return grade;
    }

    public void setGrade(Grade grade) {
        this.grade = grade;
    }

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

    private String GradeName;
    private Integer gradeId;

    public String getGradeName() {
        return GradeName;
    }

    public void setGradeName(String gradeName) {
        GradeName = gradeName;
    }

    public Integer getGradeId() {
        return gradeId;
    }

    public void setGradeId(Integer gradeId) {
        this.gradeId = gradeId;
    }

    @Override
    public String toString() {
        return "Grade{" +
                "GradeName='" + GradeName + ''' +
                ", gradeId=" + gradeId +
                '}';
    }
}
    <!--
        2.set注入对象类型
        <bean id="XXX" class="XXXX">
    <property name="属性" ref="bean的id"></property>

</bean>
    -->

<bean id="Mystudent" class="org.example.S01.Student">
    <property name="name" value="张三"></property>
    <property name="age" value="20"></property>
    <property name="grade" ref="MyGrade"></property>

</bean>


<bean id="MyGrade" class="org.example.S01.Grade">
    <property name="gradeId" value="1"></property>
    <property name="gradeName" value="一年级"></property>
</bean>

测试

image.png

有参构造下标赋值
public Student(String name, Integer age, Grade grade) {
    this.name = name;
    this.age = age;
    this.grade = grade;

}
<bean id="Mystudent1" class="org.example.S01.Student">
    <constructor-arg index="0" value="李四"></constructor-arg>
    <constructor-arg index="1" value="20" ></constructor-arg>
    <constructor-arg index="2" ref="MyGrade" ></constructor-arg>
</bean>
省略index
<bean id="Mystudent1" class="org.example.S01.Student">
    <constructor-arg  value="李四"></constructor-arg>
    <constructor-arg  value="20" ></constructor-arg>
    <constructor-arg  ref="MyGrade" ></constructor-arg>
</bean>
引用类型的自动注入

Spring可以根据某些规则给应用类型赋值

  1. byName 按名称注入,java中引用类型的数据名称和Spring中的bean的id是一样的,数据类型也一样,这些bean能个偶赋值给应用类型
  2. ByType 按类型注入 java类中引用类型的数据类型与Spring中bean的class值是同源关系,可以赋值
byName
<!--
    1。ByName按名称注入 java类中引用类型的属性名称heSpring容器中bean的id名称一样且数据类型一样
    
<bean id="XXXX" class="XXXX" autowire="byName">
    简单类型赋值
</bean>
-->
<bean id="Mystudent" class="org.example.S04.Student" autowire="byName">
    <property name="name" value="张三"></property>
    <property name="age" value="20"></property>
</bean>


<bean id="grade" class="org.example.S04.Grade">
    <property name="gradeId" value="3"></property>
    <property name="gradeName" value="三年级"></property>
</bean>
ByType
2.byTypean 按类型注入;java类中引用类型的数据类型和bean的class是同源的
    同源古纳西
        1.java中引用类型的数据类型和bean的class值是一样的
        2.java中引用类型的数据类型和bean的class值是父子类关系
        3.java中引用类型的数据类型和bean的class值是接口和实现类的关系
-->
<bean id="Mystudent" class="org.example.S04.Student" autowire="byType">
    <property name="name" value="张三"></property>
    <property name="age" value="20"></property>
</bean>


<bean id="Mygrade" class="org.example.S04.Grade">
    <property name="gradeId" value="4"></property>
    <property name="gradeName" value="四年级"></property>
</bean>
Spring怎么工作的

Spring先生成一个容器,然后根据配置文件创建对象,放在容器里然后管理对象,我们的要是用时从spring容器中获取即可

使用多个配置文件

bean越多配置文件越长,Spring管理多个配置文件。常用的是包含文件的配置文件,有一个总的文件,里边inport其他多个文件

  1. 按功能模块分
  2. 按类的功能分
总的文件
<import resource="其他文件地址">
<import resource="其他文件地址">
关键字classpathL表示类路径,Spring在类路径中加载文件

Appliction.xml总的文件

<import resource="Grade-Application.xml"></import>
<import resource="Student-Application.xml"></import>
<import resource="*-Application.xml"></import>

Grade-Application.xml

<bean id="Mygrade" class="org.example.S04.Grade">
    <property name="gradeId" value="4"></property>
    <property name="gradeName" value="四年级"></property>
</bean>

Student-Application.xml

<bean id="Mystudent" class="org.example.S04.Student" autowire="byType">
    <property name="name" value="张三"></property>
    <property name="age" value="20"></property>
</bean>

image.png

基于注解的DI

@Component

基于主键的DI: 使用Spring提供的注解完成属性赋值。注解的使用步骤

  1. 在代码里加上注解@Component
  2. 在Spring配置文件中加入组件扫描,并加上扫描路径

和@Component功能相似的有

  1. @Repository :放在Dao接口的实现类上,表示创建Dao对象,持久层访问数据库的对象
  2. @Service :放在业务层接口的实现类上 表示创建业务层对象
  3. @Controller :放在控制器类的上边,表示创建控制器对象,在表现层
/*
* @Component表示创建对象,方到Spring容器里作用和Bean标签一样 value表示对象名称,跟Bean标签的id一样
* 使用在类上,表示此类要是用spring创建对象
可以省略value,也可以省略括号里的内容,默认类名首字母小写
和
* */
@Component(value = "Mystudent")
public class Student {

    private String name;
    private Integer age;
}

扫描多个包

  1. 使用多个自检扫描器
  2. 使用分隔符: ,
  3. 适用父包
<!--
    声明组件扫描器: 使用逐渐必须加上
    component-scan组件扫描器
    属性base-package 包含注解项目的包名,Sprin回去扫描这个包的所有类,找到所有的注解,遇到逐渐,按照注解功能差创建对象
-->
<context:component-scan base-package="org.example.S01"></context:component-scan>
@Value
@Component(value = "Mystudent")
public class Student {
/*
*       简单类型赋值 @Value
*           属性: value 简单类型属性值
*           放在属性定义上边无需set方法,或者放在set方法上
* */

    @Value(value = "张三")
    private String name;
    @Value(value = "12")
    private Integer age;
}

value使用外部文件

@Value("${myname}")
private String name;
@Value("${myage}")
private Integer age;
<context:property-placeholder location="Student.properties"></context:property-placeholder>
@Autowired

给引用类型赋值,自动注入支持 ByName,ByType,默认ByType,位置在属性上边无需Set方法 他的required属性默认为真,回去检查是否赋值成功,false不检查

@Component
public class Student {

    @Value("李四")
    private String name;
    @Value("22")
    private Integer age;

    @Autowired
    private Grade grade;

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + ''' +
                ", age=" + age +
                ", grade=" + grade +
                '}';
    }
}
public class Grade {
    private Integer Id;
    private String name;

    public void setId(Integer id) {
        Id = id;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Grade{" +
                "Id=" + Id +
                ", name='" + name + ''' +
                '}';
    }
}
<bean id="grade" class="org.example.S02.Grade">
    <property name="name" value="五年级"></property>
    <property name="id" value="5"></property>
</bean>

ByName自动注入也是使用 @Autowired加 @Qualifer

@Autowired
@Qualifier(value = "grade1")
private Grade grade;
@Resource

来自jdk,默认ByName,找不到就使用ByType