上次我们的标题中用了【超进化】这个词,从配置文件升级到用注解来进行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 getBean(Class requiredType) throws BeanException {List> 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 getBean(Class requiredType) throws BeanException {return getBeanFactory().getBean(requiredType);}
嗯,甩手掌柜了属于是。直接让BeanFactory帮他干活。
到这里,我们的注解处理就算完成了。不过这里还有一点要注意,因为我们扩充了 InstantiationAwareBeanPostProcessor
的内容,所以我们之前关于AOP相关的 DefaultAdvisorAutoProxyCreator
,也要增加相应的方法,做个默认处理就行。
对于这一套,我想大家经过了这么多次练习,已经逐渐的熟悉了。一旦牵扯到后置处理器,就会有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交给用户去配置是不合适的,所以我们就要想办法在Spring框架运行起来的时候,就把它们给注册好。我们选择的节点是 ClassPathBeanDefinitionScanner
。还记得这个类吗?是之前用来做包扫描的,我们在扫描方法的最后,将内部的Bean给注册进去。
public void doScan(String... basePackages) {for (String basePackage : basePackages) {Set 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配置文件
非常的完美,对吧,再也不用在这里一个一个的配置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