【Spring】Spring 入门介绍

本文阅读 28 分钟
首页 代码,Java 正文

1.1 Spring 是什么?

Spring(Spring Framework) 是一个开源的轻量级框架,是包含了众多工具方法的 IoC 容器。

那么什么 是 IoC 容器呢?

容器 本身的含义是用来容纳某种物品的装置,而在之前的学习中,我们就应该接触过一些容器了,比如存储数据的容器 List、Map、Set 等等,Web 容器 Tomcat 等等。

那么 IoC 是什么呢?

1.2 IoC 是什么?

IoC(Inversion of Control),译为控制反转,它不是什么技术,而是一种思想。在 Java 开发中,IoC 意味着你将设计好的依赖对象 B 交给容器控制,而不是按照传统的方式,在你需要使用依赖对象 B 的对象 A 内部直接控制。

IoC 既然译为控制反转,那么我们就要理解谁控制谁,控制什么?要理解为何要反转,反转了什么?

谁控制谁,控制什么?

按照传统的设计思想,如果我们想要在 A 对象内部使用 B 对象,那么我们就会在 A 中直接 new 一个 B,这是我们程序员主动去创建的依赖对象;而 IoC 容器能够存储你需要的对象 B,当你要使用这个依赖对象 B 时,<mark>IoC 容器就会来控制依赖对象的创建</mark>,而程序只需要引入这个已经创建好的对象 B 就行。<mark>IoC 容器主要是控制了外部资源的获取(不只是包含要依赖的对象,还包括文件等等)</mark>。

为何要反转,反转了什么?

按照传统的设计思想,如果我们想要在 A 对象内部使用 B 对象,那么我们就会在 A 中直接 new 一个 B,这是我们程序员主动去创建的依赖对象;而<mark>反转则是使容器来帮忙创建并注入依赖对象,它使得对象 A 不需要再主动去创建对象 B 了,而是被动的接受由容器已经创建好的依赖对象</mark>,即<mark>对象创建、管理的控制权被反转了,依赖对象的获取被反转了</mark>。

IoC 的作用:

IoC 不是一种技术,只是一种<mark>思想</mark>,一个重要的面向对象编程的法则,它能指导我们如何<mark>设计出松耦合、更优良的程序</mark>。传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试;<mark>有了 IoC 容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是松散耦合,这样也方便测试</mark>。除此之外,<mark>将对象存储在 IoC 容器相当于将以后可能⽤到的所有⼯具制作好后都放到仓库中,需要的时候直接取出就⾏,⽤完再把它放回到仓库,利于复用</mark>。⽽ new 对象的⽅式相当于,每次需要⼯具时,才现做,⽤完就扔掉了也不会保存,下次再⽤的时候还得重新做。

注意:

并不是只有 Spring 的容器才叫 IoC 容器,基于 IoC 容器的框架还有很多,并非是 Spring 特有的

到这里 IoC 已经差不多介绍完了,从上面的介绍可知 IoC 它只是一种思想,那么 IoC 思想具体的实现是怎么样的呢?我们就不得不提到 DI。

1.3 DI 是什么?

DI(Dependency Injection),译为依赖注入,它是指 IoC 容器在运行期间,动态地将某种依赖关系注入到对象之中。依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。

DI 既然译为依赖注入,那么我们就要理解谁依赖谁,为什么需要依赖,谁注入谁,注入了什么?

  • 谁依赖谁:应用程序依赖 IoC 容器。
  • 为什么需要依赖:应用程序需要 IoC 容器来提供对象所需的外部资源。
  • 谁注入谁:IoC 容器注入应用程序某个对象。
  • 注入了什么:注入了某个对象所需的外部资源(包括对象、资源、常量数据等等)。

IoC 和 DI 的关系:

IoC 和 DI 其实是同一个概念的不同角度的描述,IoC 是一种思想,而 DI 是具体的实现,通过引入 IoC 容器,利用依赖关系注入的方式,就能实现对象之间的解耦。

1.4 Spring 的核心功能

回归主题,Spring 是包含了众多工具方法的 IoC 容器,我们已经介绍了 IoC 是什么,那么如何理解“Spring 就是一个 IoC 容器”呢?

上面介绍了很多关于 IoC 的知识,但是 Spring 的本质上是一个容器,容器才是 Spring 框架实现功能的核心,既然 Spring 是一个容器,它就会具备容器的两个最基础的功能:

  • 将对象存入到容器
  • 从容器中取出对象

也就是说 Spring 最核心的功能 就是将对象存入到 Spring 中,再从 Spring 中获取到对象。又因为 Spring 是⼀个 IoC 容器,那么除了它本身具备的存储对象和获取对象的能⼒,还具有管理对象的创建和销毁的权利。

1.5 Spring 的应用上下文

我们已经了解了 Spring 是一个 IoC 容器,但是容器只是一个提供给需要被管理的对象的空间,还需要通过 Spring 的应用上下文,来向容器中添加我们需要的被管理的对象。

Spring 的应用上下文 可以简单的理解成将你需要 Spring 帮你管理的对象放入到容器的容器对象,它是 Spring 容器的一种抽象化表述。

常用的 Spring 上下文对象为 ApplicationContext ,它本质上就是一个维护 Bean 定义以及对象之间协作关系的高级接口,它是继承了上下文对象 BeanFactory 后派生而来的应用上下文的抽象接口。在功能上,相比于 BeanFactory 只能提供基本的 DI 功能,它能提供更多的企业级的服务,比如对国际化支持、资源访问、以及事件传播等等。在性能上,ApplicationContext 是⼀次性加载并初始化所有的 Bean 对象(在获取上下文时,就会创建初始化所有的 Bean对象),所以调用快,⽽ BeanFactory 是需要哪个 Bean 才去加载哪个 Bean(在 getBean 时才会创建初始化指定的 Bean 对象),因此更加轻量,但调用慢。

对于上述两种上下文抽象接口,Spring 提供了多种类型的容器实现,用于不同场景的使用

  • ClassPathXmlApplicationContext:从类路径下的一个或多个 xml 配置文件中加载上下文,适用于 xml 配置的方式。
  • AnnotationConfigApplicationContext:从基于一个或多个 Java 的配置类中加载上下文,适用于 Java 注解的方式。
  • FileSystemXmlApplicationContext:从文件系统下的一个或多个 xml 配置文件中加载上下文,从系统盘符中加载 xml 配置文件。
  • AnnotationConfigWebApplicationContext:专门为 web 应用准备的,适用于注解方式。
  • XmlWebApplicationContext:从 web 应用下的一个或多个 xml 配置文件加载上下文,适用于 xml 配置方式。

在 Java 语言中对象也叫做 Bean,因此以下遇到的对象将以 Bean 著称。

2.1 创建 Maven 项目

  1. 创建一个 Maven 项目(在 IDEA 中创建好 Maven 项目)
  2. 添加 Spring 框架支持(在项目的 pom.xml 文件中添加 Spring 框架的支持,配置如下)
<p>Spring 项目需要添加的依赖有 spring-context(Spring 上下文)和 spring-beans(管理对象的模块)。可以去 Maven 中央仓库搜索,也可以直接使用下面的依赖。</p>
<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> 3. 添加启动类(在 /src/main/java 目录下创建一个启动类,类名自定义,包含 main 方法即可) ## 2.2 存储 Bean 对象 1. 创建好需要的 Bean(可以创建一个包,用于存放要创建的 Bean)
<p>存储 Bean 分为两个步骤,先创建好需要的 Bean,再将创建好的 Bean 注册到 Spring 容器中</p>
  1. 将 Bean 注册到 Spring 容器中
<p>先在 resources 目录下创建一个 xml 文件(文件名没有要求),用于存放 Spring 的配置文件,配置文件是固定的内容,内容如下。</p>
<?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>
<p>将创建好的 Bean 添加到 Spring 配置文件中。在原有的 beans 标签内再创建一个 beans 标签,里面用于存放要注册的 Bean,然后在创建好的 beans 标签内创建一个 bean 标签,表示要注册的某个 Bean,该 bean 标签内添加一个 id 属性表示存储对象在 Spring 中的身份标识,即 Bean 对象的名字,再添加一个 class 属性表示要存储的对象(包含包名和类名)</p>
补充: Spring 存储 Bean 时的命名规则
    2. 当 bean 标签中主动设置 id 属性来命名 Bean 对象的名字时,Spring 中存储该 Bean 的名字即为 id 的值。(源码如下)img
    2. 当 bean 标签没有设置 id 属性来命名 Bean 对象的名字时,Spring 中存储该 Bean 时生成的命名规则为:当类名的长度大于1,且第一个字符和第二个字符都为大写时,则直接命名为类名;否则将类名的第一个字符转成小写后,再返回新生成的字符。(源码如下)img

2.3 获取并使用 Bean 对象

  1. 创建 Spring 上下文(这里使用 ApplicationContext 作为上下文对象,也可以使用 BeanFactory) // 得到 Spring 的上下⽂对象,创建的时候需要配置 Spring 配置信息
    ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
  2. 获取指定的 Bean 对象(这里使用上文正已经注册到 Spring 容器中的 Bean 对象 User)
<p>getBean 方法用于加载上下文中的 Bean 对象,它有多种重载的方法</p>
    2. Object getBean(Bean 对象的名字) 2. T getBean(Bean 对象的名字, 要加载的类对象) 2. T getBean(要加载的类对象)

<p><mark>注意:</mark> 如果当前的一个类型被重复注册到 Spring 的配置文件中,那么不能使用第三种方式去获取 Bean 对象,需要根据名称来获取。</p>
<p>[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-y6UPvNZ1-1652100428089)(C:/Users/bbbbbge/Pictures/%E6%8E%A5%E5%8D%95/image-20220503022424788.png)]</p>
</blockquote> // 方式一
User user = (User) context.getBean("user");

// 方式二
User user = (User) context.getBean("user", User.class);

// 方式三
User user = (User) context.getBean(User.class);

  1. 使用 Bean user.say(); 注意:Spring 中的 Bean 默认是单例模式。可以再创建一个 User 实例,打印两个实例得到结论

上面实现了基本的 Spring 存储和读取对象的操作,但是在操作过程中并不是那么的简单,接下来将介绍更简单的存储和读取 Bean 对象的方法,即使用注解。

3.1 存储 Bean 对象

之前将 Bean 存储到 Spring 中的方法,是在 Spring 的配置文件中直接添加 bean 注册内容,示例如下:

img

而通过使用注解,就无需在存储一个 Bean 对象时,在 Spring 的配置文件中再添加一个注册内容,只要在 Spring 的配置文件中配置一个扫描路径即可实现 Bean 的批量注册。

3.1.1 配置扫描路径

Spring 引入了组件自动扫描机制,它可以在类路径底下,扫描配置的 base-package 包下所有标注了@Component、@Service、@Controller、@Repository、@Configuration 注解的类,并把这些类纳入进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" 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.t4.beans">
</content:component-scan>
</beans>

3.1.2 注册 Bean 相关的注解介绍

想要将对象存储在 Spring 中,有两种类型的注解可以实现:

  • 类注解: 注解在类上,用于标识组件的类型
注解 描述
@Controller 组合了 @Component 注解,应用在 mvc 层(控制层),DispatcherServlet 会自动扫描注解了此注解的类,然后将 web 请求映射到注解了@RequestMapping 的方法上。
@Service 组合了 @Component 注解,应用在 service 层(业务逻辑层)。
@Repository 组合了 @Component 注解,应用在 dao 层(数据访问层)。
@Component 表示该类是一个“组件”,成为 Spring 管理的 Bean。当使用基于注解的配置和类路径扫描时,这些类被视为自动检测的候选对象。同时@Component 还是一个元注解。
@Configuration 声明当前类为配置类,其中内部组合了@Component 注解,表明这个类是一个 Bean。
  • 方法注解: 注解在方法上
注解 描述
@Bean 声明当前方法的返回值是一个 Bean,返回的 Bean 对应的类中可以定义 init() 方法和 destroy() 方法。

为何说 @Controller、@Service、@Repository、@Configuration 都组合了 @Component 注解呢?

通过源码发现,这些注解本身都带了一个注解 @Component,即它们本身就属于 @Component 的“子类”

3.1.3 添加 @Controller 注解存储 Bean 对象

示例代码:

package com.t4.beans;

import org.springframework.stereotype.Controller;

@Controller
public class UserController { 
    public void say(){ 
        System.out.println("你好 Controller!");
    }
}

img

3.1.4 添加 @Service 注解存储 Bean 对象

示例代码:

package com.t4.beans;

import org.springframework.stereotype.Service;

@Service
public class UserService { 
    public void say(){ 
        System.out.println("你好 Service!");
    }
}

img

3.1.5 添加 @Repository 注解存储 Bean 对象

示例代码:

package com.t4.beans;

import org.springframework.stereotype.Repository;

@Repository
public class UserRepository { 
    public void say(){ 
        System.out.println("你好 Repository!");
    }
}

img

3.1.6 添加 @Component 注解存储 Bean 对象

示例代码:

package com.t4.beans;

import org.springframework.stereotype.Component;

@Component
public class UserComponent { 
    public void say(){ 
        System.out.println("你好 Component!");
    }
}

img

3.1.7 添加 @Configuration 注解存储 Bean 对象

示例代码:

package com.t4.beans;

import org.springframework.context.annotation.Configuration;

@Configuration
public class UserConfiguration { 
    public void say(){ 
        System.out.println("你好 Configuration!");
    }
}

img

3.1.8 添加 @Bean 注解存储 Bean 对象

注解 @Bean 需要与类注解搭配使用,例如常与注解 @Configuration 结合使用。带有 @Bean 注解的方法返回的对象会存到 Spring 容器中,Bean 对象的名字为方法名。

虽说使用 @Bean 注解要搭配类注解使用,看似要多出一笔,但是加上了类注解后能大大加快扫描的性能。

示例代码:

package com.t4.beans;

import com.t4.model.UserInfo;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class UserBeans { 
    @Bean
    public UserInfo getUserInfo(){ 
        UserInfo userInfo = new UserInfo();
        userInfo.setId(1);
        userInfo.setName("张三");
        userInfo.setPassword("1234");
        return userInfo;
    }
}

img

重命名 Bean:

由于方法名不能很直观的显示 Bean 名,为此可以重命名 Bean。重命名方式为:在 @Bean 注解后面增加一个 name 属性,该属性可以含多个值,其中 name = {} 都可以省略,只带新的 Bean 名。

img

注意1: 重命名后,如果起了多个名字,则使用任意一个都可以。但是不能再使用原方法名去获取 Bean

img

注意2: 重命名后,如果起了多个名字,获取的对象都是同一个img

3.2 获取 Bean 对象

获取 Bean 对象也叫做对象装配或者对象注入,是把对象从容器中取出来放到某个类中。

之前我们介绍了先通过获取 Spring 应用上下文,再从中获取 Bean 对象的方法去得到 Bean,但是整体显得比较麻烦。接下来将介绍三种更简单的对象注入的方法:

  • 方法一:属性注入
  • 方法二:构造方法注入
  • 方法三:Setter 注入

3.2.1 注入 Bean 相关注解介绍

这里介绍三种和注入 Bean 相关的注解:

  • 使用 @Autowired 自动注入的时候,可以在属性或参数加上 @Qualifier("xxx") 指定注入到对象;
  • 使用 @Autowired 注解时,当 Spring 无法找到匹配的 Bean 装配,它会抛出异常。要解决这个问题,可以通过 @Autowired 的 required 属性设置为 false 来禁用此检查功能。
  • Spring 将 @Resource 注解的 name 属性的值解析为为 Bean 的名字,type 属性的值解析为为 Bean 的类型。
  • 使用 @Resource 注解默认通过 Bean 名进行查询装配。如果使用 name 属性,则使用 Bean 名 自动注入策略;如果使用 type 属性,则使用 Bean 类型自动注入策略。
  • @Autowired 注解用于构造方法注入时,如果只有一个构造方法,可以省略使用 @Autowired,如果有多个注解则不能省略。
  • @Autowired 可以用于数组和使用泛型的集合类型,然后 Spring 会将容器中所有类型符合的 Bean 注入进来。@Autowired 标注作用于 Map 类型时,如果 Map 的 key 为 String 类型,则 Spring 会将容器中所有类型符合 Map 的 value 对应的类型的 Bean 增加进来,用 Bean 的 id 或 name 作为 Map 的 key。
  • @Autowired 标注作用于普通方法时,会产生一个副作用,就是在容器初始化该 Bean 实例的时候就会调用该方法。当然,前提是执行了自动装配,对于不满足装配条件的情况,该方法也不会被执行。

3.2.2 属性注入 Bean 对象

在需要的类中先创建一个要被注入的对象(该对象可以是一个集合,那么得到的结果就是所有匹配的 Bean 的集合),并在该对象上加上 @Autowired 或 @Resource 注解。

代码情景:

创建了一个 UserService 类,里面包含了 name 和 password 两个字段,将这个类存储到 Spring 中。在上述代码中的 UserController 类中通过 @Autowired 注解来将 UserService 这个对象注入到该类中,并创建了一个 getUserService 方法打印获取到的 Bean 对象。而 UserController 类本身又在启动类中被获取后调用 getUserService 方法。

示例代码1: 使用 @Autowired 注解

@Controller
public class UserController { 
    @Autowired
    private UserService userService;

    public void getUserService(){ 
        System.out.println(userService);
    }
}

示例代码2: 使用 @Resource 注解

@Controller
public class UserController { 
    @Resource
    private UserService userService;

    public void getUserService(){ 
        System.out.println(userService);
    }
}

img

3.2.3 构造方法注入 Bean 对象

在需要的类中先创建一个要被注入的对象,然后创建该类的构造方法,将要被注入的对象进行赋值,并在构造方法上加上 @Autowired 注解。

代码情景:

创建了一个 UserService 类,里面包含了 name 和 password 两个字段,将这个类存储到 Spring 中。在上述代码中的 UserController 类中通过 @Autowired 注解来将 UserService 这个对象注入到该类中,并创建了一个 getUserService 方法打印获取到的 Bean 对象。而 UserController 类本身又在启动类中被获取后调用 getUserService 方法。

示例代码: 使用 @Autowired 注解

@Controller
public class UserController { 

    private UserService userService;
    @Autowired
    public UserController(UserService userService){ 
        this.userService = userService;
    }
    
    public void getUSerService(){ 
        System.out.println(userService);
    }
}

img

3.2.4 Setter 注入 Bean 对象

在需要的类中先创建一个要被注入的对象,然后创建该类的 Setter 方法,将要被注入的对象进行赋值,并在 Setter方法上加上 @Autowired 或 @Resource 注解。

代码情景:

创建了一个 UserService 类,里面包含了 name 和 password 两个字段,将这个类存储到 Spring 中。在上述代码中的 UserController 类中通过 @Autowired 注解来将 UserService 这个对象注入到该类中,并创建了一个 getUserService 方法打印获取到的 Bean 对象。而 UserController 类本身又在启动类中被获取后调用 getUserService 方法。

示例代码1: 使用 @Autowired 注解

@Controller
public class UserController { 

    private UserService userService;
    @Autowired
    public void setUserService(UserService userService) { 
        this.userService = userService;
    }

    public void getUSerService(){ 
        System.out.println(userService);
    }

}

示例代码2: 使用 @Resource 注解

@Controller
public class UserController { 

    private UserService userService;
    @Resource
    public void setUserService(UserService userService) { 
        this.userService = userService;
    }

    public void getUSerService(){ 
        System.out.println(userService);
    }

}

img

3.2.5 三种注入方式的优缺点

3.2.6 同一类型多个 @Bean 报错问题

当出现多个 Bean 返回的对象类型都相同时,如果注入的注解不设置具体注入的哪个 Bean,则会报错。对于这个问题有两种解决方式:

  • 对于 @Autowired 注解,可以搭配一个 @Qualifuer 来指定要注入的 Bean
  • 对于 @Resource 注解,可以设置一个 name 属性来指定要注入的 Bean

代码情景:

创建了一个 User 类,里面包含了 name 和 password 两个字段。再创建一个 UserService 类这个类通过两个 @Bean 注解的方法注册两个类型相同,名字不同的 Bean 存储到 Spring 中。在上述代码中的 UserController 类中通过 @Autowired 注解来将 User 这个对象注入到该类中,并创建了一个 printUser 方法打印获取到的 Bean 对象。而 UserController 类本身又在启动类中被获取后调用 printUser 方法。

示例代码1: 对于 @Autowired 的解决方式

@Controller
public class UserController { 

    @Autowired
    @Qualifier(value = "user1")
    private User user;

    public void printUser(){ 
        System.out.println(user);
    }

}

img

示例代码2: 对于 @Resource 的解决方式

@Controller
public class UserController { 

    @Resource(name = "user2")
    private User user;

    public void printUser(){ 
        System.out.println(user);
    }

}

img

当在 Spring 中定义一个 bean 时,你可以声明该 bean 的作用域。例如,为了强制 Spring 在每次需要时都产生一个新的 bean 实例,你应该声明 bean 的作用域的属性为 prototype,即多例作用域。同理,如果你想让 Spring 在每次需要时都返回同一个 bean 实例,你应该声明 bean 的作用域的属性为 singleton,即单例作用域。

在 Spring 中 Bean 默认情况下是单例状态,即所有人拿到的 Bean 都是同一个对象。设置为单例减少了新生成实例的消耗、减少了 JVM 垃圾回收、能够快速获取到 Bean,也就是说能够很大程度的提高性能。

4.1 Bean 的六种作用域

Spring 容器在初始化一个 Bean 实例时,同时会指定该实例的作用域。Spring 有六种作用域,前两种是基于 整个 Spring 生态生效的,后四种是基于 Spring MVC 生效的。

4.1.1 单例作用域 singleton

  • 官方说明: (Default) Scopes a single bean definition to a single object instance for each Spring IoC container.
  • 描述: 该作⽤域下的 Bean 在 IoC 容器中只存在⼀个实例:获取 Bean(即通过 applicationContext.getBean等⽅法获取)及装配 Bean(即通过 @Autowired 注⼊)都是同⼀个对象。
  • 场景: 通常⽆状态的 Bean 使⽤该作⽤域。⽆状态表示 Bean 对象的属性状态不需要更新。
  • 备注: Spring 默认选择该作⽤域

4.1.2 原型作用域/多例作用域 prototype

  • 官⽅说明: Scopes a single bean definition to any number of object instances.
  • 描述: 每次对该作⽤域下的 Bean 的请求都会创建新的实例:获取 Bean(即通过 applicationContext.getBean 等⽅法获取)及装配Bean(即通过 @Autowired 注⼊)都是新的对象实例。
  • 场景: 通常有状态的 Bean 使⽤该作⽤域

4.1.3 请求作用域 request

  • 官⽅说明: Scopes a single bean definition to the lifecycle of a single HTTP request. That is, each HTTP request has its own instance of a bean created off the back of a single bean definition. Only valid in the context of a web-aware Spring ApplicationContext.
  • 描述: 每次 HTTP 请求会创建新的 Bean 实例,类似于 prototype
  • 场景: ⼀次 HTTP 的请求和响应的共享 Bean
  • 备注: 限定 Spring MVC 中使⽤

4.1.4 会话作用域 session

  • 官⽅说明: Scopes a single bean definition to the lifecycle of an HTTP Session. Only valid in the context of a web-aware Spring ApplicationContext.
  • 描述: 在⼀个 HTTP Session 中,定义⼀个 Bean 实例
  • 场景: ⽤户会话的共享 Bean,⽐如:记录⼀个⽤户的登陆信息
  • 备注: 限定 Spring MVC 中使⽤

4.1.5 全局作用域 application

  • 官⽅说明: Scopes a single bean definition to the lifecycle of a ServletContext. Only valid in the context of a web-aware Spring ApplicationContext.
  • 描述: 在⼀个 http servlet Context 中,定义⼀个 Bean 实例
  • 场景: Web 应⽤的上下⽂信息,⽐如:记录⼀个应⽤的共享信息
  • 备注: 限定 SpringMVC 中使⽤

4.1.6 HTTP WebSocket 作用域 websocket

  • 官⽅说明: Scopes a single bean definition to the lifecycle of a WebSocket. Only valid in the context of a web-aware Spring ApplicationContext.
  • 描述: 在⼀个 HTTP WebSocket 的⽣命周期中,定义⼀个 Bean 实例
  • 场景: WebSocket 的每次会话中,保存了⼀个 Map 结构的头信息,将⽤来包裹客户端消息头。第⼀次初始化后,直到 WebSocket 结束都是同⼀个 Bean。
  • 备注: 限定 Spring WebSocket 中使⽤

4.2 单例作用域和全局作用域的区别

  • singleton 是 Spring Core 的作用域;application 是 Spring Web 的作用域
  • singleton 作用于 IoC 容器;application 作用于 Servlet 容器

4.3 设置作用域的方法

设置 Bean 的作用域有两种方式

  • 方式一:在 Spring 配置文件中,对于注册的每一个 Bean 可以增加一个 scope 属性来设置 Bean 的作用域
  • 方式二:通过 @Scope 注解就可以声明 Bean 的作用域,它既可以修饰方法也可以修饰类

示例代码1: 方式一

img

代码示例2: 方式二

img

5.1 Spring 的执行流程

Spring 的执行流程可以分为以下几步:

  1. 启动 Spring 容器(在 main 方法中启动)
  2. 实例化 Bean(加载 Spring 配置文件,实例化并申请内存)
  3. 将 Bean 注册到 Spring 中(将添加了和 Spring 注册 Bean 相关的注解的对象存储到容器中)
  4. 将 Bean 装配到需要的类中

5.2 Bean 的生命周期

Bean 的生命周期可以分为以下几个部分:

  1. 实例化 Bean(为 Bean 分配内存空间)
  2. 设置 Bean 属性(依赖注入和装配,必须在初始化 Bean 之前,因为初始化的时候可能会用到 Bean 对象)
  3. 初始化 Bean(初始化 Bean 时会按顺序执行以下几小步)
    3. 实现各种 Aware 通知的方法,如 BeanNameAware、BeanFactoryAware、ApplicationContextAware 的接口方法 3. 执行 BeanPostProcessor 初始化前置方法 3. 执行 @PostConstruct 初始化方法,依赖注入操作之后被执行 3. 判断是否继承了 InitializingBean 接口(调用 afterPropertiesSet 方法) 3. 执行自己的 init-method 初始化方法 3. 执行 BeanPostProcessor 初始化后置方法
  1. 使用 Bean
  2. 销毁 Bean
    11. 执行 @PreDestory 销毁前的方法 11. 判断是否继承了 DisposableBean 接口(调用 11. 执行自己的 destory-method 销毁前的方法

接下来可以通过一段代码来验证 Bean 的生命周期的过程,示例代码如下:

@Controller
public class BeanLife implements BeanNameAware, InitializingBean, DisposableBean { 

    @Override
    public void setBeanName(String s) { 
        System.out.println("执行 BeanNameAware 接口的方法:" + s);
    }

    @PostConstruct
    public void postConstruct(){ 
        System.out.println("执行 @PostConstruct");
    }

    @Override
    public void afterPropertiesSet() throws Exception { 
        System.out.println("继承了 InitializingBean");
    }

    public void myInit(){ 
        System.out.println("执行 init-method");
    }

    @PreDestroy
    public void preDestroy(){ 
        System.out.println("执行 @PreDestroy");
    }

    public void destroy(){ 
        System.out.println("继承了 DisposableBean");
    }

    public void myDestroy(){ 
        System.out.println("执行 destroy-method");
    }

}

img

本文为互联网自动采集或经作者授权后发布,本文观点不代表立场,若侵权下架请联系我们删帖处理!文章出自:https://t4dmw.blog.csdn.net/article/details/124674940
-- 展开阅读全文 --
BUUCTF Web [极客大挑战 2019]Knife
« 上一篇 06-24
安全面试之XSS(跨站脚本攻击)
下一篇 » 07-24

发表评论

成为第一个评论的人

热门文章

标签TAG

最近回复