BeanDefinition

AnnotatedBeanDefinitionReader

可以解析注解是:@Bean、@Conditional、@Scope、@Lazy、@Primary、@DependsOn、@Role、@Description,生成AnnotateBeanBeanDefinition

  1. @Lazy
  2. @Component
  3. public class Tank {
  4. public Tank(){
  5. System.out.println("Tank初始化");
  6. }
  7. }
  8. /**
  9. * 测试注解bean定义读取器
  10. */
  11. @Test
  12. public void testAnnotatedBeanDefinitionReader() {
  13. //1.初始化IOC容器
  14. AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
  15. //2.在1中已经完成。初始化(注解)bean读取器,同时注册Spring内部原始bean,原始bena用于应用程序bean的解析
  16. AnnotatedBeanDefinitionReader annotatedBeanDefinitionReader = new AnnotatedBeanDefinitionReader(applicationContext);
  17. //3.利用给定的 Class对象,从类的注解中获取其元数据,组装beanDefinition对象,并向IOC容器注册
  18. annotatedBeanDefinitionReader.registerBean(Tank.class);
  19. //4.刷新IOC容器,完成对象实例化
  20. applicationContext.refresh();
  21. Tank tank = applicationContext.getBean("tank", Tank.class);
  22. System.out.println(tank);
  23. }

XmlBeanDefinitionReader

解析xml标签

ClassPathBeanDefinitionScanner

一个扫描器,与BeanDefinitionReader功能类似,可以进行包路径扫描,对使用@Component注解的类进行解析,生成ScannedGenericBeanDefinition
Spring在初始化时,默认在IncludeFilter中添加@Componet。所以在后续扫描根据IncludeFilter进行判断

  1. /**
  2. * 测试注解bean定义扫描器
  3. */
  4. @Test
  5. public void testClassPathBeanDefinitionScanner() {
  6. //1.初始化IOC容器
  7. applicationContext = new AnnotationConfigApplicationContext();
  8. //2.在1中已经完成。注册默认注解过滤器(即:可以识别[@Component、@Repository、@Service、@Controller]
  9. ClassPathBeanDefinitionScanner classPathBeanDefinitionScanner = new ClassPathBeanDefinitionScanner(applicationContext);
  10. //3.扫描指定路径下.class文件,并将带有@Component注解的.class文件,组装beanDefinition对象,并向IOC容器注册
  11. classPathBeanDefinitionScanner.scan("com.masterlu.ioc.entity");
  12. applicationContext.refresh();
  13. Tank tank = applicationContext.getBean("tank", Tank.class);
  14. System.out.println(tank);
  15. }

ExcludeFilter和IncludeFilter

两个Filter是Spring扫描过程中用来过滤的。ExcludeFilter表示排除过滤器,IncludeFilter表示包含过滤器

  1. //表示扫描com.masterlu包下面的所有类,但是排除UserService类,也就是就算它上面有@Component注解也不会成为Bean
  2. @ComponentScan(value = "com.masterlu",
  3. excludeFilters = {@ComponentScan.Filter(
  4. type = FilterType.ASSIGNABLE_TYPE,
  5. classes = UserService.class)}.)
  6. public class AppConfig {
  7. }
  8. //就算UserService类上没有@Component注解,它也会被扫描成为一个Bean
  9. @ComponentScan(value = "com.masterlu",
  10. includeFilters = {@ComponentScan.Filter(
  11. type = FilterType.ASSIGNABLE_TYPE,
  12. classes = UserService.class)})
  13. public class AppConfig {
  14. }

FilterType分为:

  1. 1. ANNOTATION:表示是否包含某个注解
  2. 2. ASSIGNABLE_TYPE:表示是否是某个类
  3. 3. ASPECTJ:表示否是符合某个Aspectj表达式
  4. 4. REGEX:表示是否符合某个正则表达式
  5. 5. CUSTOM:自定义

Spring在初始化时,默认在IncludeFilter中添加@Componet。所以在后续扫描根据IncludeFilter进行判断

BeanFactory

image.png

拥有很多功能:

  1. AliasRegistry:支持**别名**功能,一个名字可以对应多个别名
  2. BeanDefinitionRegistry:可以注册、保存、移除、获取某个BeanDefinition
  3. BeanFactory:Bean工厂,可以根据某个bean的名字、或类型、或别名获取某个Bean对象
  4. SingletonBeanRegistry:可以直接注册、获取某个**单例**Bean
  5. SimpleAliasRegistry:它是一个类,实现了AliasRegistry接口中所定义的功能,支持别名功能
  6. ListableBeanFactory:在BeanFactory的基础上,增加了其他功能,可以**获取所有BeanDefinition的beanNames**,可以根据某个类型获取对应的beanNames,可以根据某个类型获取{类型:对应的Bean}的映射关系
  7. HierarchicalBeanFactory:在BeanFactory的基础上,添加**获取父BeanFactory**的功能
  8. DefaultSingletonBeanRegistry:它是一个类,实现了SingletonBeanRegistry接口,拥有了直接注册、获取某个**单例**Bean的功能
  9. ConfigurableBeanFactory:在HierarchicalBeanFactory和SingletonBeanRegistry的基础上,添加了设置父BeanFactory、类加载器(表示可以指定某个类加载器进行类的加载)、设置Spring EL表达式解析器(表示该BeanFactory可以解析EL表达式)、设置类型转化服务(表示该BeanFactory可以进行类型转化)、可以添加BeanPostProcessor(表示该BeanFactory支持Bean的后置处理器),可以合并BeanDefinition,可以销毁某个Bean等等功能
  10. FactoryBeanRegistrySupport:支持了FactoryBean的功能
  11. AutowireCapableBeanFactory:是直接继承了BeanFactory,在BeanFactory的基础上,支持在创建Bean的过程中能对Bean进行**自动装配**
  12. AbstractBeanFactory:实现了ConfigurableBeanFactory接口,继承了FactoryBeanRegistrySupport,这个BeanFactory的功能已经很全面了,但是不能自动装配和获取beanNames
  13. ConfigurableListableBeanFactory:继承了ListableBeanFactory、AutowireCapableBeanFactory、ConfigurableBeanFactory
  14. AbstractAutowireCapableBeanFactory:继承了AbstractBeanFactory,实现了AutowireCapableBeanFactory,拥有了自动装配的功能
  15. **DefaultListableBeanFactory**:继承了AbstractAutowireCapableBeanFactory,实现了ConfigurableListableBeanFactory接口和BeanDefinitionRegistry接口,所以DefaultListableBeanFactory的功能很强大

ApplicationContext

ApplicationContext是个接口,实际上也是一个BeanFactory,不过比BeanFactory更加强大,比如:

  1. HierarchicalBeanFactory:拥有获取父BeanFactory的功能
  2. ListableBeanFactory:拥有获取beanNames的功能
  3. ResourcePatternResolver:资源加载器,可以一次性获取多个资源(文件资源等等)
  4. EnvironmentCapable:可以获取运行时环境(没有设置运行时环境功能)
  5. ApplicationEventPublisher:拥有广播事件的功能(没有添加事件监听器的功能)
  6. MessageSource:拥有国际化功能

    AnnotationConfigApplicationContext

    image.png

    1. ConfigurableApplicationContext:继承了ApplicationContext接口,增加了,添加事件监听器、添加BeanFactoryPostProcessor、设置Environment,获取ConfigurableListableBeanFactory等功能
    2. AbstractApplicationContext:实现了ConfigurableApplicationContext接口
    3. GenericApplicationContext:继承了AbstractApplicationContext,实现了BeanDefinitionRegistry接口,拥有了所有ApplicationContext的功能,并且可以注册BeanDefinition,注意这个类中有一个属性(DefaultListableBeanFactory beanFactory)
    4. AnnotationConfigRegistry:可以单独注册某个类为BeanDefinition(可以处理该类上的@Configuration注解,可以处理@Bean注解),同时可以扫描
    5. AnnotationConfigApplicationContext:继承了GenericApplicationContext,实现了AnnotationConfigRegistry接口,拥有了以上所有的功能

      MetadataReader、ClassMetadata、AnnotationMetadata

      Spring中需要去解析类的信息。比如类名、方法、类上的注解,这些都可以称之为类的元数据,
      Spring中对类的元数据做了抽象,并提供了一些工具类。
      MetadataReader表示类的元数据读取器,默认实现类为SimpleMetadataReader
      SimpleMetadataReader去解析类时,使用的ASM技术(不需要加载类,就可以解析)。保持JVM使用类时,再加载特性 。 ```java @Service puliic class UserService{

}

public class Test {

public static void main(String[] args) throws IOException {
    SimpleMetadataReaderFactory simpleMetadataReaderFactory = new SimpleMetadataReaderFactory();

    // 构造一个MetadataReader
    MetadataReader metadataReader = simpleMetadataReaderFactory.getMetadataReader("com.masterlu.service.UserService");

    // 得到一个ClassMetadata,并获取了类名
    ClassMetadata classMetadata = metadataReader.getClassMetadata();

    System.out.println(classMetadata.getClassName());

    // 获取一个AnnotationMetadata,并获取类上的注解信息
    AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();

    //判断是否使用了指定注解
    System.out.protintln(annotationMetadata.hasMetaAnnotation(Component.class.getName()));//true。自动识别注解继承
    System.out.protintln(annotationMetadata.hasAnnotation(Component.class.getName()));//false

    for (String annotationType : annotationMetadata.getAnnotationTypes()) {
        System.out.println(annotationType);
    }
}

}

BeanDefinition只是元数据的一部分。
<a name="ZqBNS"></a>
# 其他功能
<a name="p9cX1"></a>
## 类型转换
在Spring源码中,有可能需要把String转成其他类型,所以在Spring源码中提供了一些技术来更方便的做对象的类型转化<br />另外,使用applicationContext.getBean("beanName",Class),第二个参数传递类型。1.为了泛型,2.也是主要的作用,Spring通过beanName获取到对象实例后,会进行实例转换。将实例转换成指定的Class类型。就是利用的类型转换
<a name="F4wdW"></a>
### PropertyEditor
JDK中提供的类型转化工具类
```java
public class StringToUserPropertyEditor extends PropertyEditorSupport implements PropertyEditor {

    @Override
    public void setAsText(String text) throws IllegalArgumentException {
        User user = new User();
        user.setName(text);
        this.setValue(user);
    }
}

StringToUserPropertyEditor propertyEditor = new StringToUserPropertyEditor();
propertyEditor.setAsText("1");
User value = (User) propertyEditor.getValue();
System.out.println(value);

//本质就是手动生成对象,并使用set方法赋值

向Spring中注册PropertyEditor(Spring和JDK结合使用)

//Spring和JDK结合使用。本质,预先定义转换过程
@Bean
public CustomEditorConfigurer customEditorConfigurer() {
    CustomEditorConfigurer customEditorConfigurer = new CustomEditorConfigurer();
    Map<Class<?>, Class<? extends PropertyEditor>> propertyEditorMap = new HashMap<>();

    //表示StringToUserPropertyEditor可以将String转化成User类型,在Spring源码中,如果发现当前对象是String,而需要的类型是User,就会使用该PropertyEditor来做类型转化
    propertyEditorMap.put(User.class, StringToUserPropertyEditor.class);// 此处使用的上面JDK提供实现类
    customEditorConfigurer.setCustomEditors(propertyEditorMap);
    return customEditorConfigurer;
}

@Component
public class UserService {

    @Value("xxx")
    private User user;

    public void test() {
        System.out.println(user);
    }

}

ConversionService

仅使用Spring中提供的类型转化服务,它比PropertyEditor更强大

public class StringToUserConverter implements ConditionalGenericConverter {

    //设置使用场景:例如:String-->User
    @Override
    public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
        return sourceType.getType().equals(String.class) && targetType.getType().equals(User.class);
    }

    @Override
    public Set<ConvertiblePair> getConvertibleTypes() {
        return Collections.singleton(new ConvertiblePair(String.class, User.class));
    }

    //具体转换逻辑
    @Override
    public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
        User user = new User();
        user.setName((String)source);
        return user;
    }
}

//手工,显示使用方式
DefaultConversionService conversionService = new DefaultConversionService();
conversionService.addConverter(new StringToUserConverter());
User value = conversionService.convert("1", User.class);
System.out.println(value);

向Spring中注册ConversionService

@Bean
public ConversionServiceFactoryBean conversionService() {
    ConversionServiceFactoryBean conversionServiceFactoryBean = new ConversionServiceFactoryBean();
    conversionServiceFactoryBean.setConverters(Collections.singleton(new StringToUserConverter()));

    return conversionServiceFactoryBean;
}



@Component
public class UserService {

    @Value("xxx")
    private User user;

    public void test() {
        System.out.println(user);
    }

}

TypeConverter

整合了PropertyEditor(JDK方式)和ConversionService(Spring方式)的功能,是Spring内部用的

SimpleTypeConverter typeConverter = new SimpleTypeConverter();
//jdk模式转换
typeConverter.registerCustomEditor(User.class, new StringToUserPropertyEditor());
//Spring模式转换
typeConverter.setConversionService(conversionService);


//手工,展示使用
User value = typeConverter.convertIfNecessary("1", User.class);
System.out.println(value);

国际化

@Bean
public MessageSource messageSource() {
    ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
    messageSource.setBasename("messages");
    return messageSource;
}

你可以在你任意想要进行国际化的地方使用该MessageSource。 同时,因为ApplicationContext也拥有国家化的功能,所以可以直接这么用:
context.getMessage(“test”, null, new Locale(“en”))

事件发布

//事件监听器
@Bean
public ApplicationListener applicationListener() {
    return new ApplicationListener() {
        @Override
        public void onApplicationEvent(ApplicationEvent event) {
            System.out.println("接收到了一个事件");
        }
    };
}

//发布事件
context.publishEvent("kkk");

获取运行时环境

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
//操作系统级别
Map<String, Object> systemEnvironment = context.getEnvironment().getSystemEnvironment();
System.out.println(systemEnvironment);

System.out.println("=======");

//java -D 指定的系统变量
Map<String, Object> systemProperties = context.getEnvironment().getSystemProperties();
System.out.println(systemProperties);

System.out.println("=======");

//获取:@PropertySource注解标记的变量
MutablePropertySources propertySources = context.getEnvironment().getPropertySources();
System.out.println(propertySources);

System.out.println("=======");

//获取环境变量功能
System.out.println(context.getEnvironment().getProperty("NO_PROXY"));
System.out.println(context.getEnvironment().getProperty("sun.jnu.encoding"));
System.out.println(context.getEnvironment().getProperty("name"));

可以利用,@PropertySource(“classpath:spring.properties”)
来使得某个properties文件中的参数添加到运行时环境中

@Data
@ToString
@Component
@ConfigurationProperties(prefix = "common.thread.pool")
@PropertySource("classpath:threadPool.properties")
public class ThreadPoolProperties {

    private int corePoolSize;
    private int maxPoolSize;
    private int keepAliveTime;
    private String threadNamePrefix;
    private int queueCapacity;


}

资源加载

ApplicationContext还拥有资源加载的功能,比如,可以直接利用ApplicationContext获取某个文件的内容

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

Resource resource = context.getResource("file://D:\\IdeaProjects\\spring-framework\\test\\src\\main\\java\\com\\luban\\entity\\User.java");
System.out.println(resource.contentLength());

Resource resource1 = context.getResource("https://www.baidu.com");
System.out.println(resource1.contentLength());
System.out.println(resource1.getURL());

Resource resource2 = context.getResource("classpath:spring.xml");
System.out.println(resource2.contentLength());
System.out.println(resource2.getURL());

Resource[] resources = context.getResources("classpath:com/masterlu/*.class");
for (Resource resource : resources) {
    System.out.println(resource.contentLength());
    System.out.println(resource.getFilename());
}