likes
comments
collection
share

快速入门Spring

作者站长头像
站长
· 阅读数 31

一、Spring的核心和设计思想

1、Spring是什么?

Spring 是指 Spring Famework (Spring 框架),他是一个开源的 Java 开发框架,旨在简单化企业应用程序发开。

概括一下就是

Spring 是包含了众多工具方法的 IoC 容器

1.1 什么是 IoC?

IoC : Inversion of Control ——控制反转。先解释一下单纯的 IoC 是一种设计思想,不是一种工具。它于降低程序的耦合度,提高代码的灵活性和可维护性。且 IoC 是Spring的核心思想,是难点,也是重点。

字都认识,放到一起就很抽象。直接举例子

1.1.1 传统程序开发

假设我们要制造一台汽车,我们简化一下流程,造汽车需要车身,车身需要地盘,地盘需要轮胎,是这么个依赖关系。

那么用程序应该怎么写出来呢。可以这样写

public class CarExample {
    public static void main(String[] args) {
        Car car = new Car();
    }
}

//汽车
class Car {
    private Body body;
    public Car() {
        body = new Body();
    }
}

//车身
class Body {
    private Bottom bottom;
    public Body() {
        bottom = new Bottom();
    }
}

//底盘
class Bottom {
    private Tire tire;
    public Bottom() {
        tire = new Tire();
    }
}

//轮胎
class Tire {
    //轮胎类型
    private String type = "雪地胎";
    public Tire() {
        System.out.println(type);
    }
}

肯定有同志是和我写的一样,尤其是在刚学代码的时候。然后就会发现,我想修改一下轮胎的类型是很困难的,必须将所有类种都添加一下type这个属性,然后在创建Car的时候传过去。修改后如下面代码

public class CarExample {
    public static void main(String[] args) {
        Car car = new Car("雪地胎");
    }
}

//汽车
class Car {
    private Body body;
    public Car(String type) {
        body = new Body(type);
    }
}

//车身
class Body {
    private Bottom bottom;
    public Body(String type) {
        bottom = new Bottom(type);
    }
}

//底盘
class Bottom {
    private Tire tire;
    public Bottom(String type) {
        tire = new Tire(type);
    }
}

//轮胎
class Tire {
    //轮胎类型
    private String type;
    public Tire(String type) {
        this.type = type;
        System.out.println(type);
    }
}

这就是高耦合,就是牵一发而动全身。IoC 的思想就是降低耦合。实现控制反转就能降低耦合,上面的代码是我们在创建汽车的时候需要同时创建车身,创建车身又需同时创建底盘……其实这个同时也代表他们之间的依赖程度很高,那么我们怎么实现控制反转呢?

不同时可不可以?车身是依赖于地盘,我必须先造出车身才能造地盘?我就一身反骨,先造地盘,再造车身,制作出来先放那呗,你用你就过来拿。所以我们修改此处的代码,将制作零件这部分代码提出来放到外面,我想什么时候制作就什么时候制作,你要用,我就给你不就完事了。

1.1.2 IoC思想实现程序开发

public class CarExample {
    public static void main(String[] args) {
      	//创建完放着,用的时候直接传
        Tire tire = new Tire("普通胎");
        Bottom bottom = new Bottom(tire);
        Body body = new Body(bottom);
        Car car = new Car(body);
    }
}

//汽车
class Car {
    private Body body;
    public Car(Body body) {
        this.body = body;
    }
}

//车身
class Body {
    private Bottom bottom;
    public Body(Bottom bottom) {
        this.bottom = bottom;
    }
}

//底盘
class Bottom {
    private Tire tire;
    public Bottom(Tire tire) {
        this.tire = tire;
    }
}

//轮胎
class Tire {
    //轮胎类型
    private String type;
    public Tire(String type) {
        this.type = type;
        System.out.println(type);
    }
}

如上代码,我们将造零件的步骤全部移到外面,这样如果我们想再给轮胎加一个尺寸的时候,直接修改一个类就好,并不会影响到其他的类。这就是控制反转。

总结一下传统程序开发和IoC思想实现的程序开发,如下图

快速入门Spring

1.2 讲解Spring IoC、DI

在文章开头就说了,Spring 是包含了众多工具方法的 IoC 容器。我们先理解 Spring 是 IoC 容器这段话,一个容器的作用就是能放东西,还能取出来。所以学习Spring IoC就是学习如何将对象放入其中,然后再取出来,这个对象就叫做 Bean,那如何放进去呢?这个放入其实就是配置xml文件,将你需要的对象写入其中就可以了(后面细讲)。

这时候就有同志问了,听起来和 Java 差不多啊,都是调用库中的对象。其实不然, Java 中的对象都是管理员手动实例化的,也就是new一个对象。但是在 Spring IoC 就不同了,它不需要你进行实例化,你是需要将依赖关系写好,对象实例化这件事是由他来自动完成的,不需要管理员手动进行new对象。

拿我们上面的代码举例子

public class CarExample {
    public static void main(String[] args) {
      	//创建完放着,用的时候直接传
        Tire tire = new Tire("普通胎");
        Bottom bottom = new Bottom(tire);
        Body body = new Body(bottom);
        Car car = new Car(body);
    }
}

//汽车
class Car {
    private Body body;
    public Car(Body body) {
        this.body = body;
    }
}

//车身
class Body {
    private Bottom bottom;
    public Body(Bottom bottom) {
        this.bottom = bottom;
    }
}

//底盘
class Bottom {
    private Tire tire;
    public Bottom(Tire tire) {
        this.tire = tire;
    }
}

//轮胎
class Tire {
    //轮胎类型
    private String type;
    public Tire(String type) {
        this.type = type;
        System.out.println(type);
    }
}

这个代码中main函数中的对象都是我们自己实例化的,而如果使用Spring,main函数可以怎么写呢?

我先叠甲,下面写的都是部分代码,为了更直观的表达意思。

此时如果我们Spring容器中已经有上述的所有对象(Car、Body……)

 public static void main(String[] args) {
        // 创建Spring容器
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

        // 从容器中获取Car实例
        Car car = context.getBean(Car.class);
}

就不需要再创建那么多对象了,为什么?因为依赖注入(Dependency Injection,DI), Car car = context.getBean(Car.class);这行代码执行的时候,会先得到Car对象,Car类依赖的是Body对象,所以当执行到Car类中的private Body body;这行代码,DI 就会自动的从框架中取出Body对象,然后赋值给body。bottom、tire都是同理,这就是依赖注入。

这时候再说它的定义:在代码执行时,将依赖的某个对象注入到其中。

大白话再讲一遍:我需要这个对象,把他从容器中拿给我。

这里为什么要强调,因为这件事不需要我们做,是DI帮我们做的。

所以IoC和DI是绑定在一起的,只有两个同时存在,才能实现这件事。

二、Spring创建和使用

1. 创建 Spring 项目

1.1 创建 Maven 项目

之前没有了解过 Maven 的同志我在这里简单的概括一下。

Maven 是一个项目的管理和构建的工具,用于 Java 项目的构建、依赖和项目信息的管理。它利用一个称为 POM(Project Object Model)的 XML 文件来描述项目的配置信息,包括项目的依赖关系、构建目标、插件等。Maven 有一个中央仓库,这个仓库就是一个依赖库,需要添加的依赖直接从中央仓库中找到源,然后粘贴到 pom.xml 文件中去(和调用方法差不多,JDK就类似于这个仓库)。

我用的是魔法版的 idea,如果你是社区版的,界面可能和我的不太一样。但是创建的时候选择 Maven 然后其他配置都不变就好,大家可以先参考一下我的环境。

环境:JDK 17 、专业版 idea 2023.2.5 、专业版 win10

废话不多说,我们直接开始

其实和正常的创建项目没什么区别(图1.1 创建 Maven 项目),只是将 system 选择为 Maven 就好(在这提个醒,是 Maven,不是 Maven Archetype)。

快速入门Spring

图 1.1.1 创建 Maven 项目

其他的选项应该都见过,这个 GroupId 是个什么东西呢,这个是项目的标识符,填写的是组织的域名。为了方便项目的管理,公司一般都会将项目集结到一起,然后通过项目名,也就是 ArtifactID 进一步找到这个项目(还有一个Version,也就是项目版本号),大白话说就相当于一个文件夹,之后点击create就好。

创建好的项目如下图(图1.1.2)

快速入门Spring

图 1.1.2 完成项目创建

红框框中的两个文件是项目创建好的标志,java 文件的颜色是蓝色的,如果你的java文件夹不能创建Java类的话,说明创建失败了,再重新再来一遍。

项目中的 pom.xml 就是 Maven 的配置文件,我们将项目所需要的依赖放入其中,需要什么添加什么,比如我们学的是 Spring ,那我们就添加 Spring依赖,下面细讲。

1.2 配置国内源

创建完的 Maven 项目之后我们需要添加 Spring 依赖。但在添加 Spring 依赖之前,我们还需要做一件非常重要的事,那就是配置国内源。

1.2.1 为什么要配置国内源

配置 Maven 国内源非常重要,我们学的这些语言都来自国外,所以人家搭建的服务器肯定也是在他们家门口,我们在创建 Maven 的时候,默认使用的源是国外的。而你添加依赖之后,idea 需要从源所在的网站下载这些依赖,网络其实是很复杂的,尤其是跨国。所以在下载依赖的时候难免会出现丢包或者下载失败的情况。如果出现了这种情况,而你不知道的话,你搭建的环境就不能支持项目的运行。

如果只是单纯的一个 Java 项目,别人把源码给你,你很容易就能运行起来。但是 Spring 项目就不一样了。它需要你搭建一个正确的环境才能运行起来,所以只有代码而没有配置环境的项目源码就像没有车钥匙的车。

1.2.2 如何配置国内源

在idea当中,我们一共要配置两次国内源,首先是针对本项目的,再而是针对以后新创建的项目

1.针对本项目配置国内源

①选择File -> Settings(图1.2.1)

快速入门Spring

图 1.2.1 选择Settings

②在搜索框中搜索maven(图1.2.2)

快速入门Spring

图 1.2.2 搜索maven

③搜索完是这个界面,然后看红框中的两个选项 User settings 和 Local repository(图1.2.3)。User settings 是用户配置文件,用于配置Maven项目的创建,Local repository是本地仓库,以后我们下载的依赖都在里面。❗❗❗注意这两个目录中不要包含中文字符和空格,如果有同志和我一样在创建电脑账户的时候使用中文昵称(如果不含中文或者空格,也不想移动位置,就跳过③④两步),我们可以顺着这个目录找到.m2文件夹,然后将.m2一整个文件夹复制到别处。粘贴在其他盘中也可以,放在C盘也可以,但是他的上级目录名不要是中文或者含有空格。 像我是放在F:Maven/这个目录下(图1.2.4)

快速入门Spring

图 1.2.3 源配置界面

快速入门Spring

图 1.2.4 .m2文件位置

④移动完之后,回到idea,分别将两个Override都勾上,将这两个目录选择为你刚刚移动过后的位置, User settings 一定要选择到settings.xml这个文件,Local repository选择到repository这个文件夹。 然后把 Use settings from .mvn.maven.config 这个选项取消。(图1.2.5)

快速入门Spring

图 1.2.5 源配置界面

⑤这步才到配置国内源,上面介绍了,settings.xml文件是配置Maven项目的文件,所以配置国内源也要在里面修改,但是有的同志.m2文件中没有settings.xml,你可以上网上找,也可以用我的gitee.com/yi_keshu/sp… 直接下载压缩包,解压就可以了。我的国内源是配置过了的,可以再看一遍怎么操作。我们使用文本器打开这个settings.xml文件,怕有的人没有下载别的文本软件,所以就直接用电脑自带的记事本了。打开后我们直接搜索 (CTRL + F键,大多数软件都适用) mirrors,如图1.2.6。

快速入门Spring

图 1.2.6 搜索镜像配置代码

我们需要在绿色框中添加国内镜像源,如图1.2.7蓝色阴影部分的就是阿里云镜像源我放到这里,当然你也可以找其他大厂的。只要添加到中就可以。添加完之后记得保存一下(CTRL+S)

    <mirror>
         <id>aliyunmaven</id>
         <mirrorOf>*</mirrorOf>
         <name>阿里云公共仓库</name>
         <url>https://maven.aliyun.com/repository/public</url>
     </mirror>

快速入门Spring

图 1.2.7 添加国内镜像

以上就是配置国内源的所有步骤。

2.针对新建项目配置

我们刚刚在idea中打开的File -> Settings只是针对本项目的,但是以后新建项目还是会使用默认的配置,下面就是针对新建项目的配置。

路径:File -> New Projects Setup -> Settings for New Projects... (图1.2.8)

快速入门Spring

图 1.2.8 配置新建项目

1.3 添加 Spring 依赖

1.3.1 找到依赖

我们配置国内源就是为了能稳定的下载依赖。所有依赖都在 Maven 仓库中,我们需要什么直接去下载就可以。

Maven 仓库地址:mvnrepository.com/

Maven 仓库也是国外的网站,所以进去可能有点慢,所以大家耐心等待,进不去刷新几次,或者换成手机热点。

我们打开仓库之后如图1.3.1

快速入门Spring

图 1.3.1 Maven仓库

直接在搜索框中搜索 spring,图 1.3.2

快速入门Spring

图 1.3.2 搜索Spring

选择第一个 Spring Context,进来之后你会发现很多版本(图1.3.3),这里就需要注意了❗❗❗,从6.0开始之后需要JDK 17版本,也就是说如果你是 JDK 1.8,在版本5中随便选一个就行,JDK 17 的话从6.0以后随便选择一个版本。我是JDK 17,随便选个6.0.8做实例。

快速入门Spring

图 1.3.3 选择版本

点击6.0.8,页面如图 1.3.4,然后我们复制下面框框中的依赖(灰色部分)。

快速入门Spring

图 1.3.4 复制依赖

1.3.2 添加依赖

复制完依赖之后回到idea,在创建Spring项目开头的时候说过,项目中的pom.xml(图1.3.5)就是整个项目的配置文件,所以我们需要将依赖引入到配置文件中去。

快速入门Spring

图 1.3.5 打开pom.xml文件

打开pom.xml之后,我们要先创建一个<dependencies></dependencies>标签,然后将刚刚复制的依赖放入进去,图1.3.6。

快速入门Spring

图 1.3.6 创建标签引入依赖

1.3.3 下载依赖

以上就是引入依赖的所有过程,但是仅仅是引入依赖,细心的同志已经发现了,其实这个代码是有报错的,就是因为刚刚导入的依赖还并没有开始下载。这时候我们只需要点击 Maven 的刷新按钮,让它自动读取下载依赖就可以。如果你右上角弹出了想图1.3.6右上角黄色框框中的 m 字母上带有刷新的按钮,直接点击就可以,如果没有的话看图1.3.7。这个m就代表 Maven 仓库。

快速入门Spring

图 1.3.7 找到 Maven 的刷新按钮

点击完刷新按钮之后,idea的下面就会自动解析依赖进行下载(图1.3.8),下载完之后报错内容会自动消失,下面的进度条也会自动消失。这个过程因你的网络而异,我不到30秒就下载完了,有的人下载时间可能超过30分钟,我的建议是一旦开始下载,最好不要中断下载或者切换网络,这样会概率性出现问题,也就是依赖下载不全,导致无法启动项目。出现这种情况怎么办?在下一小节会细讲如何处理。

快速入门Spring

图1.3.8 idea下载依赖

以上就是添加依赖的所有内容。

1.4 测试配置效果

配置完怎么看依赖是否下载成功了呢?看图1.4.1

快速入门Spring

图 1.4.1 Maven 依赖包

下载完之后在你的External Libraries 中会出现很多 Maven 依赖包,如果你是从头到尾一起下载的,那就没什么问题,为了验证包的完整性,我们直接随便写个代码测试一下是否能够运行,以下的代码大家不需要知道为什么,也没啥用,只是个测试案例,后面会有更细的讲解。

1.4.1 测试配置效果

1.首先在java文件夹下创建一个testpackage 包,然后在包中创建一个测试类Function。再在java包下创建一个测试类Test。如图1.4.2

快速入门Spring

图 1.4.2 创建测试类

内容我都放下面,但是包名你们自己导入。

 public class Function {
     public Function() {
         System.out.println("依赖下载成功!");
     }
  }
  public class Test {
      public static void main(String[] args) {
          ApplicationContext context = new ClassPathXmlApplicationContext("spring_config.xml");
          Function function = context.getBean("function", Function.class);
      }
  }

2.在resources包下面创建一个spring_config.xml文件(创建位置如图1.4.3),内容我也放下面

  <?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">
  <beans>
     <bean id="function" class="testpackage.Function"></bean>
  </beans>
  </beans>

快速入门Spring

图 1.4.3 创建spring\_config.xml

3.复制完代码记得导入包,然后运行Test测试类。如图1.4.4

快速入门Spring

图 1.4.4 启动测试类

如果能运行成功的话,就说明你的依赖下载没有问题,环境已经搭建好了。如果运行不起来,下面一一帮你排除问题。

1.4.2 排除可能问题

1.依赖包下载不全

这个问题就是刚刚在上面下载依赖的时候说的,如果你的网络环境不太好,下载过程比较长(或者下载过程中你切换网络等操作),就可能造成丢包等问题。这样下载之后你的依赖就会不完全,这是最大概率的问题。解决方法也很简单,我们重新下载就好了。在配置国内源的时候我提到了一个文件夹,.m2文件夹下的 repository文件夹。这个文件夹就是存本地依赖的地方,也就是你下载的依赖都在里面。我们找到这个文件夹,具体路径在你配置国内源的界面,如图1.4.5

快速入门Spring

图 1.4.5 找到 repository 路径

打开这个文件夹之后直接CTRL + A 键全选文件,然后都删除就可以(这里都删除是因为你不知道哪个文件缺失,所以这是最方便的方法),如图 1.4.6

快速入门Spring

图 1.4.6 删除所有文件

然后重新下载依赖,也就是再次点击 Maven 的刷新按钮,如图1.4.7

快速入门Spring

图 1.4.7 重新下载依赖

等待下载完成后再次启动测试类,如果还不行,在排除下面问题。

2.配置文件路径有误

这个问题也很常见,但问题出现在程序员自己身上,所以并不容易被察觉到。我们在配置文件的时候,有两个框需要我们选择路径,选择路径的文件名要对应下图(图1.4.8)。

快速入门Spring

图 1.4.8 查看路径是否有误

如果这个也没有问题的话,我只能想到最后一个问题了。

3.网络问题

如果以上两部都没有问题,那么就只能是网络问题了,网络问题因人而异,如果你的下载时间很长,还是建议换成手机热点,因为文件本身并不大。或者切换另一个网络。

三、Bean的存入和获取

这里我们介绍两种方法来存取bean和获取bean,第一种是bean标签法,第二种是注解法,在介绍之前需要提到一个spring的配置文件。

1. Spring 配置文件

Spring配置文件是用来配置应用程序的元数据文件,其实就是一个xml文件,用来告诉Spring框架应该如何配置和管理应用程序中的各种组件,比如定义哪些对象是Spring的bean,它们之间的关系是怎样的,以及其他一些配置信息。这些文件可以是XML格式的,也可以是基于Java代码的形式。它们就像是Spring应用程序的配置说明书,告诉Spring框架应该怎样工作。

这个xml文件存于resources文件夹中,而文件名其实是任意取的,但是取名字也是有说法的嘛,最好就是见名思意。所以我平常喜欢使用spring_config.xml这个文件名,见下图。

快速入门Spring

图 3.1.1 创建spring_config.xml

创建完之后打开这个文件,将以下代码放进去,这是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 http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
 </beans>

2. <bean>标签

2.1 存入bean

2.1.1 创建bean对象

在存对象之前你需要有一个对象,这个步骤和创建类一样的。为了方便寻找,我们先创建一个包,然后再在包中创建Student类,图3.2.1

快速入门Spring

图 3.2.1 创建Student类

2.1.2 存储bean对象

有了对象之后我们就使用<bean>标签将它存储到Spring中去。<bean>标签是存在配置文件中去的,如第一节对配置问价的介绍,此处就不重复了。

快速入门Spring

图 3.2.2 存储bean对象

如图 3.2.2 打开 spring_config.xml然后创建<bean></bean>标签,在起始标签中也就是<bean>中填写id,id就是你想给这个对象起的名字,待会调用的时候需要使用,我们起名字一般都是有关联的,所以直接填写student。class就是词类的路径,我们存放在beans包下,所以填写的路径就是beans.Student

就这样一个bean对象的存储就完事了,这时候有同志就会问了,那我有多个bean对象需要存储,还得一个个存吗?当然不需要,下一小节就讲到。

2.2 获取bean

在获取bean对象之前,我们需要创建一个启动类,这个启动类负责初始化spring容器并启动程序。其实就是一个Java类,包含main()方法。当前我们先创建一个启动类,在启动类中获取bean,图3.2.3。

快速入门Spring

图 3.2.3 创建启动类

如上图,我们在java包下创建一个Myapplication作为启动类,然后在类中创建一个main()方法,然后调用ApplicationContext类,这个类就是用来获取到spring中的对象的,从导入的包来看,也是spring给我们提供的API。实例化的对象中传入的是一个配置文件,也就是spring_config.xml,通过配置文件,能读取到其中存储到的bean对象。实例化context之后,context中将包含应用程序中所有被配置的bean以及它们的依赖关系。

然后我们直接调用ApplicationContext中自带的获取bean方法就可以了。

快速入门Spring

图 3.2.4 获取bean对象

如上图3.2.4,我们获取bean对象的时候,有三种方法

第一种在获取的时候我们只填id,也就是你在注入bean对象的时候规定的id名。这种方法的弊端就是它不知道你要或许的对象是什么类型,所以默认返回的都是Object,所以我们需要强转一下。

第二种,我们只填写泛型,这样不需要强转。但如果有多个不同包含有相同类的话(一般不会出现这种情况),此处就涉及到优先级的问题了(是可以运行的,也不报错),但具体是先获取哪个类,在此不举例,好奇的同志可以试试。

第三种,是我们常用的,同时填写id和泛型。

其实这里使用的 ApplicationContext 类来获取bean对象的方法,你实例化对象之后,存储到里面的对象就会立即初始化,并不需要获取bean对象之后才初始化bean。 具体如图3.2.5。

快速入门Spring

图 3.2.5 bean的初始化

如上图我们注释了所有Student对象的实例化,运行之后也能看到存到bean中的student已经被初始化了。这种方法像什么?像不像饿汉模式, 当你获取到ApplicationContext对象后,其中的bean已经被实例化、初始化并准备好供应用程序使用了。当然对应的还有一个类,其对应的是懒汉模式,那就是BeanFactory factory = new XmlBeanFactory(resource); ,但是它已经被抛弃了,在spring 6.0以下的依赖其实还能用,但是官方不建议使用。而在6.0及以上的版本直接被删除了。我是用的是6.0以上的版本,所以就不在此举例了,感兴趣的同志可以再创建一个项目,引入6.0以下的依赖,进行尝试。

以上就是使用标签存入和获取bean。

3. 注解

3.1 配置spring_config.xml

在使用注释法存储获取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"
       xmlns:content="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">
    <content:component-scan base-package="填写包名"></content:component-scan>
</beans>

上面的配置文件唯一需要修改的就是base-package="填写包名"只需要将你想存储类所在的包名替换到其中就好了。比如我创建的包名为beans,所以我就需要修改为<content:component-scan base-package="beans"></content:component-scan>,如图3.1.1。

快速入门Spring

图 3.1.1 修改配置文件

3.2 存入bean、获取bean

在配置文件中存入包名之后还是不能直接获取到bean的,我们还需要给类或者方法加上注解,才算将对象存储在Spring中,我们有常用的五大类注和一个方法注解。

1.类注解:@Controller、@Service、@Repository、@Component、@Configuration

2.方法注解:@Bean

为什么spring要给那么多个类注解呢?给一个不行吗?其实给那么多是方便程序员对类进行分类(分层)的。在我们写程序的时候,每个类都有自己的功能,类注解就是为了方便区分每一个类的作用,下面就来讲讲。

3.2.1 Controller

Controller是控制器组件,通常用于定义处理用户请求的 Spring MVC 控制器类。作用就是验证数据请求的正确性,处理前端的请求。举个例子吧,比如说我们输入一个网站,服务器就会收到请求,这时候过的第一关就是Controller标注的类。如果一个用户大量的请求这个网站,就会导致服务器CPU过载,所以程序员就可以通过Controller注解标注的类来控制前端的请求,就可以限制同一个ip在大量获取请求的时候需要输入验证码进行验证。所以验证用户请求的类,就需要标注Controller这个注解,他是入口。

下面就创建一个类来介绍Controller注解的使用,我们先创建一个controller包(我在此已经将五大类的包都创建完了),然后在其中创建一个ControllerDemo类。再直接给类名上标注@Controller见图3.2.1.1。

快速入门Spring

图 3.2.1.1 Controller注解示例

package controller;
import org.springframework.stereotype.Controller;

@Controller
public class ControllerDemo {
    public void fun() {
        System.out.println("Controller层,用户识别通过!");
    }
}

还得记得修改配置文件中的包名,<content:component-scan base-package="controller"></content:component-scan>

最后再在启动类中获取就可以了,获取的方法和使用标签的区别就是在输入name的时候。我们在配置文件的时候并没有输入类的name,但是我们在获取bean的时候是可以输入name和泛型的。如下图3.2.1.2。

快速入门Spring

图 3.2.1.2 获取bean

package controller;
import org.springframework.stereotype.Controller;

@Controller
public class ControllerDemo {
    public void fun() {
        System.out.println("Controller层,用户识别通过!");
    }
}

还得记得修改配置文件中的包名,<content:component-scan base-package="controller"></content:component-scan>

最后再在启动类中获取就可以了,获取的方法和使用标签的区别就是在输入name的时候。我们在配置文件的时候并没有输入类的name,但是我们在获取bean的时候是可以输入name和泛型的。如下图3.2.1.2。

快速入门Spring

图 3.2.1.2 获取bean

import controller.ControllerDemo;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Myapplication {
    public static void main(String[] args) {
        ApplicationContext context =
                new ClassPathXmlApplicationContext("spring_config.xml");
        ControllerDemo controllerDemo = context.getBean("controllerDemo",ControllerDemo.class);
        controllerDemo.fun();
    }
}

那spring到底是怎么规定这个name的呢?建议先看3.3小节,篇幅有点长,所以不放这了。

那么不加泛型行不行?获取类的方法其实是大同小异的,你可以不加泛型,但是还是需要强转。不加name行不行?可以的。但是这两个可以都是在这个类没有在其他包中重复过,而且name名没有重复使用过。也就是name和类都不能重复出现在程序中。否则就会报错,我举个例吧,为了方便我直接在repository中创建了一个相同的ControllerDemo,此时的类是重复的,因为在两个包中都出现了,所以在运行程序的时候就会报这个错误,如图3.2.1.3

快速入门Spring

图 3.2.1.3 报错信息

报错中就表明了在类的存储的时候就出现了错误,因为名字和类都重复了嘛,spring无法存储,因为即使存储了到时候也没法区分你要的是哪个。如果遇到重复类的情况,应该给类重命名他的name,直接在注解的后面加上value就可以,比如我们给repository包下的类加上,如下图3.2.1.4

快速入门Spring

图 3.2.1.4 重命名

这时候我们再去运行启动类,就可以了,因为你指定的是controller中的类,为了区分我们把controller中的CotrollerDemo类的fun方法改一下,加上它的name名。如图3.2.1.5

快速入门Spring

图 3.2.1.5 修改fun方法

启动程序如下图

快速入门Spring

图 3.2.1.6 启动程序

3.2.2 Service

Service层主要负责处理业务逻辑。在Spring中,使用@Service注解来标记一个类为Service层的组件,这样Spring就能将其作为一个Bean进行管理。 假设我们有一个简单的用户服务:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;

@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;//与数据库交互的类,下面会讲到
	
    public List<User> findAllUsers() {
        return userRepository.findAll();
    }
}

其实 Service 就是用来实现具体功能的一层,比如前端要获取数据库的所有用户,Controller 收到请求之后就调用 controller 层的 findAllUsers() 方法,而这个方法的具体实现其实是在 service 层,所以 findAllUsers()方法中的代码其实就是调用 UserSerice. findAllUsers 方法。我们将 service 层加入并写入 bean,引入bean之后直接调用。

3.2.3 Repository

Repository层负责与数据库进行交互。在Spring Data JPA中,我们通过定义接口来表示Repository,并使用@Repository注解来标记它。

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    // 可以根据需要添加自定义查询方法
}


Repository 也叫做 mapper 层,是与数据库进行交互的层,我们比如我们 Service 需要查询所有的用户,就要调用这个层提供的方法,那么这个方法哪里来呢?其实就是 extends JpaRepository<User, Long> 继承了这个类,这个类是一个依赖提供的,叫做 Spring Data JPA ,需要引入以下依赖才能使用。

<dependencies> 
<dependency> 
	<groupId>org.springframework.boot</groupId> 
	<artifactId>spring-boot-starter-data-jpa</artifactId> 
</dependency> 
<dependency> 
	<groupId>com.mysql.cj</groupId> 
	<artifactId>mysql-connector-java</artifactId>
</dependency> 
</dependencies>

3.2.4 Component

component 层就是一个组件层,也叫做工具层,这里一般封装常用的工具类。比如封装一个异常类,当需要抛出异常的时候直接调用。

3.2.5 Configuration

Configuration类用于定义Spring应用上下文的配置。我们使用@Configuration注解来标记一个类为配置类,并可以在其中定义一个或多个@Bean方法,这些方法将返回Spring容器管理的Bean实例。

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import javax.sql.DataSource;

@Configuration
public class DataSourceConfig {

    @Bean
    public DataSource dataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/mydatabase");
        dataSource.setUsername("root");
        dataSource.setPassword("rootpassword");
        return dataSource;
    }
}

小结

上面的部分只有 Controller 我描述的比较详细,包括创建和获取 bean,后面的部分我只讲了这个层的作用,因为创建和获取 bean 的步骤都差不多。每一层要写的东西是什么很重要,也就是定义了代码的规范,我里面举的例子看不懂无所谓。

3.3 注解的name规则

本小节是看spring是如何处理注解法bean的name转换的。过程较为枯燥,如果大家不喜欢的话,可以直接看本小节最后总结的规律(但其实看源码也是提升自我的一部分)。

我们直接看源码,先来搜索这个类,idea快捷键默认是CTRL + N,然后搜索beanname,然后点击AnnotationBeanNameGenerator这个类,翻译过来就是生成bean名称,如图 3.3.1。

快速入门Spring

图 3.3.1 找到AnnotationBeanNameGenerator

来到这个类之后,我们打开Structure,下图3.3.2,就可以看到这个类结构,接着点击这个类的generateBeanName方法。

快速入门Spring

图 3.3.2 打开类的structure

进入到generateBeanName方法之后,我们CTRL + 左键或者滚轮点击进入到buildDefaultBeanName方法,如图3.3.3

快速入门Spring

图 3.3.3 进入buildDefaultBeanName方法

进入到buildDefaultBeanName方法之后可以看到下图(图3.3.4),有一个StringUtils.uncapitalizeAsProperty方法,细心的同志已经发现了,StringUtils不是JDK提供的方法吗,虽然平时不怎么用,但是看名字就很明显这不是spring提供的方法。如果不确定同志可以点进这个方法,然后CTRL + H,就可以看到继承关系,它是属于lang包下的,如图3.3.5。

快速入门Spring

图 3.3.4 找到StringUtils.uncapitalizeAsProperty方法

快速入门Spring

图 3.3.5 查看从属关系

搞了一番阵仗原来还是JDK的方法提供的,那我们继续来看源码写了什么,因为此方法有点长,而且不能修改,所以我放到下面,也省得接长图了。

public static String uncapitalizeAsProperty(String str) {
    return hasLength(str) && (str.length() <= 1 ||
           !Character.isUpperCase(str.charAt(0)) || 
           !Character.isUpperCase(str.charAt(1))) ? changeFirstCharacterCase(str, false) : str;
}

private static String changeFirstCharacterCase(String str, boolean capitalize) {
    if (!hasLength(str)) {
        return str;
    } else {
        char baseChar = str.charAt(0);
        char updatedChar;
        if (capitalize) {
            updatedChar = Character.toUpperCase(baseChar);
        } else {
            updatedChar = Character.toLowerCase(baseChar);
        }

        if (baseChar == updatedChar) {
            return str;
        } else {
            char[] chars = str.toCharArray();
            chars[0] = updatedChar;
            return new String(chars);
        }
    }
}

uncapitalizeAsProperty这个方法开始看,先是传进了一个String str这个str就是我们在获取bean的时候传进的name。return后面的那行代码有两个条件,第一个先判断了是否为空,不为空的情况下,就剩下&&后面的三元条件运算符,我们一个个看

1:str.length() <= 1:这个条件检查字符串的长度是否小于等于 1,也就是这个类的名字是否只有一个字符。

2:!Character.isUpperCase(str.charAt(0)):这个条件检查字符串的第一个字符是否不是大写字母(也就是是否为小写)。

3:!Character.isUpperCase(str.charAt(1)):这个条件检查字符串的第二个字符是否不是大写字母(也就是是否为小写)。

不满足上述任何条件就返回原字符串(原name),如果满足以上的任意一个条件(字符长度小于等于1、第一个字符为小写、第二个字符为小写)就调用了下面的changeFirstCharacterCase方法,这个方法是将类名,也就是我们传进的name(str)的首字母改为小写, boolean capitalize这个参数是为了来确定是要将第一个字符转换为大写还是小写。值为false的话就将第一个字符转换为小写(代码13行)。转换为小写后,判断是否为原字符串(代码19行),如果是的话就返回原字符串。

那我们来分析一下有几种情况:

①:类名长度小于等于1,如果为空的话,就返回false了。如果为1的话,就调用changeFirstCharacterCase方法将字符转为小写。举例,类名:A,name:a

②:类名长度大于1,类名首字母为小写,此时调用changeFirstCharacterCase方法,判断了第一个字母为小写,返回原字符串。所以只要首字母为小写的类名,都会直接返回原字符串。举例1,类名:studentDemo,name:studentDemo;举例2,类名:student,name:student

③:类名长度大于1,类名首字母大写,第二个字符为小写。此时调用changeFirstCharacterCase方法,将第一个字符转换为小写,然后返回。举例,类名:Student,name:studnet

④:类名长度大于1,类名首字母大写,第二个字符也大写。此时不会调用函数,直接返回原字符串。举例,类名:SDemo,name:SDemo

以上就是所有可能出现的情况,其实name取决于你类名怎么起,如果你是标准的类名:所有单词首字母大写。

单一的单词的话,name就是小写的类名(也就是②中的举例2)。多个单词的话,name就会转换成标准的驼峰式写法(第一个单词首字母大写,也就是②中的举例1)。

如果你是一个大写字母加上一个开头大写的单词的话,也就是④。此时类名就不会变了。

我觉得①这个情况应该很少见,只用一个大写单词作为类名,这种写法在职场上也是非常非常罕见的。

如果大家想验证一下的话也很简单,这是JDK提供的方法,我们直接拿来用呗,这个方法是静态方法,类名.方法名就可以直接调用,如下图3.3.6

快速入门Spring

图 3.3.6 验证类名转换name

转载自:https://juejin.cn/post/7336761948065284147
评论
请登录