Spring核心概念
BeanDefinition
BeanDefinition
用于存储bean信息,比如bean是单例还是原型、bean的类型以及是否懒加载等等,Spring判断bean是否被注册是根据判断判断容器中是否存在该BeanDefinition的,除了使用注解和xml的方式对bean进行注册,开发者还可以自己自定义创建beanDefinition并注册到容器中,如下代码。
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
beanDefinition.setScope(ConfigurableBeanFactory.SCOPE_SINGLETON);
beanDefinition.setBeanClassName("userService");
beanDefinition.setBeanClass(UserServiceImpl.class);
applicationContext.registerBeanDefinition("userService", beanDefinition);
BeanDefinitionReader
BeanDefinitionReader
是BeanDefinition
的读取器,用于读取BeanDefinition
并注册到Spring容器中,创建该对象是需要传入spring容器对象。这个类的底层是根据传入的class自动创建BeanDefinition
并注册到spring容器中,如下以注解的方式进行扫描注册代码,除了使用注解的方式进行扫描,还可以使用xml的方式进行扫描。
AnnotatedBeanDefinitionReader beanDefinitionReader = new AnnotatedBeanDefinitionReader(applicationContext);
beanDefinitionReader.registerBean(UserServiceImpl.class);
ClassPathBeanDefinitionScanner
该类用于根据包名扫描并创建BeanDefinition
,可以通过使用ClassPathBeanDefinitionScanner
扫描包下的所有类,判断类中是否用于Spring容器在注解,如果有则进行扫描注册到容器中,扫描完毕后必须对Spring容器进行刷新,如下代码所示。spring容器注解有service、Component注解等。
ClassPathBeanDefinitionScanner classPathBeanDefinitionScanner = new ClassPathBeanDefinitionScanner(applicationContext);
classPathBeanDefinitionScanner.scan("com.lyra.user.service.impl");
applicationContext.refresh();
各种BeanDefinition
- GenericBeanDefinition是BeanDifinition的基本实现
- ScannedGenericBeanDefinition 由AnnotatedBeanDefinitionReader扫描器生成的BeanDefinition
- AnnotatedGenericBeanDefinition由AnnotatedBeanDefinitionReader读取器生成的BeanDefinition
BeanFactory和ApplicationContext相关
Spring是以面向接口编程的方式进行编程,通过查看类中继承了扫描接口由此了解到了该类有哪些功能,如Application接口继承了EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory接口标识Application拥有获取环境变量、beanFactory相关功能和拥有父子bean容器的功能,通过实现不同的接口方法来拓展不同的功能。
BeanFactory和ApplicationContext都是接口
BeanFactory提供了最基础的获取bean的操作,由DefaultListableBeanFactory是Beanfactory的实现。
AnnotationConfigApplicationContext底层是由DefaultListableBeanFactory获取bean的。
类型转换
在spring中会需要将String类型转换成其他的类型,这就需要类型转换了。在spring底层获取bean是如果有传入bean类型则会使用instanceof判断类型是否为传入的类型,如果类型一致则强转后返回,如果不一致则调用自定义的类型转换器进行转换,如果转换失败则抛出异常,如下代码所示。
PropertyEditor
JDK自带的类型转换器,只能将string类型转换成其他类型,通过继承PropertyEditorSupport
以及PropertyEditor
接口,在setAsText
方法中编写具体转换逻辑,将传入的String类型的字符串转换为User并调用setValue
方法进行设置
public class StringToUserPropertyEditor extends PropertyEditorSupport implements PropertyEditor {
@Override
public void (String text) throws IllegalArgumentException {
User user = new User();
user.setName(text);
this.setValue(user);
}
}
然后将该类注册到CustomEditorConfigurer
中即可。
@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);
customEditorConfigurer.setCustomEditors(propertyEditorMap);
return customEditorConfigurer;
}
类型转换之后使用value
注解测试如下所示
ConversionService
比JDK自带的类型转换强大些,支持任意类型转换,不像JDK只支持string类型。通过实现ConditionalGenericConverter
接口来完成类型转换,matches用于判断什么时候继续类型转换,getConvertibleTypes方法定义了将什么类型转换为什么类型,convert具体业务转换逻辑,source为被转换的对象,在下方代码例子中为String类型的shabi。
public class StringToUserConverter implements ConditionalGenericConverter {
@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;
}
}
之后注册到Spring容器中即可
@Bean
public ConversionServiceFactoryBean conversionService() {
ConversionServiceFactoryBean conversionServiceFactoryBean =
new ConversionServiceFactoryBean();
conversionServiceFactoryBean.setConverters(Collections.singleton(new StringToUserConverter()));
return conversionServiceFactoryBean;
}
TypeConverter
将PropertyEditor和ConversionService进行整合,执行时可以将PropertyEditor和ConversionService设置到TypeConverter中,最终由TypeConverter选择使用哪个进行类型转换,ConversionService要比PropertyEditor优先级高一些,这种方式类型转换在Spring源码中用的比较多些。setConversionService传入ConversionService,registerCustomEditor传入PropertyEditor,最后执行convertIfNecessary进行转换即可。
ConversionServiceFactoryBean bean = applicationContext.getBean(ConversionServiceFactoryBean.class);
SimpleTypeConverter typeConverter = new SimpleTypeConverter();
typeConverter.setConversionService(bean.getObject());
typeConverter.registerCustomEditor(User.class, new JDKStringToUserConverter());
User value = typeConverter.convertIfNecessary("test", User.class);
比较器
Spring实现了通过实现Ordered
接口或使用Order
注解来完成对象的比较,从而实现排序。
实现接口
public class OrderB implements Ordered {
@Override
public int getOrder() {
return 10;
}
}
public class OrderA implements Ordered {
@Override
public int getOrder() {
return 20;
}
}
新建OrderComparator
对象并调用compare
方法进行比较,如果参数1 < 参数2则返回-1,如果参数1 > 参数2则返回1,如果相等则返回0,使用list.sort可以对list中的元素进行排序。
OrderA orderA = new OrderA();
OrderB orderB = new OrderB();
OrderComparator orderComparator = new OrderComparator();
int compare = orderComparator.compare(orderB, orderA);
System.out.println(compare);
List<Object> list = new ArrayList<>();
list.add(orderA);
list.add(orderB);
list.sort(orderComparator);
注解
使用注解也一样将注解写在类中,注解的参数就是order的值。
@Order(20)
public class OrderA {
}
@Order(10)
public class OrderB {
}
Spring提供了AnnotationAwareOrderComparator
获取注解中的值并进行比较,其余代码就和实现接口的方法一致了。
OrderA orderA = new OrderA();
OrderB orderB = new OrderB();
OrderComparator orderComparator = new AnnotationAwareOrderComparator();
int compare = orderComparator.compare(orderB, orderA);
System.out.println(compare);
List<Object> list = new ArrayList<>();
list.add(orderA);
list.add(orderB);
list.sort(orderComparator);
元数据读取器
BeanDefinition
与ClassMetadata
不同之处在于BeanDefinition中保存的数据只是bean信息数据,如是不是单例、类型是什么、是不是懒加载之类的,而ClassMetadata中存储的数据就要比BeanDefinition中存储的数据要多一些了,它主要封装的是类信息,比如类中的注解是什么,是否有子类、父类接口什么、是否final属性、是否有指定注解之类的。
SimpleMetadataReader
解析类底层使用的是ASM技术,使用ASM的好处是无需将类加载到JVM主便可以分析出类信息,而使用反射技术则需要将类加载到JVM中才可以进行获取类信息,本身仅仅只在使用的时候才进行获取类信息,在一开始将所有类都加载到JVM中有些不好。
ASM的底层原理是根据编译后的class字节码进行分析,根据字节码规则获取类信息。
使用元数据读取器可以获取类的元数据信息传入类的全限定包名,如下代码所示。
AnnotationMetadata annotationMetadata = new SimpleMetadataReaderFactory().getMetadataReader("com.lyra.user.service.impl.User").getAnnotationMetadata();
System.out.println(annotationMetadata.hasMetaAnnotation(Component.class.getName()));
System.out.println(annotationMetadata.hasAnnotation(Component.class.getName()));
获取类元数据如下代码所示。
ClassMetadata classMetadata = new SimpleMetadataReaderFactory().getMetadataReader("com.lyra.user.service.impl.User").getClassMetadata();
System.out.println(classMetadata.getClassName());
FactoryBean
FactoryBean首先是一个Bean,和Bean注解功能相同,可以通过一系列操作生成一个bean,不同之处在于创建bean的生命周期不同,使用BeanFactory创建的bean不会进行初始化前和依赖注入的操作,而使用Bean注解创建的bean拥有完整的生命周期。
Spring底层在扫描bean时会将实现BeanFctory接口的bean一并扫描并创建BeanDefinitation中去,之后会判断该类是否实现FctoryBean,如果实现Beanctory的话直接强转,然后调用getObject方法创建并存储到map中,使用FactoryBean创建的bean和普通创建的bean使用的单例池map不是同一个map。
FactoryBean创建了两个bean,一个是BeanFactory类型的bean,存储在普通创建的单例池map中,获取该bean需要添加&前缀。另一个bean是getObject获取的bean。
使用时直接实现factoryBean接口,然后将该实现类扫描进Spring容器即可。
由于经历了初始化后的方法,所以AOP也会对FactoryBean创建的bean生效。
@Component
public class LyraFactoryBean implements FactoryBean<UserServiceImpl> {
@Override
public UserServiceImpl getObject() throws Exception {
return new UserServiceImpl();
}
@Override
public Class<?> getObjectType() {
return UserServiceImpl.class;
}
}
ExcludeFilter和IncludeFilter
ExcludeFilter表示排除过滤器,只要被ExcludeFilter标识的,即便添加了bean扫描注解也不会被扫描到Spring容器中,如下所示,即便OrderServiceImpl添加Component注解也不会扫描到Spring容器中表示扫描类型,比如类,注解,切面等等,classes表示排除的类。
@Component("orderService")
public class OrderServiceImpl {
public void test() {
System.out.println("test");
}
}
@ComponentScan(value = "com.lyra.user.service.impl", excludeFilters = {
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = OrderServiceImpl.class)
})
public class ApplicationConfig {
}
IncludeFilter与之刚好相反,只要被IncludeFilter标识了,即便没用添加bean注解也会扫描到Spring容器中。
public class OrderServiceImpl {
public void test() {
System.out.println("test");
}
}
@ComponentScan(value = "com.lyra.user.service.impl", includeFilters = {
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = OrderServiceImpl.class)
})
在Spring底层执行扫描时,会默认使用IncludeFilter添加Component注解,表示在类中只要标识Component就将该类扫描进Spring容器中。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
2021-05-29 标题