Spring 核心概念与使用技巧

BeanDefinition

BeanDefinition 表示 Bean 的定义, BeanDefinition 中存在很多属性来描述 Bean 的特征。比如:

  • class, 表示 bean 的类型
  • scope, 表示 bean 的作用域,单例(singleton)或者原型(prototype
  • lazyInit, 表示 bean 是否懒加载
  • initMethodName, 表示 bean 的初始化需要执行的方法
  • destoryMethodName, 表示 bean 销毁时需要执行 bean 的方法
  • and more ...

在 Spring 中,我们可以通过一下几种方式来定义 bean 1、xml 方式,可以通过 标签定义一个 bean 2、注解方式,可以通过 @Bean、@Component(@Service, @Controller,@Repository) 这几种方式,我们称为** 申明式定义 Bean**

我们还可以通过编程式定义 bean, 那就是直接通过 BeanDefinition , 比如:

// 定义 BeanDefinition
AbstractBeanDefinition beanDefinition =
    BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
// 设置 scope
beanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON);
// 设置bean类型
beanDefinition.setBeanClass(BeanTest.class);
// 设置懒加载
beanDefinition.setLazyInit(true);

// 注册 bean
applicationContext.registerBeanDefinition("test", beanDefinition);
复制代码

和申明式事务,编程式事务类似,通过 、@Bean 、@Component 等申明方式所定义的 Bean , 最终都会被 Spring 解析为对应的 BeanDefinition 对象,并且放入 Spring 容器中。

BeanDefinitionReader

BeanDefinitionReader 是 Spring 容器中提供的 BeanDefinition 读取器,这些 BeanDefinitionReader 在我们使用 Spring 开的时候较少使用,但是 Spring 源码中使用的比较多,相当于是 Spring 内部的基础设施。 ​

AnnotationBeanDefinitionReader

可以直接把 类转换为 BeanDefinition , 并且会解析类上的注解,例如:

AnnotationConfigApplicationContext applicationContext =
    new AnnotationConfigApplicationContext(AnnotatedBeanDefinitionReaderTest.class);
AnnotatedBeanDefinitionReader annotationBeanDefinitionReader =
    new AnnotatedBeanDefinitionReader(applicationContext);

annotationBeanDefinitionReader.register(UserService.class);
System.out.println(applicationContext.getBean(UserService.class));
复制代码

AnnotationBeanDefinitionReader

AnnotationConfigApplicationContext applicationContext =
    new AnnotationConfigApplicationContext(XmlBeanDefinitionReaderTest.class);
XmlBeanDefinitionReader xmlBeanDefinitionReader =
    new XmlBeanDefinitionReader(applicationContext);
xmlBeanDefinitionReader.loadBeanDefinitions("classpath:spring-context.xml");

System.out.println(applicationContext.getBean(UserService.class));
复制代码

ClassPathBeanDefinitionScanner

ClassPathBeanDefinitionScanner 是扫描器,但是它的作用和 BeanDefinitionReader 类似,它可以进行扫描,扫描某个包路径,对扫描到的类进行解析,比如,扫描到的类上如果存在 @Component 注解,那么就会把这个类解析为一个 BeanDefinition ,比如:

AnnotationConfigApplicationContext applicationContext =
    new AnnotationConfigApplicationContext(ClassPathBeanDefinitionScannerTest.class);
ClassPathBeanDefinitionScanner classPathBeanDefinitionScanner =
    new ClassPathBeanDefinitionScanner(applicationContext);
classPathBeanDefinitionScanner.scan("com.summer.test.service");

System.out.println(applicationContext.getBean(UserService.class));
复制代码

BeanFactory

BeanFactory 表示 Bean 工厂,所以很明显, BeanFactory 负责创建 Bean,并且提供获取 Bean 的 API。 而 SpringApplicationContext 是 BeanFactory 的子类,在 Spring 的源码中是这样定义的:

public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
		MessageSource, ApplicationEventPublisher, ResourcePatternResolver {

    // ...
}            
复制代码

首先,在 Java 中,接口可以**多继承, **ApplicationContext 继承了 ListableBeanFactory 和 HierarchicalBeanFactory , 而 ListableBeanFactory 和 HierarchicalBeanFactory 都继承自 BeanFactory , 所以我们可以认为 ApplicationContext 继承了 BeanFactory ,相当于是苹果继承了水果,宝马汽车继承了汽车一样, ApplicationContext 也是 BeanFactory 的一种,用友了 BeanFactory 支持的所有功能,不过 ApplicationContext 比 BeanFactory 更加强大, ApplicationContext 还实现了其他的基础接口。 比如 MessageSource 表示国际化, ApplicationEventPublisher 表示事件发布, EnvironmentCapable 表示获取环境变量,等等,关于 ApplicationContext 后面详细讨论。 ​

在 Spring 源码的实现中,当我们 new 一个 ApplicationContext 时,其底层会 new 一个 BeanFactory 出来, 相当于使用了 ApplicationContext 的某些方法时,比如 getBean() , 底层调用的 BeanFactory 的 getBean 。 ​

在 Spring 源码中, BeanFactory 接口存在一个非常重要的实现类:DefaultLIsttableBeanFactory, 也是非常核心的。

所以,我们可以直接使用 DefaultLIsttableBeanFactory , 而不使用 ApplicationContext 的某个实现类:

DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
beanDefinition.setBeanClass(UserService.class);

beanFactory.registerBeanDefinition("userService", beanDefinition);
System.out.println(beanFactory.getBean("userService"));
复制代码

DefaultLIsttableBeanFactory 非常强大,我们可以监看它的继承关系:

image.png

从继承关系来看,它实现了很多接口,具备一下功能: 1、AliasRegistry: 支持别名功能,一个名字可以对应多个别名。 2、BeanDefinitionRegistry: 可以注册,保存,移除,获取某个 BeanDefinition 3、SingletonBeanFactory: 可以直接注册,获取一个单例 Bean 4、SimpleAliasRegistry: 它是一个雷,实现了 AliasRegistry 接口中所有的定义,支持别名功能 5、ListableBeanFactory: 在 BeanFactory 的基础上,增加了其他功能呢,可以获取所有的 BeanDefinition 的定义信息。

ApplicationContext

AnnotationConfigApplicationContext

ClassPathXmlApplicationContext

国际化

先定义个 MessageSource: ​

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

创建完这个 Bean , 我们可以在任何需要使用国际化的地方使用该 MessageSource 的 Bean 对象。 同时,因为 ApplicationContext 也拥有国际化功能,所以可以直接这么使用:

context.getMessage("test", null, new Locale("en"));
复制代码

资源加载

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

AnnotationConfigApplicationContext context = new AnnotaionConfigApplicaitonContext(AppConfig.class);
Resource resource = context.getResource("file://C:\\a.txt")
System.out.println(resource.contextLength())
复制代码

通过 ApplicationConext 来实现这个功能,可以提高我们的开发效率。 比如我们还可以这样使用:

AnnotationConfigApplicationContext context = new AnnotaionConfigApplicaitonContext(AppConfig.class);
Resource resource = context.getResource("classpath:spring-context.xml")
System.out.println(resource.contextLength())
    
    
Resource resource2 = context.getResource("https://baidu.com");
System.out.println(resource2.getURL());
复制代码

我们也可以获取多个资源

AnnotationConfigApplicationContext context = new AnnotaionConfigApplicaitonContext(AppConfig.class);
Resource resource = context.getResource("classpath:spring-context.xml")
System.out.println(resource.contextLength())
    
复制代码

获取运行时环境变量

Map<String, Object> systemEnvironment = context.getEnvironment().getSystemEnvironment();
System.out.println(systemEnvironment);

Map<String, Object> systemProperties = context.getEnvironment().getSystemProperties();
System.out.println(systemProperties);

MutablePropertySources propertySources = context.getEnvironment().getPropertySources();
System.out.println(propertySources);
复制代码

也可以解析文件

@PropertySource("classpath:spring.properties")
复制代码

可以让某一个 properties 文件添加到我们的环境变量中,可以通过一下的代码来获取:

String abc = context.getEnvironment().getProperty("abc", "1");
System.out.println(abc);
复制代码

事件发布

定义事件

public class TestEvent extends ApplicationEvent {

	public TestEvent(Object source) {
		super(source);
	}
}
复制代码

定义事件监听器

public class TestListener implements ApplicationListener<TestEvent> {

	@Override
	public void onApplicationEvent(TestEvent event) {
		System.out.println("收到一个事件 ,,,,,");
	}
}
复制代码

调用程序

@Configuration
public class ApplicationEventTest {

    @Bean
    public ApplicationListener applicationListener() {
        return new TestListener();
    }

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ApplicationEventTest.class);
        context.publishEvent(new TestEvent(new Object()));
    }
}

复制代码

类型转换

Spring 内部,有很多地方可能需要将 String 转换为其他类型,今天我们一起来学习一下 PropertyEditor、 ConversionService、TypeConverter 三种类型转换的使用。

PropertyEditor

PropertyEditor 是 JDK 提供的类型转换器,首先创建 bean :

@Service
public class OrderService {

	@Value("orderVal")
	private Order order;

	public void test() {
		System.out.println("test order : " + order);
	}
}

复制代码

创建类型转换器,将字符串转换为 Order 实例对象。

public class String2ObjectPropertyEditor extends PropertyEditorSupport implements PropertyEditor {

	@Override
	public void setAsText(String text) throws IllegalArgumentException {
		Order order = new Order();
		order.setName("haha");
		order.setAge(12);
		this.setValue(order);
	}
}
复制代码

注册转换器以及测试代码:

@Import({OrderService.class})
@Configuration
public class PropertyEditorTest {


	@Bean
	public CustomEditorConfigurer customEditorConfigurer() {
		Map<Class<?>, Class<? extends PropertyEditor>> customEditors = new HashMap<>();
		customEditors.put(Order.class, String2ObjectPropertyEditor.class);

		CustomEditorConfigurer customEditorConfigurer = new CustomEditorConfigurer();
		customEditorConfigurer.setCustomEditors(customEditors);

		return customEditorConfigurer;
	}

	public static void main(String[] args) {

		// 使用方式 1
		String2ObjectPropertyEditor  propertyEditor = new String2ObjectPropertyEditor();
		propertyEditor.setAsText("1");
		Object value = propertyEditor.getValue();
		System.out.println(value);

		// 使用方式 2
		ApplicationContext applicationContext = new AnnotationConfigApplicationContext(PropertyEditorTest.class);
		OrderService orderItemService = applicationContext.getBean(OrderService.class);
		orderItemService.test();
	}

}
复制代码

ConversionService

ConversionService 是 Sprign 中提供的类型转换器,它比 PrppertyEditor 功能更加强大。 定义转换器:

public class String2ObjectConversionService implements ConditionalGenericConverter {

	@Override
	public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
		return
				Objects.equals(sourceType.getType(), String.class)
						&&
						Objects.equals(targetType.getType(), Order.class);
	}

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


	@Override
	public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
		return new Order("haha", 32);
	}
}

复制代码

单独使用

DefaultConversionService conversionService = new DefaultConversionService();
conversionService.addConverter(new String2ObjectConversionService());
Order order = conversionService.convert("1", Order.class);
System.out.println(order);
复制代码

在 Spring 中使用:

@Bean
public ConversionServiceFactoryBean conversionService() {

    ConversionServiceFactoryBean conversionServiceFactoryBean = new ConversionServiceFactoryBean();
    conversionServiceFactoryBean.setConverters(Collections.singleton(new String2ObjectConversionService()));

    return conversionServiceFactoryBean;
}
复制代码

Bean 的注入和调用代码:

// 测试和注入
@Service
public class OrderService {

    //通过 @Value 注入
	@Value("orderVal")
	private Order order;

	public void test() {
		System.out.println("test order : " + order);
	}
}


// 调用代码
ApplicationContext appliciton = new AnnotationConfigApplicationContext(ConvertTest.class);
OrderItemService orderItemService = appliciton.getBean(OrderItemService.class);
orderItemService.test();
复制代码

TypeConverter

TypeConverter 整合了 PropertyEditor 和 ConversionService, 在 Spring 内部使用:

SimpleTypeConverter typeConverter = new SimpleTypeConverter();
typeConverter.registerCustomEditor(Order.class, new String2ObjectPropertyEditor());
Order order = typeConverter.convertIfNecessary("orderVal", Order.class);
System.out.println(order);
复制代码

比如在 AbstractBeanFacotry#adaptBeanInstance 中也有用到:

// AbstractBeanFacotry.java

<T> T adaptBeanInstance(String name, Object bean, @Nullable Class<?> requiredType) {
    // Check if required type matches the type of the actual bean instance.
    // 如果转换类型不为空,并且 bean 类型与目标类型不匹配
    if (requiredType != null && !requiredType.isInstance(bean)) {
        try {
            // 尝试转换
            Object convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
            if (convertedBean == null) {
                throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
            }
            return (T) convertedBean;
        }
        catch (TypeMismatchException ex) {
            if (logger.isTraceEnabled()) {
                logger.trace("Failed to convert bean '" + name + "' to required type '" +
                             ClassUtils.getQualifiedName(requiredType) + "'", ex);
            }
            throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
        }
    }
    return (T) bean;
}
复制代码

OrderComparator

OrderComparator 是 Spring 提供的一种比较器,可以根据 @Order 注解或者实现 Ordered 接口来进行比较,从而进行排序。 比如:

public class A implements Ordered {


	@Override
	public int getOrder() {
		return 1;
	}
}
复制代码
public class B implements Ordered {


	@Override
	public int getOrder() {
		return 2;
	}
}

复制代码

排序使用:

public class OrderComparatorTest {

	public static void main(String[] args) {
		A a = new A();
		B b = new B();


		OrderComparator orderComparator = new OrderComparator();
		System.out.println(orderComparator.compare(a, b)); // -1

		List list = new ArrayList();
		list.add(a);
		list.add(b);

		list.sort(orderComparator);
		System.out.println(list); // a,b
	}
}
复制代码

另外,Spring 还提供了一个 OrderComparator 的子类:**AnnotationAwareOrderComparator, **它支持 @Order 来指定 order 的值,比如:

@Order(1)
public class A1 {

}

复制代码
@Order(1)
public class B1 {

}

复制代码
public class OrderComparatorTest1 {

	public static void main(String[] args) {
		A1 a = new A1();
		B1 b = new B1();


		AnnotationAwareOrderComparator orderComparator = new AnnotationAwareOrderComparator();
		System.out.println(orderComparator.compare(a, b)); // -1

		List list = new ArrayList();
		list.add(a);
		list.add(b);

		list.sort(orderComparator);
		System.out.println(list); // a,b
	}
}
复制代码

BeanPostProessor

BeanPostProessor 表示 bean 的后置处理器,我们可以定义一个或者多个 BeanPostProcessor , 比如通过如下的代码定义:

public class TestBeanPostProcessor implements BeanPostProcessor {
	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		if ("userService".equals(beanName)) {
			System.out.println("postProcessBeforeInitialization userService");
		}
		return bean;
	}

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		if ("userService".equals(beanName)) {
			System.out.println("postProcessAfterInitialization userService");
		}
		return bean;
	}
}
复制代码

一个 BeanPostProcessor 可以在任意一个 Bean 的初始化之前以及初始化之后去做额外的一些用户的自定义逻辑。

BeanFactoryPostProcessor

BeanFactoryPostProcessor 表示 Bean 工厂的后置处理器,其实和 BeanPostProcessor 类似,BeanPostProcessor 是干涉 Bean 的创建过程, BeanFactoryPostProcessor 是干涉 BeanFactory 的创建过程,我们可以这样定义一个 BeanFactoryProcessor:

@Component
public class TestBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		System.out.println("bean factory post processor");
	}
}

复制代码

我们可以在 postProcessBeanFactory() 方法中对 BeanFacorty 进行拓展。

FactoryBean

上面提到,我们可以通过 BeanPostProcessor 来干涉 Spring 创建 Bean 的过程,但是如果我们想一个 Bean 完完全全由我们来创造,也是可以的,比如通过 FactoryBean 来实现。


@Component
public class TestFactoryBean implements FactoryBean {


	@Override
	public Object getObject() throws Exception {
		UserService userService = new UserService();
		return userService;
	}

	@Override
	public Class<?> getObjectType() {
		return UserService.class;
	}
}

复制代码

通过上面这段代码,我们可以创造一个 UserService 对象,并且将它定义为一个 Bean, 但是通过这种方式定义的 Bean ,** 只会经过初始化后**, 其他的 Spring 的生命周期不会经历,比如依赖注入。 ​

通过 @Bean 也可以生成一个对象作为 Bean , 那么和 FactoryBean 的区别是什么呢?其实在很多场景下他两市可以替换的,但是在原理的层面来说,区别很明显, @Bean的 Bean会经过完整的 Bean的生命周期。

ExcludeFilter 和 IncluderFilter

这两个 Filter 是 Spring 扫描过中用来过滤的,ExcludeFilter 表示排除过滤器,IncluderFilter 表示包含过滤器。 比如一下配置,表示扫描 com. 这个包下返回的所有类, 但是排除 UserService 类 ,也就是算它上面所有的 @Component 注解也不会成为 Bean 。

@ComponentScan(value = "com.summer.test.service",
excludeFilters = {
		@ComponentScan.Filter(
				type = FilterType.ASSIGNABLE_TYPE,
				classes = UserService.class)
})
public class AppConfig {
}
复制代码

比如以下配置,就算 UserService 类上没有 @Component 注解,它也会呗扫描成一个 Bean

@ComponentScan(value = "com.summer.test.service",
includeFilters = {
		@ComponentScan.Filter(
				type = FilterType.ASSIGNABLE_TYPE,
				classes = UserService.class)
})
public class AppConfig {
}
复制代码

FilterType 分为:

  • ANNOTATION,
  • ASSIGNABLE_TYPE,
  • ASPECTJ,
  • REGEX,
  • CUSTOM

MetadataReader/ClassMetaData/AnnotationMetadata

  • MetadataReader 元数据读取
  • ClassMetaData 类的元数据信息
  • AnnotationMetadata 注解的元数据信息

测试代码

SimpleMetadataReaderFactory metadataReaderFactory = new SimpleMetadataReaderFactory();

// 构造 MetadataReader
MetadataReader metadataReader = metadataReaderFactory.getMetadataReader("com.summer.test.service.UserService");

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

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

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

System.out.println(annotationMetadata.hasMetaAnnotation(Component.class.getName()));  //true
System.out.println(annotationMetadata.hasAnnotation(Component.class.getName()));  //false

for (String annotationType : annotationMetadata.getAnnotationTypes()) {
    // org.springframework.stereotype.Service
    System.out.println(annotationType);
}


作者:掘金老郑
链接:https://juejin.cn/post/7044930371812605988

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值