做java开发的小伙伴都知道,java的生态比较繁荣,有各种各样的第三方组件来满足我们日常的开发需求。很多常用的中间件(redis,kafka等)都提供java的开发接口,而且这些接口通常会被封装成比较好用的组件来满足我们使用这些中间件的场景需求。那么,你知道如何在把这些好用的第三方组件引入到我们的项目中吗?
通常java项目引入第三方组件的方式,主要是通过把第三方组件的jar包引入到当前项目中,而引入jar包通常使用一些依赖管理工具就可以了,如maven,gradel等。当jar引入到项目的类路径后,那么就可以直接使用里面的关键接口做开发了。
做java开发的老铁应该没有没用过spring的吧,如果有,那么接下来的内容可以跳过了(哈哈哈)。下面我们主要来学习一下,在spring框架下,如何优雅地将第三方组件中的接口,引入到我们的项目中。
为了方便下文描述,我们定义如下第三方组件:组件中只有一个类,用来实现将给定时间转换成“yyyy-MM-dd”格式的字符串(为了不增加读者理解负担,例子中组件实现的业务功能比较简单,但是足够说明问题)。
public class DateUtils {
public String parse(Date date) {
return new SimpleDateFormat("yyyy-mm-dd").format(date);
}
}
组件的依赖管理采用maven管理:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>Utils</artifactId>
<version>1.0-SNAPSHOT</version>
</project>
组件功能比较简单,自身没有依赖任何其他组件。
现在我们想在自己使用spring的项目中引入 DateUtils 这个类,实现日期格式转换。
一、将关键类作为Bean注入到IOC中
这种方式是最常用,也是最简单的方式,使用方法如下:
@Configuration
public class DateConfig {
@Bean
public DateUtils getDateUtils (){
return new DateUtils();
}
}
接下来,在项目需要DateUtils的地方,使用@Resource或者@Autowired等方式来完成DateUtils的依赖注入。
二、使用@Import
@Import注解的值是一个Class,使用方式如下:
@Import(xxx.class)
根据xxx的类型不同,Import解析xxx的方式也存在不同,在实际使用中,xxx同行有三种类型。
1、XXX是一个普通类,此时Import的作用就是将这个类加载到spring容器中
public class XXX {
public String getName() {
return "xxx";
}
}
@Import(XXX.class)
public class ImportConfig {
}
2、xxx实现了ImportBeanDefinitionRegistrar接口,在重写的registerBeanDefinitions方法里面,能拿到BeanDefinitionRegistry,能手工往beanDefinitionMap中注册 beanDefinition。
public class XXX implements ImportBeanDefinitionRegistrar{
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
RootBeanDefinition bd = new RootBeanDefinition();
bd.setBeanClass(YYY.class);
registry.registerBeanDefinition("yyy", bd);
}
}
此时我们就把Person的BeanDefination放到了BeanDefinationMap中了,了解spring IOC的老铁都知道,bean其实就是通过BeanDefination中的class字段信息反射产生的,在使用YYY类的场景中,直接用@Autowired注入即可。
3、XXX实现了ImportSelector 重写selectImports方法该方法返回了String[]数组的对象,数组里面的类都会注入到spring容器当中,可以理解成第一种场景的增强版。
public class XXX implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[] {YYYY.class.getName(),ZZZ.class.getName()};
}
此时我们就把YYY和ZZZ都放到了spring的IOC容器了。
此时我们要把DateUtils引入到项目中,使用Import的任何一种方式都可以。
其实我们和可以在优化一下@Import的使用方式,让其更加优雅一些,我们在第三方组件中定义一个注解形如@EnableDateUtil,在该注解内使用@Import,这样我们的项目中只需要使用@EnableDateUtil注解即可,使用方式如下:
第三方组件:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(DateUtils.class)
public @interface EnableDateUtil {
}
需要注意此时第三方项目中要引入如下依赖,才能编译成功
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<version>2.3.12.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.3.12.RELEASE</version>
<scope>compile</scope>
</dependency>
</dependencies>
我们的项目:
@SpringBootApplication
@EnableDateUtil
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
这种方式在spring-cloud提供的一些组件中,使用的比较多,比如@EnableFeignClients。
三、自动转配
使用过springboot的老铁应该多少都了解过自动装配的原理,自动装配就是在项目启动过程中,springboot会读取类路径下META-INF目录下的spring.factories文件,并把其中 key为 "org.springframework.boot.autoconfigure.EnableAutoConfiguration"的部分类放到IOC容器中(只有"部分"类会放到IOC中,是因为会根据配置的Filter,对一些类进行过滤)。
基于自动装配的原理,我们的自定义的第三方组件也可以使用这种方式,把自己当做一个"Starter"提供给其他项目使用,具体方式如下:
第三方项目
在META-INF目录下添加文件 spring.factories文件
文件内容如下
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.xxx.DateUtils
在我们的项目中,直接使用@Autowired或者@Resource把DateUtils注入进来,此种方式比第一种和第二种方式更加简单,无侵入,这也是springboot中starter的实现方式,怪不得我们在使用一些组件提供的Starter时,无需什么额外操作,就可以直接使用需要的接口了。
你还有其他在项目中引入第三方组件的方式吗?