SpringBoot原理-Bean管理(黑马学习笔记)

Bean管理

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

我们今天主要学习IOC容器中Bean的其他使用细节,主要学习以下三方面:

  1. 如何从IOC容器中手动的获取到bean对象

  2. bean的作用域配置

  3. 管理第三方的bean对象

接下来我们先来学习第一方面,从IOC容器中获取bean对象。

获取Bean

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

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

1.根据name获取bean

java 复制代码
Object getBean(String name)

2.根据类型获取bean

java 复制代码
<T> T getBean(Class<T> requiredType)

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

java 复制代码
<T> T getBean(String name, Class<T> requiredType)

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

● 要获取到IOC容器,直接将IOC容器对象注入进来就可以了

控制器:DeptController

java 复制代码
@RestController
@RequestMapping("/depts")
public class DeptController {

    @Autowired
    private DeptService deptService;

    public DeptController(){
        System.out.println("DeptController constructor ....");
    }

    @GetMapping
    public Result list(){
        List<Dept> deptList = deptService.list();
        return Result.success(deptList);
    }

    @DeleteMapping("/{id}")
    public Result delete(@PathVariable Integer id)  {
        deptService.delete(id);
        return Result.success();
    }

    @PostMapping
    public Result save(@RequestBody Dept dept){
        deptService.save(dept);
        return Result.success();
    }
}

业务实现类:DeptServiceImpl

java 复制代码
@Slf4j
@Service
public class DeptServiceImpl implements DeptService {
    @Autowired
    private DeptMapper deptMapper;

    @Override
    public List<Dept> list() {
        List<Dept> deptList = deptMapper.list();
        return deptList;
    }

    @Override
    public void delete(Integer id) {
        deptMapper.delete(id);
    }

    @Override
    public void save(Dept dept) {
        dept.setCreateTime(LocalDateTime.now());
        dept.setUpdateTime(LocalDateTime.now());
        deptMapper.save(dept);
    }
}

Mapper接口:

java 复制代码
@Mapper
public interface DeptMapper {
    //查询全部部门数据
    @Select("select * from dept")
    List<Dept> list();

    //删除部门
    @Delete("delete from dept where id = #{id}")
    void delete(Integer id);

    //新增部门
    @Insert("insert into dept(name, create_time, update_time) values (#{name},#{createTime},#{updateTime})")
    void save(Dept dept);
}

测试类:

java 复制代码
@SpringBootTest
class SpringbootWebConfig2ApplicationTests {

    @Autowired
    private ApplicationContext applicationContext; //IOC容器对象

    //获取bean对象
    @Test
    public void testGetBean(){
        //根据bean的名称获取
        DeptController bean1 = (DeptController) applicationContext.getBean("deptController");
        System.out.println(bean1);

        //根据bean的类型获取
        DeptController bean2 = applicationContext.getBean(DeptController.class);
        System.out.println(bean2);

        //根据bean的名称 及 类型获取
        DeptController bean3 = applicationContext.getBean("deptController", DeptController.class);
        System.out.println(bean3);
    }
}

程序运行后控制台日志:

问题:输出的bean对象地址值是一样的,说明IOC容器当中的bean对象有几个?

答案:只有一个。(默认情况下,IOC中的bean对象是单例)

那么能不能将bean对象设置为非单例的(每次获取的bean都是一个新对象)?

可以,在下一个知识点(bean作用域)中讲解。

注意事项:

● 上述所说的【Spring项目启动时,会把其中的bean都创建好】还会受到作用域及延迟初始化影响,这里主要针对于默认的单例非延迟加载的bean而言。

Bean作用域

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

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

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

知道了bean的5种作用域了,我们要怎么去设置一个bean的作用域呢?

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

1). 测试一

● 控制器

java 复制代码
//默认bean的作用域为:singleton (单例)
@Lazy //延迟加载(第一次使用bean对象时,才会创建bean对象并交给ioc容器管理)
@RestController
@RequestMapping("/depts")
public class DeptController {

    @Autowired
    private DeptService deptService;

    public DeptController(){
        System.out.println("DeptController constructor ....");
    }

    //省略其他代码...
}

● 测试类

java 复制代码
@SpringBootTest
class SpringbootWebConfig2ApplicationTests {

    @Autowired
    private ApplicationContext applicationContext; //IOC容器对象

    //bean的作用域
    @Test
    public void testScope(){
        for (int i = 0; i < 10; i++) {
            DeptController deptController = applicationContext.getBean(DeptController.class);
            System.out.println(deptController);
        }
    }
}

重启SpringBoot服务,运行测试方法,查看控制台打印的日志:

注意事项:

● IOC容器中的bean默认使用的作用域:singleton(单例)

● 默认singleton的bean,在容器启动时被创建,可以使用@Lazy注解来延迟初始化(延迟到第一次使用时)

2).测试二

修改控制器DeptController代码:

java 复制代码
@Scope("prototype") //bean作用域为非单例
@Lazy //延迟加载
@RestController
@RequestMapping("/depts")
public class DeptController {

    @Autowired
    private DeptService deptService;

    public DeptController(){
        System.out.println("DeptController constructor ....");
    }

    //省略其他代码...
}

重启SpringBoot服务,再次执行测试方法,查看控制台打印的日志:

注意事项:

● prototype的bean,每一次使用该bean的时候都会创建一个新的实例

● 实际开发当中,绝大部分的Bean都是单例的,也就是说绝大部分Bean不需要配置scope属性

第三方Bean

学习完bean的获取、bean的作用域之后,接下来我们再来学习第三方bean的配置。

之前我们所配置的bean,像controller、service、dao三层体系下编写的类,这些类都是我们在项目当中自己定义的类(自定义类)。当我们要声明这些bean,也非常简单,我们只需在类上加上@Component以及它的这三个衍生注解(@Controller、@Service、@Reposity),就可以来生命这个bean对象了。

但是在我们项目开发当中,还有一种情况就是这个类它不是我们自己编写的,而是我么引入的第三方依赖当中提供的。

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

XML 复制代码
<!--Dom4j-->
<dependency>
    <groupId>org.dom4j</groupId>
    <artifactId>dom4j</artifactId>
    <version>2.1.3</version>
</dependency>

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

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

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

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

那么我们应该怎样使用并定义第三方的bean呢?

● 如果要管理的bean对象来自于第三方(不是自定义的),是无法用@Component及衍生注解声明bean的,就需要用到@Bean注解。

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

java 复制代码
@SpringBootApplication
public 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 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<emp>
    <name>Tom</name>
    <age>18</age>
</emp>

测试类:

java 复制代码
@SpringBootTest
class 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);
    }

    //省略其他代码...
}

重启SpringBoot服务,执行测试方法后,控制台输出日志:

java 复制代码
Tom : 18

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

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

● 如果需要定义第三方Bean时, 通常会单独定义一个配置类

java 复制代码
@Configuration //配置类  (在配置类当中对第三方bean进行集中的配置管理)
public class CommonConfig {

    //声明第三方bean
    @Bean //将当前方法的返回值对象交给IOC容器管理, 成为IOC容器bean
          //通过@Bean注解的name/value属性指定bean名称, 如果未指定, 默认是方法名
    public SAXReader reader(DeptService deptService){
        System.out.println(deptService);
        return new SAXReader();
    }

}

注释掉SpringBoot启动类中创建第三方bean对象的代码,重启服务,执行测试方法,查看控制台日志:

java 复制代码
Tom : 18

在方法上加上一个@Bean注解,Spring 容器在启动的时候,它会自动的调用这个方法,并将方法的返回值声明为Spring容器当中的Bean对象。

注意事项:

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

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

关于bean大家只需要保持一个原则:

● 如果是项目当中我们自己定义的类,想将这些类交给IOC容器管理,我们直接使用@Component以及它的衍生注解来声明皆可以。

● 如果这个类它不是我们自己定义的,而是引入的第三方依赖当中提供的类,而且我们还想将这个类交给IOC容器管理。此时我们就需要在配置类中定义一个方法,在方法上加上一个@Bean注解,通过这种方式来声明第三方的bean对象。

相关推荐
KK溜了溜了1 小时前
JAVA-springboot log日志
java·spring boot·logback
我命由我123452 小时前
Spring Boot 项目集成 Redis 问题:RedisTemplate 多余空格问题
java·开发语言·spring boot·redis·后端·java-ee·intellij-idea
面朝大海,春不暖,花不开2 小时前
Spring Boot消息系统开发指南
java·spring boot·后端
hshpy2 小时前
setting up Activiti BPMN Workflow Engine with Spring Boot
数据库·spring boot·后端
jay神2 小时前
基于Springboot的宠物领养系统
java·spring boot·后端·宠物·软件设计与开发
不知几秋3 小时前
Spring Boot
java·前端·spring boot
howard20054 小时前
5.4.2 Spring Boot整合Redis
spring boot·整合redis
TracyCoder1234 小时前
接口限频算法:漏桶算法、令牌桶算法、滑动窗口算法
spring boot·spring·限流
饮长安千年月4 小时前
JavaSec-SpringBoot框架
java·spring boot·后端·计算机网络·安全·web安全·网络安全
考虑考虑5 小时前
Jpa中的@ManyToMany实现增删
spring boot·后端·spring