SpringBoot-@Import 注解的工作原理
在没有接触SpringBoot的时候想要把一个对象注入到Spring容器中的方法:
1、可以使用@Component注解,代码如下:
package com.boot.enable.imp.demo;import org.springframework.stereotype.Component;@Componentpublic class Book {}package com.boot.enable.imp.demo;import org.springframework.stereotype.Component;@Componentpublic class User {}
主函数:
package com.boot.enable.imp.demo;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.context.ConfigurableApplicationContext;import org.springframework.context.annotation.Bean;/*** 普通得方式来装配得*/@SpringBootApplicationpublic class ImportApplication {@Beanpublic User createUser(){return new User();}public static void main(String[] args) {ConfigurableApplicationContext context =SpringApplication.run(ImportApplication.class, args);System.out.println(context.getBean(Book.class));System.out.println(context.getBean(User.class));context.close();}}
运行的结果如下:
2、第二种方式:使用一个配置类来配置:
package com.boot.enable.imp.demo;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.context.ConfigurableApplicationContext;import org.springframework.context.annotation.Bean;/*** 普通得方式来装配得*/@SpringBootApplicationpublic class ImportApplication {@Beanpublic Book book(){return new Book();}public static void main(String[] args) {ConfigurableApplicationContext context =SpringApplication.run(ImportApplication.class, args);System.out.println(context.getBean(Book.class));System.out.println(context.getBean(User.class));context.close();}}
运行结果如下:
3、第三种方式:通过@Import的方式把这个对象引入进来:这个类型的实例将会交给spring容器去管理。
运行的结果如下:
原因所在:@Import这个注解的接口对应两个配置类:
A:ImportSelector B:ImportBeanDefinitionRegistrator
package org.springframework.context.annotation;import java.lang.annotation.Documented;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface Import {Class<?>[] value();}
在ImportSelector这个接口里面有一个方法:把返回当前的类的全路径的都实例化所有的对象都统统纳入到spring容器种去进行管理。
只要我去实现了下面接口的方法,只要这个方法里面传过来所有的对象都会统统的纳入到spring容器中去管理它。
package org.springframework.context.annotation;import org.springframework.core.type.AnnotationMetadata;public interface ImportSelector {String[] selectImports(AnnotationMetadata var1);}
代码如下:
package com.boot.enable.imp.demo1;import org.springframework.context.annotation.ImportSelector;import org.springframework.core.type.AnnotationMetadata;public class BeanImportSelector implements ImportSelector {@Overridepublic String[] selectImports(AnnotationMetadata annotationMetadata) {return new String[]{"com.boot.enable.imp.demo1.Book","com.boot.enable.imp.demo1.User"};}}
主函数代码改下:
package com.boot.enable.imp.demo1;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.context.ConfigurableApplicationContext;import org.springframework.context.annotation.Import;/*** 使用@Import方式来装配*/@SpringBootApplication@Import(BeanImportSelector.class)public class ImportApplication1 {public static void main(String[] args) {ConfigurableApplicationContext context =SpringApplication.run(ImportApplication1.class, args);System.out.println(context.getBean(User.class));System.out.println(context.getBean(Book.class));context.close();}}
运行结果如下:
4:第四种方式:ImportBeanefinitionRegistrator这个类也可以做到,代码如下:
package com.boot.enable.imp.demo2;import org.springframework.beans.factory.config.BeanDefinition;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 MyBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {//beanDefinitionRegistry把一个对象注册到Spring容器中去管理@Overridepublic void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {//创建构建器对象BeanDefinitionBuilder bdb=BeanDefinitionBuilder.rootBeanDefinition(User.class);BeanDefinition beanDefinition = bdb.getBeanDefinition();
//第一个参数是别名
beanDefinitionRegistry.registerBeanDefinition("user",beanDefinition);BeanDefinitionBuilder bdb1=BeanDefinitionBuilder.rootBeanDefinition(Book.class);BeanDefinition beanDefinition1 = bdb1.getBeanDefinition();beanDefinitionRegistry.registerBeanDefinition("book",beanDefinition1);}}
主函数的代码如下:
package com.boot.enable.imp.demo2;import com.boot.enable.imp.demo1.BeanImportSelector;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.context.ConfigurableApplicationContext;import org.springframework.context.annotation.Import;/*** 使用@Import方式来装配*/@SpringBootApplication//@Import({User.class,Book.class})@Import(MyBeanDefinitionRegistrar.class)public class ImportApplication1 {public static void main(String[] args) {ConfigurableApplicationContext context =SpringApplication.run(ImportApplication1.class, args);System.out.println(context.getBean("user",User.class));System.out.println(context.getBean("book",Book.class));context.close();}}
运行的结果如下:
第三种和第四种方法的区别:第四种可以注入参数和属性到spring容器中。
小节总结:@Import注解的工作原理,就是对应的类去实现接口中的方法从而把对象到spring容器中去。
注解装配监控器实现(BeanDefinitionProcessor回调)-如何去做自己的组件
实现的功能:看哪些对象被装配到spring容器中去,就把它对应的实例给打印出来。
步骤 1、自定义注解:用来扫描包的路径的:
package com.boot.enable.sample;import org.springframework.context.annotation.Import;import java.lang.annotation.*;@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Import(ScannerPackageRegister.class)//指定扫描所有的包public @interface EnableScanner {String[] packages();}
2、主类函数如下:
package com.boot.enable.sample;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.scheduling.annotation.EnableAsync;@SpringBootApplication//启用监控扫描类的注解@EnableScanner(packages = {"com.boot.enable.sample.bean","com.boot.enable.sample.vo"})public class ScannerPackageApplication {public static void main(String[] args) {SpringApplication.run(ScannerPackageApplication.class,args);}}
3、ScannerPackageRegister这个类的代码如下:
package com.boot.enable.sample;import org.springframework.beans.factory.config.BeanDefinition;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;import java.util.Arrays;import java.util.List;public class ScannerPackageRegister implements ImportBeanDefinitionRegistrar {//AnnotationMetadata:通过它来获取到EnableScanner这个注解中packages这个属性对应的值@Overridepublic void registerBeanDefinitions(AnnotationMetadata annotationMetadata,BeanDefinitionRegistry beanDefinitionRegistry) {String [] args= (String[])annotationMetadata.getAnnotationAttributes(EnableScanner.class.getName()).get("packages");- //packages这个拿出来是一个数组,要转成集合后面好操作
List<String> packages= Arrays.asList(args);System.out.println(packages);//MyBeanDefinitionProcessor通过这个类来进行统一的实例化//在实例化的前后都会进行拦截的处理。BeanDefinitionBuilder bdb=BeanDefinitionBuilder.rootBeanDefinition(MyBeanDefinitionProcessor.class);- //把这个集合装配到spring容器中去。
bdb.addPropertyValue("packages",packages);BeanDefinition beanDefinition = bdb.getBeanDefinition();beanDefinitionRegistry.registerBeanDefinition(MyBeanDefinitionProcessor.class.getName(),beanDefinition);}}
4、要装配类的的代码如下:
package com.boot.enable.sample;import org.springframework.beans.BeansException;import org.springframework.beans.factory.config.BeanPostProcessor;import java.util.List;//当类被装配到Spring容器中是需要通过这个接口的,会回调这个接口中postProcessBeforeInitialization这个方法的public class MyBeanDefinitionProcessor implements BeanPostProcessor {private List<String> packages;//等会要注入的所以要提供set方法public void setPackages(List<String> packages) {this.packages = packages;}@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {for(String pack:packages){if(bean.getClass().getName().startsWith(pack)){System.out.println("-----------"+bean.getClass().getName());}}return bean;}}
运行的结果如下:
小节总结:这就是使用@Import注解的工作原理来模拟一个监控被注入到spring容器中的类的实例,一开始是不知道哪些类被装配到spring容器中的。相当于做一个过滤器来判断哪些类被装配到spring容器中,从而做一个监控的作用。
归根结低还是@Import。比如可以做劫持的操作。