万人之敌,通过注解给属性注入配置和Bean对象
本文转载自微信公众号「bugstack虫洞栈」,万人之敌作者小傅哥。通过转载本文请联系bugstack虫洞栈公众号。注解置和
一、性注前言
写代码,入配就是对象从能用到好用的不断折腾!
你听过扰动函数吗?你写过斐波那契(Fibonacci)散列吗?你实现过梅森旋转算法吗?怎么 没听过这些写不了代码吗!不会的,即使没听过你一样可以写的万人之敌了代码,比如你实现的通过数据库路由数据总是落在1库1表它不散列分布、你实现的注解置和抽奖系统总是把运营配置的最大红包发出去提高了运营成本、你开发的性注秒杀系统总是在开始后的1秒就挂了货品根本给不出去。
除了一部分仅把编码当成搬砖应付工作外的入配程序员,还有一部分总是对象在追求极致的码农。写代码还能赚钱,万人之敌真开心! 这样的通过码农总是会考虑??还有没有更好的实现逻辑能让代码不仅是能用,还要好用呢?注解置和其实这一点的云服务器提供商追求到完成,需要大量扩展性学习和深度挖掘,这样你设计出来的系统才更你考虑的更加全面,也能应对各种复杂的场景。
二、目标
在目前 IOC、AOP 两大核心功能模块的支撑下,完全可以管理 Bean 对象的注册和获取,不过这样的使用方式总感觉像是刀耕火种有点难用。因此在上一章节我们解决需要手动配置 Bean 对象到 spring.xml 文件中,改为可以自动扫描带有注解 @Component 的对象完成自动装配和注册到 Spring 容器的操作。
那么在自动扫描包注册 Bean 对象之后,就需要把原来在配置文件中通过 property name="token" 配置属性和Bean的操作,也改为可以自动注入。这就像我们使用 Spring 框架中 @Autowired、@Value 注解一样,完成我们对属性和对象的注入操作。
三、方案
其实从我们在完成 Bean 对象的基础功能后,后续陆续添加的功能都是云南idc服务商围绕着 Bean 的生命周期进行的,比如修改 Bean 的定义 BeanFactoryPostProcessor,处理 Bean 的属性要用到 BeanPostProcessor,完成个性的属性操作则专门继承 BeanPostProcessor 提供新的接口,因为这样才能通过 instanceof 判断出具有标记性的接口。所以关于 Bean 等等的操作,以及监听 Aware、获取 BeanFactory,都需要在 Bean 的生命周期中完成。那么我们在设计属性和 Bean 对象的注入时候,也会用到 BeanPostProcessor 来完成在设置 Bean 属性之前,允许 BeanPostProcessor 修改属性值。整体设计结构如下图:
四、实现
1. 工程结构
small-spring-step-14 └── src ├── main │ └── java │ └── cn.bugstack.springframework │ ├── aop │ │ ├── aspectj │ │ │ └── AspectJExpressionPointcut.java │ │ │ └── AspectJExpressionPointcutAdvisor.java │ │ ├── framework │ │ │ ├── adapter │ │ │ │ └── MethodBeforeAdviceInterceptor.java │ │ │ ├── autoproxy │ │ │ │ └── MethodBeforeAdviceInterceptor.java │ │ │ ├── AopProxy.java │ │ │ ├── Cglib2AopProxy.java │ │ │ ├── JdkDynamicAopProxy.java │ │ │ ├── ProxyFactory.java │ │ │ └── ReflectiveMethodInvocation.java │ │ ├── AdvisedSupport.java │ │ ├── Advisor.java │ │ ├── BeforeAdvice.java │ │ ├── ClassFilter.java │ │ ├── MethodBeforeAdvice.java │ │ ├── MethodMatcher.java │ │ ├── Pointcut.java │ │ ├── PointcutAdvisor.java │ │ └── TargetSource.java │ ├── beans │ │ ├── factory │ │ │ ├── annotation │ │ │ │ ├── Autowired.java │ │ │ │ ├── AutowiredAnnotationBeanPostProcessor.java │ │ │ │ ├── Qualifier.java │ │ │ │ └── Value.java │ │ │ ├── config │ │ │ │ ├── AutowireCapableBeanFactory.java │ │ │ │ ├── BeanDefinition.java │ │ │ │ ├── BeanFactoryPostProcessor.java │ │ │ │ ├── BeanPostProcessor.java │ │ │ │ ├── BeanReference.java │ │ │ │ ├── ConfigurableBeanFactory.java │ │ │ │ ├── InstantiationAwareBeanPostProcessor.java │ │ │ │ └── SingletonBeanRegistry.java │ │ │ ├── support │ │ │ │ ├── AbstractAutowireCapableBeanFactory.java │ │ │ │ ├── AbstractBeanDefinitionReader.java │ │ │ │ ├── AbstractBeanFactory.java │ │ │ │ ├── BeanDefinitionReader.java │ │ │ │ ├── BeanDefinitionRegistry.java │ │ │ │ ├── CglibSubclassingInstantiationStrategy.java │ │ │ │ ├── DefaultListableBeanFactory.java │ │ │ │ ├── DefaultSingletonBeanRegistry.java │ │ │ │ ├── DisposableBeanAdapter.java │ │ │ │ ├── FactoryBeanRegistrySupport.java │ │ │ │ ├── InstantiationStrategy.java │ │ │ │ └── SimpleInstantiationStrategy.java │ │ │ ├── support │ │ │ │ └── XmlBeanDefinitionReader.java │ │ │ ├── Aware.java │ │ │ ├── BeanClassLoaderAware.java │ │ │ ├── BeanFactory.java │ │ │ ├── BeanFactoryAware.java │ │ │ ├── BeanNameAware.java │ │ │ ├── ConfigurableListableBeanFactory.java │ │ │ ├── DisposableBean.java │ │ │ ├── FactoryBean.java │ │ │ ├── HierarchicalBeanFactory.java │ │ │ ├── InitializingBean.java │ │ │ ├── ListableBeanFactory.java │ │ │ └── PropertyPlaceholderConfigurer.java │ │ ├── BeansException.java │ │ ├── PropertyValue.java │ │ └── PropertyValues.java │ ├── context │ │ ├── annotation │ │ │ ├── ClassPathBeanDefinitionScanner.java │ │ │ ├── ClassPathScanningCandidateComponentProvider.java │ │ │ └── Scope.java │ │ ├── event │ │ │ ├── AbstractApplicationEventMulticaster.java │ │ │ ├── ApplicationContextEvent.java │ │ │ ├── ApplicationEventMulticaster.java │ │ │ ├── ContextClosedEvent.java │ │ │ ├── ContextRefreshedEvent.java │ │ │ └── SimpleApplicationEventMulticaster.java │ │ ├── support │ │ │ ├── AbstractApplicationContext.java │ │ │ ├── AbstractRefreshableApplicationContext.java │ │ │ ├── AbstractXmlApplicationContext.java │ │ │ ├── ApplicationContextAwareProcessor.java │ │ │ └── ClassPathXmlApplicationContext.java │ │ ├── ApplicationContext.java │ │ ├── ApplicationContextAware.java │ │ ├── ApplicationEvent.java │ │ ├── ApplicationEventPublisher.java │ │ ├── ApplicationListener.java │ │ └── ConfigurableApplicationContext.java │ ├── core.io │ │ ├── ClassPathResource.java │ │ ├── DefaultResourceLoader.java │ │ ├── FileSystemResource.java │ │ ├── Resource.java │ │ ├── ResourceLoader.java │ │ └── UrlResource.java │ ├── stereotype │ │ └── Component.java │ └── utils │ ├── ClassUtils.java │ └── StringValueResolver.java └── test └── java └── cn.bugstack.springframework.test ├── bean │ ├── IUserService.java │ └── UserService.java └── ApiTest.java自动扫描注入占位符配置和对象的类关系,如图 15-2
图 15-2
在整个类图中以围绕实现接口 InstantiationAwareBeanPostProcessor 的类 AutowiredAnnotationBeanPostProcessor 作为入口点,被 AbstractAutowireCapableBeanFactory创建 Bean 对象过程中调用扫描整个类的属性配置中含有自定义注解 Value、Autowired、Qualifier,的属性值。 这里稍有变动的是关于属性值信息的获取,在注解配置的属性字段扫描到信息注入时,包括了占位符从配置文件获取信息也包括 Bean 对象,Bean 对象可以直接获取,但配置信息需要在 AbstractBeanFactory 中添加新的属性集合 embeddedValueResolvers,由 PropertyPlaceholderConfigurer#postProcessBeanFactory 进行操作填充到属性集合中。2. 把读取到属性填充到容器
定义解析字符串接口
cn.bugstack.springframework.util.StringValueResolver
public interface StringValueResolver { String resolveStringValue(String strVal); } 接口 StringValueResolver 是一个解析字符串操作的接口填充字符串
public class PropertyPlaceholderConfigurer implements BeanFactoryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { try { // 加载属性文件 DefaultResourceLoader resourceLoader = new DefaultResourceLoader(); Resource resource = resourceLoader.getResource(location); // ... 占位符替换属性值、设置属性值 // 向容器中添加字符串解析器,供解析@Value注解使用 StringValueResolver valueResolver = new PlaceholderResolvingStringValueResolver(properties); beanFactory.addEmbeddedValueResolver(valueResolver); } catch (IOException e) { throw new BeansException("Could not load properties", e); } } private class PlaceholderResolvingStringValueResolver implements StringValueResolver { private final Properties properties; public PlaceholderResolvingStringValueResolver(Properties properties) { this.properties = properties; } @Override public String resolveStringValue(String strVal) { return PropertyPlaceholderConfigurer.this.resolvePlaceholder(strVal, properties); } } } 在解析属性配置的类 PropertyPlaceholderConfigurer 中,最主要的其实就是这行代码的操作 beanFactory.addEmbeddedValueResolver(valueResolver) 这是把属性值写入到了 AbstractBeanFactory 的 embeddedValueResolvers 中。 这里说明下,embeddedValueResolvers 是 AbstractBeanFactory 类新增加的集合 List embeddedValueResolvers String resolvers to apply e.g. to annotation attribute values3. 自定义属性注入注解
自定义注解,Autowired、Qualifier、Value
@Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD}) public @interface Autowired { } @Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented public @interface Qualifier { String value() default ""; } @Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Value { /** * The actual value expression: e.g. "#{ systemProperties.myProp}". */ String value(); }3个注解在我们日常使用 Spring 也是非常常见的,注入对象、注入属性,而 Qualifier 一般与 Autowired 配合使用。
4. 扫描自定义注解
cn.bugstack.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor
public class AutowiredAnnotationBeanPostProcessor implements InstantiationAwareBeanPostProcessor, BeanFactoryAware { private ConfigurableListableBeanFactory beanFactory; @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { this.beanFactory = (ConfigurableListableBeanFactory) beanFactory; } @Override public PropertyValues postProcessPropertyValues(PropertyValues pvs, Object bean, String beanName) throws BeansException { // 1. 处理注解 @Value Class<?> clazz = bean.getClass(); clazz = ClassUtils.isCglibProxyClass(clazz) ? clazz.getSuperclass() : clazz; Field[] declaredFields = clazz.getDeclaredFields(); for (Field field : declaredFields) { Value valueAnnotation = field.getAnnotation(Value.class); if (null != valueAnnotation) { String value = valueAnnotation.value(); value = beanFactory.resolveEmbeddedValue(value); BeanUtil.setFieldValue(bean, field.getName(), value); } } // 2. 处理注解 @Autowired for (Field field : declaredFields) { Autowired autowiredAnnotation = field.getAnnotation(Autowired.class); if (null != autowiredAnnotation) { Class<?> fieldType = field.getType(); String dependentBeanName = null; Qualifier qualifierAnnotation = field.getAnnotation(Qualifier.class); Object dependentBean = null; if (null != qualifierAnnotation) { dependentBeanName = qualifierAnnotation.value(); dependentBean = beanFactory.getBean(dependentBeanName, fieldType); } else { dependentBean = beanFactory.getBean(fieldType); } BeanUtil.setFieldValue(bean, field.getName(), dependentBean); } } return pvs; } } AutowiredAnnotationBeanPostProcessor 是实现接口 InstantiationAwareBeanPostProcessor 的一个用于在 Bean 对象实例化完成后,设置属性操作前的处理属性信息的类和操作方法。只有实现了 BeanPostProcessor 接口才有机会在 Bean 的生命周期中处理初始化信息 核心方法 postProcessPropertyValues,主要用于处理类含有 @Value、@Autowired 注解的属性,进行属性信息的提取和设置。 这里需要注意一点因为我们在 AbstractAutowireCapableBeanFactory 类中使用的是 CglibSubclassingInstantiationStrategy 进行类的创建,所以在 AutowiredAnnotationBeanPostProcessor#postProcessPropertyValues 中需要判断是否为 CGlib 创建对象,否则是不能正确拿到类信息的。ClassUtils.isCglibProxyClass(clazz) ? clazz.getSuperclass() : clazz;5. 在Bean的生命周期中调用属性注入
cn.bugstack.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory { private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy(); @Override protected Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws BeansException { Object bean = null; try { // 判断是否返回代理 Bean 对象 bean = resolveBeforeInstantiation(beanName, beanDefinition); if (null != bean) { return bean; } // 实例化 Bean bean = createBeanInstance(beanDefinition, beanName, args); // 在设置 Bean 属性之前,允许 BeanPostProcessor 修改属性值 applyBeanPostProcessorsBeforeApplyingPropertyValues(beanName, bean, beanDefinition); // 给 Bean 填充属性 applyPropertyValues(beanName, bean, beanDefinition); // 执行 Bean 的初始化方法和 BeanPostProcessor 的前置和后置处理方法 bean = initializeBean(beanName, bean, beanDefinition); } catch (Exception e) { throw new BeansException("Instantiation of bean failed", e); } // 注册实现了 DisposableBean 接口的 Bean 对象 registerDisposableBeanIfNecessary(beanName, bean, beanDefinition); // 判断 SCOPE_SINGLETON、SCOPE_PROTOTYPE if (beanDefinition.isSingleton()) { registerSingleton(beanName, bean); } return bean; } /** * 在设置 Bean 属性之前,允许 BeanPostProcessor 修改属性值 * * @param beanName * @param bean * @param beanDefinition */ protected void applyBeanPostProcessorsBeforeApplyingPropertyValues(String beanName, Object bean, BeanDefinition beanDefinition) { for (BeanPostProcessor beanPostProcessor : getBeanPostProcessors()) { if (beanPostProcessor instanceof InstantiationAwareBeanPostProcessor){ PropertyValues pvs = ((InstantiationAwareBeanPostProcessor) beanPostProcessor).postProcessPropertyValues(beanDefinition.getPropertyValues(), bean, beanName); if (null != pvs) { for (PropertyValue propertyValue : pvs.getPropertyValues()) { beanDefinition.getPropertyValues().addPropertyValue(propertyValue); } } } } } // ... } AbstractAutowireCapableBeanFactory#createBean 方法中有这一条新增加的方法调用,就是在设置 Bean 属性之前,允许 BeanPostProcessor 修改属性值 的操作 applyBeanPostProcessorsBeforeApplyingPropertyValues 那么这个 applyBeanPostProcessorsBeforeApplyingPropertyValues 方法中,首先就是获取已经注入的 BeanPostProcessor 集合并从中筛选出继承接口 InstantiationAwareBeanPostProcessor 的实现类。 最后就是调用相应的 postProcessPropertyValues 方法以及循环设置属性值信息,beanDefinition.getPropertyValues().addPropertyValue(propertyValue);五、测试
1. 事先准备
配置 Dao
@Component public class UserDao { private static Map<String, String> hashMap = new HashMap<>(); static { hashMap.put("10001", "小傅哥,北京,亦庄"); hashMap.put("10002", "八杯水,上海,尖沙咀"); hashMap.put("10003", "阿毛,香港,铜锣湾"); } public String queryUserName(String uId) { return hashMap.get(uId); } } 给类配置上一个自动扫描注册 Bean 对象的注解 @Component,接下来会把这个类注入到 UserService 中。注解注入到 UserService
@Component("userService") public class UserService implements IUserService { @Value("${ token}") private String token; @Autowired private UserDao userDao; public String queryUserInfo() { try { Thread.sleep(new Random(1).nextInt(100)); } catch (InterruptedException e) { e.printStackTrace(); } return userDao.queryUserName("10001") + "," + token; } // ... } 这里包括了两种类型的注入,一个是占位符注入属性信息 @Value("${ token}"),另外一个是注入对象信息 @Autowired2. 属性配置文件
token.properties
token=RejDlI78hu223Opo983Dsspring.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context"> <bean class="cn.bugstack.springframework.beans.factory.PropertyPlaceholderConfigurer"> <property name="location" value="classpath:token.properties"/> </bean> <context:component-scan base-package="cn.bugstack.springframework.test.bean"/> </beans>