Spring基础知识讲解

Spring是什么?

Spring指的是Spring Framework(Spring框架),它是一个开源框架,有非常活跃和庞大的社区支持,Spring支持广泛的应用场景,可以让Java企业级开发更简单。Spring是包含众多工具方法的IoC容器,Spring最核心的功能就是容器管理

IoC容器与DI

我们需要理解一下Spring是包含众多工具方法的IoC容器这句话,什么是IoC呢?IoC = Inversion of Control 翻译成中文就是控制反转,我们通过一个例子来解释一下这个名词。

构建⼀辆⻋(Car Class),然⽽⻋需要依赖⻋身(FrameWork Class),⽽⻋身需要依赖底盘(BottomClass),⽽底盘需要依赖轮胎(Tire Class),此时我们传统开发的方法来完成这个需求:


class Car {

    private Framework framework;

    public Car(int size) {
        this.framework = new Framework(size);
    }

    public void init() {
        System.out.println("构造Car");
        framework.init();
    }
}


class Framework {

    private Bottom bottom;

    public Framework(int size ) {
        this.bottom = new Bottom(size);
    }

    public void init() {
        System.out.println("构造Framework");
        bottom.init();
    }

}

class Bottom {
    private Tire tire;

    public Bottom(int size) {
        this.tire = new Tire(size);
    }

    public void init() {
        System.out.println("构造Bottom");
        tire.init();
    }
}

class Tire {
    int size = 15;

    public Tire(int size) {
        this.size = size;
    }

    public void init() {
        System.out.println("车轮子 = "  + size);
    }
}

public class Test {
    public static void main(String[] args) {
        int size = 20;
        Car car = new Car(size);
        car.init();
    }
}

在这里插入图片描述
运行代码完成了需求,但是当我们需要增加一个属性的时候会非常困难,比如我们增加一个轮子的颜色属性。
在这里插入图片描述
可以看到,当我们更改了底层的代码,整个调用链上的所有代码都需要修改,如何解决这个问题呢?我们可以模拟一下IoC容器,将传统的new对象的方式改为注入传递的方式。

class Car {

    private Framework framework;

    /*public Car(int size) {
        this.framework = new Framework(size);
    }*/

    public Car(Framework framework) {
        this.framework = framework;
    }

    public void init() {
        System.out.println("构造Car");
        framework.init();
    }
}


class Framework {

    private Bottom bottom;

    /*public Framework(int size ) {
        this.bottom = new Bottom(size);
    }
*/
    public Framework(Bottom bottom) {
        this.bottom = bottom;
    }

    public void init() {
        System.out.println("构造Framework");
        bottom.init();
    }

}

class Bottom {
    private Tire tire;

   /* public Bottom(int size) {
        this.tire = new Tire(size);
    }*/

    public Bottom(Tire tire) {
        this.tire = tire;
    }

    public void init() {
        System.out.println("构造Bottom");
        tire.init();
    }
}

class Tire {
    int size = 15;
    String color;

    public Tire(int size,String color) {
        this.size = size;
        this.color = color;
    }

    public void init() {
        System.out.println("车轮子 = "  + size);
    }
}

public class Test {

    private Car car;
    private Framework framework;
    private Bottom bottom;
    private Tire tire;

    public Test() {
        this.tire = new Tire(20,"blue");
        this.bottom = new Bottom(this.tire);
        this.framework = new Framework(this.bottom);
        this.car = new Car(this.framework);
    }

    public static void main(String[] args) {
        Test test = new Test();
        test.car.init();
    }
}

经过调整,现在更改底层代码是不会影响整个调用链的,这样完成对代码的解耦。我们发现,传统的代码是上层代码依赖下层代码,由上级对象创建下级对象,而更改后的代码,是把下层代码注入到上层代码,这样下级代码的控制权不在由上级控制,自然下级代码更改不会影响上级代码了,这就是所谓的控制权反转,也是IoC容器的思想。

所以Spring 是包含了多个⼯具⽅法的 IoC 容器,可以理解为Spring是一个控制反转容器,既然是容器那么就可以存东西和取东西。所以对象的创建和销毁的权利都交给 Spring 来管理了,它本身⼜具备了存储对象和获取对象的能⼒。也就是,创建类的时候将类存到Spring容器中,在创建对象时不需要new了,直接去Spring中取对象。所以学习Spring最核心的功能就是如何将对象存到Spring中,再从Spring中获取对象的过程。

DI与IoC的区别

上面我们知道了IoC是控制权反转的一种思想,那么DI又是什么呢?DIDependency Injection 的缩写,翻译成中文是依赖注入。他与IoC是从不同角度描述的同一件事,IoC是一种思想,而思想执行需要方法,IoC就是思想,DI就是具体得到实现方式。

Spring项目的创建

配置maven国内源

在这里插入图片描述
在设置中勾选setters.xml配置文章和本次仓库文件。
在这里插入图片描述
在settings.xml中配置国内源

<mirror>
        <id>alimaven</id>
        <name>aliyun maven</name>
        <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
        <mirrorOf>central</mirrorOf>        
</mirror>

在这里插入图片描述
删除本地所有的jar包,重新下载
在这里插入图片描述

创建Spring项目

首先创建一个maven项目
在这里插入图片描述
添加Spring依赖

<dependencies>
   <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.2.3.RELEASE</version>
    </dependency>
  
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>5.2.3.RELEASE</version>
    </dependency>
</dependencies>

在这里插入图片描述

有关Bean的操作

存储Bean

创建一个启动类
在这里插入图片描述

存储Bean对象分为两个步骤:
1、创建一个Bean对象。
2、将创建的Bean对象注册到Spring容器中。

创建一个Bean对象,就是一个普通的类对象。
在这里插入图片描述
添加Spring配置文件spring-config.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: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">
        <bean id="student" class="com.java.demo.model.Student"></bean>
</beans>

使用Bean

使用Bean对象,分为三步:
1、得到Spring上下文对象,因为对象都交给了Spring管理了,所以对象需要从Spring中获取。
2、通过Spring上下文,获取某一个指定的Bean对象。
3、使用Bean对象。

使用 ApplicationContext获取上下文对象,使用getBean()方法获取Bean对象。

在这里插入图片描述
此时运行代码我们看到打印出了Student对象的信息。
在这里插入图片描述

ApplicationContext和BeanFactory的区别

BeanFactoryApplicationContext的作用一样都是获取上下文对象的,他俩的区别可以从两个方面说:

1、从继承关系来说,ApplicationContextBeanFactory的子类,他有更多的功能,比如对国际化的支持、资源访问支持、以及事件传播等方面的支持。
2、从性能放面来说:ApplicationContext是一次性加载所有Bean对象,BeanFactory是懒加载,只加载使用的Bean对象。

getBean()的三种使用方法

在这里插入图片描述
使用图中的三种方法都可以获取到Bean对象,注意如果有多个相同的Bean对象,使用类名获取Bean对象的方法就会报错。

更简单的存储和获取对象

我们发现使用框架写代码竟然比传统开发的代码还要复杂,有没有方法使存储Bean对象和获取Bean对象的过程便简单呢?

我们可以通过两个方法实现更简单的存储Bean:
1、使用类注解的方法实现Bean对象的存储。
2、使用方法注解的方法实现Bean对象的存储。

首先同样要添加Spring配置文件spring-config.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: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="com.java.demo"></content:component-scan>
</beans>

类注解

五大类注解分别是:
1、@Controller 控制器,检测数据的合法性
2、@Service 服务层,进行业务组装
3、@Repository 数据持久层,实际业务的处理
4、@Component 组件 工具类层
5、@Configuration配置层 进行配置

使用这些类注解都可以将类储存到IoC容器中,那为什么要分这么多注解呢?原因就是让程序员看到类注解之后,就可以了解到当前类的用途

在这里插入图片描述
此时会有一个问题,我们在配置文件中并没有配置Bean对象,也就是说没有设置Bean对象在IoC中id,那我们如何获取Bean对象呢?

使用类注解存储Bean的命名规则如下:
1、当类的命名为默认大驼峰(首字母大写第二个字母小写),可以使用类名首字母小写获取到Bean对象。
2、如果不满足上面的大驼峰的情况,直接使用类名就可以获取到Bean对象。

方法注解

除了使用类注解的方法进行存储,还可以使用方法注解的方式。
在这里插入图片描述
@Bean的默认命名方式为方法名,我们还可以进行重命名。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
还可以指定多个名称
在这里插入图片描述

@Bean获取时的一些注意事项:
1、如果多个Bean使用相同的名称,执行程序不会报错,只会将第一个Bean对象存储到容器中,后面的会自动忽略。
2、如果对@Bean进行了重命名,那么默认使用方法名获取Bean的方法将会失效。

获取Bean对象的简单方法——依赖注入

之前我们获取Bean对象的方法称为依赖查找,很麻烦,我们还有更简单的方法依赖注入。

我们有三种方式进行对象注入:
1、属性注入
2、构造方法注入
3、Setter注入

属性注入:
在这里插入图片描述

使用属性注入的优缺点:
优点:代码简单
缺点:无法注入final修饰的变量,可能违反单一性设计原则

构造方法注入:
在这里插入图片描述

使用属性注入的优缺点:
优点:可以注入一个final修饰的变量,注入的对象不会被修改,构造方法可以保证注入对象完全初始化,构造方法注入通用性更好。
缺点:代码更加复杂,无法解决循环依赖的问题。

setter方法注入:
在这里插入图片描述

使用属性注入的优缺点:
优点:Setter注入更符合单一设计原则
缺点:无法注入final修饰的变量,Setter注入的对象可以被修改。

除了使用@Autowired注解进行注入,还可以使@Resource进行注入。
在这里插入图片描述

@Autowired和@Resource的区别

1、@Autowired来自于Spring,而@Resource来自于JDK
2、使用时可以设置的参数不同,@Resource有更多的参数
3、@Resource只能用于Setter注入和属性注入,不能用于构造函数。

关于设置的参数我们举个例子来说明解释一下:
我们使用@Bean注入两个返回相同对象类型的Bean:
在这里插入图片描述
此时使用@Autowired@Resource获取Bean对象会报错
在这里插入图片描述
报错原因是使用@Autowried进行依赖注入的过程是先根据getType获取对象,如果只获取到一个,那么会直接注入到属性上去,如果有多个会使用getName进行匹配。
在这里插入图片描述
所以此处我们有三种方法解决Bug
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Bean的作用域和生命周期

作用域

我们通过一个例子来观察一下Bean的作用域:现在有一个Student类通过@Bean的方式进行了注入,此时有两个类都需要使用这个Bean对象,但是其中一个类需要进行修改Bean对象中的属性,一个不需要任何改动,此时会发生什么呢?
在这里插入图片描述
我们的预期是,公共的Bean对象在各自的类中可以被修改,但是不能影响其他类,但是结果不尽人意。
在这里插入图片描述
造成这个结果的原因就是Bean的作用域,Bean在默认的情况下是单例的状态,也就是所有人都是同一个对象,自然我们在一个类中进行修改,另一个类中的对象也会更改。解决这个问题的方式就是改变Bean对象的作用域,Bean的作用域有哪些呢?

Bean的作用域有6种:

  1. singleton:单例作⽤域 (默认)
  2. prototype:原型作⽤域(多例作⽤域)
  3. request:请求作⽤域
  4. session:回话作⽤域 一个会话共享一个Bean
  5. application:全局作⽤域 一个Context容器共享一个作用域
  6. websocket:HTTP WebSocket 作⽤域

前两个作用域适用于普通Spring项目,后四个都是Spring MVC的项目。

设置作用域

我们可以使用@Scope来设置作用域
在这里插入图片描述
再次运行项目此时完成我们的需求。
在这里插入图片描述

生命周期

Bean的执行流程

1、启动Spring容器
2、实例化Bean
3、Bean注册到Spring中
4、将Bean装配到需要的类中

Bean的生命周期

1、实例化Bean(为Bean分配内存空间)
2、设置属性(Bean注入和装配)
3、Bean初始化
3.1、实现了各种Aware通知的方法,如 BeanNameAware、BeanFactoryAware、ApplicationContextAware的接口方法
3.2、执行 BeanPostProcessor初始化前置方法
3.3、执行@PostConstruct初始化方法,依赖注入操作后被执行
3.4、执行自己指定的init-method方法
3.5、执行BeanPostProcessor初始化后置方法
4、使用Bean
5、销毁Bean

Bean的初始化比较复杂简单总结就是:

1、执行各种通知
2、初始化的前置方法
3、初始化方法
4、初始化的后置方法

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

悲伤猪小猪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值