欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

[框架学习 | 第三部分] Spring 上篇(Spring 入门、核心功能、Spring Bean -> 定义、范围、生命周期、依赖注入) - 3.

最编程 2024-03-11 19:05:11
...

3.1Bean定义

简单来说,Bean 代指的就是那些被 IoC 容器所管理的对象

我们需要告诉 IoC 容器帮助我们管理哪些对象,这个是通过配置元数据来定义的。配置元数据可以是 XML 文件、注解或者 Java 配置类。

3.1.1元数据—>Bean标签的属性

  1. 在IOC容器创建Bean对象时候,需要知道这三个信息
    • 如何创建一个bean(class)
    • bean的生命周期
    • bean的依赖关系

所有配置元数据都转换为一组构成每个 bean 标签定义的以下属性

序号 属性 描述
1 class 指定了用于创建 bean 的 bean类型
2 id或者name 唯一地指定 bean 标识符
3 scope 指定从特定 bean 定义创建的对象的范围
4 constructor-arg 用于注入依赖关系
5 properties 用于注入依赖关系
6 autowiring mode 用于注入依赖关系
7 lazy-initialization mode 延迟初始化的 bean,即 告诉 IoC 容器在第一次被请求时创建一个 bean 实例,而不是在启动时
8 initialization method 在容器设置了 bean 上的所有必要属性之后调用的回调
9 destruction method 包含 bean 的容器被销毁时要使用的回调

3.1.2配置的方式

Spring IoC 容器与实际写入此配置元数据的格式完全分离。 以下是向 Spring 容器提供配置元数据的三个重要方法 −

  • 基于 XML 的配置文件。(Spring)
  • 基于注解的配置(SpringBoot)
  • 基于 Java 的配置
(1)XML方式
<bean id = "helloWorld" class = "com.tutorialspoint.HelloWorld">
      <property name = "message" value = "Hello World!"/>
   </bean>
(2)注解方式
  1. @Component通用的注解,可标注任意类为 Spring 组件。如果一个 Bean 不知道属于哪个层,可以使用@Component 注解标注。
  2. @Repository : 对应持久层即 Dao 层,主要用于数据库相关操作。
  3. @Service : 对应服务层,主要涉及一些复杂的逻辑,需要用到 Dao 层。
  4. @Controller : 对应 Spring MVC 控制层,主要用于接受用户请求并调用 Service 层返回数据给前端页面。

3.1.3@Component和@Bean的区别?

  • 相同:两者都是用来声明一个Bean类,交给IOC容器处理

  • 不同:

    1. 作用的地方不同@Component 注解作用于类,而==@Bean注解作用于方法==。
    2. 产生的作用不同
      1. @Component通常是通过类路径扫描来自动侦测以及自动装配到 Spring 容器中(我们可以使用 @ComponentScan 注解定义要扫描的路径从中找出标识了需要装配的类自动装配到 Spring 的 bean 容器中)。
      2. @Bean 注解通常是我们在标有该注解的方法中定义产生这个 bean,@Bean告诉了 Spring 这是某个类的实例,当我需要用它的时候还给我。
    3. 自定义强度不同
      1. @Bean 注解比 @Component 注解的自定义性更强
      2. 很多地方我们只能通过 @Bean 注解来注册 bean。比如当我们引用第三方库中的类需要装配到 Spring容器时,则只能通过 @Bean来实现
(1)@Bean使用示例
@Configuration
public class AppConfig {
    @Bean
    public TransferService transferService() {
        return new TransferServiceImpl();
    }

}

上面的代码相当于下面的 xml 配置:

<beans>
    <bean id="transferService" class="com.acme.TransferServiceImpl"/>
</beans>

3.2Bean作用域

3.2.1作用域分类

  1. singleton : IoC 容器中只有唯一的 bean 实例。Spring 中的 bean 默认都是单例的,是对单例设计模式的应用。
  2. prototype : 每次获取都会创建一个新的 bean 实例。也就是说,连续 getBean() 两次,得到的是不同的 Bean 实例。
  3. request (仅 Web 应用可用): 每一次 HTTP 请求都会产生一个新的 bean(请求 bean),该 bean 仅在当前 HTTP request 内有效。
  4. session (仅 Web 应用可用) : 每一次来自新 session 的 HTTP 请求都会产生一个新的 bean(会话 bean),该 bean 仅在当前 HTTP session 内有效。
  5. application/global-session (仅 Web 应用可用):每个 Web 应用在启动时创建一个 Bean(应用 Bean),该 bean 仅在当前应用启动时间内有效。
  6. websocket (仅 Web 应用可用):每一次 WebSocket 会话产生一个新的 bean。

3.2.2配置作用域的方式

(1)xml方式
<bean id="..." class="..." scope="singleton"></bean>
(2)注解方式
@Bean
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Person personPrototype() {
    return new Person();
}

3.3Bean生命周期

对于普通的 Java 对象来说,它们的生命周期就是:

  • 实例化
  • 该对象不再被使用时通过垃圾回收机制进行回收

对于 Spring Bean 的生命周期来说:

  1. 实例化bean
  2. bean属性赋值
  3. 初始化bean
    1. 执行处理aware接口,分别调用 setBeanName()、setBeanFactory()、 setApplicationContext()方法
    2. 调用BeanPostProcessor的预初始化方法
    3. 调用InitializingBean的afterPropertiesSet()方法
    4. 调用init-method属性指定初始化方法
    5. 调用BeanPostProcessor的初始化后方法
  4. 使用bean
  5. 销毁bean
    1. 调用DisposableBean的destroy方法
    2. 调用destroy-method属性指定的方法

Spring Bean 生命周期

3.3.1初始化回调

(1)InitializingBean的afterPropertiesSet()方法

org.springframework.beans.factory.InitializingBean 接口指定单个方法

void afterPropertiesSet() throws Exception;

可以简单地实现上述接口,并且可以在 afterPropertiesSet() 方法中完成初始化工作,如下所示 −

public class ExampleBean implements InitializingBean {
   public void afterPropertiesSet() {
      // do some initialization work
   }
}
public class ExampleBean implements InitializingBean {
   public void afterPropertiesSet() {
      // do some initialization work
   }
}
(2)init-method属性指定初始化方法

基于 XML 的配置元数据的情况下,您可以使用 init-method 属性来指定具有 void 无参数签名的方法的名称。

<bean id = "exampleBean" class = "examples.ExampleBean" init-method = "init"/>
  • 下面是类的定义
public class ExampleBean {
   public void init() {
      // do some initialization work
   }
}

3.3.2销毁回调

(1)DisposableBean的destroy方法

org.springframework.beans.factory.DisposableBean 接口指定一个方法

void destroy() throws Exception;

因此,您可以简单地实现上述接口,并且可以在destroy() 方法中完成如下工作

public class ExampleBean implements DisposableBean {
   public void destroy() {
      // do some destruction work
   }
}
(2)destroy-method属性指定销毁方法
  • xml配置文件
<bean id = "exampleBean" class = "examples.ExampleBean" destroy-method = "destroy"/>
  • 类的定义
public class ExampleBean {
   public void destroy() {
      // do some destruction work
   }
}

3.4Bean依赖注入

3.4.1定义

  • 依赖bean对象的依赖于容器
  • 注入bean对象中的所有属性,由容器来注入

依赖注入,是IOC的一个方面,是个通常的概念,它有多种解释。这概念是说你不用创建对象,而只需要描述它如何被创建。你不在代码里直接组装你的组件和服务,但是要在配置文件里描述哪些组件需要哪些服务,之后一个容器(IOC容器)负责把他们组装起来

3.4.2依赖注入的作用

  1. 作用:降低程序间的耦合

    依赖关系的管理,以后都交给spring来维护

    在当前类需要用到其他类的对象,由spring为我们提供,我们只需要在配置文件中说明依赖关系的维护,就称之为依赖注入。

3.4.3依赖注入的方式

  • 可以注入的数据(三类):

    • 基本类型和String
    • 其他bean类型(在配置文件中或者注解配置过的bean)
    • 复杂类型/集合类型:(不演示)
  • 注入的方式(三种):十分重要

    • 构造函数注入
    • set方法注入
    • 基于xml的自动装配(bean标签的autowire属性(byName—>根据bean标签的id属性查找、byType——>根据bean标签的Class属性查找))
    • 注解注入()
(1)构造函数注入
  • 定义:用类中的构造函数,给成员变量赋值。注意,赋值的操作不是我们自己做的,而是通过配置的方式,让 spring 框架来为我们注入。

  • 说明:

    • 使用的标签:constructor-arg
    • 标签出现的位置:bean标签的内部
    • 标签中的属性:
      • type:用于指定要注入的数据的数据类型,该数据类型也是构造函数中某个或某些参数的类型。
      • index:用于指定要注入的数据给构造函数中指定索引位置的参数赋值。索引的位置是从0开始。
      • name:用于指定给构造函数中指定名称的参数赋值。
      • ref:用于指定其他的bean类型数据。它指的就是在spring的Ioc核心容器中出现过的bean对象
      • value:要注入的数据
    • 优点:在获取bean对象时,注入数据是必须的操作,否则对象无法创建成功。
    • 缺点:改变了bean对象的实例化方式,使得我们在创建对象时,如果用不到这些数据,也必须提供
  • 举例

    <!--默认构造器方式-->
    <bean id="user" class="com.kuang.pojo.User">
      <property name="name" value="张三"/>
    </bean>
    
    <!--通过有参构造创建对象。方式一:下标赋值-->
    <bean id="user" class="com.kuang.pojo.User">
      <constructor-arg index="0" value="jerry"/>
    </bean>
    
    <!--通过有参构造创建对象。方式二:类型创建,不建议使用-->
    <bean id="user" class="com.kuang.pojo.User">
      <constructor-arg type="java.lang.String" value="jarry"/>
    </bean>
    
    <!--通过有参构造创建对象。方式三:通过参数名,推荐使用-->
    <bean id="user" class="com.kuang.pojo.User">
      <constructor-arg name="name" value="jarry"/>
      <constructor-arg name="birthday" ref="now"/>
    </bean>
    <!-- 配置一个日期对象 -->
    <bean id="now" class="java.util.Date"></bean>
    
(2)Set方式注入(常用)
  • 定义:在类中提供需要注入成员的 set 方法
  • 说明:
    • 涉及的标签:property
    • 出现的位置:bean标签的内部
    • 标签的属性
      • name:用于指定注入时注入的对象的名称(IOC容器会调用该对象的set方法)
      • value:用于提供基本类型和String类型的数据
      • ref:用于指定其他的bean类型数据
    • 优势:创建对象时没有明确的限制,可以直接使用默认构造函数。
    • 弊端:如果有某个成员必须有值,则获取对象是有可能set方法没有执行。
(3)基于xml的自动装配
  • 定义:
    • 自动装配是spring满足bean依赖注解一种方式
    • Spring会在上下文中自动寻找,并自动给bean装配属性
  • 三种自动装配方式:
    • 在xmI中显示的配置(ByName、ByType)
    • 注解显示配置
    • 隐式的自动装配bean 【重要】
①搭建环境
    public class Cat {
        public void shout(){
            System.out.println("喵喵");
        }
    }
    
    public class Dog {
        public void shout(){
            System.out.println("旺旺");
        }
    }
    
    public class Person {
    
        private Cat cat;
        private Dog dog;
        private String name;
    
        @Override
        public String toString() {
            return "Person{" +
                    "cat=" + cat +
                    ", dog=" + dog +
                    ", name='" + name + '\'' +
                    '}';
        }
    	//getting、setting
    }
②ByName自动装配
  • byName:会自动在容器上下文中查找,和自己对象set方法后面的值对应的beanId
<bean id="cat" class="com.kuang.pojo.Cat"/>
<bean id="dog" class="com.kuang.pojo.Dog"/>

<bean id="person" class="com.kuang.pojo.Person" autowire="byName">
  <property name="name" value="小海子"/>
</bean>
③ByType自动装配
  • byType:会自动在容器上下文中查找,和自己对象属性类型相同的bean
    <bean class="com.kuang.pojo.Cat"/>
    <bean class="com.kuang.pojo.Dog"/>
    
    <bean id="person" class="com.kuang.pojo.Person" autowire="byType">
      <property name="name" value="小海子"/>
    </bean>
④小结
  • byname的时候,需要保证所有bean的id唯一 ,并且这个bean需要和自动注入的属性的set方法的值一致。
  • bytype的时候, 需要保证所有bean的class唯一 ,并且这个bean需要和自动注入的属性的类型一致;全局唯一,id属性可以省略。
(4)基于注解的自动装配
  • jdk1.5支持的注解,Spring2.5支持注解

  • 作用:和在xml配置文件中的bean标签中写一个标签的作用是一样

  • 注解:

    注解 功能 使用前提
    @Autowired 根据属性类型进行自动装配
    即通过byType——>bean标签的class属性方式实现
    /
    @Qualifer 根据属性名称(由@Component的value值指定)进行注入
    即通过byName——>bean标签的id属性实现
    在该属性注入的基础上
    @Resource 根据类型注入,可以根据名称注入(默认根据类型注入) /
    @Value 注入普通类型属性 /

3.4.4详讲@Autowired、@Qualifer、@Resource、@Value

  • spring的IOC底层实际上就是一个Map结构容器,所谓**key 就是 bean标签 中的 idvalue 则是对应 bean标签 中的 class**
(1)@Autowired
①用在何处?
  1. Spring 2.5 引入了 @Autowired 注释,可以对类成员变量方法构造函数、参数等进行标注【主要还是用在变量方法】,完成自动装配的工作。
  2. 使用@Autowired注解注入的属性,该属性不需要这个类提供set方法,方便快捷
  3. @Autowired作用就和在xml配置文件中的bean标签中写一个< property >标签的作用是一样的。
  4. @Autowired自动装配首先会在IOC容器中跳过key直接去容器中找到对应的属性(bean标签中的Class)!也就是说与key无关
②三种情况
  1. 容器中有==唯一的一个bean对象类型(Class类路径)==和被@Autowired修饰的变量类型匹配,就可以注入成功!
  2. 容器中没有一个bean对象类型和被@Autowired修饰的变量类型匹配,则注入失败运行报错。
  3. 容器中有==多个bean对象类型和被@Autowired修饰的变量类型匹配,则根据被@Autowired修饰的变量名寻找==,找到则注入成功【重点】
(2)@Qualifer
①用在何处
  1. @Qualifier的作用是在按照类中注入的基础之上按照名称注入
  2. 它在给类成员注入时不能单独使用(但是在给方法参数注入时可以单独使用)
  3. @Qualifier常常组合@Autowired一起使用,用来==指明具体名字的自动装配==
②使用情况
  • UserDaoImpl2类

image-20240309170911266

  • UserService3类注入UserDaoImpl2类

image-20240309170941360

(3)@Resource
  1. @Resource由J2EE提供,默认是按照byName自动注入(通过名字自动注入

  2. @Resource有两个重要的属性,name和type

    当然默认是通过name属性

    type属性多此一举,还不如用@Autowired

  3. @Resource 相当于 @Autowired + @Qualifier

    举例:

    @Autowired
    @Qualifier(value="userDaoImpl2")
    //相当于
    @Resource(name="userDaoImpl2")
    
(4)@Value
  1. @Value专门用来注入基本类型和String类型数据
  2. @Value注解有一个value 属性:用于指定数据的值。它可以使用spring中SpEL(也就是spring的EL表达式)。SpEL的写法:${表达式},当然也可以类似mybatis中的 #{表达式} 的写法
@Value("#{2*3}")  //#写法 表示6
private int age;

@Value("178")    //普遍写法 178
private int height;

@Value("${man.weight}")  //SpEL的写法一般操作配置文件中数据
private int weight;
(5)辨别@Autowired和@Resource的异同
  • 相同:两者都是用来自动装配的,都可以放在属性字段上
  • 不同:
    • 实现方式不同:
      • @ Autowired 通过byType的方式实现
      • @Resource 默认通过byname的方式实现
    • 找的过程不同:
      • 若@ Autowired 通过byType的方式找不到,则报错
      • 若@Resource 默认通过byname的方式找不到,则通过byType实现。如果两个都找不到的情况下,就报错。

在这里插入图片描述