前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SpringBoot中Spring IOC的运用

SpringBoot中Spring IOC的运用

作者头像
手撕代码八百里
发布2021-04-20 10:38:09
4810
发布2021-04-20 10:38:09
举报
文章被收录于专栏:猿计划

SpringBoot中Spring IOC的运用

维基百科上对IOC的描述:

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

上一篇:

《Spring&SpringMVC&SpringBoot》 https://cloud.tencent.com/developer/article/1815152

(一)项目准备

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

代码语言:javascript
复制
.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:

代码语言:javascript
复制
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的。

代码语言:javascript
复制
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

代码语言:javascript
复制
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

代码语言:javascript
复制
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

代码语言:javascript
复制
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);
    }

}

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

代码语言:javascript
复制
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管理了?

代码语言:javascript
复制
@Repository
public class HelloJDBCImpl implements HelloDao { }


@Service
public class HelloServiceImpl implements HelloService { }

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

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

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

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

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

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

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

代码语言:javascript
复制
@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源代码如下:

代码语言:javascript
复制
@ConfigurationProperties(
    prefix = "server",
    ignoreUnknownFields = true
)
public class ServerProperties {
    private Integer port;
    private InetAddress address;
    ......
}

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

因为使用配置文件配置一些信息会比较方便。 我开发的框架中也采用的大多数开源框架的思想。

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

代码语言:javascript
复制
/**
 * Spring IOC统一管理对象
 */
@Configuration
public class SwaggerPluginConfig {

	//从配置文件中获取swagger-plugin开头的信息
    @ConfigurationProperties(prefix = "swagger-plugin")
    @Bean(name = "swaggerPluginConfigBean")
    public SwaggerPluginConfigBean getSwaggerPluginConfigBean(){
        return new SwaggerPluginConfigBean();
    }

}

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

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

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

代码语言:javascript
复制
  @Autowired
  private SwaggerPluginConfigBean swaggerPluginConfigBean;

使用的时候直接调用:

代码语言:javascript
复制
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

代码语言:javascript
复制
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()测试单元的时候,就会打印:

代码语言:javascript
复制
Hello{msg='你好呀', code=100}

所以是有效的。

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

代码语言:javascript
复制
@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"));

}

结果:

代码语言:javascript
复制
Hello{msg='你好呀', code=100}
Hello{msg='哈哈哈', code=1022}
Hello{msg='66666', code=666}

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

例如业务实现层:

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

代码语言:javascript
复制
@Service("helloimpl")
public class HelloServiceImpl implements HelloService {...}

那么获取的时候:

代码语言:javascript
复制
@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的原理

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

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021/03/25 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • SpringBoot中Spring IOC的运用
    • (一)项目准备
      • (二)编写测试单元&验证
        • (三)分析哪些对象被BeanFactory管理了?
          • (四)为什么实体类没有被BeanFactory管理?
            • (五)哪些实体类应该被管理?
              • (六)获取Spring IOC管理的Bean
              • SpringBoot中Spring AOP的运用
              • Spring IoC的原理
              • Spring AOP的原理
              相关产品与服务
              对象存储
              对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档