文章目录

  • 先看这部分
  • 1. Spring简介
  • 1.1 Spring框架优点
  • 1.2 Spring体系结构
  • 2. 核心功能IOC
  • 2.1 IOC简介
  • 2.2 IOC创建对象
  • 2.2.1 单例模式对象
  • 2.2.2 多例模式对象
  • 2.3 获取容器信息
  • 2.3.1 对象数量
  • 2.3.2 对象名称
  • 2.4 基于XML的DI
  • 2.4.1 set注入
  • 2.4.2 构造注入
  • 2.4.3 引用类型自动注入
  • byName
  • byType
  • 2.4.4 多个XML文件创建
  • 2.5 基于注解的DI
  • 2.5.1 组件扫描器
  • 2.5.2 @Component
  • @Repository
  • @Service
  • @Controller
  • 2.5.3 @Value
  • 2.5.4 @Autowired
  • @Qualifier
  • 2.5.5 @Resource
  • 2.5.6 注解信息配置文件


先看这部分

  • 使用Spring之前需要导入依赖
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.3.9</version>
</dependency>
  • 在Spring中,java基本数据类型和String类型都是简单类型


1. Spring简介

Spring框架有很多,各有各的功能特点,有spring、springmvc、springboot等等

Spring出现在2002年左右,解决企业开发的难度,减轻对项目模块之间的管理,类和类之间的管理,来帮助开发人员创建对象,管理对象之间的关系。核心技术是 ioc(控制反转)aop(面向切面编程)。Spring能实现模块之间,类之间的解耦合。

  • 一个框架,核心技术是ioc,aop,实现解耦合
  • 一个容器,容器中存放的是java对象,需要做的是把对象放入到容器中


1.1 Spring框架优点

  • 轻量

jar包很小,3M左右

  • 针对接口编程,解耦合
  • AOP编程的支持
  • 方便集成各种优秀框架

可和其他框架一起使用,降低项目开发难度


1.2 Spring体系结构

spring中ioc如何管理对象 spring的ioc_spring

2. 核心功能IOC

2.1 IOC简介

IOC(Inversion of Control):控制反转,这是一个理论,概念,思想

把对象的创建,赋值,管理工作都交给代码之外的容器实现,也就是对象的创建时有其他外部资源完成

IOC能够实现业务对象之间的解耦合,例如service和dao对象之间的解耦合



控制

创建对象,对象的属性赋值,对象之间的关系管理



反转

将管理对象的控制权交给容器



容器

一个服务器软件,一个框架



为什么使用IOC?

减少对代码的改动,也能实现不同的功能,实现解耦合



IOC的体现

Servlet对象的创建和调用都是有web容器完成,我们开发人员并没有创建Servlet对象



IOC技术实现

DI(Dependency Injection):依赖注入,这个是IOC的技术实现

只需要在程序中提供要使用的对象名称即可,对象创建和调用等等交给容器实现



Spring是使用DI实现了IOC的功能,Spring底层创建对象,使用的是反射机制


2.2 IOC创建对象



创建Spring配置文件

<!--
    Spring的配置文件
    1. beans: 根标签,Spring把java对象当做bean
    2. spring-beans.xsd 是约束文件,和mybatis指定的dtd文件一样
-->

<?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">

    <!--
        Spring创建对象
        声明bean,就是告诉Spring要创建某个类的对象
        id: 对象的自定义名称,唯一值,Spring通过这个名称找到对象
        class: 类的全限定名称(不能是接口,因为Spring是反射机制创建对象,必须使用类)

        Spring相当于完成 Service myService = new ServiceImpl();
        Spring是把创建好的对象放入到map中,Spring框架有一个map存放对象
        springMap.put(id, 对象)
        例如,springMap.put("myService", new ServiceImpl());
    -->
    <bean id="myService" class="com.company.impl.ServiceImpl" />
</beans>



创建并使用对象

@Test
public void testIoc() {
    String config = "beans.xml";
    
    // 创建Spring容器的对象,ApplicationContext
    // ApplicationContext: Spring容器,通过容器获取对象
    // ClassPathXmlApplicationContext: 从类路径中加载Spring的配置文件
    ApplicationContext ac = new ClassPathXmlApplicationContext(config);

    // 从容器中获取对象
    Service service = (Service) ac.getBean("myService");

    // 使用容器创建好的对象
    service.doSome();
}


ApplicationContext ac = new ClassPathXmlApplicationContext(config);

在这阶段读取配置文件,遇到bean就会创建对象,将其以(id,对象)的Map形式存放

也就是创建所有的对象



Spring创建对象默认是调用无参数构造方法




2.2.1 单例模式对象

<bean id="myService" class="com.company.impl.ServiceImpl" />

这样的默认是单例模式,也就是说一个类中只有一个对象,该对象在创建容器的时候就会被创建



2.2.2 多例模式对象

<bean id="myService" class="com.company.impl.ServiceImpl" scope="prototype"/>

这样是多例模式创建对象,该对象在执行 getBean() 时创建,并且是多个对象



2.3 获取容器信息

2.3.1 对象数量

// 获取容器内对象数量
int num = ac.getBeanDefinitionCount();
System.out.println("容器中有" + num + "个对象");



2.3.2 对象名称

// 获取容器内对象名称
String[] names = ac.getBeanDefinitionNames();
for (String name : names) {
    System.out.println(name);
}



2.4 基于XML的DI

注入就是属性赋值



测试类

public class Student {
    private int stuId;
    private String stuName;
    private ServiceImpl service;

    public void setStuId(int stuId) {
        System.out.println("setStuId执行");
        this.stuId = stuId;
    }

    public void setStuName(String stuName) {
        System.out.println("setStuName执行");
        this.stuName = stuName;
    }

    public void setService(ServiceImpl service) {
        this.service = service;
    }

    @Override
    public String toString() {
        return "Student{" +
                "stuId=" + stuId +
                ", stuName='" + stuName + '\'' +
                ", service=" + service +
                '}';
    }
}



2.4.1 set注入

set注入的原理是调用类的setter方法,所以叫做set注入

name:对象属性名

value:对象简单类型属性值

ref:对象复杂类型引用

  • 使用set注入一定要有setter方法
  • set注入先是调用无参数构造,再调用setter方法
  • 只是调用setter方法
  • set+属性首字母大写 组成方法名,通过反射机制调用


简单类型注入

<bean id="student" class="com.company.impl.Student">
    <property name="stuId" value="10001" />
    <property name="stuName" value="张三" />
</bean>



复杂类型注入

<bean id="student" class="com.company.impl.Student">
    <property name="stuId" value="10001" />
    <property name="stuName" value="张三" />
    <property name="service" ref="myService1" />
</bean>

<bean id="myService1" class="com.company.impl.ServiceImpl" />



2.4.2 构造注入

构造注入的原理是调用类的有参数构造方法,创建对象的同时,在构造方法中给属性赋值

name:构造方法形参名

index:构造方法参数的位置,0,1, …

value:形参类型是简单类型的属性值

ref:形参类型是复杂类型的属性值



任意位置

<bean id="student2" class="com.company.impl.Student">
    <constructor-arg name="stuId" value="10002"/>
    <constructor-arg name="stuName" value="李四"/>
    <constructor-arg name="service" ref="myService1"/>
</bean>



<bean id="student4" class="com.company.impl.Student">
    <constructor-arg index="0" value="10002"/>
    <constructor-arg index="1" value="李四"/>
    <constructor-arg index="2" ref="myService1"/>
</bean>



指定位置(0,1,2,…):

<bean id="student3" class="com.company.impl.Student">
    <constructor-arg value="10002"/>
    <constructor-arg value="李四"/>
    <constructor-arg ref="myService1"/>
</bean>



2.4.3 引用类型自动注入

引用类型就是除简单类型之外的复杂类型



Spring框架根据某些规则可以给引用类型赋值,不用手动给引用类型赋值



byName

java类中引用类型的属性名和Spring容器中的配置文件的id相同,且数据类型相同



语法

<bean id="xx" class="yyy" autowire="byName">
    简单类型赋值
</bean>



实例:

public class Student {
    private int stuId;
    private String stuName;
    private ServiceImpl service;

    ...
}
<bean id="service" class="com.company.impl.ServiceImpl" />

<bean id="student" class="com.company.impl.Student" autowire="byName">
    <property name="stuId" value="10001" />
    <property name="stuName" value="张三" />
</bean>



byType

java类中引用属性的类型和的class值属于同源关系

同类关系

继承关系

实现关系

如果有多个同源关系的bean,则会报错。



语法

<bean id="xx" class="yyy" autowire="byType">
    简单类型赋值
</bean>



实例:

public class Student {
    private int stuId;
    private String stuName;
    private Service service;

    ...
}
<bean id="myService1" class="com.company.impl.ServiceImpl" />

<bean id="student" class="com.company.impl.Student" autowire="byType">
    <property name="stuId" value="10001" />
    <property name="stuName" value="张三" />
</bean>



2.4.4 多个XML文件创建

在实际开发中,并不建议将多个bean写到同一个Spring配置文件当中。

可以按照模块功能分类等等方式,创建多个Spring配置文件。

其中还要创建一个主配置文件,其中导入了各个Spring配置文件。



主配置文件

<?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">
    <import resource="classpath:spring-student.xml"/>
    <import resource="classpath:spring-service.xml"/>
</beans>



2.5 基于注解的DI

使用注解的步骤:

  • 需要加入Maven的依赖spring-context,这个在一开始就需要加入,实际上注解需要spring-aop依赖,只不过spring-context顺便将其导入了进来
  • 在类中加入spring注解
  • spring配置文件中,加入组件扫描器的标签,说明注解在项目中的位置


2.5.1 组件扫描器

组件扫描器的声明时放在spring的主配置文件当中



  • base-package:指定注解在项目中的包名
  • component-scan

Spring会扫描遍历base-package指定的包,找出包中的相关注解,按照注解的功能创建对象,或赋值


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

    <context:component-scan base-package="com.company.anno"/>
</beans>



2.5.2 @Component

创建对象,等同于的功能,声明在类的上面



  • value

对象的名称,bean的id值

package com.company.anno;

import org.springframework.stereotype.Component;

@Component(value="myTeacher")
public class Teacher {
    private int id;
    private String name;

    ...
}



等同于:

<bean name="myTeacher" class="com.company.anno.Teacher"/>
  • 省略value,默认值是首字母小写的类名
  • 省略value,声明对象名称
@Component("myTeacher")


Spring中和@Component基本功能一致,创建对象的注解还有:

@Repository

持久层类

dao实现类

表示创建dao对象,dao对象是能访问数据库的



@Service

业务层类

service的实现类

创建service对象,service对象是做业务处理的,还有事务等功能



@Controller

控制器类

控制器对象

接收用户提交的参数,显示请求的处理结果



以上三个注解的使用语法和@Component一样。都能创建对象,但是这三个注解还有额外的功能

@Repository、@Service、@Controller是给项目的对象分层的



2.5.3 @Value

简单类型属性赋值



  • value:String类型,表示简单类型的属性值
  • 属性定义的上面,无需set方法推荐使用
  • set方法的上面


package com.company.anno;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component("myTeacher")
public class Teacher {
    // @Value(value="10001")
    @Value("10001")
    private int id;
    
    @Value("张三")
    private String name;

    ...
}



2.5.4 @Autowired

引用类型属性赋值



  • 自动注入原理,支持byName、byType
  • 默认是byType自动注入
  • 属性定义的上面,无需set方法推荐使用
  • set方法的上面
  • required=true

引用类型赋值失败,程序报错并终止执行

  • required=false

引用类型赋值失败,程序正常执行,引用类型是null

  • required默认是true


package com.company.anno;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component("myTeacher")
public class Teacher {
    @Value("10001")
    private int id;
    @Value("张三")
    private String name;

    @Autowired
    private School school;

    ...
}
package com.company.anno;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component("mySchool")
public class School {
    @Value("清华大学")
    private String name;
    @Value("北京市")
    private String addr;

    ...
}



@Qualifier

与@Autowired搭配使用,相当于@Autowired的byName模式

其中是对象id

  • @Qualifier与@Autowired顺序可不一致,但是必须两个一起用
package com.company.anno;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component("myTeacher")
public class Teacher {
    @Value("10001")
    private int id;
    @Value("张三")
    private String name;

    @Autowired
    @Qualifier("mySchool")
    private School school;

}
package com.company.anno;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component("mySchool")
public class School {
    @Value("清华大学")
    private String name;
    @Value("北京市")
    private String addr;

    ...
}



2.5.5 @Resource

来自jdk中的注解,Spring对其提供了支持

对引用类型属性进行赋值



  • 自动注入原理,byName、byType,默认是byName
  • 如果使用byName未找到,则使用byTye查找
  • 属性定义的上面,无需set方法推荐使用
  • set方法的上面


若只使用byName方式,需要增加一个属性 name

name值是bean的id



package com.company.anno;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;

@Component("myTeacher")
public class Teacher {
    @Value("10001")
    private int id;
    @Value("张三")
    private String name;

    @Resource
    private School school;

    ...
}
@Resource(name="mySchool")
private School school;
package com.company.anno;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component("mySchool")
public class School {
    @Value("清华大学")
    private String name;
    @Value("北京市")
    private String addr;

    ...
}



2.5.6 注解信息配置文件

创建配置文件

teaId=10002
teaName=李四



在Spring主配置文件中添加文件路径

<context:property-placeholder location="classpath:anno/teacher.properties" file-encoding="GBK"/>



使用配置文件信息

package com.company.anno;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;

@Component("myTeacher")
public class Teacher {
    @Value("${teaId}")
    private int id;
    @Value("${teaName}")
    private String name;

    @Resource(name="mySchool")
    private School school;

    ...
}