关于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)。

相关推荐
子兮曰1 天前
jev-ultrafast 深度解析:7 秒订机票的浏览器 Agent 是如何炼成的
前端·后端·agent
子兮曰1 天前
Jev 爆发一周:7 秒 Agent 背后的 System One 生态与三场争议
前端·后端·ai编程
爱勇宝1 天前
ZCode 开源 24 小时:一份没有历史的账本,回答不了"有没有偷代码"
前端·后端·chatglm (智谱)
胡写代码1 天前
别再前后端各写一套表单校验了
java·后端
大勇前进1 天前
原生 PHP 还是 Laravel?小项目到底要不要上框架
后端
yuzhi_liu1 天前
我用 LangGraph4j 实现 Multi-Agent Supervisor
后端
alsmile1 天前
Node-RED 之外,国产规则引擎的新方案:基于标准语法,Go 先行实现
后端·开源·go
大白801 天前
PHP 内存溢出排查思路:看懂报错日志,精准定位问题
后端
二月龙1 天前
PHP 接口返回统一响应封装,让前后端对接更省心
后端
盖伦发发1 天前
软件工程SOLID 五大设计原则
后端·软件工程