前言

上次我们的标题中用了【超进化】这个词,从配置文件升级到用注解来进行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;@Overridepublic 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();}@AllArgsConstructorprivate class PlaceholderResolvingStringValueResolver implements StringValueResolver {private final Properties properties;@Overridepublic 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);
    @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(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;@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeanException {return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeanException {return bean;}@Overridepublic Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeanException {return null;}@Overridepublic 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);}}@Overridepublic 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中进行实现。

    @Overridepublic <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中。

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

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

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

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

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

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

            //实例化beanbean = 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() {//注册处理注解的BeanPostProcessorregistry.registerBeanDefinition("autowiredAnnotationBeanPostProcessor", new BeanDefinition(AutowiredAnnotationBeanPostProcessor.class));//注册处理属性占位符的beanBeanDefinition 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;@Autowiredprivate 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));}}@Overridepublic void destroy() throws Exception {System.out.println("userService的destroy执行了");}@Overridepublic 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 {@Testpublic 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

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

  1. 【正点原子STM32连载】第五十四章 手写识别实验 摘自【正点原子】MiniPro STM32H750 开发指南_V1.1

    1)实验平台:正点原子MiniPro H750开发板 2)平台购买地址:https://detail.tmall.com/item.htm?id=677017430560 3)全套实验源码+手册+视频 ...

  2. 《深入理解 Spring Cloud 与微服务构建》第十四章 服务链路追踪 Spring Cloud Sleuth

    <深入理解 Spring Cloud 与微服务构建>第十四章 服务链路追踪 Spring Cloud Sleuth 文章目录 <深入理解 Spring Cloud 与微服务构建> ...

  3. 十年java架构师分享:我是这样手写Spring的

    人见人爱的 Spring 已然不仅仅只是一个框架了.如今,Spring 已然成为了一个生态.但深入了解 Spring 的却寥寥无几.这里,我带大家一起来看看,我是如何手写 Spring 的.我将结合对 ...

  4. 《Dreamweaver CS6 完全自学教程》笔记 第十四章:使用 CSS 设计网页

    文章目录 第十四章:使用 CSS 设计网页 14.1 CSS 样式表简介 14.2 CSS 的基本语法 14.3 伪类.伪元素以及样式表的层叠顺序 14.3.1 伪类和伪元素 14.3.2 样式表的层 ...

  5. 天堂向左,深圳往右 第十三章第十四章

    天堂向左,深圳往右 第十三章第十四章[@more@] 第十三章 周振兴是肖然见过的最严谨的人.此人一年四季打着领带,头发永远硬硬地顶在头上,绝不会有一根错乱,每天上班后都有个固定的程序:上厕所.擦桌子 ...

  6. 手写Spring DI依赖注入,嘿,你的益达!

    手写DI 提前实例化单例Bean DI分析 DI的实现 构造参数依赖 一:定义分析 二:定义一个类BeanReference 三:BeanDefinition接口及其实现类 四:DefaultBean ...

  7. 达芬奇密码 第七十四章

    达芬奇密码 第七十四章[@more@] 第七十四章 "你怎么不说话呢?"兰登注视着"猎鹰者"号机舱对面的索菲说. "太累了.还有这首诗,我怎么也看不明 ...

  8. 【正点原子FPGA连载】第三十四章RGB-LCD触摸屏实验 -摘自【正点原子】新起点之FPGA开发指南_V2.1

    1)实验平台:正点原子新起点V2开发板 2)平台购买地址:https://detail.tmall.com/item.htm?id=609758951113 2)全套实验源码+手册+视频下载地址:ht ...

  9. 数学:确定性的丧失---第十四章 数学向何处去

    发信人: paradax (秀树*冬眠中...), 信区: Philosophy 标  题: 数学:确定性的丧失(15) 发信站: 北大未名站 (2002年10月23日22:40:41 星期三), 转 ...

最新文章

  1. pytorch学习笔记(十二):详解 Module 类
  2. video怎么重新加载 vue_vue.js中vue-video-player中的怎么插入多个视频,视频可以同时播放的问题及解决办法...
  3. 小米KK:智能家居谁能破局?
  4. 您必须在sources.list中指定代码源_python如何从源代码构建lxml
  5. 做总账凭证FB50报错“错误调用功能模块 CHECK_PLANTS_ABROAD_ACTIVE”
  6. 前端学习(3105):react-hello-jsx语法规则
  7. mac 二进制安装mysql_在mac下安装mysql二进制分发版的方法(不是dmg的)
  8. NLP之路-实验nltk中的raw 和 words
  9. redis java操作
  10. 一、optimizer_trace介绍
  11. 史前技术:Mac使用SVN
  12. 疫情防控中小学开学错峰错时返校放学方案
  13. APP 基本框架设计
  14. 海马玩安卓模拟器-安装流程详解
  15. 维度打击,机器学习中的降维算法:ISOMAP MDS
  16. Java学习:从入门到精通week3
  17. OpenCV特征检测(三)SIFT,Surf及其引申的思考
  18. android 微信高仿,Android 高仿微信发朋友圈浏览图片效果(转)
  19. VS CODE Python 包路径报错解决方案could not be resolved
  20. python IO模块【一】:IO类

热门文章

  1. 真实的生活比戏剧更残酷
  2. HTTP协议报文解析
  3. EMBOSS的安装以及使用
  4. 荣誉丨国辰机器人荣获维科杯•OFweek 2021中国机器人行业年度优秀应用案例奖
  5. Mac锁屏的各种方法
  6. 《C++入门经典(第6版)》——2.2 程序的组成部分
  7. webview.addJavascriptInterface() doen not work on API 16+
  8. 瑞典留学硕士申请条件及流程
  9. Java:扑克牌发牌程序(除大小王)
  10. 论文阅读笔记:《PatchMatch Stereo - Stereo Matching with Slanted Support Windows》