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的原理

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

目录
相关文章
|
17天前
|
前端开发 Java 应用服务中间件
《深入理解Spring》 Spring Boot——约定优于配置的革命者
Spring Boot基于“约定优于配置”理念,通过自动配置、起步依赖、嵌入式容器和Actuator四大特性,简化Spring应用的开发与部署,提升效率,降低门槛,成为现代Java开发的事实标准。
|
17天前
|
XML Java 测试技术
《深入理解Spring》:IoC容器核心原理与实战
Spring IoC通过控制反转与依赖注入实现对象间的解耦,由容器统一管理Bean的生命周期与依赖关系。支持XML、注解和Java配置三种方式,结合作用域、条件化配置与循环依赖处理等机制,提升应用的可维护性与可测试性,是现代Java开发的核心基石。
|
17天前
|
前端开发 Java 微服务
《深入理解Spring》:Spring、Spring MVC与Spring Boot的深度解析
Spring Framework是Java生态的基石,提供IoC、AOP等核心功能;Spring MVC基于其构建,实现Web层MVC架构;Spring Boot则通过自动配置和内嵌服务器,极大简化了开发与部署。三者层层演进,Spring Boot并非替代,而是对前者的高效封装与增强,适用于微服务与快速开发,而深入理解Spring Framework有助于更好驾驭整体技术栈。
|
25天前
|
XML Java 应用服务中间件
【SpringBoot(一)】Spring的认知、容器功能讲解与自动装配原理的入门,带你熟悉Springboot中基本的注解使用
SpringBoot专栏开篇第一章,讲述认识SpringBoot、Bean容器功能的讲解、自动装配原理的入门,还有其他常用的Springboot注解!如果想要了解SpringBoot,那么就进来看看吧!
226 2
|
2月前
|
人工智能 Java 机器人
基于Spring AI Alibaba + Spring Boot + Ollama搭建本地AI对话机器人API
Spring AI Alibaba集成Ollama,基于Java构建本地大模型应用,支持流式对话、knife4j接口可视化,实现高隐私、免API密钥的离线AI服务。
1148 1
基于Spring AI Alibaba + Spring Boot + Ollama搭建本地AI对话机器人API
存储 JSON Java
382 0
|
2月前
|
人工智能 Java 开发者
【Spring】原理解析:Spring Boot 自动配置
Spring Boot通过“约定优于配置”的设计理念,自动检测项目依赖并根据这些依赖自动装配相应的Bean,从而解放开发者从繁琐的配置工作中解脱出来,专注于业务逻辑实现。
|
3月前
|
监控 Java API
Spring Boot 3.2 结合 Spring Cloud 微服务架构实操指南 现代分布式应用系统构建实战教程
Spring Boot 3.2 + Spring Cloud 2023.0 微服务架构实践摘要 本文基于Spring Boot 3.2.5和Spring Cloud 2023.0.1最新稳定版本,演示现代微服务架构的构建过程。主要内容包括: 技术栈选择:采用Spring Cloud Netflix Eureka 4.1.0作为服务注册中心,Resilience4j 2.1.0替代Hystrix实现熔断机制,配合OpenFeign和Gateway等组件。 核心实操步骤: 搭建Eureka注册中心服务 构建商品
535 3