Spring 的设计思想、创建和使用、Bean 作用域和生命周期

文章目录

  • Spring 设计思想
    • Spring 是什么?
    • 什么是 IoC?
  • Spring 创建和使用
    • 创建 Spring 项目
    • 注册 Bean 对象
    • 获取并使用 Bean 对象
  • Spring 更方便地存储和读取对象
    • 配置文件
    • 使用注解
      • 使用类注解
      • 使用方法注解
    • 获取 Bean 对象
      • 属性注入
      • Setter 注入
      • 构造方法注入
      • `@Resource`
  • Bean 作用域和生命周期
    • 认识作用域
    • 设置作用域
    • Spring 执行流程
    • Bean 生命周期

Spring 设计思想

Spring 是什么?

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

什么是 IoC?

控制反转(英语:Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。

技术描述

往往一个类中都需要获取与其合作的类的对象(也就是它所依赖的对象)的引用。如果这个获取过程要靠自身实现,那么这将导致代码高度耦合并且难以维护和调试。

比如,Class A中用到了Class B的对象b,一般情况下,需要在A的代码中显式地用 new 建立 B 的对象。

采用依赖注入技术之后,A 的代码只需要定义一个 private 的B对象,不需要直接 new 来获得这个对象,而是通过相关的容器控制程序来将B对象在外部new出来并通过构造方法或其他方法注入到A类里的引用中。而具体获取的方法、对象被获取时的状态由配置文件(如XML)来指定。

为什么说在 A 里面 new B 的对象的耦合度会比在外面 new B 对象然后注入到 A 里面要高呢?

  • 比如 B 的构造方法发生改变,new B 需要传入其他类型的参数了,那么 A 类里面的 new B 就要跟着修改,耦合度就比较高了,把 new B 放到外面,A 和 B 之间的耦合度就没这么高了。

反转在哪?

  • 依赖对象的获得被反转了

依赖注入和依赖查找

这是实现控制反转的两种主要方式,两者的区别在于,前者是被动的接收对象,在类A的实例创建过程中即创建了依赖的B对象,通过类型或名称来判断将不同的对象注入到不同的属性中,而后者是主动索取相应类型的对象,获得依赖对象的时间也可以在代码中自由控制。

依赖注入有如下实现方式:

  • 基于接口。实现特定接口以供外部容器注入所依赖类型的对象。
  • 基于 set 方法。实现特定属性的public set方法,来让外部容器调用传入所依赖类型的对象。
  • 基于构造函数。实现特定参数的构造函数,在新建对象时传入所依赖类型的对象。
  • 基于注解。基于Java的注解功能,在私有变量前加“@Autowired”等注解,不需要显式的定义以上三种代码,便可以让外部容器传入对应的对象。该方案相当于定义了public的set方法,但是因为没有真正的set方法,从而不会为了实现依赖注入导致暴露了不该暴露的接口(因为set方法只想让容器访问来注入而并不希望其他依赖此类的对象访问)。

依赖查找更加主动,在需要的时候通过调用框架提供的方法来获取对象,获取时需要提供相关的配置文件路径、key等信息来确定获取对象的状态

Spring 是一个 IoC 容器,说的是对象的创建和销毁的权利都交给 Spring 来管理了,它本身又具备存储对象和获取对象的能力

Spring 创建和使用

创建 Spring 项目

创建 Spring 项目和创建 Servlet 项目类似,分为以下 3 步:

  1. 创建一个 Maven 项目

  2. 添加 Spring 框架支持(spring-context、spring-beans)

    直接去中央仓库找就行了,也可以在这里复制:

    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.26</version>
        </dependency>
    
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-beans -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>5.3.26</version>
        </dependency>
    </dependencies>
    
  3. 添加启动类

    就是创建一个带有 main 方法的类。之前学习 Servlet 没有写过 main 方法,是因为 main 方法是 Tomcat 实现的,这里的 Spring 就不一样了。

注册 Bean 对象

Bean 对象就是普通的 Java 对象。

  1. 定义一个 Bean

    public class User {
        public void hi() {
            System.out.println("Hello");
        }
    }
    
  2. 将 Bean 注册到 Spring(并非真正存储,而是告诉 Spring,此 Bean 需要托管给 Spring)

    首先在 resources 里面创建 xml 文件,名字随意,如果是 IDEA 专业版,在右键 resources->new 会看到 XML Configuration File-> Spring Config 选项,直接选择就可以生成。如果是社区版,可以复制以下代码:

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

    接下来,要将 User 对象注册到 Spring 中,具体操作是在 <beans> 中添加如下配置:

    <bean id="user" class="User"></bean>
    

    idclass 属性分别指定了该 Bean 的标识符和实现类。如果实现类写在包里面,那么这里还要带上包名路径。

    这个 <bean> 元素,就是告诉 Spring 容器创建一个名为 “user” 的Bean,该Bean的实现类是 “User” 类。其他部分的应用程序或配置可以通过这个唯一的标识符 “user” 来引用和使用这个Bean。

    注意:id 不可重复,而 class 可以重复,也就是说,同一个类,可以在 Spring 中注册两次,只要 id 不同即可。一个 id 就代表一个对象。

获取并使用 Bean 对象

  1. 得到 Spring 上下文对象,因为对象都交给 Spring 管理了,所以获取对象要从 Spring 中获取,那么就要先得到 Spring 上下文
  2. 通过 Spring 上下文,获取某一个指定的 Bean 对象
  3. 使用 Bean 对象

例:

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

public class App {
    public static void main(String[] args) {
        // 1.得到 Spring 上下文对象
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        // 2.从 Spring 中获取 bean 对象
        //User u = new User(); 传统方式,在 spring 中不推荐使用
        User user = (User) context.getBean("user");
        // 3.使用 bean
        user.hi();
    }
}
// 输出:Hello

获取 bean 的其他方法

通过类对象

User user = context.getBean(User.class);

通过此方法获取,有一个弊端,如果同一个类型的 Bean 在 xml 中注册了两次或多次,那么 Spring 就不知道你要获取的是哪个,从而报错。

根据 String 和 Class 获取 bean

User user = context.getBean("user", User.class);

这种方法就避免了上一个方法的弊端,而且不需要强制类型转换。

常用方法总结

方法说明
Object getBean(String name)返回指定 bean 的一个实例,该实例可以是共享的,也可以是独立的
T getBean(Class<T> requiredType)返回唯一匹配给定对象类型的 bean 实例(如果有的话)
T getBean(String name, Class<T> requiredType)返回指定 bean 的一个实例,该实例可以是共享的,也可以是独立的

获取上下文对象的其他方法

BeanFactory context = new XmlBeanFactory(new ClassPathResource("spring-config.xml"));

这种方法不建议使用。

ApplicationContext 和 BeanFactory 的区别是什么

共同点:都是用来获取 Spring 上下文对象

不同点:

  1. 继承关系和功能:ApplicationContext 是 BeanFactory 的子类,BeanFactory 只具备最基本的访问容器的能力,而 ApplicationContext 还提供了对国际化支持,资源访问支持,以及事件传播等方面的支持
  2. 性能和加载方式:BeanFactory 按需加载,当需要使用 bean 的时候再加载,ApplicationContext 一次性加载所有的 bean

Spring 更方便地存储和读取对象

配置文件

在 Spring 配置文件中设置 Bean 扫描根路径,在该路径下的使用了注解的类会被存储到 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.cero"></content:component-scan>
</beans>

就是在 <beans> 里面添加 <content:component-scan base-package="com.cero"></content:component-scan> 这一行,其中的 base-package=“” 自己设置。

使用注解

使用注解 将 Bean 对象更简单地存储到 Spring

注解类型有两种

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

    @Controller 控制器,用来验证前端传递的参数

    @Service 服务层,服务调用的编排和汇总

    @Repository 仓库,直接操作数据库

    @Component 组件,通用化的工具类

    @Configuration 配置,项目的所有配置

  2. 方法注解:@Bean

使用类注解

package com.cero.service;

import org.springframework.stereotype.Service;

@Service
public class UserService {
    public void doService() {
        System.out.println("Do user service");
    }
}
import com.cero.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        // 使用注解的方式,bean 的 id 是类名的小驼峰
        UserService userService = context.getBean("userService", UserService.class);
        userService.doService();
    }
}

虽然我们没有在配置文件中显式写出 id,但是 Spring 存储的时候会自动把 id 命名成小驼峰。

这些注解的功能是一样的,但为什么要有这么多注解?

  • 为了主程序员看到类注解之后,就能直接了解当前类的用途比如:
    • @Controller 表示的业务逻辑层
    • @Service 服务层
    • @Repository 持久层
    • @Configuration配置层

程序的工程分层,设备流程如下:

img

五大类注解之间的关系

  • @Controller @Service @Repository @Configuration 都是基于 @Component,它们的作用都是将 Bean 存储到 Spring 中

这一点通过看源代码可以发现

使用注解方式时 Bean 的 id

  • 默认情况下,就是原类名的首字母小写,如:StudentController->studentController
  • 特例:当首字母和第二个字母都是大写的情况下,那么 Bean 的名称为原类名,如:SController->SController

使用方法注解

拥有方法注解的方法,返回的对象会被存储到 Spring 中。

注意

  • 方法注解 @Bean 一定要和类注解配合使用。因为 Spring 是先扫描哪些类要存储,然后再去扫描这个类里面的方法。
  • 方法注解 @Bean 的命名规则和类注解不一样,它的 bean 对象的 id 就是方法名。
  • 使用了方法注解的方法必须是无参的,因为 Spring 初始化存储时,无法提供相应的参数。

例子 :

先定义一个普通的 User 类

package com.cero.model;

public class User {
    private int id;
    private String name;
    private int age;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

定义一个 UserBeans 类,其中的 user 方法会返回一个 User 对象,该对象会被存到 Spring:

package com.cero.controller;

import com.cero.model.User;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

@Component
public class UserBeans {
    @Bean
    public User getUser() {
        User user = new User();
        user.setId(1);
        user.setName("张三");
        user.setAge(18);
        return user;
    }
}

获取存储在 Spring 里的 User 对象:

import com.cero.model.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");

        User user = context.getBean("getUser", User.class);
        System.out.println(user);
    }
}
// 输出:User{id=1, name='张三', age=18}

@Bean 重命名

思考方法注解的命名规则,如果有两个同名的方法返回了不同的对象怎么办,这两个对象怎么区分呢?

如下代码,有两个同名方法,它们返回的对象都将被注册到 Spring 中

package com.cero.controller;

import com.cero.model.User;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

@Component
public class UserBeans {
    @Bean
    public User getUser() {
        User user = new User();
        user.setId(1);
        user.setName("张三");
        user.setAge(18);
        return user;
    }
}

@Component
class UserBeans2 {
    @Bean
    public User getUser() {
        User user = new User();
        user.setId(1);
        user.setName("李四");
        user.setAge(80);
        return user;
    }
}

获取对象并打印:

import com.cero.model.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");

        User user = context.getBean("getUser", User.class);
        System.out.println(user);
    }
}
// 输出:User{id=1, name='李四', age=80}

结果并没有报错,而是打印了李四,说明两个对象都存了,只是李四把张三给覆盖了。

而我要想分别获得这两个方法返回的对象怎么办?

答案是使用 @Bean 重命名

给注解提供 name 参数,具体有 3 种写法:

  1. @Bean(name = "user1")
  2. 也可以省略 name :@Bean("user1")
  3. 使用 {} 来命名多个名字: @Bean(name = {"user1", "user2"})
  4. 使用{} 的 name 也可以省略 @Bean({"user1", "user2"})
package com.cero.controller;

import com.cero.model.User;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

@Component
public class UserBeans {
    @Bean(name = "user1")
    public User getUser() {
        User user = new User();
        user.setId(1);
        user.setName("张三");
        user.setAge(18);
        return user;
    }
}

@Component
class UserBeans2 {
    @Bean("user2")
    public User getUser() {
        User user = new User();
        user.setId(1);
        user.setName("李四");
        user.setAge(80);
        return user;
    }
}

这样它们返回的对象就不再使用默认命名规则了,而是使用由程序员指定的名字。

import com.cero.model.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");

        User user1 = context.getBean("user1", User.class);
        User user2 = context.getBean("user2", User.class);
        System.out.println(user1);
        System.out.println(user2);
    }
}
/* 输出:
User{id=1, name='张三', age=18}
User{id=1, name='李四', age=80}
*/

获取 Bean 对象

获取 Bean 对象也叫做对象装配,对象注入,是把对象取出来放到某个类中

对象装配(对象注入)的实现方法有以下 3 种:

  1. 属性注入
  2. 构造方法注入
  3. Setter 注入

属性注入

这是最简单的对象注入方式,和平常的定义属性类似,这里以我们之前存在 Spring 中的 UserService 为例,

在定义了 private UserService userService; 这个属性之后,在上面加上 @Autowired 注解。然后我们就可以直接调用 UserService 里的方法了。

属性注入的时候,Spring 会优先按照类型在容器中进行查找,如果找不到相同类型的 bean 对象,就会抛异常,如果找到相同类的多个 bean 对象,就再按照属性名查找,找到就赋值到该属性上,找不到就抛异常。

package com.cero.controller;

import com.cero.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

@Controller
public class UserController {
    // 从 Spring 中读取 UserService
    // 属性注入
    @Autowired
    private UserService userService;

    public void hi() {
        userService.doService();
    }
}
import com.cero.controller.UserController;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");

        // 获取 UserController,并调用其中的 hi 方法
        UserController userController = context.getBean("userController", UserController.class);
        userController.hi();
    }
}
// 输出:Do user service

一个细节问题,我的 main 方法可以使用属性注入的方式获取 UserController 对象吗?

import com.cero.controller.UserController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class App {
    @Autowired
    private static UserController userController;

    public static void main(String[] args) {
        userController.hi();
    }
}
/* 输出:
Exception in thread "main" java.lang.NullPointerException
	at App.main(App.java:11)
*/

抛出了空指针异常,说明没有注入成功。这是因为,main 方法是静态方法,它的执行时间是比 Spring 装配的时机要早的,所以它并不能通过属性注入的方式获取 Bean 对象。

优点

  • 属性注入最大的优点就是实现简单,使用简单

缺点

  • 功能性问题:无法注入一个 final 修饰的属性,因为 Java 规定了 final 修饰的变量,要么在定义时直接赋值,要么使用构造方法赋值。这里的属性注入无法满足这两个条件,所以无法注入。
  • 通用性问题:只能适应于 IoC 容器
  • 设计原则问题:更容易违背单一设计原则。因为使用简单,所以滥用的风险更大

Setter 注入

给属性添加 Setter 方法,然后在 Setter 方法上添加 @Autowired 注解

package com.cero.controller;

import com.cero.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

@Controller
public class UserController {
    // 从 Spring 中读取 UserService
    // setter 注入
    private UserService userService;

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

    public void hi() {
        userService.doService();
    }
}

优点:

  • 符合单一设计原则。一个 Setter 只针对一个对象

缺点:

  • 不能注入 final 修饰的对象。原因和属性注入一样。
  • 注入的对象可能被改变。因为 Setter 可能被调用多次。

构造方法注入

——这是目前官方推荐的写法

在构造方法的上添加 @Autowired

package com.cero.controller;

import com.cero.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

@Controller
public class UserController {
    // 从 Spring 中读取 UserService
    // 构造方法注入
    private UserService userService;

    @Autowired
    public UserController(UserService userService) {
        this.userService = userService;
    }

    public void hi() {
        userService.doService();
    }
}

注意:

  • 当这个类只有一个构造方法的时候,@Autowired 可以省略
  • 构造方法的参数必须是 Spring 容器中有的
  • 一个类,最多只能在一个构造方法上添加 @Autowired

优点:

  • 可以注入 final 修饰的对象
  • 注入的对象不会被修改
  • 依赖对象在使用前会被完全初始化。
  • 通用性更好,因为构造方法是 Java(JDK)支持,所以更换任何的框架,它都是适用的。

缺点:

  • 容易违背单一设计原则,因为一个构造方法可以传入多个参数。

@Resource

它的用法和 @Autowired 是一样的。

@Resource@Autowired 的区别:

  1. @Resource 来自于 JDK,@Autowired 来自 Spring

  2. @Resource 支持属性注入和 Setter 注入,但是不支持构造方法注入

  3. @Resource 支持 name 参数

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

    上述代码读取 Spring 中名称为 “user2” 的对象,赋值给 user

@Autowired 不支持传入 name 参数,但是它可以配合 @Qualifier 注解来达到相同的效果,上述代码等价于:

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

Bean 作用域和生命周期

认识作用域

程序中限定变量的可用范围叫做作用域。

Bean 作用域是指 Bean 在 Spring 整个框架中的某种行为模式。比如 singleton单例作用域,就表示 Bean 在整个 Spring 中只有一份,全局共享。

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

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

singleton

  • 描述:该作用域下的 Bean 在 IoC 容器中只存在一个实例,获取 Bean(即通过 applicationContext.getBean等方法获取)及装配 Bean(即通过 @Autowired 注入)都是同一个对象
  • 场景:通常无状态的 Bean 使用该作用域。无状态即 Bean 对象的属性状态不需要更新。比如 A 和 B 都用到了这个 Bean,因为 A 和 B 共享这一个 Bean 对象,如果 A 把这个 Bean 对象内的属性修改了,那么就会影响到 B,造成不便。
  • 注:Spring 默认选择该作用域

prototype

  • 描述:每次对该作用域下的 Bean 的请求都会创建新的实例:获取 Bean 和装配 Bean 都是新的对象实例。
  • 场景:通常有状态的 Bean 使用该作用域

request

  • 描述:每次 http 请求都会创建新的 Bean 实例,类似于 prototype
  • 场景:一次 http 的请求和响应的共享 Bean
  • 注:限定在 SpringMVC 中使用

session

  • 描述:每次 http 会话都会创建新的 Bean 实例
  • 场景:用户会话的共享 Bean,比如:记录一个用户的登录信息
  • 注:限定在 SpringMVC 中使用

application(不常用)

  • 描述:在一个 http servlet Context 中,定义一个 Bean 实例
  • 场景:Web 应用的上下文信息,比如:记录一个应用的共享信息
  • 注:限定在 SpringMVC 中使用

websocket(不常用)

  • 描述:在一个 WebSocket 的生命周期中,定义一个 Bean 实例
  • 注:限定在 Spring WebSocket 中使用

设置作用域

使用 @Scope 标签用来声明 Bean 的作用域

@Scope 标签既可以修饰方法也可以修饰类,@Scope 有两种设置方式:

  1. 直接设置值:@Scope("prototype")
  2. 使用类似枚举的方式设置:@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)

例:

package com.cero.service;

import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;

@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Controller
public class UserService {
    public void doService() {
        System.out.println("Do user service");
    }
}

Spring 执行流程

  1. 启动容器

    ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
    

    加载配置文件(类加载路径下的 spring-config.xml)

  2. Bean 的初始化(根据配置文件中的 bean,以及 base-package 路径下有类注解的类进行初始化)

    <content:component-scan base-package="com.cero"></content:component-scan>
    <bean id="userConfiguration" class="com.cero.config.UserConfiguration"></bean>
    
  3. 注册 Bean 对象到容器中

  4. 使用 Bean

  5. Spring 销毁

Bean 生命周期

在 Spring 中,Bean 的生命周期指的是 Bean 实例从创建到销毁的整个过程。Spring 容器负责管理 Bean 的生命周期,包括实例化、属性赋值、初始化、销毁等过程。

  1. 实例化 Bean(为 Bean 分配内存空间)
  2. 设置属性,Spring通过反射机制给Bean的属性赋值
  3. Bean 初始化,如果Bean配置了初始化方法,Spring就会调用它。初始化方法是在Bean创建并赋值之后调用,可以在这个方法里面写一些业务处理代码或者做一些初始化的工作。
    • 执行各种通知
    • 执行初始化前置方法(xml 里面定义 init-method | @PostConstruct)
    • 执行初始化方法
    • 执行初始化的后置方法
  4. 使用 Bean
  5. 销毁 Bean(xml destroy-method | @PreDestroy),如果Bean配置了销毁方法,Spring会在所有Bean都已经使用完毕,且IOC容器关闭之前调用它,可以在销毁方法里面做一些资源释放的工作,比如关闭连接、清理缓存等。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/219374.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ORACLE数据库实验总集 实验二 Oracle数据库逻辑存储结构管理

一、实验目的 &#xff08;1&#xff09;掌握 Oracle数据库表空间的管理 &#xff08;2&#xff09;掌握数据库表空间不同状态时对数据操作的影响。 二、实验要求 &#xff08;1&#xff09;分别创建永久性表空间、临时性表空间、撤销表空间 &#xff08;2&#xff09;完成表…

Vue--第六天

vuex概述&#xff1a; 组件通信感觉有点白雪。。。。。。。。。。 创建项目&#xff1a; 为了学习简介&#xff0c;先选几个&#xff0c;后续是要勾选很多的 建好后再进行组件导入 创建空仓库&#xff1a; 使用&#xff1a; 上面是store访问&#xff0c;下面是辅助函数的方式…

2023年个人工作总结怎么写?工作任务完成自动记录的待办软件

2023年已经接近尾声&#xff0c;不少人已经开始期待新的一年到来了。不过对于大多数职场人士来说&#xff0c;最近还有一项让人头疼的任务需要完成&#xff0c;这就是撰写2023年个人工作总结。 那么年度个人工作总结怎么写呢&#xff1f;其实很简单&#xff0c;年度工作总结一…

5、RocketMQ-Producer生成消息过程 (五)

生产者已经被启动了&#xff0c;接下来我们就得研究研究如何去发送消息给Broker。 前面分析Producer的启动逻辑&#xff0c;启动完成之后就是发消息&#xff0c;接下来我们就来分析Producer的send消息过程&#xff0c;同时发消息过程中存在一些问题以及解决方法也得考虑。 查…

Java POI读写Excel文档

Java POI读写Excel文档 简介 由apache公司提供Java编写的免费开源的跨平台的Java API提供API给Java程序对Microsoft Office格式档案读和写的功能 包结构 HSSF读写Microsoft Excel XLS(2003版本的Excel)XSSF读写Microsoft Excel OOXML XLSXHWPF读写Microsoft Word DocHSLF提…

Honeywell PM43 loadfile.lp RCE漏洞复现(CVE-2023-3710)

0x01 产品简介 Honeywell PM43 是美国霍尼韦尔&#xff08;Honeywell&#xff09;公司的一款打印机产品。 0x02 漏洞概述 Honeywell PM43 P10.19.050004之前版本存在输入验证错误漏洞&#xff0c;攻击者可通过该漏洞在服务器端任意执行代码&#xff0c;写入后门&#xff0c;获…

行云海CMS SQL注入漏洞复现

0x01 产品简介 行云海cms是完全开源的一套CMS内容管理系统,简洁,易用,安全,稳定,免费。 0x02 漏洞概述 行云海cms中ThinkPHP在处理order by排序时可利用key构造SQL语句进行注入,LtController.class.php中发现传入了orderby未进行过滤导致sql注入。攻击者除了可以利用 SQL 注入…

关于 Python 的最全面试题

1 Python的函数参数传递 看两个例子: a 1 def fun(a):a 2 print a # 1a [] def fun(a):a.append(1) print a # [1]所有的变量都可以理解是内存中一个对象的“引用”&#xff0c;或者&#xff0c;也可以看似c中void*的感觉。 这里记住的是类型是属于对象的&#xff0c;而…

Python使用netmiko配置华为交换机

一、netmiko介绍 1.更适合网络设备的自动化运维模块。 二、场景 1、批量查询 2、批量配置变更、备份 三、项目地址 GitHub - ktbyers/netmiko: Multi-vendor library to simplify Paramiko SSH connections to network devices 三、使用步骤 1.安装netmiko pip install ne…

【带头学C++】----- 九、类和对象 ---- 9.5 初始化列表

目录 9.5 初始化列表 9.5.1 对象成员 代码&#xff1a; 9.5.2 初始化列表 9.5 初始化列表 9.5.1 对象成员 在类中定义的数据成员一般都是基本的数据类型。但是类中的成员也可以是对象&#xff0c;叫做对象成员。 先调用对象成员的构造函数&#xff0c;再调用本身的构造函数…

几句对话就能生成简历?“搜索界奥林匹克”上演AI原生应用开发热潮

只需要回答几个问题&#xff0c;就能生成个性化的简历&#xff0c;还提供优化建议&#xff0c;安排AI模拟面试。这样的效率神器&#xff0c;就出现第二届百度搜索创新大赛的赛场上。 来自南京航空航天大学的“肝到凌晨”团队&#xff0c;利用文心一言插件平台“灵境矩阵”和百度…

C语言为什么不建议把变量作为数组长度?

C语言为什么不建议把变量作为数组长度&#xff1f; 在开始前我有一些资料&#xff0c;是我根据自己从业十年经验&#xff0c;熬夜搞了几个通宵&#xff0c;精心整理了一份「C语言从专业入门到高级教程工具包」&#xff0c;点个关注&#xff0c;全部无偿共享给大家&#xff01;&…

Postman 脚本的奥秘:JavaScript 的内置对象和方法

postman的前后置脚本中是完全支持 JavaScript 编写代码&#xff0c;JavaScript 有很多内置的对象和方法&#xff0c;可以帮助我们完成各种任务&#xff0c;比如生成随机数和测试响应数据 生成随机数 使用Math.random()方法来生成一个 0 到 1 之间的随机小数&#xff0c;比如&…

Mysql 行转列,把逗号分隔的字段拆分成多行

目录 效果如下源数据变更后的数据 方法第一种示例SQL和业务结合在一起使用 第二种示例SQL和业务结合在一起使用 结论 效果如下 源数据 变更后的数据 方法 第一种 先执行下面的SQL&#xff0c;看不看能不能执行&#xff0c;如果有结果&#xff0c;代表数据库版本是可以的&…

Web端功能测试的测试方向有哪些?

一、功能测试 1.1链接测试 链接是web应用系统的一个很重要的特征&#xff0c;主要是用于页面之间切换跳转&#xff0c;指导用户去一些不知道地址的页面的主要手段&#xff0c;链接测试一般关注三点&#xff1a; 1&#xff09;链接是否按照既定指示那样&#xff0c;确实链接到…

ISIS配置以及详解

作者简介&#xff1a;大家好&#xff0c;我是Asshebaby&#xff0c;热爱网工&#xff0c;有网络方面不懂的可以加我一起探讨 :1125069544 个人主页&#xff1a;Asshebaby博客 当前专栏&#xff1a; 网络HCIP内容 特色专栏&#xff1a; 常见的项目配置 本文内容&am…

09、pytest多种调用方式

官方用例 # content of myivoke.py import sys import pytestclass MyPlugin:def pytest_sessionfinish(self):print("*** test run reporting finishing")if __name__ "__main__":sys.exit(pytest.main(["-qq"],plugins[MyPlugin()]))# conte…

java8 升级 java11

1.安装java11 1.1 安装参考 ​​​​​​LINUX安装JDK_liunx上安装ocean-CSDN博客 1.2 检查 java -version 2.Maven 项目pom文件修改 <properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEnc…

打车送咖啡?这个冬天,滴滴携Tims带来双倍暖意

天气愈发寒冷&#xff0c;打车出行成为越来越多人的选择。如果打车后还能被送一杯热腾腾的咖啡&#xff0c;是一种什么体验&#xff1f;11月27日&#xff0c;滴滴携手咖啡连锁品牌Tims天好咖啡&#xff08;以下简称“Tims”&#xff09;推出“打车送咖啡套餐”活动&#xff0c;…

高低压供配电智能监控系统

高低压供配电智能监控系统是一种综合运用物联网、云计算、大数据和人工智能等技术的智能化监控系统&#xff0c;用于实时监测高低压供配电设备的运行状态和电能质量&#xff0c;及时发现和处理供配电系统中存在的问题&#xff0c;提高供配电系统的安全性和可靠性。依托电易云-智…
最新文章