SpringBoot Bean管理

admin 2024年2月15日15:31:57评论8 views字数 7202阅读24分0秒阅读模式

我们知道可以通过Spring当中提供的注解@Component以及它的三个衍生注解(@Controller、@Service、@Repository)来声明IOC容器中的bean对象,同时我们也学习了如何为应用程序注入运行时所需要依赖的bean对象,也就是依赖注入DI。

本篇主要介绍IOC容器中Bean的使用细节:

0x01,如何从IOC容器中手动的获取到bean对象

0x02,bean的作用域

0x03,管理第三方的bean对象(重点)

0x01,如何从IOC容器中手动的获取到bean对象

1.1、获取Bean

默认情况下,SpringBoot项目在启动的时候会自动的创建IOC容器(也称为Spring容器),并且在启动的过程当中会自动的将bean对象都创建好,存放在IOC容器当中。应用程序在运行时需要依赖什么bean对象,就直接进行依赖注入就可以了。

而在Spring容器中提供了一些方法,可以主动从IOC容器中获取到bean对象,下面介绍3种常用方式:

1)根据name获取bean

Object getBean(String name)

2)根据类型获取bean

<T> T getBean(Class<T> requiredType)

3)根据name获取bean(带类型转换)

<T> T getBean(String name, Class<T> requiredType)

思考:要从IOC容器当中来获取到bean对象,需要先拿到IOC容器对象,怎么样才能拿到IOC容器呢?

想获取到IOC容器,直接将IOC容器对象注入进来就可以了,java中万物皆对象。

控制器:SectsController

@RestController@Slf4jpublic class SectsController {    @Autowired    private SectsService sectsService;    @GetMapping("/sects")    public Result getAllSects() {        log.info("调用了查询所有部门接口:/sects");        List<Sect> sects = sectsService.getAllSects();        return Result.success(sects);    }    @Log    @PostMapping("/sects")    public Result addSect(@RequestBody Sect sect) {        log.info("新增门派:{}", sect.getName());        sectsService.addSect(sect);        return Result.success();    }    @Log    @DeleteMapping("/sects/{id}")    public Result deleteSect(@PathVariable Integer id) {        log.info("根据门派id删除部门:{}", id);        sectsService.deleteSect(id);        return Result.success();    }    @GetMapping("/sects/{id}")    public Result selectSectById(@PathVariable Integer id) {        log.info("根据门派id查询:{}", id);        Sect sect = sectsService.selectSectById(id);        log.info("查询的门派数据是:{}", sect);        return Result.success(sect);    }    @Log    @PutMapping("/sects")    public Result updateSectById(@RequestBody Sect sect) {        log.info("更新部门信息,门派id:{} -- 门派名称:{}", sect.getId(), sect.getName());        sectsService.updateSect(sect);        return Result.success();    }}

业务实现类:SectsServiceImpl

@Servicepublic class SectsServiceImpl implements SectsService {    @Autowired    private SectsMapper sectsMapper;    @Override    public List<Sect> getAllSects() {        List<Sect> sects =  sectsMapper.selectAll();        return sects;    }    @Override    public void addSect(Sect sect) {        sect.setCreateTime(LocalDateTime.now());        sect.setUpdateTime(LocalDateTime.now());        sectsMapper.insert(sect);    }    @Override    public void deleteSect(Integer id) {        sectsMapper.delete(id);    }    @Override    public Sect selectSectById(Integer id) {        Sect sect = sectsMapper.selectById(id);        return sect;    }    @Override    public void updateSect(Sect sect) {        sect.setUpdateTime(LocalDateTime.now());        sectsMapper.update(sect);    }}

Mapper接口:

@Mapperpublic interface SectsMapper {    //查询所有门派    @Select("select * from sects")    List<Sect> selectAll();    //新增门派    @Insert("insert into sects(name, introduction, create_time, update_time) value (#{name}, #{introduction}, #{createTime}, #{updateTime})")    void insert(Sect sect);    //根据门派id删除门派    @Delete("delete from sects where id = #{id}")    void delete(Integer id);    //根据门派id查询门派信息    @Select("select * from sects where id = #{id}")    Sect selectById(Integer id);    //根据门派id修改门派信息    @Update("update sects set name = #{name}, introduction = #{introduction}, update_time = #{updateTime} where id = #{id}")    void update(Sect sect);}

测试类:

@SpringBootTestclass TianlongManagementApplicationTests {    @Autowired    private ApplicationContext applicationContext;    @Test    public void testGetBean() {        //根据bean的名称获取        SectsController bean1 = (SectsController) applicationContext.getBean("sectsController");        System.out.println(bean1);        //根据bean的类型获取        SectsController bean2 = applicationContext.getBean(SectsController.class);        System.out.println(bean2);        //根据bean的名称及类型获取        SectsController bean3 = applicationContext.getBean("sectsController", SectsController.class);        System.out.println(bean3);    }}

程序运行后控制台日志:

SpringBoot Bean管理

输出的bean对象地址值一样,说明在IOC容器中SectsController这个bean是单例的。那能否将bean对象设置为非单例?

说到这,笔者之前想过一个问题,为什么在SpringBoot项目中,都是Controller类、ServiceImpl实现类和Mapper接口需要添加@Componet及其衍生注解,而我们写的POJO实现类却不用添加?POJO也是一个一个的类,在项目里也需要创建对象,为什么这些POJO对象不需要添加到IOC容器来管理呢?(如下图Sects这个实现类并没有添加@Component注解)

SpringBoot Bean管理

谈一下我的拙见,首先需要知道为啥Spring框架IOC控制反转很重要,因为通过这个可以实现Controller、Service、DAO三层的解耦,大白话就是当Controller需要用ServiceImpl对象,就不用new的方式创建,而是直接通过DI依赖注入从IOC容器中拿对象。

明白了上面这一点,我们想想为啥POJO实现类和一些工具类不用加入IOC容器?

原因一:普通的POJO类、工具类通常是不涉及业务逻辑的辅助功能,不涉及复杂的依赖关系和配置,因此不需要被IOC容器管理;

原因二:Controller、Service和Mapper这三层主要是处理业务,而普通的POJO类一般用于封装数据,封装数据意味着每次封装的数据可能不同,也就是每次对象不同,交给IOC容器管理反而更复杂。

0x02,bean的作用域

在前面我们提到的IOC容器当中,默认bean对象是单例模式(只有一个实例对象)。那么如何设置bean对象为非单例呢?需要设置bean的作用域。

在Spring中支持五种作用域,后三种在web环境才生效:

作用域 说明
singleton 容器内同名称的bean只有一个实例(单例)(默认)
prototype 每次使用该bean时会创建新的实例(非单例
request 每个请求范围内会创建新的实例(web环境中,了解)
session 每个会话范围内会创建新的实例(web环境中,了解)
application 每个应用范围内会创建新的实例(web环境中,了解)

可以借助Spring中的@Scope注解来配置作用域:

SpringBoot Bean管理

再次运行后控制台日志显示SectsController这个bean就生成了多份,不过实际开发中,绝大多数bean都是单例的,也就是不用配置scope属性。

SpringBoot Bean管理

除此之外,Spring还提供了一个注解:@Lazy,用于延迟加载,意思是当第一次使用bean对象时,才会创建bean对象并交给ioc容器管理。

0x03,管理第三方的bean对象(重点)

前面所配置的bean,像controller、service,dao三层体系下编写的类,这些类都是我们在项目当中自己定义的类(自定义类)。当我们要声明这些bean,也非常简单,我们只需要在类上加上@Component以及它的这三个衍生注解(@Controller、@Service、@Repository),就可以来声明这个bean对象了。但是在我们项目开发当中,还有一种情况就是这个类它不是我们自己编写的,而是我们引入的第三方依赖当中提供的。

在pom.xml文件中,引入dom4j:

<!--Dom4j--><dependency>    <groupId>org.dom4j</groupId>    <artifactId>dom4j</artifactId>    <version>2.1.3</version></dependency>

dom4j就是第三方组织提供的。dom4j中的SAXReader类就是第三方编写的。

当我们需要使用到SAXReader对象时,直接进行依赖注入是不是就可以了呢?

按照我们之前的做法,需要在SAXReader类上添加一个注解@Component(将当前类交给IOC容器管理),如下:

SpringBoot Bean管理

注意看,第三方提供的类是只读的,无法在第三方类上添加@Component注解或衍生注解。

那应该怎样定义并使用第三方的bean对象呢?

Spring框架又提供了一个注解@Bean,如果要管理的bean对象来自于第三方(不是自定义的),是无法用@Component 及衍生注解声明bean的,就需要用到**@Bean**注解。

解决方案1:在引导类/启动类上添加@Bean标识的方法

@SpringBootApplicationpublic class SpringbootWebConfig2Application {    public static void main(String[] args) {        SpringApplication.run(SpringbootWebConfig2Application.class, args);    }    //声明第三方bean    @Bean //将当前方法的返回值对象交给IOC容器管理, 成为IOC容器bean    public SAXReader saxReader(){        return new SAXReader();    }}

测试xml文件:

<?xml version="1.0" encoding="UTF-8"?><emp>    <name>Tom</name>    <age>18</age></emp>

测试类:

@SpringBootTestclass SpringbootWebConfig2ApplicationTests {    @Autowired    private SAXReader saxReader;    //第三方bean的管理    @Test    public void testThirdBean() throws Exception {        Document document = saxReader.read(this.getClass().getClassLoader().getResource("1.xml"));        Element rootElement = document.getRootElement();        String name = rootElement.element("name").getText();        String age = rootElement.element("age").getText();        System.out.println(name + " : " + age);    }}

运行测试方法,程序可以正常运行并解析xml数据,说明SAXReader类的对象被成功注入进来了。

Tom : 18

可以断点调试一下看一下程序saxReader对象:

SpringBoot Bean管理

解决方案2:在配置类中定义@Bean标识的方法

以上在启动类中声明第三方Bean的作法,不建议使用,项目中要保证启动类的纯粹性。

创建配置类:CommonConfig

package com.nvyao.conf;import com.nvyao.pojo.Dept;import com.nvyao.service.DeptService;import org.dom4j.io.SAXReader;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import java.util.List;@Configuration  //配置类public class CommonConfig {    //声明第三方Bean,一般情况下专门定一个配置类来声名第三方bean对象    //加了这个注解,spring框架会自动调用这个方法,并且将方法返回值交给IOC容器管理,成为IOC容器的bean对象    //通过@Bean注解的name或value属性可以声明Bean的名称,如果不指定,默认bean的名称就是方法名    @Bean//("reader")    public SAXReader saxReader() {        return new SAXReader();    }}

注视掉之前启动类的@Bean方法,重启依然有效。

假如第三方Bean在使用过程还需要使用其他Bean,也就是还需要依赖其他对象,可以直接通过在@Bean注解的这个方法添加需要依赖的对象的形参来解决,如下:

@Configuration  //配置类public class CommonConfig {    public SAXReader saxReader(DeptService deptService) {        System.out.println(deptService);        List<Dept> deptList = deptService.listDept();        System.out.println(deptList);        return new SAXReader();    }}

可以断点调试看到,形参的对象已经注入进来了,真挺好!

SpringBoot Bean管理

总结一下:

1)SpringBoot的启动类也是一个配置类,理论上声明第三方Bean的方法可以放在启动类,但一般是单独定义一个配置类

2)加了@Bean注解,Spring框架会自动调用@Bean注解的方法,并且将方法返回值交给IOC容器管理,成为IOC容器的bean对象

3)通过@Bean注解的name或value属性可以声明Bean的名称,如果不指定,默认bean的名称就是方法名

4)如果第三方Bean需要依赖其他bean对象,直接在bean定义方法中设置行参即可,容器会根据类型自动装配

原文始发于微信公众号(安全随笔):SpringBoot Bean管理

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年2月15日15:31:57
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   SpringBoot Bean管理http://cn-sec.com/archives/2190940.html

发表评论

匿名网友 填写信息