SpringBoot中Spring IOC的运用

简介: SpringBoot中Spring IOC的运用

SpringBoot中Spring IOC的运用

维基百科上对IOC的描述:

早在2004年,Martin Fowler就提出了“哪些方面的控制被反转了?”这个问题。他总结出是依赖对象的获得被反转了,因为大多数应用程序都是由两个或是更多的类通过彼此的合作来实现业务逻辑,这使得每个对象都需要获取与其合作的对象(也就是它所依赖的对象)的引用。如果这个获取过程要靠自身实现,那么这将导致代码高度耦合并且难以维护和调试。

(一)项目准备

来继续深造这个项目,通过这个项目来了解一下。

.com.shousidaima.truede.
             -
                       -controller.
                         -HelloController.java
                       -dao.
                         -.impl.
                          -HelloDaoJDBCImpl.java
                         -HelloDao.java
                       -entity.
                         -Hello.java
                       -service.
                         -HelloService.java
                         -impl.
                -HeloServiceImpl.java

具体如下图:

com.shousidaima.truede.HelloDao.java:

package com.shousidaima.truede.dao;
import com.shousidaima.truede.entity.Hello;
//数据访问层
public interface HelloDao {
    Hello getHello(int code,String msg);
}

com.shousidaima.truede.HelloJDBCImpl.java

我们使用java类来模拟JDBC的链接;

通常我们都是使用Mybatis来绑定dao的。

package com.shousidaima.truede.dao.Impl;
import com.shousidaima.truede.dao.HelloDao;
import com.shousidaima.truede.entity.Hello;
import org.springframework.stereotype.Repository;
@Repository
public class HelloJDBCImpl implements HelloDao {
    @Override
    public Hello getHello(int code, String msg) {
        Hello hello = new Hello();
        hello.setCode(code);
        hello.setMsg(msg);
        return hello;
    }
}

com.shousidaima.truede.Hello.java

package com.shousidaima.truede.entity;
public class Hello {
    private String msg;
    private int code;
    public String getMsg() {
        return msg;
    }
    public void setMsg(String msg) {
        this.msg = msg;
    }
    public int getCode() {
        return code;
    }
    public void setCode(int code) {
        this.code = code;
    }
    @Override
    public String toString() {
        return "Hello{" +
                "msg='" + msg + '\'' +
                ", code=" + code +
                '}';
    }
}

com.shousidaima.truede.HelloService.java

package com.shousidaima.truede.service;
import com.shousidaima.truede.entity.Hello;
public interface HelloService {
    Hello getHello(int code, String msg);
}

com.shousidaima.truede.HelloServiceImpl.java

package com.shousidaima.truede.service.impl;
import com.shousidaima.truede.dao.HelloDao;
import com.shousidaima.truede.entity.Hello;
import com.shousidaima.truede.service.HelloService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class HelloServiceImpl implements HelloService {
    @Autowired
    HelloDao helloDao;
    @Override
    public Hello getHello(int code, String msg) {
        return helloDao.getHello(code,msg);
    }
}

(二)编写测试单元&验证

package com.shousidaima.truede;
import com.shousidaima.truede.entity.Hello;
import com.shousidaima.truede.service.HelloService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class TruedeApplicationTests {
  @Autowired
  HelloService helloService;
  @Test
  void contextLoads() {
    Hello hello = helloService.getHello(200, "访问成功啦!");
    System.out.println(hello);
  }
}

启动测试单元后:

(三)分析哪些对象被BeanFactory管理了?

@Repository
public class HelloJDBCImpl implements HelloDao { }
@Service
public class HelloServiceImpl implements HelloService { }

可以看到这两个类的头部都加了一个注解,@Repository和@Service。

@Repository:标识这个类是一个数据访问层的代码;

@Service:标识这个类是业务层的代码;

其实这两个是没有本质的区别的,代码一摸一样,只是注解的名字不同:

这样做的目的也是为类区分你这个类是什么类型的类对象实例。

重点是这两个类上都有@Component这个注解,这个注解就是Spring用来注入bena的其中的一种方式。

再会过头来看HelloController.java对象:

@Controller
@RequestMapping(value = "hello")
public class HelloController {
    @RequestMapping(value = "getHello",method = RequestMethod.GET)
    @ResponseBody
    public String getHello(){
        return "HelloController hello.";
    }
}

@Controller注解的源代码如下:

是不是很惊讶,与@Repository和@Service的源代码都是一样的,同样是为了标识这个类是什么类型的。

加上@Controller后就标志着这是一个控制层的java类,那么访问的时候,会访问这个对象(当然了,还会对其进行一系列的处理)。

(四)为什么实体类没有被BeanFactory管理?

因为一个实体类我们在一个项目中可能会创建很多次&使用很多次,数据也是都不相同的,完全是没必要被BeanFactory管理的。

(五)哪些实体类应该被管理?

单例对象应该被管理:

(1)统一资源类;

(2)N次使用同一个的对象;

在Spring或者SpringBoot或者Mybatis,或者一些和Spring相关的开源框架中,基本上离不开IOC(依赖注入,也就是BeanFactory托管对象)。

(1)例如:我们配置server.port=8081

实际上传递的参数是被ServerProperties.java文件给接收了,然后管理起来了。

SpringBoot在启动过的时候,就会直接拿ServerProperties.java中的参数port参数。

ServerProperties.java源代码如下:

@ConfigurationProperties(
    prefix = "server",
    ignoreUnknownFields = true
)
public class ServerProperties {
    private Integer port;
    private InetAddress address;
    ......
}

(2)例如:我开源的一个框架中的配置文件数据就这样托管的

因为使用配置文件配置一些信息会比较方便。

我开发的框架中也采用的大多数开源框架的思想。

我统一的入口:SwaggerPluginConfig.java中的一段代码

/**
 * Spring IOC统一管理对象
 */
@Configuration
public class SwaggerPluginConfig {
  //从配置文件中获取swagger-plugin开头的信息
    @ConfigurationProperties(prefix = "swagger-plugin")
    @Bean(name = "swaggerPluginConfigBean")
    public SwaggerPluginConfigBean getSwaggerPluginConfigBean(){
        return new SwaggerPluginConfigBean();
    }
}

那么使用我框架的项目中需要在配置文件(application.yml)中:

#swagger 配置
swagger-plugin:
    #配置要扫描的controller层
    scan:
        path: com.springbootswagger1.controller
    #是否开启swagger-plugin框架的debug模式
    debug: true

那么我在框架中就可以直接获取:

@Autowired
  private SwaggerPluginConfigBean swaggerPluginConfigBean;

使用的时候直接调用:

try {
    ....
} catch (CannotCompileException e) {
    //从配置文件中拿到的,如果开启的debug模式,就打印日志
    if(swaggerPluginConfigBean.isDebug()){
        logger.warn("找不到了1:"+e.getMessage());
    }
} catch (NotFoundException e) {
    if(swaggerPluginConfigBean.isDebug()){
        logger.warn("找不到了2:"+e.getMessage());
    }
}

这就是IOC的好处。

(六)获取Spring IOC管理的Bean

package com.shousidaima.truede;
import com.shousidaima.truede.entity.Hello;
import com.shousidaima.truede.service.HelloService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationContextFactory;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.web.context.WebApplicationContext;
@SpringBootTest
class TruedeApplicationTests implements ApplicationContextAware {
  @Autowired
  HelloService helloService;
  @Test
  void contextLoads() {
    Hello hello = helloService.getHello(200, "访问成功啦!");
    System.out.println(hello);
  }
  private  ApplicationContext applicationContext;
  @Override
  public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    if (this.applicationContext == null) {
      this.applicationContext = applicationContext;
    }
  }
  @Test
  public void testGetBean(){
    HelloService bean = applicationContext.getBean(HelloService.class);
    System.out.println(bean.getHello(100,"你好呀"));
  }
}

当我们执行testGetBean()测试单元的时候,就会打印:

Hello{msg='你好呀', code=100}

所以是有效的。

还可以根据不同的需求来设置

@Test
public void testGetBean(){
    //通过具体的类的class获取
    HelloService bean = applicationContext.getBean(HelloService.class);
    System.out.println(bean.getHello(100,"你好呀"));
    //通过名字+class 获取
    HelloService helloService = applicationContext.getBean("helloServiceImpl", HelloService.class);
    System.out.println(helloService.getHello(1022,"哈哈哈"));
    //通过名字获取
    //默认的名字是有规律的:java名:HelloServiceImpl -->bean名:helloServiceImpl(首字母大写变小写)
    HelloService helloService1 = (HelloService) applicationContext.getBean("helloServiceImpl");
    System.out.println(helloService.getHello(666,"66666"));
}

结果:

Hello{msg='你好呀', code=100}
Hello{msg='哈哈哈', code=1022}
Hello{msg='66666', code=666}

也可以指定bean的名字,不同的注入方式有不同的方法

例如业务实现层:

在@Service注解中指定名字就可以

@Service("helloimpl")
public class HelloServiceImpl implements HelloService {...}

那么获取的时候:

@Test
public void test1(){
    HelloService helloimpl = applicationContext.getBean("helloimpl", HelloService.class);
    System.out.println(helloimpl.getHello(10086,"hhhhh"));
}

SpringBoot中Spring AOP的运用

为了给读者更好的阅读体验,特此分开来写,关注我,见下一篇。

Spring IoC的原理

为了给读者更好的阅读体验,特此分开来写,关注我,见下一篇。

Spring AOP的原理

为了给读者更好的阅读体验,特此分开来写,关注我,见下一篇。

目录
打赏
0
0
0
0
4
分享
相关文章
【SpringFramework】Spring IoC-基于XML的实现
本文主要讲解SpringFramework中IoC和DI相关概念,及基于XML的实现方式。
112 69
SpringBoot是如何简化Spring开发的,以及SpringBoot的特性以及源码分析
Spring Boot 通过简化配置、自动配置和嵌入式服务器等特性,大大简化了 Spring 应用的开发过程。它通过提供一系列 `starter` 依赖和开箱即用的默认配置,使开发者能够更专注于业务逻辑而非繁琐的配置。Spring Boot 的自动配置机制和强大的 Actuator 功能进一步提升了开发效率和应用的可维护性。通过对其源码的分析,可以更深入地理解其内部工作机制,从而更好地利用其特性进行开发。
20 6
|
19天前
|
Spring Boot 3 集成 Spring Security + JWT
本文详细介绍了如何使用Spring Boot 3和Spring Security集成JWT,实现前后端分离的安全认证概述了从入门到引入数据库,再到使用JWT的完整流程。列举了项目中用到的关键依赖,如MyBatis-Plus、Hutool等。简要提及了系统配置表、部门表、字典表等表结构。使用Hutool-jwt工具类进行JWT校验。配置忽略路径、禁用CSRF、添加JWT校验过滤器等。实现登录接口,返回token等信息。
202 12
【SpringFramework】Spring IoC-基于注解的实现
本文主要记录基于Spring注解实现IoC容器和DI相关知识。
54 21
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
本文详细介绍了Spring框架的核心功能,并通过手写自定义Spring框架的方式,深入理解了Spring的IOC(控制反转)和DI(依赖注入)功能,并且学会实际运用设计模式到真实开发中。
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
|
25天前
|
Spring Boot 3 集成Spring AOP实现系统日志记录
本文介绍了如何在Spring Boot 3中集成Spring AOP实现系统日志记录功能。通过定义`SysLog`注解和配置相应的AOP切面,可以在方法执行前后自动记录日志信息,包括操作的开始时间、结束时间、请求参数、返回结果、异常信息等,并将这些信息保存到数据库中。此外,还使用了`ThreadLocal`变量来存储每个线程独立的日志数据,确保线程安全。文中还展示了项目实战中的部分代码片段,以及基于Spring Boot 3 + Vue 3构建的快速开发框架的简介与内置功能列表。此框架结合了当前主流技术栈,提供了用户管理、权限控制、接口文档自动生成等多项实用特性。
72 8
【Spring】IoC和DI,控制反转,Bean对象的获取方式
IoC,DI,控制反转容器,Bean的基本常识,类注解@Controller,获取Bean对象的常用三种方式
【Spring】——SpringBoot项目创建
SpringBoot项目创建,SpringBootApplication启动类,target文件,web服务器,tomcat,访问服务器
详解Spring Batch:在Spring Boot中实现高效批处理
详解Spring Batch:在Spring Boot中实现高效批处理
397 12
Spring面试必问:手写Spring IoC 循环依赖底层源码剖析
在Spring框架中,IoC(Inversion of Control,控制反转)是一个核心概念,它允许容器管理对象的生命周期和依赖关系。然而,在实际应用中,我们可能会遇到对象间的循环依赖问题。本文将深入探讨Spring如何解决IoC中的循环依赖问题,并通过手写源码的方式,让你对其底层原理有一个全新的认识。
67 2
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等