目录
前言
1、@Import用法分类
2、@Import各用法详解
1、导入Bean
2、导入配置类(上篇讲过)
?3、导入 ImportSelector 实现类
4、导入ImportBeanDefinitionRegistrar实现类
前言
????????上一篇介绍了@Enable*注解,底层实际上是用到了@Import注解,上一篇只简单讲了@Import导入配置类的用法,本文将详细讲解@Import四种用法。
1、@Import用法分类
① 导入Bean;
② 导入配置类;
③ 导入 ImportSelector 实现类,一般用于加载配置文件中的类;
④ 导入 ImportBeanDefinitionRegistrar 实现类。
2、@Import各用法详解
1、导入Bean
????????沿用上篇中的两个模块springboot-enable、springboot-embedded,两个模块的具体代码本次不再重新贴出,需要的请移步SpringBoot自动配置之@Enable*注解源码分析(9),现在修改springboot-enable的启动类如下:
package com.itlean;
import com.embedded.domain.User;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Import;
import java.util.Map;
@SpringBootApplication
@Import(User.class)//直接导入三方jar中的对象
public class SpringbootEnableApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(SpringbootEnableApplication.class, args);
//此处不能用别名获取了,因为SpringBoot注入IOC容器的时候不一定就叫user,要根据类型获取
User user = context.getBean(User.class);
System.out.println(user);
//获取IOC中所有User类型的对象
Map<String, User> users = context.getBeansOfType(User.class);
System.out.println(users);
}
}
启动后,输出如下,说明导入成功:
2、导入配置类(上篇讲过)
springboot-embedded新增加对象:
package com.embedded.domain;
/**
* @describe:
* @author: weny.yang
* @date: 2021-09-08 21:01
*/
public class Role {
}
配置类UserConfig.java中新增一个Role的Bean配置:
package com.embedded.config;
import com.embedded.domain.Role;
import com.embedded.domain.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @describe:
* @author: weny.yang
* @date: 2021-09-08 20:39
*/
//@Configuration 注意:调用方使用@Import导入配置类方式情况下,此处(三方jar配置Bean的配置类)可以不加@Configuration注解
public class UserConfig {
@Bean
public User user(){
return new User();
}
@Bean
public Role role(){
return new Role();
}
}
修改springboot-enable的启动类如下:
package com.itlean;
import com.embedded.config.UserConfig;
import com.embedded.domain.Role;
import com.embedded.domain.User;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Import;
import java.util.Map;
@SpringBootApplication
@Import(UserConfig.class)//直接导入三方jar中的对象的配置类
public class SpringbootEnableApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(SpringbootEnableApplication.class, args);
//此处不能用别名获取了,因为SpringBoot注入IOC容器的时候不一定就叫user,要根据类型获取
User user = (User)context.getBean("user");
System.out.println(user);
Role role = (Role)context.getBean("role");
System.out.println(role);
//获取IOC中所有User类型的对象
Map<String, User> users = context.getBeansOfType(User.class);
System.out.println(users);
Map<String, Role> roles = context.getBeansOfType(Role.class);
System.out.println(roles);
}
}
????????导入一个配置类,可以注入多个Bean,另外,调用方使用@Import导入配置类方式情况下,三方jar配置Bean的配置类可以不加@Configuration注解,启动后,输出如下,说明导入成功:
3、导入 ImportSelector 实现类
ImportSelecto是一个接口,我们先来看一下源码:
package org.springframework.context.annotation;
import java.util.function.Predicate;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.lang.Nullable;
public interface ImportSelector {
String[] selectImports(AnnotationMetadata var1);
@Nullable
default Predicate<String> getExclusionFilter() {
return null;
}
}
????????ImportSelecto接口源码类中有String[] selectImports(AnnotationMetadata var1);方法,该方法返回值是Bean的全限定路径名,这个方法需要我们来实现它,下面我们在springboot-embedded模块中自定义一个名为MyImportSelecto.java的实现类:
package com.embedded.config;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
/**
* @describe:
* @author: weny.yang
* @date: 2021-09-08 21:21
*/
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
//入参为要创建的Bean的全限定路径名
return new String[]{"com.embedded.domain.User","com.embedded.domain.Role"};
}
}
修改springboot-enable的启动类如下:
package com.itlean;
import com.embedded.config.MyImportSelector;
import com.embedded.config.UserConfig;
import com.embedded.domain.Role;
import com.embedded.domain.User;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Import;
import java.util.Map;
@SpringBootApplication
@Import(MyImportSelector.class)//直接导入三方jar中的对象的ImportSelector接口的实现类MyImportSelector配置类
public class SpringbootEnableApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(SpringbootEnableApplication.class, args);
//此处不能用别名获取了,因为SpringBoot注入IOC容器的时候不一定就叫user,要根据类型获取
User user = (User)context.getBean(User.class);
System.out.println(user);
Role role = (Role)context.getBean(Role.class);
System.out.println(role);
//获取IOC中所有User类型的对象
Map<String, User> users = context.getBeansOfType(User.class);
System.out.println(users);
Map<String, Role> roles = context.getBeansOfType(Role.class);
System.out.println(roles);
}
}
启动后,输出如下:
????????说明注入成功,到这,有些伙伴可能说了,这种方式不是比前两种还要复杂吗,方式二就已经满足同时注入多个Bean了呀,但是细想,方式二导入配置类,整个配置类里面可能配置了几十上百个Bean,一下子全部注入到IOC了,有一些我们用不到的也一并注入了,肯定是不合理的,方式三却可以将要注入的Bean的全路径限定名配置在配置文件中,根据实际业务动态的实现Bean的注入,更加可控灵活。
4、导入ImportBeanDefinitionRegistrar实现类
首先来看一下ImportBeanDefinitionRegistrar.java接口源码:
package org.springframework.context.annotation;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.core.type.AnnotationMetadata;
public interface ImportBeanDefinitionRegistrar {
default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {
this.registerBeanDefinitions(importingClassMetadata, registry);
}
default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
}
}
????????源码内部有一个default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {}空实现方法,现在我们实现它,在springboot-embedded中自定义一个名为MyImportBeanDefinitionRegistrar.java的ImportBeanDefinitionRegistrar.java的实现类:
package com.embedded.config;
import com.embedded.domain.User;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
/**
* @describe:
* @author: weny.yang
* @date: 2021-09-08 21:42
*/
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AbstractBeanDefinition userBeanDefinition = BeanDefinitionBuilder.rootBeanDefinition(User.class).getBeanDefinition();
registry.registerBeanDefinition("user", userBeanDefinition);
}
}
修改springboot-enable的启动类如下:
package com.itlean;
import com.embedded.config.MyImportBeanDefinitionRegistrar;
import com.embedded.config.MyImportSelector;
import com.embedded.config.UserConfig;
import com.embedded.domain.Role;
import com.embedded.domain.User;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Import;
import java.util.Map;
@SpringBootApplication
@Import(MyImportBeanDefinitionRegistrar.class)
public class SpringbootEnableApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(SpringbootEnableApplication.class, args);
//此处不能用别名获取了,因为SpringBoot注入IOC容器的时候不一定就叫user,要根据类型获取
User user1 = (User)context.getBean(User.class);
User user2 = (User)context.getBean("user");
System.out.println(user1);
System.out.println(user2);
//获取IOC中所有User类型的对象
Map<String, User> users = context.getBeansOfType(User.class);
System.out.println(users);
}
}
启动后,输出如下,因为实现类中有指定Bean的别名,所以获取的时候可以根据名字也可以根据类型。
============================================优雅的分割线=============================================
以上就是@Import的四种用法,现在我们像上篇一样,再次回看SpringBoot启动类上面的@SpringBootApplication注解,内部组合使用到的@Import注解是这样的:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class}) //说明是本文介绍的第三种方式
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
????????说明SpringBoot底层的@Import注解使用第三种方式,实现了ImportSelector接口,以这种方式完成了SpringBoot启动的时候动态的加载类,也是SpringBoot自动装配的核心注解之一,至于@SpringBootApplication中的ImportSelector在启动的时候到底干了哪些的不为人知的事情,我们下一篇文章再行详解。
如果学到了,不妨点赞收藏哦,一起加油!
|