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

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

目录
相关文章
|
16天前
|
监控 Java 应用服务中间件
spring和springboot的区别
spring和springboot的区别
20 1
|
11天前
|
Java 测试技术 开发者
Spring IoC容器通过依赖注入机制实现控制反转
【4月更文挑战第30天】Spring IoC容器通过依赖注入机制实现控制反转
21 0
|
1天前
|
XML Java 数据库连接
Spring框架与Spring Boot的区别和联系
Spring框架与Spring Boot的区别和联系
8 0
|
11天前
|
SQL Java 数据库连接
Springboot框架整合Spring JDBC操作数据
JDBC是Java数据库连接API,用于执行SQL并访问多种关系数据库。它包括一系列Java类和接口,用于建立数据库连接、创建数据库操作对象、定义SQL语句、执行操作并处理结果集。直接使用JDBC涉及七个步骤,包括加载驱动、建立连接、创建对象、定义SQL、执行操作、处理结果和关闭资源。Spring Boot的`spring-boot-starter-jdbc`简化了这些步骤,提供了一个在Spring生态中更便捷使用JDBC的封装。集成Spring JDBC需要添加相关依赖,配置数据库连接信息,并通过JdbcTemplate进行数据库操作,如插入、更新、删除和查询。
|
11天前
|
SQL Java 数据库连接
Springboot框架整合Spring Data JPA操作数据
Spring Data JPA是Spring基于ORM和JPA规范封装的框架,简化了数据库操作,提供增删改查等接口,并可通过方法名自动生成查询。集成到Spring Boot需添加相关依赖并配置数据库连接和JPA设置。基础用法包括定义实体类和Repository接口,通过Repository接口可直接进行数据操作。此外,JPA支持关键字查询,如通过`findByAuthor`自动转换为SQL的`WHERE author=?`查询。
|
11天前
|
安全 Java 开发者
在Spring框架中,IoC和AOP是如何实现的?
【4月更文挑战第30天】在Spring框架中,IoC和AOP是如何实现的?
21 0
|
11天前
|
XML Java 程序员
什么是Spring的IoC容器?
【4月更文挑战第30天】什么是Spring的IoC容器?
19 0
|
14天前
|
Java Spring 容器
【Spring系列笔记】IOC与DI
IoC 和 DI 是面向对象编程中的两个相关概念,它们主要用于解决程序中的依赖管理和解耦问题。 控制反转是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入和依赖查找。
30 2
|
14天前
|
Java 测试技术 数据库连接
Spring中ioc的优点
总之,Spring中的IoC提供了一种更加灵活、可维护、可测试和可扩展的方式来管理组件之间的依赖关系,从而提高了应用程序的质量和可维护性。这使得开发人员能够更专注于业务逻辑而不是底层的技术细节。
32 1
|
15天前
|
Java Maven Docker
0.07 秒启动一个 SpringBoot 项目!Spring Native 很强!!
0.07 秒启动一个 SpringBoot 项目!Spring Native 很强!!
26 2