SpringBoot自动装配的应用

一、什么是自动装配

在使用SpringBoot的时候,会自动将Bean装配到IoC容器中。例如我们在使用Redis数据库的时候,会引入依赖spring-boot-starter-data-redis。在引入这个依赖后,服务初始化的时候,会将操作Redis需要的组件注入到IoC容器中进行后续使用

自动装配大致过程如下:

  • 1)通过注解@SpringBootApplication=>@EnableAutoConfiguration=>@Import({AutoConfigurationImportSelector.class})实现自动装配
  • 2)AutoConfigurationImportSelector类中重写了ImportSelector中selectImports方法,批量返回需要装配的配置类
  • 3)通过Spring提供的SpringFactoriesLoader机制,扫描classpath下的META-INF/spring.factories文件,读取需要自动装配的配置类
  • 4)依据条件筛选的方式,把不符合的配置类移除掉,最终完成自动装配

具体细节可以参考以下两篇文章:
https://baijiahao.baidu.com/s?id=1725265949551075777&wfr=spider&for=pc

https://blog.youkuaiyun.com/weixin_41979002/article/details/119378858

二、自动装配的应用

我们看了springboot的源码后,我们知道了什么是自动装配及其原理。但光知道原理还不行,还需要知道如何使用。

2.1 直接使用springboot自动装配好的配置类

springboot为我们装配好的配置类有哪些呢?答案就在spring.factories文件里:

在这里插入图片描述

可以看到,这个文件写了很多配置类,springboot为了方便我们使用,已经事先把许多常用的配置类准备好,当我们具体需要使用哪个功能配置时,再具体将其依赖引入即可。

下面以RedisAutoConfiguration这个自动配置类为例讲解,看下图:
在这里插入图片描述
这个配置类是springboot事先提供的,也放在spring-boot-autoconfigure-xxx.RELEASE.jar这个依赖下,这个配置类为我们装备两个bean,分别为redisTemplate和stringRedisTemplate,原代码如下:

package org.springframework.boot.autoconfigure.data.redis;

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {

	@Bean
	@ConditionalOnMissingBean(name = "redisTemplate")
	public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory)
			throws UnknownHostException {
		RedisTemplate<Object, Object> template = new RedisTemplate<>();
		template.setConnectionFactory(redisConnectionFactory);
		return template;
	}

	@Bean
	@ConditionalOnMissingBean
	public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory)
			throws UnknownHostException {
		StringRedisTemplate template = new StringRedisTemplate();
		template.setConnectionFactory(redisConnectionFactory);
		return template;
	}

}

那么问题来了,这个配置类已经存在了,那么项目里是否就可以直接使用Redis相关的功能了吗?
答案是,不能。

为啥呢?且看下面,慢慢道来…

我们可以看到,每个自动配置类都会有下面这行代码:

@ConditionalOnClass(RedisOperations.class)

其意思,就是如果这个RedisOperations类存在,则这个配置类生效。

细心的同学看过源码都会发现,看上面源码截图,有些类是显示红色的(有些idea不会显示红色),如RedisOperations、RedisTemplate…
而且点击不进去,也看不到源码,说明我们的项目里这些类是不存在的,所以我们这个自动配置类是无效的,简言之,就是一个空壳。springboot为我们提前准备好的一系列自动配置类,大多数都是空壳,不生效的。

那么我们如何能使得RedisAutoConfiguration 这个自动配置类生效呢?(这就是应用了

答案是,将相关的自动配置类引入即可,在pom文件里加入:

 <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-data-redis</artifactId>
 </dependency>

我们再从头看RedisAutoConfiguration 的源码:
在这里插入图片描述
且看,之前飘红的类已经不红了,而且也能点进去查看源码,说明这个配置类生效了。redis的相关功能,我们也可以正常使用了,这就是自动装配的应用。

2.2 自定义的配置类手动装配

有些时候,由于业务需求的原因,我们会产生许多公共的组件,最具代表性的,如系统操作记录的日志收集。

做日志收集时,我们一般都会应用aop切面编程。如果你这部分aop的代码不是很复杂,也许不需要抽取为公共的。

但如果这个日志业务及其复杂,而且很多个微服务项目也要用到,如果我们不想将这部分代码在每个微服务项目都拷贝一份,那我们就可以将这部分代码抽出为一个独立的项目组件,并将其打包成为一个jar依赖,放到我们的仓库,供其他项目引入并使用。

但我们完成这部分切面代码的打包后,并在其他项目引入后,你会发现,这些aop切面代码是不生效的
如这篇文章:https://blog.youkuaiyun.com/whj826069748/article/details/84068201 也说到了这个问题,并有相应的解决方案。需要在引用模块的启动类中加入扫描的代码:

@ComponentScan(basePackages = {"xxx.xxx.*"})

在这里插入图片描述

为什么要这么做呢?
原因是,我们的切面代码,有一些类是需要装配到ioc容器中才能使用的,如下代码:

/**
 * @ClassName MyAspect
 * @Description TODO
 * @Author Oneby
 * @Date 2021/1/22 11:27
 * @Version 1.0
 */
@Aspect
@Component
public class MyAspect {
    @Before("execution(public int com.heygo.spring.aop.CalcServiceImpl.*(..))")
    public void beforeNotify() {
        System.out.println("******** @Before我是前置通知MyAspect");
    }

    @After("execution(public int com.heygo.spring.aop.CalcServiceImpl.*(..))")
    public void afterNotify() {
        System.out.println("******** @After我是后置通知");
    }

    @AfterReturning("execution(public int com.heygo.spring.aop.CalcServiceImpl.*(..))")
    public void afterReturningNotify() {
        System.out.println("********@AfterReturning我是返回后通知");
    }

    @AfterThrowing("execution(public int com.heygo.spring.aop.CalcServiceImpl.*(..))")
    public void afterThrowingNotify() {
        System.out.println("********@AfterThrowing我是异常通知");
    }

    @Around("execution(public int com.heygo.spring.aop.CalcServiceImpl.*(..))")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        Object retValue = null;
        System.out.println("我是环绕通知之前AAA");
        retValue = proceedingJoinPoint.proceed();
        System.out.println("我是环绕通知之后BBB");
        return retValue;
    }
}


这是一个简单的切面类,它需要装配到ioc容器里才能生效,但由于其是外部依赖jar引入的,所以我们的启动类是扫描不到它的, 因此,我们可以通过@ComponentScan的方式指定包扫描,这样我们的切面代码就能装配到容器中了。

上述这个解决方案,有些人成功了,有些人失败了,博主就是失败的那个…

失败的也没关系,这个方案并不是最好的,假设这种公共组件越来越多,每个你都需要亲自去加一下这行代码,依然是挺麻烦的。

我们可以通过自动配置原理来实现自动装配:

1、先创建一个配置类:

package com.junjie.configure;

@Configuration
@ComponentScan(basePackages = "com.work.*")
public class LogConfigure {

}

2、在aop项目组件创建META-INF文件夹,并在该文件夹下创建spring.factories文件

这个文件内容要怎么写呢?

我们在看一下springboot自带的spring.factories文件
在这里插入图片描述
看到这里,我们就知道了,只要参考这个写法,我们就能将我们定义的组件自动装配到容器里去。

org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.junjie.configure.LogConfigure

到这里,我们的自定义组件,就已经加入到自动配置里去了。

后续使用时,只需要将我们的组件打包到maven仓库,在其他项目添加此依赖即可。springboot会自动扫描项目里所有META-INF文件夹下的spring.factories文件,并将这些文件下所配置好的自动配置类装配到容器中。如此就实现了自动装配,采用自动装配的优点就是,客户端想要使用我们的组件,只需要引入依赖即可,无需其他操作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值