Spring中@Import原理和使用

1,359 阅读4分钟

@Import注解是Spring框架中一个非常强大的工具,它允许你将普通类、组件类、ImportSelector实现类和ImportBeanDefinitionRegistrar实现类引入到容器中。通过@Import,你可以实现配置的模块化,使得代码更加清晰和易于维护。

Spring集成很多框架时,就是通过@Import来实现的。

本文中源码来自Spring 5.3.x分支,github源码地址:github.com/spring-proj…

一 如何使用@Import

该注解只有一个value属性,取值为需要导入的类的class对象。

有三种用法,介绍如下。

1.1 @Import导入普通类

对于普通类(没有被声明为Component),通过 @Import 也可以添加到Spring容器中。

例如,使用@Import(OrderService.class) ,就能向容器中添加OrderService类型的bean

import com.xiakexing.service.OrderService;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Configuration
@Import(OrderService.class)
public class AppConfig {
}
// 没有使用@Component的普通类
public class OrderService {

    public void test() {
        System.out.println("执行OrderService.test");
    }
}
public class Test {

	public static void main(String[] args) {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
		OrderService orderService = context.getBean("orderService", OrderService.class);
		orderService.test();
    }
}

当执行上面代码时,会出现报错:No bean named 'orderService' available.

注意,通过@Import直接导入的bean,名称是该类的全类名。

因此应这样获取context.getBean(OrderService.class.getName(), OrderService.class)

1.2 @Import与ImportSelector 接口

ImportSelector接口,允许你自定义条件动态选择要导入的配置类,有两个方法:

  1. selectImports:返回一个全类名的数组,这些类将被添加到spring容器中。注意,该方法可以返回空数组,但是不能返回null!
  2. getExclusionFilter:返回一个Predicate,用于排除selectImports方法返回值中的某些类。

来看示例,OrderService与上面相同,增加UserService类。

public class UserService {
    public void test() {
        System.out.println("执行UserService.test");
    }
}

SimpleImportSelector只导入UserService类。

import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;

import java.util.function.Predicate;

public class SimpleImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{OrderService.class.getName(), UserService.class.getName()};
    }

    // 排除全类名中有Order的类
    @Override
    public Predicate<String> getExclusionFilter() {
        return new Predicate<String>() {
            @Override
            public boolean test(String name) {
                return name.contains("Order");
            }
        };
    }
}
import com.xiakexing.service.SimpleImportSelector;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Configuration
@Import(SimpleImportSelector.class)
public class AppConfig {
}
import com.xiakexing.service.OrderService;
import com.xiakexing.service.UserService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Test {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        UserService userService = context.getBean(UserService.class.getName(), UserService.class);
        userService.test();
        OrderService orderService = context.getBean(OrderService.class.getName(), OrderService.class);
        orderService.test();
    }
}

结果就是userService.test()执行成功,而获取orderService时报错。

1.3 @Import与 ImportBeanDefinitionRegistrar 接口

ImportBeanDefinitionRegistrar 接口允许通过编程方式动态注册BeanDefinition。

来看示例,定义User类。

public class User {

    private String name;
    private int age;

    public User() {
    }

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }


    public void setName(String name) {
        this.name = name;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
        "name='" + name + ''' +
        ", age=" + age +
        '}';
    }
}

自定义ImportBeanDefinitionRegistrar 接口实现,编程式向容器中注册User类的beanDefinition,Spring容器将创建对应的bean。

import com.xiakexing.entity.User;
import org.springframework.beans.factory.config.ConstructorArgumentValues;
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;

public class SimpleBeanRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        AbstractBeanDefinition definition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
        definition.setBeanClass(User.class);
        ConstructorArgumentValues values = new ConstructorArgumentValues();
        values.addIndexedArgumentValue(0, "Tom");
        values.addIndexedArgumentValue(1, 29);
        definition.setConstructorArgumentValues(values);
        definition.setScope("singleton");
        registry.registerBeanDefinition("user", definition);
    }
}
import com.xiakexing.service.SimpleBeanRegistrar;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Configuration
@Import(SimpleBeanRegistrar.class)
public class AppConfig {
}

二 @Import的原理

Spring中ConfigurationClassPostProcessor类,会扫描所有的@Configuration类,并处理其中的@Import注解。

  1. 扫描配置类:Spring容器启动时,会扫描所有的@Configuration类。
  2. 解析@Import注解:对于每个配置类,Spring会解析其中的@Import注解,获取需要导入的类。
  3. 处理导入的类:根据导入的类的类型(普通类、@Configuration类、ImportSelector实现类、ImportBeanDefinitionRegistrar实现类),Spring会采取不同的处理方式。
  4. 注册BeanDefinition:最终,Spring会将所有导入的类注册为BeanDefinition,让Spring容器管理。

源码中ConfigurationClassParser类负责解析@Configuration类,其中doProcessConfigurationClass方法逻辑如下:

  1. 先递归地收集配置类或某个注解上的@Import的value值;
  2. 然后调用processImports方法来处理@Import注解,分3种情况处理导入的类。
  3. 例如,对于ImportBeanDefinitionRegistrar接口实现,先收集到Map中缓存起来,然后遍历Map,逐个调用registerBeanDefinitions方法

三 源码中使用

Spring源码中,模块化、插拔式的功能选项,就是通过@Import实现的。如aop、

3.1 @EnableAspectJAutoProxy实现

当在配置类上添加@EnableAspectJAutoProxy时,就启用了Spring aop功能。 源码中,EnableAspectJAutoProxy注解上通过Import引入了AspectJAutoProxyRegistrar,该类ImportBeanDefinitionRegistrar接口的实现,在registerBeanDefinitions方法中向容器注册了AnnotationAwareAspectJAutoProxyCreator。

AnnotationAwareAspectJAutoProxyCreator类是BeanPostProcessor接口的一个实现,会解析容器中@Aspect注解的bean,封装@Before等方法为Advisor对象;在bean的初始化后阶段,为切面命中的bean创建代理对象。

3.2 @EnableAsync实现

@EnableAsync注解提供了开箱即用的异步解决方案,我们只需在想要异步执行的方法上加@Async即可。 源码中,EnableAsync注解上通过Import引入了AsyncConfigurationSelector类, 该类是ImportSelector接口实现,在selectImports方法中引入了ProxyAsyncConfiguration类(默认使用JDK动态代理)。 在ProxyAsyncConfiguration中,又声明了AsyncAnnotationBeanPostProcessor的bean。后者是BeanPostProcessor接口实现,在bean的初始化后阶段,为使用了@Async的bean创建代理对象。