Spring 5 详细教程 IDEA版本 复习笔记 狂神笔记 面试宝典

本文详细介绍了Spring框架的起源、核心特性和组成,包括IOC(控制反转)和DI(依赖注入)的概念及实现方式。通过实例展示了Spring如何创建对象、管理bean及其生命周期,解释了bean的作用域以及如何进行自动装配。此外,还探讨了Spring的AOP(面向切面编程)和动态代理,包括切面、通知类型和两种实现方式。最后,文章提到了Spring与Mybatis的整合以及Spring的声明式事务管理,强调了事务管理在确保数据完整性和一致性中的重要性。
摘要由CSDN通过智能技术生成

一、Spring 简介

1.简介

发展历程:

  • 2002年,首次出现spring框架雏形,interface21框架
  • 2004年,正式发布spring 1.0 版本
  • Rod Johnson 是创始人,音乐学博士

轮子理论:

  • “轮子理论”,也即“不要重复发明轮子”,这是西方国家的一句谚语,原话是:Don’t Reinvent the Wheel。
  • 拿到软件领域中就是指有的项目或功能,别人已经做过,我们需要用的时候,直接拿来用即可,而不要重新制造。
  • 当我们进行项目开发的时候,若已有的技术能满足我们的开发需求,我们不需要在去创造新的技术,只需要把现有的技术拿过来用就可以了。若已有的技术不能满足我们的开发需求,这时,我们就要去创造新的“轮子”。

Spring 理念:

  • 简化服务器开发,使现有技术更容易使用
  • 本身是个大杂烩,整合了现有的技术框架
  • 源码质量高,有助于开发者阅读学习

官方文档:

官方下载:

GitHub:

Maven:

  • 导入 spring web mvc 会自动把所需的spring依赖全部导入
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.3.4</version>
</dependency>

2.特点

  • 开源、免费的框架(容器)
  • 轻量级,非入侵的框架,不会对原有项目产生影响
  • 最大特性:控制反转(IOC)、面向切面编程(AOP)
  • 支持事务处理
  • 支持对各类框架的整合(号称大杂烩)

缺点:

  • 随着不断发展,配置越来越多

3.组成

在这里插入图片描述
七大模块:
在这里插入图片描述
官网介绍:现代化的Java开发,即基于Spring的开发

学好spring,为下一步springboot打下良好基础

二、Spring IOC

项目准备

新建maven项目,导入spring依赖,新建子模块
在这里插入图片描述
我们发现导入spring-webmvc之后,会自动导入其他相关依赖
在这里插入图片描述

1.IOC本质

分析

早期的项目:

  • 调用流程,serviceImpl实现service,为了调用Dao层方法,serviceImpl还要实现Dao层接口,这样用户操作service层对象实现业务,
  • 当用户需要调用很多Dao层实现业务时,service就必须实现很多Dao层,修改很多代码或者创造很多对象,非常不方便
  • 用户的需求可能会影响原来的代码,我们需要根据用户的需求来修改源代码
  • 用户需要什么Dao层的业务,我们就要在实现类中new一个对应的实现类对象

我们可在serviceImpl添加方法解决这个问题,如,

private UserDao userDao;
public void setUserDao(UserDao userDao) {
   
	this.userDao = userDao;
}
  • 之前是程序主动创建对象,控制权在程序员手上,每换业务,程序员都需要手动创建新对象
  • 使用set注入后,程序不再主动,而是被动接收对象

这种思想就是控制反转,程序员不用再去管理对象创建,系统耦合性大大降低,可以更加专注业务实现

这是IOC的原型,原理推导可以参考

https://blog.csdn.net/lbllol365/article/details/105083178?ops_request_misc=&request_id=&biz_id=102&utm_term=%E6%8E%A7%E5%88%B6%E5%8F%8D%E8%BD%AC%E5%8E%9F%E7%90%86&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduweb~default-6-105083178.first_rank_v2_pc_rank_v29

其实就是,之前那是业务层去根据用户需求,调用各个Dao接口实现类,程序员需要主动创建对象

现在是业务层只提供set方法,用户自己决定调用什么业务就注入什么对象,主动权在用户身上

在这里插入图片描述

IOC本质

控制反转IOC,是一种设计思想,DI(y依赖注入)是实现IOC的一种方法

在没有IOC的程序中,我们使用面向对象编程,对象的创建于对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方

也就是获得依赖对象的方式反转了

IOC是Spring的核心内容,可以使用xml配置,也可以使用注解,新版本spring也可以零配置实现IOC

spring容器在初始化时,先读取配置文件,根据配置文件或元数据创建与组织对象存入容器中,程序使用时再从IOC容器中取出需要的对象

业务代码本身不再主动去创建对象,而是准备个配置文件,需要什么对象就去配置,spring读取配置帮助用户创建对象,在业务层中,需要的时候直接注入即可;

由spring来创建对象,在spring中,这些对象都称之为bean

在配置文件加载的时候,容器中管理的对象就已经初始化了

小结:

  1. 传统的创建对象,我们使用new的方式,在业务代码中,根据业务需要提前new对象,缺点是代码耦合度高,修改业务也会导致代码修改

  2. IOC控制反转的核心在于,程序员不用主动在程序去new对象,而是通过spring加载配置文件来创建;作为业务的调用方,用户完全可以根据需要将
    bean配置在配置文件中,使用时直接在容器中取出即可;用户只需修改配置文件便可以决定创建哪些对象bean,而创建对象的过程则由spring处理。
    spring IOC容器创建对象的过程就是利用了反射原理

  3. 加载上下文配置创建上下文对象,是spring官方规定的标准写法,只有这样spring才能根据配置创建bean;默认情况下配置文件加载bean使用单例模式,
    所以多次调用同一个bean得到的都是同一个对象;bean对象在上下文对象加载配置文件后就创建了,用户getBean只是从spring IOC容器中取出对象,

  4. 从上下文对象到上下文接口,经历了很多层接口实现,这也间接说明spring帮助我们创建对象,在底层做了很多工作,

  5. resources中创建xml,写入上下文配置模板,IDEA会提示需要添加为spring的上下文配置文件,根据提示操作,可以选择单独添加为配置文件,
    或者合并入现有的spring配置文件中

  6. 上下文配置文件除了加载注册类,也提供其他功能,给bean别名,给属性赋值,选择无参构造还是有参构造创建对象,bean作用域,多个配置文件的合并关系

  7. spring加载bean,可以使用上下文配置文件xml,也可以使用java程序和注解的方式

2. IOC 容器

官方解读

https://docs.spring.io/spring-framework/docs/5.2.0.RELEASE/spring-framework-reference/core.html

org.springframework.context.ApplicationContext接口代表Spring IoC容器,并负责实例化,配置和组装Bean。容器通过读取配置元数据获取有关要实例化,配置和组装哪些对象的指令。

配置元数据以XML,Java批注或Java代码表示。

ApplicationContextSpring提供了该接口的几种实现。通常创建实例ClassPathXmlApplicationContextFileSystemXmlApplicationContext

下图显示了Spring的工作原理的高级视图。您的应用程序类与配置元数据结合在一起,因此,在ApplicationContext创建和初始化后,您将拥有一个完全配置且可执行的系统或应用程序

在这里插入图片描述

Spring IoC容器使用配置文件告诉Spring容器如何实例化,配置和组装应用程序中的对象。

传统上,配置元数据以简单直观的XML格式提供

基于注释的配置:Spring 2.5引入了对基于注释的配置元数据的支持。

基于Java的配置:从Spring 3.0开始,Spring JavaConfig项目提供的许多功能已成为核心Spring Framework的一部分。因此,您可以使用Java而不是XML文件来定义应用程序类外部的bean。要使用这些新功能,请参阅 @Configuration, @Bean, @Import,和@DependsOn注释。

Spring配置由容器必须管理的至少一个(通常是一个以上)bean定义组成。

  • XML配置元使用bean标签。
  • Java配置使用注释@Bean在@Configuration类中使用带注释的方法。
  • 这些bean定义对应于组成应用程序的实际对象。

3.Spring IOC 创建对象过程

项目准备

创建模块
在这里插入图片描述
实体类

public class User {
   
    private String name;
    public User() {
   
        System.out.println("User无参构造执行了");
    }
    public String getName() {
   
        return name;
    }
    public void setName(String name) {
   
        this.name = name;
    }
    public void show() {
   
        System.out.println("name="+name);
    }
}

测试类

public class MyTest {
   
    public static void main(String[] args) {
   
        User user = new User();
    }
}

执行代码,无参构造执行了,说明对象创建,这是之前我们创建对象的方式
在这里插入图片描述

spring 创建对象

添加xml配置文件
在这里插入图片描述
IDEA提示我们没有配置该文件到项目中,需要配置文件上下文,根据提示点击右侧确认即可

编写配置文件,将User类注册为spring的一个bean,使用spring来创建对象,在spring中这些都成为bean

<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="user" class="com.swy.pojo.User">
        <property name="name" value="Jack"/>
    </bean>

</beans>
  • bean标签将类注册为bean,相当于创建对象,id就是对象名,class就是对象的全限定类名
  • property描述类中的属性,相当于给属性赋值,name对应属性名,value对应属性值
  • 这个过程就是控制反转,将创建对象的过程交给spring来做
  • beans就相当于容器,里面可以存放多个bean
  • 注入属性的过程需要使用set方法,因此被注册的类中需要有set方法
  • 一旦注册这个类之后,表示该类交给spring管理,所以被注册类也会出现叶子的标志

注册之后,我们发现User类左侧出现变化,表示当前User类已经注册为Spring的一个bean
在这里插入图片描述
开始测试,使用spring创建对象

public class MyTest {
   
    public static void main(String[] args) {
   
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        User user = (User) context.getBean("user");
    }
}

无参构造执行,说明spring上下文对象读取了xml配置,帮助我们创建了User对象
在这里插入图片描述

含参构造创建对象

这方式默认使用无参构造创建对象,如果想使用含参构造创建对象,除了类中添加含参构造方法,还要进行xml配置

首先User类中添加含参构造

public User(String name) {
   
        this.name = name;
        System.out.println("User含参构造执行了");
    }

xml配置可以使用三种方法,先将原有的无参构造property标签去掉

通过属性的索引,

	<!-- index="0"表示第一个属性,也即是name -->
	<bean id="user" class="com.swy.pojo.User">
        <constructor-arg index="0" value="Mike"/>
    </bean>

通过属性的类型,基本类型直接写,引用类型使用全限定类名,当有多个同类型属性时,存在问题,不推荐使用

	<bean id="user" class="com.swy.pojo.User">
        <constructor-arg type="java.lang.String" value="Mike"/>
    </bean>

直接给属性名赋值,简单,推荐使用

	<bean id="user" class="com.swy.pojo.User">
        <constructor-arg name="name" value="Mike"/>
    </bean>

控制台输出,说明含参构造创建对象
在这里插入图片描述
在添加一个类User1,并且在xml中进行配置,然后执行代码

public class MyTest {
   
    public static void main(String[] args) {
   
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        User user = (User) context.getBean("user");
    }
}

控制台输出
在这里插入图片描述
我们发现,虽然我们只是从上下文对象中获取user对象,但user1对象也被创建了,说明spring加载配置文件后就将bean实例化了,我们只是从容器取出而已

如果执行代码,

public class MyTest {
   
    public static void main(String[] args) {
   
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        User user = (User) context.getBean("user");
        User user0 = (User) context.getBean("user");
        System.out.println(user == user0);
    }
}

控制台输出
在这里插入图片描述
我们发现,多次从容器中取同一个类的bean,每次取的都是同一个对象

分析
  • ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");是固定写法,如果使用xml配置bean就必须使用这种写法,参数填写自己准备的xml配置文件,可以填写多个,这就为什么我们添加xml时会有提示,否则spring不识别这个xml
  • 这段代码表示,获取spring的上下文对象。加载配置文件之后,我们配置的对象交给spring管理,使用时直接取出来就可以,返回的是Object对象,所以需要强转,
  • 通过IDEA点进去查看ClassPathXmlApplicationContext,我们发现进入了很多层,才找到ApplicationContext接口,说明spring框架帮助我们在底层实现了很多功能,所以我们使用起来才如此方便

小结:

到这一步,我们可以不再到程序中去改动了(new 对象),要实现不同的操作,只需在xml中配置修改即可,所以IOC就是,将对象交给Spring来创建、管理、装配

4.Spring 配置说明

详情见:https://docs.spring.io/spring-framework/docs/5.2.0.RELEASE/spring-framework-reference/core.html

xml配置文件除了上述配置外,还有一些其他的配置,这里进行一下补充

别名
<alias name="user" alias="userNew"/>

表示给user起了个别名叫userNew,有了别名,上下文对象可以通过别名从容器中取对象,如,

User user = (User) context.getBean("userNew");
Bean配置

id:bean的唯一标识,相当于我们学的对象名

class:bean对象所对应的全限定类名,包名+类名

name:别名,并且name可同时取多个别名(功能比alias更强大,有别名需要用这个就够了),空格分号逗号都可以用来分隔

scope:设置单例还是多例,后面介绍

<bean id="user1" class="全限定类名" name="user2,user3,user4...">
import

一般用于团队开始用,可将多个配置文件导入合并为一个,

当我们再创建一个配置文件beans1.xml时,根据IDEA提示,可以将其并入已有的xml配置文件中,
在这里插入图片描述
然后,在负责合并的配置文件中,导入其他的配置文件即可

<import resource="beans1.xml"/>

在创建上下文对象时,我们只需导入总的配置文件,相应的子配置文件也会被加载

如果导入的多个配置中,有重复的bean,spring也会自动的去重

配置文件名字自定,通常正规叫法为applicationContext.xml

5.DI 依赖注入

构造器注入、Set方式注入

构造器注入

上面已经介绍

public class SimpleMovieLister {
   

    // the SimpleMovieLister has a dependency on a MovieFinder
    private MovieFinder movieFinder;

    // a constructor so that the Spring container can inject a MovieFinder
    public SimpleMovieLister(MovieFinder movieFinder) {
   
        this.movieFinder = movieFinder;
    }

    // business logic that actually uses the injected MovieFinder is omitted...
}

请注意,该类没有什么特别的。它是一个POJO,不依赖于特定于容器的接口,基类或注释。

Set 方式注入(重点)
  • 依赖:bean对象的创建依赖于容器
  • 注入:bean对象中的所有时属性,由容器来注入(重点只Set注入)

Spring通过Set方式注入是最核心的注入方式

创建项目,准备一个复杂类型

首先创建一个类,用来作为引用类型

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Job {
   
    private String job1;
    private String job2;
}

接着,创建我们用来实例的bean,为了方便测试,这个类中我们引入复杂参数,包括上面的引用类,为了节省文章空间,我们使用lombok注解代表该类中已包含get/set方法,构造方法,toSting方法

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Person {
   
    private String name;
    private Job job;
    private String[] books;
    private List<String> hobbys;
    private Map<String,String> card;
    private Set<String> games;
    private String wife;
    private Properties info;
}

创建配置文件,根据提示添加为当前spring项目的上下文配置文件

开始注册bean对象,属性,官网使用很多例子来演示不同的属性注入,我们全部集中到一个例子中演示

实际上,在添加属性注入时,我们也可以根据IDEA的提示,选择不同属性应该对应哪个标签

<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd">


    <bean id="job" class="com.swy.pojo.Job">
        <property name="job1" value="老师"/>
        <property name="job2" value="农民"/>
    </bean>
    <bean id="person" class="com.swy.pojo.Person">
        <!-- 普通属性注入 -->
        <property name="name" value="Rose"/>
        <!-- 引用属性注入 -->
        <property name="job">
            <ref bean="job"/>
        </property>
        <!-- 数组属性注入 -->
        <property name="books">
            <array>
                <value>数学</value>
                <value>语文</value>
                <value>英语</value>
            </array>
        </property>
        <!-- list集合注入 -->
        <property name="hobbys">
            <list>
                <value>运动</value>
                <value>音乐</value>
            </list>
        </property>
        <!-- map集合注入 -->
        <property name="card">
            <map>
                <entry key="身份证" value="666"/>
                <entry key="银行卡" value="999"/>
            </map>
        </property>
        <!-- set集合注入 -->
        <property name="games">
            <set>
                <value>英雄联盟</value>
                <value>魔兽世界</value>
            </set>
        </property>
        <!-- Null注入 -->
        <property name="wife">
            <null/>
        </property>
        <!-- properties配置注入 -->
        <property name="info">
            <props>
                <prop key="username">admin</prop>
                <prop key="password">123456</prop>
            </props>
        </property>
    </bean>
</beans>

运行测试

public class MyTest {
   
    public static void main(String[] args) {
   
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        Person person = context.getBean("person",Person.class);
        System.out.println(person);
    }
}
  • getBean第二个参数可以添加对应的类对象,返回值可以不用强转

在这里插入图片描述

出现的问题

问题描述:

  1. 在这里我首先给Student和Job使用普通get/set方法,但是由于bean配置出现错误,导致异常;(j异常为属性不可写,没有set之类的)
  2. 将配置修改为正确(也就是现在的配置),运行代码依然异常;
  3. 个人怀疑可能是构造方法问题,于是分别添加无参构造、有参构造,依然异常
  4. 将手写get/set,构造去掉,改用lombok注解@Data @NoArgsConstructor @AllArgsConstructor,依然异常
  5. 将注解去掉,重写改为手动生成,终于正常运行
  6. 将手写去掉,重新使用lombok注解,正常运,之后,手写与lombok都可以正常运行

分析:

目前还不清楚问题的确切原因,个人感觉和缓存有关系,或者是某一次运行的错误代码编译后的字节码文件没有即使更新,导致的,还需要进一步分析

如果之前出现过异常,修改正确之后依然异常,可以考虑删除target目录清除缓存,或者新建项目、换台机器试一试,排除缓存可能带来的干扰

使用p/c-namespace 命名空间 方式注入(扩展方式)

Spring支持名称空间的方式实现注入,这些名称空间基于XML Schema定义,也即是在beans标签中定义

p命名空间在XSD文件(约束文件)中定义,仅存在于Spring的核心中,举例,

新增一个beans1.xml

<?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:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="student" class="com.swy.pojo.Student" p:name="Mike" p:age="18"/>
</beans>

注意beans标签新增一条约束 xmlns:p="http://www.springframework.org/schema/p"

同样 p 命名空间也可以使用 ref 引用

新建类

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {
   
    private String name;
    private int age;
}
<
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值