关于Spring Ioc容器的理解和使用

作为一个以项目为驱动的程序员,我以为学习一项技术时,很少在使用它之前就系统地学习过这项技术,深刻地理解这项技术的由来。所以快速学习如何使用技术成为程序员的基本操作,但如果只有基本操作无疑提升很慢,想要进步,还是要根据应用继续学习原理。因此学习顺序从上学时候的"是什么-为什么-怎么做",变成了"怎么用-为什么-是什么"。

所以今天从怎么用容器开始理解。

怎么用

new一个对象

在我们还不懂什么是"面向对象"时,就学会了创建对象。一个类中想调用另一个类中的方法,当然要先new一个对象。TestController中想要使用TestService的方法,需要先初始化一个TestServiceImpl的对象,在哪一步调用,就在哪一步创建对象,因此这两个方法中各创建了一个对象。

java 复制代码
package com.example.demo.controller;

import com.example.demo.service.TestService;
import com.example.demo.service.impl.TestServiceImpl;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/test")
public class TestController {

    @GetMapping("/test1")
    public void test1() {
        TestService testService = new TestServiceImpl();
        testService.test1();
    }

    @GetMapping("/test2")
    public void test2() {
        TestService testService = new TestServiceImpl();
        testService.test2();
    }

}
typescript 复制代码
package com.example.demo.service.impl;

import com.example.demo.service.TestService;

public class TestServiceImpl implements TestService {

    @Override
    public void test1() {
        System.out.println("this is test1");
    }

    @Override
    public void test2() {
        System.out.println("this is test2");
    }
}

如果有更多的方法,每个方法都需要这个类的对象,那么更好的方法是把这个对象变成一个公共对象。

kotlin 复制代码
package com.example.demo.controller;

import com.example.demo.service.TestService;
import com.example.demo.service.impl.TestServiceImpl;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/test")
public class TestController {

    TestService testService = new TestServiceImpl();

    @GetMapping("/test1")
    public void test1() {
        testService.test1();
    }

    @GetMapping("/test2")
    public void test2() {
        testService.test2();
    }

}

把testService变成一个成员对象后,就省去了每次调用前先创建对象的步骤。但如果别的类中也需要一个TestService的对象呢?

kotlin 复制代码
package com.example.demo.controller;

import com.example.demo.service.TestService;
import com.example.demo.service.impl.TestServiceImpl;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/test2")
public class TestTwoController {

    TestService testService = new TestServiceImpl();

    @GetMapping("/test1")
    public void test1() {
        testService.test1();
    }

    @GetMapping("/test2")
    public void test2() {
        testService.test2();
    }

}

创建全局对象

这样每个需要TestService的类中都需要创建对象,但其实我只需要一个对象,怎么让这个对象再公共一点呢?我想到一个好方法,一个简单的单例模式

java 复制代码
package com.example.demo.util;

import com.example.demo.service.TestAService;
import com.example.demo.service.TestBService;
import com.example.demo.service.TestService;
import com.example.demo.service.impl.TestAServiceImpl;
import com.example.demo.service.impl.TestBServiceImpl;
import com.example.demo.service.impl.TestServiceImpl;

public class ObjectUtil {

    private static TestService testService = new TestServiceImpl();

    private static TestAService testAService = new TestAServiceImpl();

    private static TestBService testBService = new TestBServiceImpl();

    public static TestService getTestService() {
        return testService;
    }

    public static TestAService getTestAService() {
        return testAService;
    }

    public static TestBService getTestBService() {
        return testBService;
    }

}

这样每个需要引用这个对象的地方,只需要获取就可以了

ini 复制代码
TestService testService = ObjectUtil.getTestService();

TestAService testAService = ObjectUtil.getTestAService();

TestBService testBService = ObjectUtil.getTestBService();

写到这里,还有一些问题需要解决,比如,每一个需要全局对象的类都要创建,一个项目中那么多类,写的完吗?比如,初始化对象时如果需要参数怎么办?比如,每个对象有没有作用域之分?

这就是Spring容器要解决的问题了。

@Component注解的使用

在web项目中,controller层要用@RestController的注解,service层要用@Service的注解,配置要用@Configuration,这些注解都是由一个@Component衍生来的。@Component就是需要创建对象的信号了。

typescript 复制代码
package com.example.demo.service.impl;

import com.example.demo.service.TestService;
import org.springframework.stereotype.Service;

@Service
public class TestServiceImpl implements TestService {

    @Override
    public void test1() {
        System.out.println("this is test1");
    }

    @Override
    public void test2() {
        System.out.println("this is test2");
    }
}
kotlin 复制代码
package com.example.demo.controller;

import com.example.demo.service.TestService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/test")
public class TestController {

    @Autowired
    TestService testService;

    @GetMapping("/test1")
    public void test1() {
        testService.test1();
    }

    @GetMapping("/test2")
    public void test2() {
        testService.test2();
    }

}

首先在TestServiceImpl上加@Service的注解,然后在需要用到TestService对象的地方加上@Autowired的注解,这两个注解配合,在项目中就完全省去了创建对象这个步骤。

至此,虽然还不完全理解容器,但已经会使用容器了。

为什么

关于为什么这么用,以上述简单的操作可以得到浅显的认知:每次都要创建对象,管理对象太麻烦,对象又要占用堆内存。我想要一个工具,能帮我实现管理对象,包括对象的初始化、对象的保存、对象的使用、对象的销毁等等。并且这个工具最好让我写代码时能无感使用,我不需要关心每个对象的具体创建过程,直接能使用每个对象的核心方法。

是什么

那么容器到底是什么?

概念层面看,管理对象的东西就叫容器。 代码层面看,以上操作中的ObjectUtil,就是我们自己写的容器。Spring中的容器叫ApplicationContext,而Spring容器所管理的对象,就是bean。

以上操作从我们自己创建对象变成了由容器创建对象,这就是控制反转,即Inversion of Control (IoC)。

相关推荐
bing_1586 分钟前
我写的 @Service 类就是一个 Bean 吗?
spring·bean·ioc
星辰大海的精灵12 分钟前
Java 线程池的工作原理及实践
java·后端·架构
满分观察网友z15 分钟前
从选择困难到最优策略:我如何用DP搞定“鱼和熊掌兼得”的排程难题(1751. 最多可以参加的会议数目 II)
后端·算法
我命由我1234519 分钟前
Spring Boot - Spring Boot 集成 MyBatis 分页实现 手写 SQL 分页
java·spring boot·后端·sql·spring·java-ee·mybatis
天天摸鱼的java工程师32 分钟前
每天导入100万数据导致数据库死锁?
java·后端·面试
Xxtaoaooo1 小时前
手撕Spring底层系列之:IOC、AOP
java·后端·spring·spring框架·底层源码剖析
Penge6661 小时前
Lucene 索引文件结构
后端
胡gh1 小时前
线程与进程:从零开始理解它们的区别与联系
前端·javascript·后端
满分观察网友z1 小时前
从“最短响应路径”到二叉树最小深度:一个Bug引发的BFS探险之旅(111. 二叉树的最小深度)
后端·算法
LaoZhangAI1 小时前
Sora Image API完全指南2025,平替gpt-image-1 API
前端·后端