spring在初始化容器的时候除了扫描出我们定义的普通类之外,还会扫描出特殊类完成容器的初始化动,这种特殊的类有一种就是我们使用了注解@Import导入的类。
@Import有下面三种使用形式:
- @Import(xxx.class),其中xxx为普通形式的类;
- @Import(A.class),其中,A为ImportSelector的实现类;
- @Import(B.class),其中,B为ImportBeanDefinitionRegistrar的实现类。
在本节文章中只讨论ImportSelector,(ImportBeanDefinitionRegistrar比ImportSelector更强大,可以人为的为spring容器中添加bean,例如mybatis就是使用了这种方式将mapper添加到容器中的。具体的等到了分析mybatis文章的时候说吧)。关于ImportBeanDefinitionRegistrar的使用demon可以看这篇吧。 ImportBeanDefinitionRegistrar——spring对外提供的注册bean功能
使用起来也很简单,首先ImportSelector
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{IndexDao1.class.getName()};
}
}
通过代码可以看到,这里只是将IndexDao1这个类的名字返回。这里返回某个类的名字后,spring就可以根据类名得到对应的类,然后得到相应的BeanDefinition,最会中放入spring容器当中。
IndexDao1类如下:
public class IndexDao1 implements BeanPostProcessor, Dao {
public void query() {
System.out.println("IndexDao1");
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (beanName.equals("indexDao")) {
return Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{Dao.class}, new MyInvocationHandler(bean));
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return null;
}
}
通过以上代码,我们看到这么几点:
- 1、IndexDao1 通过实现BeanPostProcessor接口来插手bean的实例化;
- 2、将名字为indexDao的bean返回了一个代理对象。 关于BeanPostProcessor,其实就是spring提供给我们插手bean实例化过程的一个扩展点。
IndexDao和MyInvocationHandler如下:
@Repository
public class IndexDao implements Dao {
public void query() {
System.out.println("query");
}
}
public class MyInvocationHandler implements InvocationHandler {
Object target;
public MyInvocationHandler(Object o) {
target = o;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("我是代理");
return method.invoke(target, args);
}
}
然后直接使用@Import(MyImportSelector.class)即可,不过我们为了优雅点,可以定义一个注解,当做使用我们自定义这个导入类的“开关”
@Retention(RetentionPolicy.RUNTIME)
@Import(MyImportSelector.class)
public @interface EnableMySelector {
}
最后,我们在配置类上引入EnableMySelector注解,表示开启MyImportSelector的使用
@Configuration
@ComponentScan("com.study")
@EnableMySelector
public class AppConfig {
}
测试类如下:
public class Test {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
Dao dao = (Dao)context.getBean("indexDao");
dao.query();
}
这里我们从容器中获取了indexDao这个bean,输出如下:
我是代理
query
总结:
可以看到,当我们获取indexDao时,实际上spring返回的是一个代理对象完成的功能,回顾上面的代码,我们主要是通过MyImportSelector和IndexDao1联合使用来完成了这个功能。这个呢,也就是ImportSelector这个接口的一个简单使用场景,当我们需要改变一个类的行为,或者说增强的时候我们就可以使用这种处理方式,直接在配置类上增加EnableMySelector注解,如果不需要增强类行为,在配置类上移除EnableMySelector注解即可,代码灵活性比较好。