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

news2024/12/25 0:11:59

文章目录

  • 一、目标:通过注解注入属性信息
  • 二、设计:通过注解注入属性信息
  • 三、实现:通过注解注入属性信息
    • 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<>();

    ...

    @Override
    public void addEmbeddedValueResolver(StringValueResolver valueResolver) {
        this.embeddedValueResolvers.add(valueResolver);
    }

    @Override
    public 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;

    @Override
    public 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;
        }

        @Override
        public 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 {

    ...

    @Override
    public Object getBean(String name) throws BeansException {
        return getBeanFactory().getBean(name);
    }

    @Override
    public Object getBean(String name, Object... args) throws BeansException {
        return getBeanFactory().getBean(name, args);
    }

    @Override
    public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
        return getBeanFactory().getBean(name, requiredType);
    }

    @Override
    public <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 {

    ...

    @Override
    public <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;

    ...

    @Override
    public 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;

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = (ConfigurableBeanFactory) 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;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return null;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return null;
    }

    @Override
    public 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、prototype
                String 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();

    @Override
    protected Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) {
        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()) {
            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;

    @Autowired
    private UserDao userDao;

    @Override
    public String queryUserInfo() {
        try {
            Thread.sleep(new Random(1).nextInt(100));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return userDao.queryUserName("10001") + ", " + token;
    }

    @Override
    public String register(String userName) {
        try {
            Thread.sleep(new Random(1).nextInt(100));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "注册用户:" + userName + " success!";
    }

    @Override
    public 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/beans
	         http://www.springframework.org/schema/beans/spring-beans.xsd
		 http://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.coloradmin.cn/o/982453.html

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

相关文章

低能量电子束曝光技术

引言 直接蚀刻和剥离是两种比较流行的图案转移工艺。在直接蚀刻工艺中&#xff0c;首先使用光刻技术对聚合物抗蚀剂进行构图&#xff0c;然后通过干法蚀刻技术用抗蚀剂作为掩模将图案转移到衬底或子层上。 剥离过程中&#xff0c;膜(通常是金属)被涂覆在抗蚀剂结构上&#xf…

RoboTAP:由 Google DeepMind 开发的一款机器人操作系统

Google DeepMind 开发的一款机器人操作系统RoboTAP。该系统能够通过只需几分钟的示范&#xff0c;就能让机器人学会新的视觉运动任务。你只需要给它展示几次如何做某件事&#xff0c;比如拿起一个苹果放到果冻上&#xff0c;它就能学会这个动作。 工作原理 该系统能够通过视觉…

CMS-织梦[dede]-通用免登发布插件

CMS-织梦[dede]-通用免登发布插件 1. 织梦通用免登陆发布插件功能说明2. 织梦通用免登陆发布接口使用说明2-1 下载插件2-2 安装插件3 对接火车头等采集工具 3 爬虫【古诗文网】示例[可选]测试火车头入库模型 使用火车头&#xff0c;简数采集器&#xff0c;八爪鱼等文章采集工具…

直播系统源码,系统分析篇:不可或缺的云转码系统

科技的进步发展让人们的生活越来越便利&#xff0c;而当今社会我们最常使用让我们生活变得更便利的方式&#xff0c;就是下载适合我们解决困难的相关直播系统源码搭建出来的APP&#xff0c;在一个完整的APP内&#xff0c;有着多种的功能强大的系统&#xff0c;从这篇文章开始&a…

凡邦数据提供了多种API接口服务,包括淘宝API、1688API、京东API、拼多多API、抖音API等

API接口在现代数字化时代中扮演着至关重要的角色&#xff0c;它们连接着不同的系统、软件和服务&#xff0c;促进着数据流动和业务创新。API接口供应商则是提供这些接口的服务的商家&#xff0c;它们为开发者提供了方便快捷的渠道&#xff0c;以便快速获取和使用各种API接口。 …

一站式数字营销SaaS平台Klaviyo申请纳斯达克IPO上市

来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经 猛兽财经获悉&#xff0c;美国一站式数字营销SaaS平台Klaviyo近期已向美国证券交易委员会&#xff08;SEC&#xff09;提交招股书&#xff0c;申请在纳斯达克IPO上市&#xff0c;股票代码为&#xff08;KVYO&#xff09;&am…

用Windows自带的工具检查磁盘

例如用的是win7。打开计算机&#xff0c;点击选中某个磁盘&#xff0c;右键点击鼠标&#xff0c;出现弹出菜单&#xff1a; 选中属性&#xff0c;出现下面窗口&#xff1a; 点击“工具”这个tab页&#xff1a; 点击开始检查&#xff0c;出现如下界面&#xff1a; 两个复选框都选…

开源机密计算平台:蓬莱-OpenHarmony

演讲嘉宾 | 杜 东 回顾整理 | 廖 涛 排版校对 | 李萍萍 嘉宾简介 杜东&#xff0c;上海交通大学助理研究员。中国计算机学会CCF会员&#xff0c;ACM会员。研究兴趣为操作系统与体系结构、服务器无感知&#xff08;Serverless&#xff09;计算、系统安全。在包括ASPLOS、ISC…

【扩散模型 李宏毅B站教学以及基础代码运用】

李宏毅教学视频&#xff1a; Link1 B站DDPM公式推导以及代码实现&#xff1a; Link2 这个视频里面有论文里面的公式推导&#xff0c;并且1小时10分开始讲解实例代码。 文章目录 扩散模型概念&#xff1a;Diffusion Model工作原理&#xff1a;影像生成模型本质上的共同目标B站…

qt使用QCustomplot绘制cpu和内存使用率图

一、QCustomPlot介绍 QCustomPlot是一个开源的Qt C图表库&#xff0c;用于可视化数据。该库提供了多种类型的可定制的图表&#xff0c;包括散点图、线图、柱状图和等高线图等。它还支持自定义绘制&#xff0c;可以创建任意形状和大小的元素&#xff0c;并使其与其他元素交互。Q…

iPad电容笔贵吗?开学季比较好用的ipad手写笔

“ipad好买&#xff0c;但是ipad的配件不好买”&#xff0c;我相信很多人都会有这个问题&#xff0c;如果你想要购买像Apple Pencil这样的官方配件&#xff0c;却很难舍得下手&#xff0c;因为一款Apple Pencil的价格就已经接近1000元了。就像许多人不愿购买昂贵的苹果官方产品…

05 C/C++ 指针复杂类型说明 9月5日

目录 C语⾔ (1)数组 (2)指针 指针变量 空指针 (3)指针复杂类型 int a 0; int *p &a; int p[3];​​​​​​​ int *p[3]; int (*p)[3]; int **p; int p(int); int(*p)(int); C语⾔ (1)数组 当数据具有相同的数据类型&#xff1b;使用过程中需要保留原始…

在学习DNS的过程中给我的启发

在国内&#xff0c;关于DNS相关的话题一直络绎不绝&#xff0c;比如DNS根服务器为什么中国没有&#xff0c;还有Anycast BGP实现负载&#xff0c;为什么DNS只有13个&#xff0c;还有DNS over HTTPS 和 DNS over TLS的优劣等等问题&#xff0c;接下来我会找出几个一一说一下其中…

【Linux】- 一文秒懂shell编程

shell编程 1.1 Shell 是什么1.2 Shell 脚本的执行方式1.3 编写第一个 Shell 脚本2.1 Shell 的变量2.2 shell 变量的定义2.3 设置环境变量3.1 位置参数变量3.2 预定义变量4.1 运算符4.2 条件判断5.1 流程控制5.2 case 语句5.3 for 循环5.4 while 循环5.5 read基本语法6.1函数6.2…

API接口已经成为企业应用程序开发和管理的重要组成部分

API接口的价值 随着数字化时代的到来&#xff0c;API接口已经成为企业应用程序开发和管理的重要组成部分。API不仅是一种连接不同系统、提高数据流动性和促进协作的工具&#xff0c;而且还是一种重要的商业战略&#xff0c;可以为组织带来许多实际的价值。本文将探讨API接口的…

Android的本地数据

何为本地&#xff0c;即写完之后除非手动修改&#xff0c;否像嘎了一样在那固定死了 在实际安卓开发中&#xff0c;这种写死的概念必不可少&#xff0c;如控件的id&#xff0c;某一常量&#xff0c;Kotlin中的Val 当然&#xff0c;有些需求可能也会要求我们去写死数据&#x…

一文搞懂XaaS

云服务是指通过互联网按需提供给企业和客户的各种服务&#xff0c;大致可以分为IaaS、PaaS、SaaS三类&#xff0c;每一类又衍生出不同细分的云服务模式。本文介绍了当前已经提出的19种云服务模式&#xff0c;原文: The Comprehensive Concept of IaaS, PaaS, SaaS, AaaS, BaaS,…

基于STM32,TB6612,TCRT5000的简易红外循迹小车

提醒&#xff1a;本文章只叙述此小车相关大概内容&#xff08;如模块的设置&#xff0c;C语言基础实现等&#xff09;&#xff0c;单片机详细教学不涉及。 摘要 循迹小车是学习单片机的“地基”&#xff0c;它能够让初学者认识单片机内部硬件结构及其功能&#xff0c;熟悉单片机…

安装RabbitMQ的各种问题(包括已注册成windows服务后,再次重新安装,删除服务重新注册遇到的问题)

一、安装Erlang&#xff08;傻瓜式安装&#xff09; 安装完成之后&#xff0c;配置环境变量&#xff1a; 1.新建系统变量名为&#xff1a;ERLANG_HOME 变量值为erlang安装地址 2. 双击系统变量path&#xff0c;点击“新建”&#xff0c;将%ERLANG_HOME%\bin加入到path中。 …

学习笔记——Java入门第一季

1.1 Java的介绍与前景 Java语言最早期的制作者&#xff1a;James Gosling&#xff08;詹姆斯高斯林&#xff09; 1995年5月23日&#xff0c;Sun Microsystems公司宣布Java语言诞生。 1.2 Java的特性与版本 跨平台 开源&#xff08;开放源代码&#xff09; Java代码&#xff…