Spring框架的创建使用与Bean的作用域和生命周期

一句话描述Spring :

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

一 、IOC的理解

IOC:是一个控制反转的容器 :
那什么是控制反转:

就是在我们创建的一个A类中, 如果要使用B类按照传统的做法,我们需要在A类中new一个B类的对象, 从而使用B类中的方法.这样在如果我们修改了B类中的一些属性, 在A类中new的时候也需要进行相应的修改,程序的耦合程度( 相关性) 比较高.一旦要修改,就需要修改很多的代码
控制反转就是我们要在A类中使用 B类的方法, 不在A类中创建B类对象, 而是将 创建对象的过程交给 Spring 来完成,放入IOC容器中, 在我们需要B类的这个对象的时候,直接去Spring的IOC容器中取出来使用即可. 这样就实现了代码之间的解耦, 使得代码之间的相关性大大降低

DI 依赖注入:

就是为IOC容器中的Spring所管理的对象和属性进行赋值 , 交给Spring所管理的对象称为bean

二、创建一个Spring项目

1)创建一个Maven项目
2)导入 Spring相关的依赖 Spring-context 和 Spring-Bean;

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

3)创建main方法,用于启动

4)创建相关类进行使用Spring

public class User {
    public static void sayHi() {
        System.out.println("SayHi");
    }
}

5)配置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: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
       http://www.springframework.org/schema/context/spring-context-4.0.xsd">
</beans>

6)相关类存放到Spring中,进行管理
在spring配置文件中使用<bean id = “自定义名称”, class = “类的路径”

 <bean id="user" class="com.ykh.User"/>

注意点:通常 id 设置为 类名的小驼峰方式

7)使用

主类中 ApplicationContext context = new ClassPathXmlApplicationContext(“spring-config.xml”);先获取Spring容器

在通过 context.getBean(“配置文件中自定义的bean名称”)获取bean对象, 此处返回Object型对象,需要进行强转.

public class Main {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");  //参数为 spring配置文件的名称
        //获取bean对象的3种方法 :
        // 1.使用 id 唯一标识获取
        User bean1 = (User)context.getBean("user");
        System.out.println(bean1);
        // 2.通过全类名获取 ( 最常用)
        // (注意:  1. 根据类型获取Bea时,要求IOC容器中有且只有一个类型匹配的bean, 如果有多个会 抛异常 NoUniqueBeanDefinitionException
        //        2. 若一个都没有 , 抛异常: NoSuchBeanDefinitionException
        User bean2 = context.getBean(User.class);
        System.out.println(bean2);

        //3 .根据bean 的 id 和 类型来获取
        User bean3 = context.getBean("user",User.class);
        System.out.println(bean3);
    }
}

三、基于注解管理bean

通过上面的方法可以获取到bean对象, 但是还是过于麻烦, 我们每创建一个类要想将其通过Spring进行管理,都需要进行在 配置文件中 添加一个bean.

Spring中为我们提供了一种更加方便的方式去 将类 交给Spring管理

image-20221107123525077

 <context:component-scan base-package="com.ykh"/>

在 Spring-config.xml 中写上如下语句 , 用于扫描 base-package = 后面的路径下的所有类。

那我们如何让Spring知道,我们要将哪些类交给Spring管理呢?

我们可以使用两类注解:

(一) 类注解管理bean

将要托管给Spring的类上加上类注解 :

@Controller  (控制器)
@Service     (服务)
@Repository  (仓库)
@Component    (组件)
@Configuration (配置)

结论: @Controller (控制器) \ @Service (服务) @Repository (仓库) @Configuration (配置) 都是基于@Component 实现的

加入其中一个即可, 具体哪个根据你这个类的作用进行选择

此时运行程序, 就会自动扫描 对应的路径 下的类, 将被注解标记的类,交给Spring管理, 简化了写bean的过程.

使用:

public class Main {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");  //参数为 spring配置文件的名称
        //获取bean对象的3种方法 :
        // 1.使用 id 唯一标识获取
        User bean1 = (User)context.getBean("user");
    }
}

使用扫描的方式添加bean, 其每个类对应的id 默认是 该类名的小驼峰写法

(二)方法注解@bean

类注解是作用在类上,将类交给Spring管理, 我们可以使用@Bean注解将方法同样交给Spring管理, 只需要方法上加上@Bean注解即可。

注意: Spring在实例化容器中管理的Bean时, 是通过无参构造来创建对象的, 加入Spring容器中的类必须要有无参构造. 否则在创建的时候就会报错.如下图

image-20221120221310266

四、引入bean的三种方式

(一)属性注入

使用@Autowired注解,得到的是一个单例的对象

@Autowired
UserService userService;

优点: 写法简单

缺点:

  1. 不能注入一个final 修饰的属性

    原因: java语法规定 : final 修饰的变量需满足两种场景,

    在使用时直接赋值

    在构造方法中直接赋值

  2. 通用性问题: 只适用于IOC框架(容器),代码的可移植性不高, 代码放到其他框架中就无法使用

  3. 设计原则问题: 更容易违背单一设计原则.(因为使用简单,所以滥用的风险更大)

(二)setter注入

写法:

将@Autowired注解加在setter方法上

@Autowird
UserService uerService;

public void setterUserService(UserService uerService){
	this.userService = userService;
}

优点: 符合单一设计的原则( 一个Setter只针对一个对象)

缺点:

  1. 不能注入一个不可变对象 ( 无法使用final修饰该变量
  2. 注入对象可能被改变
(三)构造器注入
    UserService userService;
    @Autowired
    public UserController(UserService userService) {
        this.userService = userService;
    }

注意:

  1. 当使用构造器注入时, 如果只有一个构造方法,可以不用在构造方法上加@Autowired注解
  2. 加了@Autowired的构造中的类必须都在spring容器中存在,否则会报错
  3. 一个类中可以有多个构造方法, 但只能有一个使用了@Autowired注解的构造方法.

优点:

  1. 可以注入一个不可变的对象( final修饰的对象)

原因: 根据java语法的规定, final修饰的变量在必须在定义的时候进行赋值,或者在构造方法中进行赋值, 此时我们使用构造方法进行注入,符合java的语法规范

  1. 注入的对象不可以被修改.

原因: 构造方法只会在创建的时候执行一次

  1. 依赖对象在使用前一定会被完全初始化.

原因: 依赖是在类的构造方法中执行的,而 构造方法是类创建之初就会执行的方法.

  1. 通用性更好

原因: 因为构造方法是java支持的,所以换到其他任何框架都适用**

缺点:

如果一次性进行多个对象的注入, 会不符合单一设计的原则

(四) 使用Resource注解注入bean

在进⾏类注⼊时,除了可以使⽤ @Autowired 关键字之外,我们还可以使⽤ @Resource 进⾏注⼊,如下:

  1. 属性注入

        @Resource
        UserService userService;
    
  2. Setter注入

       UserService userService;
        @Resource
        public void setUserService(UserService userService) {
            this.userService = userService;
        }
    
  3. 目前Resource注解并不支持构造器注入

    因为构造器是在类加载的时候就进行的, 而Resource是源于JDK的,所以,在使用构造器的时候,Resource可能还没有被加载

    image-20221118145357087

Resource 和 Autowired的区别

  1. 查找Bean的顺序上的区别

Resource : 实现先根据我们定义的对象名去Spring容器中进行查找,再根据对象的类型查找

Autowired: 先根据类名查找,再根据对象名查找

  1. 其中所具有的方法的区别

    Autowired中只有一个Require方法,默认为true

    image-20221118150108898

​ Resource中则有多个方法,可适应多种不同的场景

image-20221118150228882

解决同⼀个类型,多个 bean 的解决⽅案有以下两个:

  1. 使⽤ @Resource(name=“user1”) 定义。
  2. 使⽤ @Qualifier 注解定义名称。

五、Bean的作用域与生命周期

(一)作用域

Bean的作用域指的是Bean在Spring容器中的一种行为模式, Bean的作用域共有以下六种

  1. singleton: 单例作用域
  2. prototype: 原型作用域
  3. request: 请求作用域
  4. session: 会话作用域
  5. application: 全局作用域
  6. websocket: HTTP Websocket作用域

注意: 后四种是SpringMVC中的作用域, 普通的Spring项目只有前两种

1. Singleton作用域

描述:该作⽤域下的Bean在IoC容器中只存在⼀个实例:获取Bean(即通过 applicationContext.getBean等⽅法获取)及装配Bean(即通过@Autowired注⼊)都是同⼀ 个对象。( 单例模式)

场景:通常⽆状态的Bean使⽤该作⽤域。⽆状态表示Bean对象的属性状态不需要更新

备注: Spring中默认使用该作用域

2. prototype: 原型作用域

描述:每次对该作⽤域下的Bean的请求都会创建新的实例:获取Bean(即通过 applicationContext.getBean等⽅法获取)及装配Bean(即通过@Autowired注⼊)都是新的 对象实例。 (多例模式)

场景:通常有状态的Bean使⽤该作⽤域

3. request: 请求作用域

描述:每次http请求会创建新的Bean实例,类似于prototype

场景:⼀次http的请求和响应的共享Bean

备注:限定SpringMVC中使⽤

4. session: 会话作用域

描述:在⼀个http servlet Context中,定义⼀个Bean实例

场景:Web应⽤的上下⽂信息,⽐如:记录⼀个应⽤的共享信息

备注:限定SpringMVC中使⽤

5. application 全局作用域

描述:在⼀个http servlet Context中,定义⼀个Bean实例

场景:Web应⽤的上下⽂信息,⽐如:记录⼀个应⽤的共享信息

备注:限定SpringMVC中使⽤

6. websocket : HTTP Websocket作用域

描述:在⼀个HTTP WebSocket的⽣命周期中,定义⼀个Bean实例

场景:WebSocket的每次会话中,保存了⼀个Map结构的头信息,将⽤来包裹客户端消息 头。第⼀次初始化后,直到WebSocket结束都是同⼀个Bean。

备注:限定Spring WebSocket中使⽤

单例作用域(singleton) 和 全局作用域 (application) 的区别

  • singleton 是 SpringCore中的作用域, application 是SpringWeb 中的作用域
  • singletons作用于IOC容器, 而application作用于Servlet容器
7. Bean的作用域设置

可以通过@Scope注解 , 写在你要改变作用域的Bean的位置

第一种写法: (这种IDEA会提示,不容易出错)

@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Repository
public class UserRepositoryImpl {

    public User getUser() {
        return new User(1,"张三");
    }
}

第二种写法:

@Scope("prototype")
@Repository
public class UserRepositoryImpl {

    public User getUser() {
        return new User(1,"张三");
    }
}

(二)Bean的生命周期

Bean 的生命周期分为以下 5 部分:

1.实例化 Bean(为 Bean 分配内存空间)

2.设置属性(Bean 注入和装配)

3.Bean 初始化

  • 实现了各种 Aware 通知的方法,如 BeanNameAware、BeanFactoryAware、ApplicationContextAware 的接口方法;
  • 执行 BeanPostProcessor 初始化前置方法;
  • 执行 @PostConstruct 初始化方法,依赖注入操作之后被执行;
  • 执行自己指定的 init-method 方法(如果有指定的话);
  • 执行 BeanPostProcessor 初始化后置方法。

4.使用 Bean

5.销毁 Bean

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值