手写Spring-第十四章-再次进化!用注解完成属性的注入

前言

上次我们的标题中用了【超进化】这个词,从配置文件升级到用注解来进行bean的注册,这确实可以称得上是超进化。但总觉得进化的不是那么完全,大概是从亚古兽进化到暴龙兽这样的程度(?)。因为我们还是在测试过程中,在配置文件中进行了一些Bean的配置。这些配置,不仅是为了测试两种Bean注册方式都可用,更多的是一种无奈之举。因为我们还没法用注解往bean中注册一些属性和依赖的bean。所以我们只能用原始的方式来注册。那么,这一章,我们就来完成这一块内容,让我们可以用注解完成所有的工作,彻底摆脱配置文件。

工程结构

├─src
│  ├─main
│  │  ├─java
│  │  │  └─com
│  │  │      └─akitsuki
│  │  │          └─springframework
│  │  │              ├─aop
│  │  │              │  │  AdvisedSupport.java
│  │  │              │  │  Advisor.java
│  │  │              │  │  BeforeAdvice.java
│  │  │              │  │  ClassFilter.java
│  │  │              │  │  MethodBeforeAdvice.java
│  │  │              │  │  MethodMatcher.java
│  │  │              │  │  Pointcut.java
│  │  │              │  │  PointcutAdvisor.java
│  │  │              │  │  TargetSource.java
│  │  │              │  │  
│  │  │              │  ├─aspect
│  │  │              │  │      AspectJExpressionPointcut.java
│  │  │              │  │      AspectJExpressionPointcutAdvisor.java
│  │  │              │  │  
│  │  │              │  └─framework
│  │  │              │      │  AopProxy.java
│  │  │              │      │  Cglib2AopProxy.java
│  │  │              │      │  JdkDynamicAopProxy.java
│  │  │              │      │  ProxyFactory.java
│  │  │              │      │  ReflectiveMethodInvocation.java
│  │  │              │      │  
│  │  │              │      ├─adapter
│  │  │              │      │      MethodBeforeAdviceInterceptor.java
│  │  │              │      │  
│  │  │              │      └─autoproxy
│  │  │              │              DefaultAdvisorAutoProxyCreator.java
│  │  │              │      
│  │  │              ├─beans
│  │  │              │  ├─exception
│  │  │              │  │      BeanException.java
│  │  │              │  │  
│  │  │              │  └─factory
│  │  │              │      │  Aware.java
│  │  │              │      │  BeanClassLoaderAware.java
│  │  │              │      │  BeanFactory.java
│  │  │              │      │  BeanFactoryAware.java
│  │  │              │      │  BeanNameAware.java
│  │  │              │      │  ConfigurableListableBeanFactory.java
│  │  │              │      │  DisposableBean.java
│  │  │              │      │  FactoryBean.java
│  │  │              │      │  HierarchicalBeanFactory.java
│  │  │              │      │  InitializingBean.java
│  │  │              │      │  ListableBeanFactory.java
│  │  │              │      │  PropertyPlaceholderConfigurer.java
│  │  │              │      │  
│  │  │              │      ├─annotation
│  │  │              │      │      Autowired.java
│  │  │              │      │      AutowiredAnnotationBeanPostProcessor.java
│  │  │              │      │      Qualifier.java
│  │  │              │      │      Value.java
│  │  │              │      │  
│  │  │              │      ├─config
│  │  │              │      │      AutowireCapableBeanFactory.java
│  │  │              │      │      BeanDefinition.java
│  │  │              │      │      BeanDefinitionRegistryPostProcessor.java
│  │  │              │      │      BeanFactoryPostProcessor.java
│  │  │              │      │      BeanPostProcessor.java
│  │  │              │      │      BeanReference.java
│  │  │              │      │      ConfigurableBeanFactory.java
│  │  │              │      │      DefaultSingletonBeanRegistry.java
│  │  │              │      │      InstantiationAwareBeanPostProcessor.java
│  │  │              │      │      PropertyValue.java
│  │  │              │      │      PropertyValues.java
│  │  │              │      │      SingletonBeanRegistry.java
│  │  │              │      │  
│  │  │              │      ├─support
│  │  │              │      │      AbstractAutowireCapableBeanFactory.java
│  │  │              │      │      AbstractBeanDefinitionReader.java
│  │  │              │      │      AbstractBeanFactory.java
│  │  │              │      │      BeanDefinitionReader.java
│  │  │              │      │      BeanDefinitionRegistry.java
│  │  │              │      │      CglibSubclassingInstantiationStrategy.java
│  │  │              │      │      DefaultListableBeanFactory.java
│  │  │              │      │      DisposableBeanAdapter.java
│  │  │              │      │      FactoryBeanRegistrySupport.java
│  │  │              │      │      InstantiationStrategy.java
│  │  │              │      │      SimpleInstantiationStrategy.java
│  │  │              │      │  
│  │  │              │      └─xml
│  │  │              │              XmlBeanDefinitionReader.java
│  │  │              │      
│  │  │              ├─context
│  │  │              │  │  ApplicationContext.java
│  │  │              │  │  ApplicationContextAware.java
│  │  │              │  │  ApplicationEvent.java
│  │  │              │  │  ApplicationEventPublisher.java
│  │  │              │  │  ApplicationListener.java
│  │  │              │  │  ConfigurableApplicationContext.java
│  │  │              │  │  
│  │  │              │  ├─annotation
│  │  │              │  │      ClassPathBeanDefinitionScanner.java
│  │  │              │  │      ClassPathScanningCandidateComponentProvider.java
│  │  │              │  │      Scope.java
│  │  │              │  │  
│  │  │              │  ├─event
│  │  │              │  │      AbstractApplicationEventMulticaster.java
│  │  │              │  │      ApplicationContextEvent.java
│  │  │              │  │      ApplicationEventMulticaster.java
│  │  │              │  │      ContextClosedEvent.java
│  │  │              │  │      ContextRefreshEvent.java
│  │  │              │  │      SimpleApplicationEventMulticaster.java
│  │  │              │  │  
│  │  │              │  └─support
│  │  │              │          AbstractApplicationContext.java
│  │  │              │          AbstractRefreshableApplicationContext.java
│  │  │              │          AbstractXmlApplicationContext.java
│  │  │              │          ApplicationContextAwareProcessor.java
│  │  │              │          ClasspathXmlApplicationContext.java
│  │  │              │  
│  │  │              ├─core
│  │  │              │  └─io
│  │  │              │          ClasspathResource.java
│  │  │              │          DefaultResourceLoader.java
│  │  │              │          FileSystemResource.java
│  │  │              │          Resource.java
│  │  │              │          ResourceLoader.java
│  │  │              │          UrlResource.java
│  │  │              │  
│  │  │              ├─stereotype
│  │  │              │      Component.java
│  │  │              │  
│  │  │              └─util
│  │  │                      ClassUtils.java
│  │  │                      StringValueResolver.java
│  │  │              
│  │  └─resources
│  └─test
│      ├─java
│      │  └─com
│      │      └─akitsuki
│      │          └─springframework
│      │              └─test
│      │                  │  ApiTest.java
│      │                  │  
│      │                  └─bean
│      │                          UserDao.java
│      │                          UserService.java
│      │                  
│      └─resources
│              application.yml
│              spring.xml

如果你真的看了这个长的不行的工程目录(笑),你就可以看到,我们熟悉的 @Autowired,终于来了。可以说,@Autowired就是Spring!它是Spring之魂!

我已经等不及了,快点端上来罢!三大注解!

这次,我们准备完成三个注解的内容:@Value、@Autowired、@Qualifier。这三个注解,想必大家已经熟悉的不能再熟悉了。我们这就来实现它们。

package com.akitsuki.springframework.beans.factory.annotation;

import java.lang.annotation.*;

/**
 * @author ziling.wang@hand-china.com
 * @date 2022/12/12 10:28
 */
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Value {

    String value();
}
package com.akitsuki.springframework.beans.factory.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author ziling.wang@hand-china.com
 * @date 2022/12/12 10:27
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD})
public @interface Autowired {
}

package com.akitsuki.springframework.beans.factory.annotation;

import java.lang.annotation.*;

/**
 * @author ziling.wang@hand-china.com
 * @date 2022/12/12 10:32
 */
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Qualifier {

    String value() default "";
}

注解本身没什么好说的,也不是很复杂,只是规定了注解可以加在哪些位置,以及有哪些属性而已。

接下来,我们按顺序来。首先,我们实现 @Value的功能。我们知道,这个注解,是用来给Bean中的属性注入值的。我们之前实现这个功能,都是在配置文件中,用属性来定义的。关于占位符的处理,我们上一章已经实现了。我们这一次要对其进行一些改造,加入对 @Value注解中,占位符的处理。

首先,我们要有一个字符串解析接口。这个接口专门用来解析处理字符串占位符

package com.akitsuki.springframework.util;

/**
 * 字符串解析接口
 * @author ziling.wang@hand-china.com
 * @date 2022/12/12 9:36
 */
public interface StringValueResolver {

    /**
     * 解析字符串
     * @param value
     * @return
     */
    String resolveStringValue(String value);
}

然后,我们就要对上一章实现的 PropertyPlaceholderConfigurer进行改造了。改造的内容呢,就是在原本的解析配置文件占位符并替换的基础上,将读取到的配置文件内容(被放在Properties中,还记得吗?),用字符串解析类包装起来,放到BeanFactory中备用。这么说可能会觉得有些乱,我们先来看代码。

package com.akitsuki.springframework.beans.factory;

import com.akitsuki.springframework.beans.exception.BeanException;
import com.akitsuki.springframework.beans.factory.annotation.Value;
import com.akitsuki.springframework.beans.factory.config.BeanDefinition;
import com.akitsuki.springframework.beans.factory.config.BeanFactoryPostProcessor;
import com.akitsuki.springframework.beans.factory.config.PropertyValue;
import com.akitsuki.springframework.beans.factory.config.PropertyValues;
import com.akitsuki.springframework.core.io.DefaultResourceLoader;
import com.akitsuki.springframework.core.io.Resource;
import com.akitsuki.springframework.util.StringValueResolver;
import lombok.AllArgsConstructor;

import java.io.IOException;
import java.util.Properties;

/**
 * 处理spring配置中的占位符${xxx},替换为真正的值
 * @author ziling.wang@hand-china.com
 * @date 2022/12/9 9:59
 */
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) {
        try {
            //原有逻辑省略,处理Bean定义中的占位符


            //添加字符串解析器,供解析@Value注解使用
            StringValueResolver resolver = new PlaceholderResolvingStringValueResolver(properties);
            beanFactory.addEmbeddedValueResolver(resolver);
        } catch (IOException e) {
            throw new BeanException("加载配置时出错", e);
        }
    }

    /**
     * 处理占位符
     * @param value
     * @param properties
     * @return
     */
    private String resolvePlaceholder(String value, Properties properties) {
        StringBuilder buffer = new StringBuilder(value);
        int startIdx = value.indexOf(DEFAULT_PLACEHOLDER_PREFIX);
        int stopIdx = value.indexOf(DEFAULT_PLACEHOLDER_SUFFIX);
        if (startIdx != -1 && stopIdx != -1 && startIdx < stopIdx) {
            String propKey = value.substring(startIdx + 2, stopIdx);
            String propVal = properties.getProperty(propKey);
            buffer.replace(startIdx, stopIdx + 1, propVal);
        }
        return buffer.toString();
    }

    @AllArgsConstructor
    private class PlaceholderResolvingStringValueResolver implements StringValueResolver {

        private final Properties properties;

        @Override
        public String resolveStringValue(String value) {
            //这种写法等效于this,但这样写能够体现出resolvePlaceholder这个方法是PropertyPlaceholderConfigurer这个类的
            return PropertyPlaceholderConfigurer.this.resolvePlaceholder(value, properties);
        }
    }
}

好像看起来还是有点复杂?首先我们定义了一个内部类,实现了我们上面的字符串解析接口。这个类的具体实现呢,其实也就是调用外部类的 resolvePlaceholder方法。这个方法的内容,也就是我们上次实现的对占位符的替换过程,这个核心逻辑是没有变的。然后我们在后置处理器的最后,注册了一个字符串处理器到BeanFactory中。这个过程是为了后面对 @Value注解进行处理的时候,能拿到这个处理器。

这里的 embeddedValueResolver之前可能没有见过,我们需要在 AbstractBeanFactory中维护一个 StringValueResolver类型的List,这里的 addEmbeddedValueResolver实际上也就是往这个List中添加一条。这个方法放在了 ConfigurableBeanFactory中进行定义,在 AbstractBeanFactory进行实现。同时,这个接口还增加了另外一个方法 resolveEmbeddedValue,就是真正处理的逻辑,也是在 AbstractBeanFactory中进行实现。


    /**
     * 添加一个StringValueResolver
     * @param valueResolver
     */
    void addEmbeddedValueResolver(StringValueResolver valueResolver);

    /**
     * 处理StringValue
     * @param value
     * @return
     */
    String resolveEmbeddedValue(String value);
    @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(value);
        }
        return result;
    }

逻辑还是比较简单的。我们接下来开始关注对上面的三个注解的具体处理。

对于这些注解的处理,我们自然也是放在后置处理器中。但是用哪个后置处理器呢?答案是我们前不久实现的 InstantiationAwareBeanPostProcessor。还记得这个处理器吗?之前是为了提供一种特殊的bean实例化过程,不走标准的实例化流程。这一次,我们要扩充它的功能,将对属性的处理,也添加到它的功能中。

package com.akitsuki.springframework.beans.factory.config;

import com.akitsuki.springframework.beans.exception.BeanException;

/**
 * @author ziling.wang@hand-china.com
 * @date 2022/12/6 15:46
 */
public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor {

    /**
     * bean实例化之前处理
     *
     * @param beanClass
     * @param beanName
     * @return
     * @throws BeanException
     */
    Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeanException;

    /**
     * 处理属性值
     * @param pvs
     * @param bean
     * @param beanName
     * @return
     * @throws BeanException
     */
    PropertyValues postProcessPropertyValues(PropertyValues pvs, Object bean, String beanName) throws BeanException;
}

接下来,我们就要实现我们的注解处理类了。

package com.akitsuki.springframework.beans.factory.annotation;

import cn.hutool.core.bean.BeanUtil;
import com.akitsuki.springframework.beans.exception.BeanException;
import com.akitsuki.springframework.beans.factory.BeanFactory;
import com.akitsuki.springframework.beans.factory.BeanFactoryAware;
import com.akitsuki.springframework.beans.factory.ConfigurableListableBeanFactory;
import com.akitsuki.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
import com.akitsuki.springframework.beans.factory.config.PropertyValues;
import com.akitsuki.springframework.util.ClassUtils;

import java.lang.reflect.Field;

/**
 * @author ziling.wang@hand-china.com
 * @date 2022/12/12 10:35
 */
public class AutowiredAnnotationBeanPostProcessor implements InstantiationAwareBeanPostProcessor, BeanFactoryAware {

    private ConfigurableListableBeanFactory beanFactory;

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

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

    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeanException {
        return null;
    }

    @Override
    public PropertyValues postProcessPropertyValues(PropertyValues pvs, Object bean, String beanName) throws BeanException {
        Class<?> clazz = bean.getClass();
        clazz = ClassUtils.isCglibProxyClass(clazz) ? clazz.getSuperclass() : clazz;
        Field[] fields = clazz.getDeclaredFields();
        for(Field field : fields) {
            resolveValueAnnotation(bean, field);
            resolveAutowiredAnnotation(bean, field);
        }
        return null;
    }

    /**
     * 处理@Value注解
     * @param bean
     * @param field
     */
    private void resolveValueAnnotation(Object bean, Field field) {
        Value valueAnnotation = field.getAnnotation(Value.class);
        if (null != valueAnnotation) {
            String value = valueAnnotation.value();
            value = beanFactory.resolveEmbeddedValue(value);
            BeanUtil.setFieldValue(bean, field.getName(), value);
        }
    }

    /**
     * 处理@Autowired和@Qualifier注解
     * @param bean
     * @param field
     */
    private void resolveAutowiredAnnotation(Object bean, Field field) {
        Autowired autowiredAnnotation = field.getAnnotation(Autowired.class);
        if (null != autowiredAnnotation) {
            Class<?> fieldType = field.getType();
            String dependBeanName;
            Qualifier qualifierAnnotation = field.getAnnotation(Qualifier.class);
            Object dependBean;
            if (null != qualifierAnnotation) {
                dependBeanName = qualifierAnnotation.value();
                dependBean = beanFactory.getBean(dependBeanName, fieldType);
            } else {
                dependBean = beanFactory.getBean(fieldType);
            }
            BeanUtil.setFieldValue(bean, field.getName(), dependBean);
        }
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeanException {
        this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
    }
}

挺长的,我们来分析。

首先前面三个方法,都是我们用不上,但是接口规定了的。所以我们也就返回一些默认值即可。要返回对象的,就直接返回传入的对象。而对于需要实例化对象的方法,则直接返回null。

我们的重点在 postProcessPropertyValues方法上。这个方法可以粗略的概括为:拿到bean的所有属性进行遍历,然后分别处理属性上的 @Value注解和 @Autowired注解。

先来看 @Value注解,可以看到这里调用了BeanFactory中的 resolveEmbeddedValue来处理,最后用反射重新设置进Bean中,还是比较简单的。

然后是 @Autowired注解,这个就稍微复杂一点,还包括了 @Qualifier的处理。我们概括一下内容,就是如果这个属性既有 @Autowired注解,也有 @Qualifier注解,则以 @Qualifier中配置的值作为beanName,去向BeanFactory索要Bean。如果只有 @Autowired注解,则直接通过Field的类型来查找bean。最后也是通过反射,将Bean进行注入。

这里多了按类型查找bean的方法,我们介绍一下。

这个方法我们新增在 BeanFactory接口中,在 DefaultListableBeanFactory中进行实现。

    @Override
    public <T> T getBean(Class<T> requiredType) throws BeanException {
        List<Map.Entry<String, BeanDefinition>> filteredDefinition = beanDefinitionMap.entrySet().stream()
                .filter(x -> requiredType.isAssignableFrom(x.getValue().getBeanClass()))
                .collect(Collectors.toList());
        if (1 == filteredDefinition.size()) {
            return getBean(filteredDefinition.get(0).getKey(), requiredType);
        }
        throw new BeanException(requiredType + "expect 1 single bean but found " + filteredDefinition.size() + " "
                + filteredDefinition.stream().map(Map.Entry::getKey).collect(Collectors.toList()));
    }

可以看到,其实本质上我们还是按照名称进行查找。只不过流程是从Bean定义中,按照类型过滤出对应的Bean定义,如果只有一条,那证明我们找到了,之后就是拿到这个Bean定义对应的名称,去调用原有的按名称getBean即可。如果没有找到,或者找到了不止一条,那我们就得抛异常了。

因为我们在 BeanFactory接口中新增了方法,那么我们同样实现经过多重继承实现了这个接口的Context,也要实现这个方法。最终,这个方法落实在了 AbstractApplicationContext中。

    @Override
    public <T> T getBean(Class<T> requiredType) throws BeanException {
        return getBeanFactory().getBean(requiredType);
    }

嗯,甩手掌柜了属于是。直接让BeanFactory帮他干活。

到这里,我们的注解处理就算完成了。不过这里还有一点要注意,因为我们扩充了 InstantiationAwareBeanPostProcessor的内容,所以我们之前关于AOP相关的 DefaultAdvisorAutoProxyCreator,也要增加相应的方法,做个默认处理就行。

织入!将注解的解析融合进Bean生命周期

对于这一套,我想大家经过了这么多次练习,已经逐渐的熟悉了。一旦牵扯到后置处理器,就会有Bean生命周期的织入。由此也可以看出,后置处理器真的是Spring中很重要的部分。我们很多的逻辑扩充,都可以通过它来实现,然后再插入到Bean生命周期的某个过程中。

那么这次的织入,我们要放在什么节点呢?答案是在bean实例化完成之后,设置属性之前。其实也很好理解,这个时候属性中还是一堆占位符,我们要把它们替换成真正的值。对于引用的Bean,现在也只是个字符串,甚至只是个注解,所以我们也要在这一步进行处理。好了,我们又要来折磨 AbstractAutowireCapableBeanFactory了。这回它的这个名字总算是有些能够理解了,不再是有名无实,不明所以,白白的挂着Autowire名号的一个类了。

            //实例化bean
            bean = createBeanInstance(beanDefinition, beanName, args);


            //设置bean属性之前,允许beanPostProcessor修改属性值
            applyBeanPostProcessorsBeforeApplyingPropertyValues(beanName, bean, beanDefinition);


            //设置bean属性
            applyPropertyValues(beanName, beanDefinition, bean);
            //初始化bean,执行beanPostProcessor的前置和后置方法
            initializeBean(beanName, bean, beanDefinition);
    protected void applyBeanPostProcessorsBeforeApplyingPropertyValues(String beanName, Object bean, BeanDefinition beanDefinition) {
        for(BeanPostProcessor processor : getBeanPostProcessors()) {
            if (processor instanceof InstantiationAwareBeanPostProcessor) {
                PropertyValues pvs = ((InstantiationAwareBeanPostProcessor) processor).postProcessPropertyValues(beanDefinition.getPropertyValues(), bean, beanName);
                if (null != pvs) {
                    for (PropertyValue pv : pvs.getPropertyValues()) {
                        beanDefinition.getPropertyValues().addPropertyValue(pv);
                    }
                }
            }
        }
    }

处理过程实在是有些乏善可陈,毕竟基本上后置处理器都是这么个流程,我们主要看这个过程插在哪里就可以了。

队长别开枪,是我!内部bean的注册

到这里其实我们的大体功能已经开发完毕了。但是在使用层面上还是有些问题。我们有一些内部bean,比如我们用来处理注解的后置处理器,还有用来处理占位符的bean等等。这些Bean交给用户去配置是不合适的,所以我们就要想办法在Spring框架运行起来的时候,就把它们给注册好。我们选择的节点是 ClassPathBeanDefinitionScanner。还记得这个类吗?是之前用来做包扫描的,我们在扫描方法的最后,将内部的Bean给注册进去。

 public void doScan(String... basePackages) {
        for (String basePackage : basePackages) {
            Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
            for (BeanDefinition beanDefinition : candidates) {
                String scope = resolveBeanScope(beanDefinition);
                if (StrUtil.isNotEmpty(scope)) {
                    beanDefinition.setScope(scope);
                }
                registry.registerBeanDefinition(determineBeanName(beanDefinition), beanDefinition);
            }
        }
        registerInnerBean();
    }

    /**
     * 注册Spring内部使用的bean
     */
    private void registerInnerBean() {
        //注册处理注解的BeanPostProcessor
        registry.registerBeanDefinition("autowiredAnnotationBeanPostProcessor"
                , new BeanDefinition(AutowiredAnnotationBeanPostProcessor.class));
        //注册处理属性占位符的bean
        BeanDefinition propertyPlaceholder = new BeanDefinition(PropertyPlaceholderConfigurer.class);
        PropertyValues propertyPlaceholderPv = new PropertyValues();
        propertyPlaceholderPv.addPropertyValue(new PropertyValue("location", "classpath:application.yml"));
        propertyPlaceholder.setPropertyValues(propertyPlaceholderPv);
        registry.registerBeanDefinition("propertyPlaceholderConfigurer", propertyPlaceholder);
    }

这样就完成了,用户可以直接使用注解和占位符,不需要额外的配置了。

解放双手,测试环节

终于,又一次来到了测试环节。这次我们要做的是测试注解的使用。大体上的测试类,和上一次没什么区别。不过这一次,我们要把UserService也用注解进行配置。

package com.akitsuki.springframework.test.bean;

import com.akitsuki.springframework.beans.factory.DisposableBean;
import com.akitsuki.springframework.beans.factory.InitializingBean;
import com.akitsuki.springframework.beans.factory.annotation.Autowired;
import com.akitsuki.springframework.beans.factory.annotation.Value;
import com.akitsuki.springframework.context.ApplicationContext;
import com.akitsuki.springframework.stereotype.Component;
import lombok.Getter;
import lombok.Setter;

/**
 * @author ziling.wang@hand-china.com
 * @date 2022/11/8 14:42
 */
@Getter
@Setter
@Component
public class UserService implements InitializingBean, DisposableBean {

    @Value("${dummyString}")
    private String dummyString;
    @Value("${dummyInt}")
    private int dummyInt;
    @Autowired
    private UserDao userDao;

    public void queryUserInfo(Long id) {
        System.out.println("dummyString:" + dummyString);
        System.out.println("dummyInt:" + dummyInt);
        String userName = userDao.queryUserName(id);
        if (null == userName) {
            System.out.println("用户未找到>_<");
        } else {
            System.out.println("用户名:" + userDao.queryUserName(id));
        }
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("userService的destroy执行了");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("userService的afterPropertiesSet执行了");
    }
}

嗯,已经很有Spring的味道了对吧,@Component,@Autowired,@Value这些注解一加上去,熟悉的感觉就回来了。

然后我们看看现在的spring.xml配置文件

<?xml version="1.0" encoding="utf-8" ?>
<beans>
    <component-scan base-package="com.akitsuki.springframework.test.bean" />
</beans>

非常的完美,对吧,再也不用在这里一个一个的配置bean了,这里现在只有关于包扫描路径的配置,可以说是极致的简洁。

最后再来看一下我们的主要测试类

package com.akitsuki.springframework.test;

import com.akitsuki.springframework.context.support.ClasspathXmlApplicationContext;
import com.akitsuki.springframework.test.bean.UserService;
import org.junit.Test;

/**
 * @author ziling.wang@hand-china.com
 * @date 2022/11/15 13:58
 */
public class ApiTest {

    @Test
    public void test() {
        ClasspathXmlApplicationContext context = new ClasspathXmlApplicationContext("classpath:spring.xml");
        context.registerShutdownHook();
        UserService userService = context.getBean("userService", UserService.class);
        userService.queryUserInfo(1L);
    }
}

依旧是老样子,没什么变化。

测试结果

执行UserDao的initMethod
userService的afterPropertiesSet执行了
dummyString:kamisama
dummyInt:114514
用户名:akitsuki
userService的destroy执行了

Process finished with exit code 0

嗯,功能都很正常,这次的测试也圆满完成了~

相关源码可以参考我的gitee:https://gitee.com/akitsuki-kouzou/mini-spring,这里对应的代码是mini-spring-14

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值