手写Spring:第15章-通过注解注入属性信息

文章目录

  • 一、目标:通过注解注入属性信息
  • 二、设计:通过注解注入属性信息
  • 三、实现:通过注解注入属性信息
    • 3.1 工程结构
    • 3.2 自动扫描注入占位符配置和对象类图
    • 3.3 读取属性并填充到容器中
      • 3.3.1 定义解析字符串接口
      • 3.3.2 配置Bean工厂添加解析器
      • 3.3.3 抽象Bean工厂基类
      • 3.3.4 填充字符串
    • 3.4 自定义属性注入注解
      • 3.4.1 定义注入对象注解
      • 3.4.2 定义注入对象辅助注解
      • 3.4.3 定义注入属性注解
    • 3.5 扫描自定义注解
      • 3.5.1 修改Bean工厂接口
      • 3.5.2 抽象应用上下文
      • 3.5.3 默认Bean工厂实现类
      • 3.5.4 实例化感知对象处理
      • 3.5.5 默认自动代理创建者
      • 3.5.6 扫描自定义注解类
    • 3.6 在Bean生命周期中属性注入
      • 3.6.1 在对象工厂注册
      • 3.6.2 在Bean生命周期内调用属性注入
  • 四、测试:通过注解注入属性信息
    • 4.1 添加测试配置
      • 4.1.1 用户数据层类
      • 4.1.2 用户服务层实现类
      • 4.1.3 Spring属性配置文件
    • 4.2 单元测试
  • 五、总结:通过注解注入属性信息

一、目标:通过注解注入属性信息

💡 怎么完成自动注入属性?

  • 我们已经解决需要手动配置 Bean 对象到 spring.xml 文件中,改为可以自动扫描带有注解 @Component 的对象完成自动装配和注册到 Spring 容器的操作。
  • 那么在自动扫描包注册 Bean 对象之后,就需要把原来在配置文件中通过 property name="token" 配置属性和 Bean 的操作,也改为可以自动注入。
    • 使用 @Autowired@Value 注解,完成对属性和对象的注入操作。

二、设计:通过注解注入属性信息

💡 设计:简化注入属性,通过注解的形式自动化扫描注册。

  • 在完成 Bean 对象的基础功能后,后续添加的功能都是围绕着 Bean 的生命周期进行的。比如:
    • 修改 Bean 的定义 BeanFactoryPostProcessor
    • 处理 Bean 的属性要用到 BeanPostProcessor
    • 完成个性的属性操作则专门继承 BeanPostProcessor 提供新的接口。
  • 因为这样才能通过 instanceof 判断出具有标记性的接口,所以关于 Bean 等等的操作,以及监听 Aware、获取 BeanFactory,都需要在 Bean 的生命周期中完成。
  • 设计:属性和 Bean 对象的注入,也会用到 BeanPostProcessor 来完成在设置 Bean 属性之前,允许 BeanPostProcessor 修改属性值。

在这里插入图片描述

  • 要处理自动扫描注入,包括属性注入、对象注入,则需要在对象属性 applyPropertyValues 填充之前,把属性信息写入到 PropertyValues 的集合中去。
    • 这一步相当于:解决之前在 spring.xml 配置属性的过程。
  • 而在属性的读取中,需要依赖于对 Bean 对象的类中属性配置了注解的扫描。
    • field.getAnnotation(Value.class)。依次拿出符合的属性并填充上相应的配置信息。
    • 属性的配置信息需要依赖于 BeanFactoryPostProcessor 的实现类 PropertyPlaceholderConfigurer,把值写入到 AbstractBeanFactory#embeddedValueResolvers 集合中,这样才能在属性填充中利用 beanFactory 获取相应的属性值。
  • @Autowired 对于对象的注入,这一个和属性注入的唯一区别是对于对象的获取 beanFactory.getBean(fieldType)
  • 当所有的属性被设置到 PropertyValues 完成以后,接下来就是创建对象的下一步,属性填充。此时就会把我们获取到的配置和对象填充到属性上,也就实现了自动注入的功能。

三、实现:通过注解注入属性信息

3.1 工程结构

spring-step-14
|-src|-main|	|-java|		|-com.lino.springframework|			|-aop|			|	|-aspectj|			|	|	|-AspectJExpressionPointcut.java|			|	|	|-AspectJExpressionPointcutAdvisor.java|			|	|-framework|			|	|	|-adapter|			|	|	|	|-MethodBeforeAdviceInterceptor.java|			|	|	|-autoproxy|			|	|	|	|-DefaultAdvisorAutoProxyCreator.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|			|	|	|	|-AbstractBeabDefinitionReader.java|			|	|	|	|-AbstractBeabFactory.java|			|	|	|	|-BeabDefinitionReader.java|			|	|	|	|-BeanDefinitionRegistry.java|			|	|	|	|-CglibSubclassingInstantiationStrategy.java|			|	|	|	|-DefaultListableBeanFactory.java|			|	|	|	|-DefaultSingletonBeanRegistry.java|			|	|	|	|-DisposableBeanAdapter.java|			|	|	|	|-FactoryBeanRegistrySupport.java|			|	|	|	|-InstantiationStrategy.java|			|	|	|	|-SimpleInstantiationStrategy.java|			|	|	|-xml|			|	|	|	|-XmlBeanDefinitionReader.java|			|	|	|-Aware.java|			|	|	|-BeanClassLoaderAware.java|			|	|	|-BeanFactory.java|			|	|	|-BeanFactoryAware.java|			|	|	|-BeanNameAware.java|			|	|	|-ConfigurableListableBeanFactory.java|			|	|	|-DisposableBean.java|			|	|	|-FactoryBean.java|			|	|	|-HierarcgicalBeanFactory.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|			|-util|			|	|-ClassUtils.java|			|	|-StringValueResolver.java|-test|-java|-com.lino.springframework.test|-bean|	|-IUserService.java|	|-UserDao.java|	|-UserService.java|-ApiTest.java|-resources|-spring.xml|-token.properties

3.2 自动扫描注入占位符配置和对象类图

在这里插入图片描述

  • 在整个类图中围绕接口 InstantiationAwareBeanPostProcessor 的类 AutowiredAnnotationBeanPostProcessor 作为入口点。
    • AbstractAutowireCapableBeanFactory 创建 Bean 对象过程中。
    • 调用扫描整个类的属性配置中含有自定义注解 ValueAutowiredQualifier 的属性值。
  • 关于属性值信息的获取,在注解配置的属性字段扫描到信息注入时。
    • 包括了占位符从配置文件获取信息,也包括 Bean 对象。
    • Bean 对象可以直接获取,但配置信息需要在 AbstractBeanFactory 中添加新的属性集合 embeddedValueResolvers,由 PropertyPlaceholderConfigurer#postProcessBeanFactory 进行操作填充到属性集合中。

3.3 读取属性并填充到容器中

3.3.1 定义解析字符串接口

StringValueResolver.java

package com.lino.springframework.util;/*** @description: 解析字符串接口*/
public interface StringValueResolver {/*** 解析字符串** @param strVal 字符串* @return 解析后的结果*/String resolveStringValue(String strVal);
}

3.3.2 配置Bean工厂添加解析器

ConfigurableBeanFactory.java

package com.lino.springframework.beans.factory.config;import com.lino.springframework.beans.factory.HierarchicalBeanFactory;
import com.lino.springframework.util.StringValueResolver;/*** @description: 配置Bean工厂接口*/
public interface ConfigurableBeanFactory extends HierarchicalBeanFactory, SingletonBeanRegistry {String SCOPE_SINGLETON = "singleton";String SCOPE_PROTOTYPE = "prototype";/*** 添加修改新实例化 Bean 对象的扩展点** @param beanPostProcessor 新实例化 Bean 对象*/void addBeanPostProcessor(BeanPostProcessor beanPostProcessor);/*** 销毁单例*/void destroySingletons();/*** 添加字符串解析器** @param valueResolver 解析器*/void addEmbeddedValueResolver(StringValueResolver valueResolver);/*** 解析嵌入值** @param value 嵌入值* @return 解析后的结果*/String resolveEmbeddedValue(String value);
}

3.3.3 抽象Bean工厂基类

AbstractBeanFactory.java

package com.lino.springframework.beans.factory.support;import com.lino.springframework.beans.BeansException;
import com.lino.springframework.beans.factory.FactoryBean;
import com.lino.springframework.beans.factory.config.BeanDefinition;
import com.lino.springframework.beans.factory.config.BeanPostProcessor;
import com.lino.springframework.beans.factory.config.ConfigurableBeanFactory;
import com.lino.springframework.util.ClassUtils;
import com.lino.springframework.util.StringValueResolver;
import java.util.ArrayList;
import java.util.List;/*** @description: 抽象的 Bean 工厂基类,定义模板方法*/
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();private final List<BeanPostProcessor> beanPostProcessors = new ArrayList<>();private final List<StringValueResolver> embeddedValueResolvers = new ArrayList<>();...@Overridepublic void addEmbeddedValueResolver(StringValueResolver valueResolver) {this.embeddedValueResolvers.add(valueResolver);}@Overridepublic String resolveEmbeddedValue(String value) {String result = value;for (StringValueResolver resolver : this.embeddedValueResolvers) {result = resolver.resolveStringValue(result);}return result;}...
}

3.3.4 填充字符串

PropertyPlaceholderConfigurer.java

package com.lino.springframework.beans.factory;import com.lino.springframework.beans.BeansException;
import com.lino.springframework.beans.PropertyValue;
import com.lino.springframework.beans.PropertyValues;
import com.lino.springframework.beans.factory.config.BeanDefinition;
import com.lino.springframework.beans.factory.config.BeanFactoryPostProcessor;
import com.lino.springframework.core.io.DefaultResourceLoader;
import com.lino.springframework.core.io.Resource;
import com.lino.springframework.util.StringValueResolver;
import java.io.IOException;
import java.util.Properties;/*** @description: 处理占位符配置类*/
public class PropertyPlaceholderConfigurer implements BeanFactoryPostProcessor {/*** 占位符前缀*/public static final String DEFAULT_PLACEHOLDER_PREFIX = "${";/*** 占位符后缀*/public static final String DEFAULT_PLACEHOLDER_SUFFIX = "}";private String location;@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {try {// 加载属性文件DefaultResourceLoader resourceLoader = new DefaultResourceLoader();Resource resource = resourceLoader.getResource(location);// 占位符替换属性值Properties properties = new Properties();properties.load(resource.getInputStream());String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames();for (String beanName : beanDefinitionNames) {BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);PropertyValues propertyValues = beanDefinition.getPropertyValues();for (PropertyValue propertyValue : propertyValues.getPropertyValues()) {Object value = propertyValue.getValue();if (!(value instanceof String)) {continue;}value = resolvePlaceholder((String) value, properties);propertyValues.addPropertyValue(new PropertyValue(propertyValue.getName(), value));}}// 向容器中添加字符串解析器,共解析@Value注解使用StringValueResolver valueResolver = new PlaceholderResolvingStringValueResolver(properties);beanFactory.addEmbeddedValueResolver(valueResolver);} catch (IOException e) {throw new BeansException("Could not load properties", e);}}private String resolvePlaceholder(String value, Properties properties) {String strVal = value;StringBuilder buffer = new StringBuilder(strVal);int startIdx = strVal.indexOf(DEFAULT_PLACEHOLDER_PREFIX);int stopIdx = strVal.indexOf(DEFAULT_PLACEHOLDER_SUFFIX);if (startIdx != -1 && stopIdx != -1 && startIdx < stopIdx) {String propKey = strVal.substring(startIdx + 2, stopIdx);String propVal = properties.getProperty(propKey);buffer.replace(startIdx, stopIdx + 1, propVal);}return buffer.toString();}public void setLocation(String location) {this.location = location;}private class PlaceholderResolvingStringValueResolver implements StringValueResolver {private Properties properties;public PlaceholderResolvingStringValueResolver(Properties properties) {this.properties = properties;}@Overridepublic String resolveStringValue(String strVal) {return PropertyPlaceholderConfigurer.this.resolvePlaceholder(strVal, properties);}}
}
  • 在解析属性配置的类 PropertyPlaceholderConfigurer 中,最重要的是 beanFactory.addEmbeddedValueResolver(valueResolver)
    • 这是把属性值写入到了 AbstractBeanFactoryembeddedValueResolvers 集合中。
    • embeddedValueResolvers 是在 AbstractBeanFactory 类新增加的集合 List<StringValueResolver> embeddedValueResolvers

3.4 自定义属性注入注解

3.4.1 定义注入对象注解

Autowired.java

package com.lino.springframework.beans.factory.annotation;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** @description: 注入对象注解*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD})
public @interface Autowired {
}

3.4.2 定义注入对象辅助注解

Qualifier.java

package com.lino.springframework.beans.factory.annotation;import java.lang.annotation.*;/*** @description: 注入对象辅助注解*/
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Qualifier {String value() default "";
}
  • Qualifier 一般与 Autowired 搭配使用。

3.4.3 定义注入属性注解

Value.java

package com.lino.springframework.beans.factory.annotation;import java.lang.annotation.*;/*** @description: 注入属性注解*/
@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.5 扫描自定义注解

3.5.1 修改Bean工厂接口

BeanFactory.java

package com.lino.springframework.beans.factory;import com.lino.springframework.beans.BeansException;/*** @description: 定义 Bean 工厂接口*/
public interface BeanFactory {/*** 返回 Bean 的实例对象** @param name 要检索的bean的名称* @return 实例化的 Bean 对象* @throws BeansException 不能获取 Bean 对象,抛出异常*/Object getBean(String name) throws BeansException;/*** 返回含构造函数的 Bean 实例对象** @param name 要检索的bean的名称* @param args 构造函数入参* @return 实例化的 Bean 对象* @throws BeansException 不能获取 Bean 对象,抛出异常*/Object getBean(String name, Object... args) throws BeansException;/*** 返回指定泛型的对象** @param name         要检索的bean的名称* @param requiredType 类型* @param <T>          泛型* @return 实例化的的 Bean 对象* @throws BeansException 不能获取 Bean 对象,抛出异常*/<T> T getBean(String name, Class<T> requiredType) throws BeansException;/*** 返回指定泛型的对象** @param requiredType 类型* @param <T>          泛型* @return 实例化的的 Bean 对象* @throws BeansException 不能获取 Bean 对象,抛出异常*/<T> T getBean(Class<T> requiredType) throws BeansException;
}
  • 添加 getBean(Class<T> requiredType) 方法

3.5.2 抽象应用上下文

AbstractApplicationContext.java

package com.lino.springframework.context.support;import com.lino.springframework.beans.BeansException;
import com.lino.springframework.beans.factory.ConfigurableListableBeanFactory;
import com.lino.springframework.beans.factory.config.BeanFactoryPostProcessor;
import com.lino.springframework.beans.factory.config.BeanPostProcessor;
import com.lino.springframework.context.ApplicationEvent;
import com.lino.springframework.context.ApplicationListener;
import com.lino.springframework.context.ConfigurableApplicationContext;
import com.lino.springframework.context.event.ApplicationEventMulticaster;
import com.lino.springframework.context.event.ContextClosedEvent;
import com.lino.springframework.context.event.ContextRefreshedEvent;
import com.lino.springframework.context.event.SimpleApplicationEventMulticaster;
import com.lino.springframework.core.io.DefaultResourceLoader;
import java.util.Collection;
import java.util.Map;/*** @description: 抽象应用上下文*/
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {...@Overridepublic Object getBean(String name) throws BeansException {return getBeanFactory().getBean(name);}@Overridepublic Object getBean(String name, Object... args) throws BeansException {return getBeanFactory().getBean(name, args);}@Overridepublic <T> T getBean(String name, Class<T> requiredType) throws BeansException {return getBeanFactory().getBean(name, requiredType);}@Overridepublic <T> T getBean(Class<T> requiredType) throws BeansException {return getBeanFactory().getBean(requiredType);}...}
  • 添加 getBean(Class<T> requiredType) 方法实现

3.5.3 默认Bean工厂实现类

DefaultListableBeanFactory.java

package com.lino.springframework.beans.factory.support;import com.lino.springframework.beans.BeansException;
import com.lino.springframework.beans.factory.ConfigurableListableBeanFactory;
import com.lino.springframework.beans.factory.config.BeanDefinition;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** @description: 默认的Bean工厂实现类*/
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory implements BeanDefinitionRegistry, ConfigurableListableBeanFactory {...@Overridepublic <T> T getBean(Class<T> requiredType) throws BeansException {List<String> beanNames = new ArrayList<>();for (Map.Entry<String, BeanDefinition> entry : beanDefinitionMap.entrySet()) {Class beanClass = entry.getValue().getBeanClass();if (requiredType.isAssignableFrom(beanClass)) {beanNames.add(entry.getKey());}}if (1 == beanNames.size()) {return getBean(beanNames.get(0), requiredType);}throw new BeansException(requiredType + "expected single bean but found " + beanNames.size() + ": " + beanNames);}
}
  • 添加 getBean(Class<T> requiredType) 方法实现

3.5.4 实例化感知对象处理

InstantiationAwareBeanPostProcessor.java

package com.lino.springframework.beans.factory.config;import com.lino.springframework.beans.BeansException;
import com.lino.springframework.beans.PropertyValues;/*** @description: 实例化感知对象处理*/
public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor {/*** 在 Bean 对象执行初始化方法之前,执行此方法** @param beanClass 对象类* @param beanName  对象名* @return 新对象* @throws BeansException 异常*/Object postProcessBeforeInitialization(Class<?> beanClass, String beanName) throws BeansException;/*** 在 Bean 对象实例化完成后,设置属性操作之前执行此方法** @param pvs      属性值集合* @param bean     对象* @param beanName 对象名称* @return 属性值集合* @throws BeansException 异常*/PropertyValues postProcessPropertyValues(PropertyValues pvs, Object bean, String beanName) throws BeansException;
}
  • 添加 postProcessPropertyValues 方法

3.5.5 默认自动代理创建者

DefaultAdvisorAutoProxyCreator.java

package com.lino.springframework.aop.framework.autoproxy;import com.lino.springframework.aop.*;
import com.lino.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor;
import com.lino.springframework.aop.framework.ProxyFactory;
import com.lino.springframework.beans.BeansException;
import com.lino.springframework.beans.PropertyValues;
import com.lino.springframework.beans.factory.BeanFactory;
import com.lino.springframework.beans.factory.BeanFactoryAware;
import com.lino.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
import com.lino.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.MethodInterceptor;
import java.util.Collection;/*** @description: 默认自动代理创建者*/
public class DefaultAdvisorAutoProxyCreator implements InstantiationAwareBeanPostProcessor, BeanFactoryAware {private DefaultListableBeanFactory beanFactory;...@Overridepublic PropertyValues postProcessPropertyValues(PropertyValues pvs, Object bean, String beanName) throws BeansException {return pvs;}...
}
  • 添加 postProcessPropertyValues 默认实现

3.5.6 扫描自定义注解类

AutowiredAnnotationBeanPostProcessor.java

package com.lino.springframework.beans.factory.annotation;import cn.hutool.core.bean.BeanUtil;
import com.lino.springframework.beans.BeansException;
import com.lino.springframework.beans.PropertyValues;
import com.lino.springframework.beans.factory.BeanFactory;
import com.lino.springframework.beans.factory.BeanFactoryAware;
import com.lino.springframework.beans.factory.config.ConfigurableBeanFactory;
import com.lino.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
import com.lino.springframework.util.ClassUtils;
import java.lang.reflect.Field;/*** @description: 扫描自定义注解类*/
public class AutowiredAnnotationBeanPostProcessor implements InstantiationAwareBeanPostProcessor, BeanFactoryAware {private ConfigurableBeanFactory beanFactory;@Overridepublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {this.beanFactory = (ConfigurableBeanFactory) beanFactory;}@Overridepublic PropertyValues postProcessPropertyValues(PropertyValues pvs, Object bean, String beanName) throws BeansException {// 1.处理注解 @ValueClass<?> 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.处理注解 @Autowiredfor (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;}@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {return null;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {return null;}@Overridepublic Object postProcessBeforeInitialization(Class<?> beanClass, String beanName) throws BeansException {return null;}
}
  • AutowiredAnnotationBeanPostProcessor 是实现接口 InstantiationAwareBeanPostProcessor 的一个用于在 Bean 对象实例化完成后,设置属性操作前的处理属性信息的类和操作方法。
    • 只有实现了 BeanPostFactory 接口才有机会在 Bean 的生命周期中处理初始化信息。
  • 核心方法 postProcessPropertyValues,主要用于处理类含有 @Value@Autowired 注解的属性,进行属性信息的提取和设置。
  • 注意:因为在 AbstractAutowireCapableBeanFactory 类中使用的是 CglibSubclassingInstantiationStrategy 进行类的创建,所以在 AutowiredAnnotationBeanPostProcessor#postProcessPropertyValues 中需要判断是否为 CGLIB 创建对象,否则是不能拿到类信息的。
    • ClassUtils.isCglibProxyClass(clazz) ? clazz.getSuperclass() : clazz

3.6 在Bean生命周期中属性注入

3.6.1 在对象工厂注册

ClassPathBeanDefinitionScanner.java

package com.lino.springframework.context.annotation;import cn.hutool.core.util.StrUtil;
import com.lino.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
import com.lino.springframework.beans.factory.config.BeanDefinition;
import com.lino.springframework.beans.factory.support.BeanDefinitionRegistry;
import com.lino.springframework.stereotype.Component;
import java.util.Set;/*** @description: 类路径扫描装配实现类*/
public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateComponentProvider {private BeanDefinitionRegistry registry;public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry) {this.registry = registry;}public void doScan(String... basePackages) {for (String basePackage : basePackages) {Set<BeanDefinition> candidates = findCandidateComponents(basePackage);for (BeanDefinition beanDefinition : candidates) {// 解析 bean 的作用域 singleton、prototypeString beanScope = resolveBeanScope(beanDefinition);if (StrUtil.isNotEmpty(beanScope)) {beanDefinition.setScope(beanScope);}registry.registerBeanDefinition(determineBeanName(beanDefinition), beanDefinition);}}// 注册处理注解的 BeanPostProcessor(@AutoWired、@Value)registry.registerBeanDefinition("com.lino.springframework.context.annotation.internalAutowiredAnnotationProcessor", new BeanDefinition(AutowiredAnnotationBeanPostProcessor.class));}...
}
  • 由于 AutowiredAnnotationBeanPostProcessor 并没有标注 @Component,所以无法在类扫描时注入到 beanFactory 中,此处需要手动注册。

3.6.2 在Bean生命周期内调用属性注入

AbstractAutowireCapableBeanFactory.java

package com.lino.springframework.beans.factory.support;import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.lino.springframework.beans.BeansException;
import com.lino.springframework.beans.PropertyValue;
import com.lino.springframework.beans.PropertyValues;
import com.lino.springframework.beans.factory.*;
import com.lino.springframework.beans.factory.config.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;/*** @description: 实现默认bean创建的抽象bean工厂超类*/
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory {private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();@Overrideprotected Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) {Object bean = null;try {// 判断是否返回代理 Bean 对象bean = resolveBeforeInstantiation(beanName, beanDefinition);if (null != bean) {return bean;}// 实例化Beanbean = 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_PROTOTYPEif (beanDefinition.isSingleton()) {registerSingletonBean(beanName, bean);}return bean;}.../*** 在设置 Bean 属性之前,允许 BeanPostProcessor 修改属性值** @param beanName       对象名称* @param bean           对象* @param beanDefinition 实例化对象*/private 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
      • 在这个方法中,首先就是获取已经注入的 BeanPostProcessor 集合并从中筛选出继承接口 InstantiationAwareBeanPostProcessor 的实现类。
      • 最后就是调用相应的 postProcessPropertyValues 方法以及循环设置属性值信息。beanDefinition.getPropertyValues().addPropertyValue(propertyValue)

四、测试:通过注解注入属性信息

4.1 添加测试配置

4.1.1 用户数据层类

UserDao.java

package com.lino.springframework.test.bean;import com.lino.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;/*** @description: 用户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 中。

4.1.2 用户服务层实现类

UserService.java

package com.lino.springframework.test.bean;import com.lino.springframework.beans.factory.annotation.Autowired;
import com.lino.springframework.beans.factory.annotation.Value;
import com.lino.springframework.stereotype.Component;
import java.util.Random;/*** @description: 用户接口实现类*/
@Component("userService")
public class UserService implements IUserService {@Value("${token}")private String token;@Autowiredprivate UserDao userDao;@Overridepublic String queryUserInfo() {try {Thread.sleep(new Random(1).nextInt(100));} catch (InterruptedException e) {e.printStackTrace();}return userDao.queryUserName("10001") + ", " + token;}@Overridepublic String register(String userName) {try {Thread.sleep(new Random(1).nextInt(100));} catch (InterruptedException e) {e.printStackTrace();}return "注册用户:" + userName + " success!";}@Overridepublic String toString() {return "UserService#token = {" + token + "}";}public String getToken() {return token;}public void setToken(String token) {this.token = token;}public UserDao getUserDao() {return userDao;}public void setUserDao(UserDao userDao) {this.userDao = userDao;}
}
  • 这里包括了两种类型的注入,一个是占位符注入属性信息 @Value("${token}"),另外一个是注入对象信息 @Autowired

4.1.3 Spring属性配置文件

spring.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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/context"><bean class="com.lino.springframework.beans.factory.PropertyPlaceholderConfigurer"><property name="location" value="classpath:token.properties"/></bean><context:component-scan base-package="com.lino.springframework.test.bean"/></beans>

4.2 单元测试

ApiTest.java

@Test
public void test_scan() {ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring.xml");IUserService userService = applicationContext.getBean("userService", IUserService.class);System.out.println("测试结果:" + userService.queryUserInfo());
}
  • 单元测试时,可以完整的测试一个类注入到 Spring 容器,同时这个属性信息也可以被自动扫描填充。

测试结果

测试结果:张三,浙江,杭州, RejDlI78hu223Opo983Ds

在这里插入图片描述

  • 测试结果看,使用方式已经通过了,有自动扫描类,有注解注入属性。

五、总结:通过注解注入属性信息

  • 从整个注解信息扫描注入的实现内容看,我们围绕着在 Bean 的生命周期中进行处理。
    • 就像 BeanPostProcessor 用于修改新实例化 Bean 对象的扩展点。
    • 提供的接口方法可以用于处理 Bean 对象实例化前后进行处理操作。
  • 而有时候需要做一些差异化的控制,所以还需要继承 BeanPostProcessor 接口。
    • 定义新的接口 InstantiationAwareBeanPostProcessor 这样就可以区分出不同扩展点的操作。
  • 像是接口 instanceof 判断,注解用 field.getAnnotation(Value.class) 获取,都是相当于在类上做的一些标识性信息,便于可以用一些方法找到这些功能点,以便进行处理。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/73963.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于Java+SpringBoot+Vue前后端分离农产品直卖平台设计和实现

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…

手写签名到背景上合为1张图

手写签名到背景上合为1张图 package.json中 "signature_pad": "3.0.0-beta.3"<template><div class"home"><canvas id"canvas" width"500" height"300"></canvas><button click"…

ELK高级搜索(三)

文章目录 11&#xff0e;索引Index入门11.1 索引管理11.2 定制分词器11.3 type底层结构11.4 定制dynamic mapping11.5 零停机重建索引 12&#xff0e;中文分词器 IK分词器12.1 Ik分词器安装使用12.2 ik配置文件12.3 使用mysql热更新 13&#xff0e;java api 实现索引管理14&…

026:vue中el-progress逆向倒计时方式显示

第026个 查看专栏目录: VUE ------ element UI 专栏目标 在vue和element UI联合技术栈的操控下&#xff0c;本专栏提供行之有效的源代码示例和信息点介绍&#xff0c;做到灵活运用。 &#xff08;1&#xff09;提供vue2的一些基本操作&#xff1a;安装、引用&#xff0c;模板使…

Redis多机数据库实现

Redis多机数据库实现 为《Redis设计与实现》笔记 复制 客户端可以使用SLAVEOF命令将指定服务器设置为该服务器的主服务器 127.0.0.1:12345> SLAVEOF 127.0.0.1 6379127.0.0.1:6379将被设置为127.0.0.1:123456的主服务器 旧版复制功能的实现 Redis的复制功能分为同步&a…

Linux之history、tab、alias、命令执行顺序、管道符以及exit

目录 Linux之history、tab、alias、命令执行顺序、管道符以及exit history历史命令 格式 参数 修改默认记录历史命令条数 案例 案例1 --- 显示history历史记录中出现次数最高的top10 案例2 --- 增加history显示的时间信息 命令与文件名补全 --- tab 命令别名 格式 案…

MySQL事务管理

文章目录 MySQL事务管理0. MySQL的CURD不加控制&#xff0c;出现的问题1. 什么是事务2. 为什么会出现事务3. 事务的版本支持与提交方式3.1 版本支持3.2 提交方式 4. 事务的操作4.0 准备工作4.1 事务正常操作(1) 创建保存点后, rollback(2) 直接rollback(3) 正常提交 4.2 事务异…

学生免费申请IDEA使用流程

IntelliJ IDEA一般简称IDEA&#xff0c;是Java编程语言开发的集成环境&#xff0c;在业界被公认为最好的Java开发工具。 1 IDEA官网下载 1.1 官网地址 https://www.jetbrains.com/idea/ 1.2 IDEA下载 访问官网&#xff0c;单击download按钮&#xff0c;下载“IntelliJ IDE…

牛客练习赛115 A Mountain sequence

题目&#xff1a; 样例&#xff1a; 输入 3 5 1 2 3 4 5 3 3 3 3 3 1 2 1 输出 16 1 3 思路&#xff1a; 依据题意&#xff0c;再看数据范围&#xff0c;可以知道暴力肯定是不可能了&#xff0c;然后通过题目意思&#xff0c;我们可以排列模拟一下&#xff0c;这里排列所得结…

Vue+NodeJS上传图片到腾讯云Cos

一.前端Vue 1.选择图片 --HTML <input type"file" accept"image/*" change"handleFileChange"> <el-button size"large" click"changeAvatar">上传头像</el-button> //选择图片 function handleFileC…

ubuntu22.04 设置网卡开机自启

配置文件路径 在Ubuntu中&#xff0c;网络配置文件通常位于/etc/netplan/目录下&#xff0c;其文件名以.yaml为后缀。Netplan是Ubuntu 17.10及更高版本中默认的网络配置工具&#xff0c;用于配置网络接口、IP地址、网关、DNS服务器等。 我们可以看到配置文件为 01-network-ma…

48、springboot 的国际化之让用户在程序界面上弄个下拉框,进行动态选择语言

上一篇是直接改浏览器的支持语言。 在浏览器上面直接改国际化语言 这次要实现的功能是直接在程序界面动态选择语言。 Locale 代表语言、国家。 ★ 在界面上动态改变语言 应用之所以能动态呈现不同的语言界面&#xff0c;其实关键在于如何确定客户端的Locale&#xff08;代…

Hugging News #0904: 登陆 AWS Marketplace

每一周&#xff0c;我们的同事都会向社区的成员们发布一些关于 Hugging Face 相关的更新&#xff0c;包括我们的产品和平台更新、社区活动、学习资源和内容更新、开源库和模型更新等&#xff0c;我们将其称之为「Hugging News」。本期 Hugging News 有哪些有趣的消息&#xff0…

Python库-coverage测试覆盖率

Coverage.py 是用于测量Python程序代码覆盖率的工具。它 监视程序&#xff0c;注意代码的哪些部分已执行&#xff0c;然后 分析源以识别可以执行但未执行的代码。 覆盖率测量通常用于衡量测试的有效性。它 可以显示测试正在执行代码的哪些部分&#xff0c;以及哪些部分是 不。…

掌握API数据检索:过滤和排序的综合指南

API可以返回大量的数据&#xff0c;这使得开发人员很难只检索他们需要的信息。这就是API的过滤和排序功能的用武之地。 过滤和排序是API设计的两个基本功能&#xff0c;它们允许开发人员有效地从API检索特定的数据。过滤使开发人员能够通过指定返回的数据必须满足的标准来缩小A…

javaee spring 测试aop 切面

切面类 package com.test.advice;import org.aspectj.lang.ProceedingJoinPoint;//增强类 public class MyAdvice {//将这个增强方法切入到service层的add方法前public void before(){System.out.println("添加用户之前");}}目标类 package com.test.service;publi…

[Vue3 博物馆管理系统] 使用Vue3、Element-plus tabs组件构建选项卡功能

系列文章目录 第一章 定制上中下&#xff08;顶部菜单、底部区域、中间主区域显示&#xff09;三层结构首页 第二章 使用Vue3、Element-plus菜单组件构建菜单 第三章 使用Vue3、Element-plus走马灯组件构建轮播图 第四章 使用Vue3、Element-plus tabs组件构建选项卡功能 [第五…

探索 Wall-E 的寻路算法

几年前,Yandex 组织了一场名为“机器人快递员”的竞赛,并提供了诱人的奖品:一张参加专业人士封闭式自动驾驶会议的门票。该竞赛类似于一场游戏,参与者的任务是在地图上找到最佳路线并使用机器人快递员优化送货。 当我深入研究这个主题时,我发现尽管路线查找问题已经解决,…

【MongoDB】Ubuntu22.04 下安装 MongoDB | 用户权限认证 | skynet.db.mongo 模块使用

文章目录 Ubuntu 22.04 安装 MongoDB后台启动 MongoDBshell 连入 MongoDB 服务 MongoDB 用户权限认证创建 root 用户开启认证重启 MongoDB 服务创建其他用户查看用户信息验证用户权限删除用户 skynet.db.mongo 模块使用authensureIndexfind、findOneinsert、safe_insertdelete、…

服务器分析和监控

在当今数字化时代&#xff0c;对于网络流量的分析和监控变得越来越重要。本文将详细介绍如何利用HTTPS代理服务器来实现高效、安全且可靠的流量分析与监控功能&#xff0c;并提供具体操作步骤以及相关技巧。无论是企业需要优化网络性能还是个人用户&#xff0c;在遵循法规合规前…