SpringBoot-@Import 注解的工作原理
在没有接触SpringBoot的时候想要把一个对象注入到Spring容器中的方法:
1、可以使用@Component注解,代码如下:
package com.boot.enable.imp.demo;
import org.springframework.stereotype.Component;
@Component
public class Book {
}
package com.boot.enable.imp.demo;
import org.springframework.stereotype.Component;
@Component
public 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;
/**
* 普通得方式来装配得
*/
@SpringBootApplication
public class ImportApplication {
@Bean
public 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;
/**
* 普通得方式来装配得
*/
@SpringBootApplication
public class ImportApplication {
@Bean
public 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)
@Documented
public @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 {
@Override
public 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容器中去管理
@Override
public 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这个属性对应的值
@Override
public 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;
}
@Override
public 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。比如可以做劫持的操作。