Small Spring系列七:annotation Injection(三)
秋水共長天一色 落霞與孤鶩齊飛。
概述
前兩章我們已經完成了使用 ASM
讀取 Annotation
、新增 SimpleMetadataReader
封裝了複雜的 Vister
、同時引入了 AnnotatedBeanDefinition
和 ScannedGenericBeanDefinition
表明註解掃描的 BeanDefinition
。本章我們來實現最後的 Field Injection
。
分析
@Component(value = "nioCoder") public class NioCoderService { @Autowired private AccountDao accountDao; @Autowired private ItemDao itemDao; }
要實現 Field Injection
我們需要根據 class
型別從 BeanFactory
中獲取一個物件然後注入。即需要在 BeanFactory
新增一個 BeanFactory.resolveDepency(Class type)
方法,如果 type
為 AccountDao
則找到(或建立)對應的例項並且返回。
這麼一看,貌似是可行的,但我們在使用 Spring
的 @Autowired
註解時發現,該註解可以應用於構造器注入、屬性注入和 setter
注入。 spring
提供了一個 DependencyDescriptor
來封裝 @Autowired
所有情況。
再有我們確定要把 resolveDependency
方法放置到 BeanFactory
中嗎? BeanFactory
是我們的一個頂級介面,我們不希望對外暴露太多的方法,所以我們新增一個 AutowireCapableBeanFactory
介面,在 AutowireCapableBeanFactory
中增加 resolveDependency
方法。 AutowireCapableBeanFactory
繼承 BeanFactory
。
DependencyDescriptor
封裝 @Autowired
所有情況。(暫只支援欄位注入,不支援方法注入)
package com.niocoder.beans.factory.config; import java.lang.reflect.Field; /** * 表明屬性注入或者方法注入 * * @author zhenglongfei */ public class DependencyDescriptor { /** * 屬性注入 */ private Field field; /** * 方法注入 */ //private MethodParameter methodParameter; private boolean required; public DependencyDescriptor(Field field, boolean required) { this.field = field; this.required = required; } public Class<?> getDependencyType() { if (this.field != null) { // 欄位的型別如 AccountDao ItemDao return field.getType(); } // TODO 方法注入不支援 throw new RuntimeException("only support field dependency"); } public boolean isRequired() { return this.required; } }
AutowireCapableBeanFactory
增加 resolveDependency(DependencyDescriptor descriptor)
方法,根據 field
返回對應的例項。
package com.niocoder.beans.factory.config; import com.niocoder.beans.factory.BeanFactory; /** * 表明註解 注入的beanFactory */ public interface AutowireCapableBeanFactory extends BeanFactory { /** * 根據欄位屬性的描述,獲得所對應的例項 * * @param descriptor * @return */ Object resolveDependency(DependencyDescriptor descriptor); }
DefaultBeanFactory
由實現 BeanFactory
改成實現 AutowireCapableBeanFactory
。
public class DefaultBeanFactory extends DefaultSingletonBeanRegistry implements AutowireCapableBeanFactory, BeanDefinitionRegistry { ...... @Override public Object resolveDependency(DependencyDescriptor descriptor) { Class<?> typeToMatch = descriptor.getDependencyType(); for (BeanDefinition bd : this.beanDefinitionMap.values()) { // 確保BeanDefinition 有Class物件,而不是class的字串 resolveBeanClass(bd); Class<?> beanClass = bd.getBeanClass(); if (typeToMatch.isAssignableFrom(beanClass)) { return this.getBean(bd.getId()); } } return null; } public void resolveBeanClass(BeanDefinition bd) { if (bd.hasBeanClass()) { return; } else { try { bd.resolveBeanClass(ClassUtils.getDefaultClassLoader()); } catch (ClassNotFoundException e) { throw new RuntimeException("can't load class" + bd.getBeanClassName()); } } } ...... }
GenericBeanDefinition
新增 beanClass
屬性,快取 bean
的 class
物件
public class GenericBeanDefinition implements BeanDefinition { private Class<?> beanClass; ...... @Override public Class<?> getBeanClass() { if (this.beanClass == null) { throw new IllegalStateException("Bean class name [" + this.getBeanClassName() + "] has not been resolve into an actual Class"); } return this.beanClass; } @Override public boolean hasBeanClass() { return this.beanClass != null; } @Override public Class<?> resolveBeanClass(ClassLoader beanClassLoader) throws ClassNotFoundException { String className = getBeanClassName(); if (className == null) { return null; } Class<?> resolvedClass = beanClassLoader.loadClass(className); this.beanClass = resolvedClass; return resolvedClass; } }
DependencyDescriptorTest
測試 DependencyDescriptor
/** * 測試DependencyDescriptor */ public class DependencyDescriptorTest { @Test public void testResolveDependency() throws Exception { DefaultBeanFactory factory = new DefaultBeanFactory(); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory); reader.loadBeanDefinition(new ClassPathResource("bean-v4.xml")); Field f = NioCoderService.class.getDeclaredField("accountDao"); DependencyDescriptor descriptor = new DependencyDescriptor(f, true); Object o = factory.resolveDependency(descriptor); Assert.assertTrue(o instanceof AccountDao); } }
程式碼下載
Field Injection
上面我們已經可以通過 class
型別來獲取 bean
的例項了,那麼怎麼才能實現自動注入呢?
首先我們需要一個類含有 targetClass
和一個集合的屬性列表 List<Class>
,每個集合中的元素呼叫各自的 inject(taget)
方法(反射)從而實現屬性注入。類圖如下:
InjectionElement
抽象類,表明需要注入的屬性欄位
package com.niocoder.beans.factory.annotation; import com.niocoder.beans.factory.config.AutowireCapableBeanFactory; import java.lang.reflect.Member; /** * @author zhenglongfei */ public abstract class InjectionElement { protected Member member; protected AutowireCapableBeanFactory factory; InjectionElement(Member member, AutowireCapableBeanFactory factory) { this.member = member; this.factory = factory; } /** * 屬性注入的抽象方法 * * @param target */ public abstract void inject(Object target); }
AutowiredFieldElement
繼承抽象類 InjectionElement
實現屬性注入
package com.niocoder.beans.factory.annotation; import com.niocoder.beans.factory.BeanCreationException; import com.niocoder.beans.factory.config.AutowireCapableBeanFactory; import com.niocoder.beans.factory.config.DependencyDescriptor; import com.niocoder.util.*; import java.lang.reflect.Field; /** * @author zhenglongfei */ public class AutowiredFieldElement extends InjectionElement { boolean required; public AutowiredFieldElement(Field f, boolean required, AutowireCapableBeanFactory factory) { super(f, factory); this.required = required; } public Field getField() { return (Field) this.member; } @Override public void inject(Object target) { Field field = this.getField(); try { DependencyDescriptor desc = new DependencyDescriptor(field, this.required); Object value = factory.resolveDependency(desc); if (value != null) { ReflectionUtils.makeAccessible(field); field.set(target, value); } } catch (Throwable e) { throw new BeanCreationException("could not autowire field " + field, e); } } }
InjectionMetadata
將 targetClass
和 List<InjectionElement>
結合,從而實現屬性注入
package com.niocoder.beans.factory.annotation; import java.util.List; /** * @author zhenglongfei */ public class InjectionMetadata { private final Class<?> targetClass; private List<InjectionElement> injectionElements; public InjectionMetadata(Class<?> targetClass, List<InjectionElement> injectionElements) { this.targetClass = targetClass; this.injectionElements = injectionElements; } public List<InjectionElement> getInjectionElements() { return injectionElements; } public void inject(Object target) { if (injectionElements == null || injectionElements.isEmpty()) { return; } for (InjectionElement ele : injectionElements) { ele.inject(target); } } }
InjectionMetadataTest
測試屬性注入
package com.niocoder.test.v4; import com.niocoder.beans.factory.annotation.AutowiredFieldElement; import com.niocoder.beans.factory.annotation.InjectionElement; import com.niocoder.beans.factory.annotation.InjectionMetadata; import com.niocoder.beans.factory.support.DefaultBeanFactory; import com.niocoder.beans.factory.xml.XmlBeanDefinitionReader; import com.niocoder.core.io.ClassPathResource; import com.niocoder.dao.v4.AccountDao; import com.niocoder.dao.v4.ItemDao; import com.niocoder.service.v4.NioCoderService; import org.junit.Assert; import org.junit.Test; import java.lang.reflect.Field; import java.util.LinkedList; public class InjectionMetadataTest { @Test public void testInjection() throws Exception { DefaultBeanFactory factory = new DefaultBeanFactory(); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory); reader.loadBeanDefinition(new ClassPathResource("bean-v4.xml")); Class<?> clz = NioCoderService.class; LinkedList<InjectionElement> elements = new LinkedList<InjectionElement>(); { Field f = NioCoderService.class.getDeclaredField("accountDao"); InjectionElement injectionElement = new AutowiredFieldElement(f, true, factory); elements.add(injectionElement); } { Field f = NioCoderService.class.getDeclaredField("itemDao"); InjectionElement injectionElement = new AutowiredFieldElement(f, true, factory); elements.add(injectionElement); } InjectionMetadata metadata = new InjectionMetadata(clz, elements); NioCoderService nioCoderService = new NioCoderService(); metadata.inject(nioCoderService); Assert.assertTrue(nioCoderService.getAccountDao() instanceof AccountDao); Assert.assertTrue(nioCoderService.getItemDao() instanceof ItemDao); } }
程式碼下載
自動構建InjectionMetadata
以上我們只是通過手動的將 NioCoderService
轉變成了 InjectionMetadata
並且呼叫 inject
方法,從而實現了 Field Injection
,我們需要一個類來自動幫我們處理這些操作。
AutowiredAnnotationBeanPostProcessor
用於自動構建 InjectionMetadata
public class AutowiredAnnotationBeanPostProcessor{ private AutowireCapableBeanFactory beanFactory; private String requiredParameterName = "required"; private boolean requiredParameterValue = true; /** * 儲存需要判斷的註解資訊 */ private final Set<Class<? extends Annotation>> autowiredAnnotationTypes = new LinkedHashSet<>(); /** * 新增Autowired註解 */ public AutowiredAnnotationBeanPostProcessor() { this.autowiredAnnotationTypes.add(Autowired.class); } /** * 給定class物件構建InjectionMetadata * @param clazz * @return */ public InjectionMetadata buildAutowiringMetadata(Class<?> clazz) { List<InjectionElement> elements = new LinkedList<>(); Class<?> targetClass = clazz; do { List<InjectionElement> currElements = new LinkedList<>(); for (Field field : targetClass.getDeclaredFields()) { Annotation ann = findAutowiredAnnotation(field); if (ann != null) { if (Modifier.isStatic(field.getModifiers())) { continue; } boolean required = determineRequiredStatus(ann); currElements.add(new AutowiredFieldElement(field, required, beanFactory)); } } for (Method method : targetClass.getDeclaredMethods()) { //TODO } elements.addAll(0, currElements); targetClass = targetClass.getSuperclass(); } while (targetClass != null && targetClass != Object.class); return new InjectionMetadata(clazz, elements); } /** * 判斷是否是required是否必須 * * @param ann * @return */ private boolean determineRequiredStatus(Annotation ann) { try { Method method = ReflectionUtils.findMethod(ann.annotationType(), this.requiredParameterName); if (method == null) { // Annotations like @Inject and @Value don't have a method (attribute) named "required" // -> default to required status return true; } return (this.requiredParameterValue == (Boolean) ReflectionUtils.invokeMethod(method, ann)); } catch (Exception ex) { // An exception was thrown during reflective invocation of the required attribute // -> default to required status return true; } } /** * 判斷屬性是否存在Autowired 註解 * * @param field * @return */ private Annotation findAutowiredAnnotation(Field field) { for (Class<? extends Annotation> type : this.autowiredAnnotationTypes) { Annotation ann = AnnotationUtils.getAnnotation(field, type); if (ann != null) { return ann; } } return null; } public void setBeanFactory(ConfigurableBeanFactory beanFactory) { this.beanFactory = beanFactory; } }
AutowiredAnnotationProcessorTest
測試 AutowiredAnnotationProcessor
public class AutowiredAnnotationProcessorTest { AccountDao accountDao = new AccountDao(); ItemDao itemDao = new ItemDao(); DefaultBeanFactory beanFactory = new DefaultBeanFactory() { @Override public Object resolveDependency(DependencyDescriptor descriptor) { if (descriptor.getDependencyType().equals(AccountDao.class)) { return accountDao; } if (descriptor.getDependencyType().equals(ItemDao.class)) { return itemDao; } throw new RuntimeException("can't support types except AccountDao and ItemDao"); } }; @Test public void testGetInjectionMetadata() { AutowiredAnnotationBeanPostProcessor processor = new AutowiredAnnotationBeanPostProcessor(); processor.setBeanFactory(beanFactory); InjectionMetadata injectionMetadata = processor.buildAutowiringMetadata(NioCoderService.class); List<InjectionElement> elements = injectionMetadata.getInjectionElements(); Assert.assertEquals(2, elements.size()); assertFieldExists(elements,"accountDao"); assertFieldExists(elements,"itemDao"); NioCoderService nioCoderService = new NioCoderService(); injectionMetadata.inject(nioCoderService); Assert.assertTrue(nioCoderService.getAccountDao() instanceof AccountDao); Assert.assertTrue(nioCoderService.getItemDao() instanceof ItemDao); } private void assertFieldExists(List<InjectionElement> elements, String fieldName) { for (InjectionElement ele : elements) { AutowiredFieldElement fieldElement = (AutowiredFieldElement) ele; Field field = fieldElement.getField(); if (field.getName().equals(fieldName)) { return; } } Assert.fail(fieldName + "does not exist!"); } }
完善Field Injection
至此我們已經完成了 Field Injection
90%的工作,剩下要考慮的就是應該在什麼時候呼叫類的這些方法?
所以我們要考慮一下 bean
的生命週期,關於 bean
的生命週期,可以看下圖
參考 Bean的生命週期
我們需要找 InstantiationAwareBeanPostProcessor.postProcessPropertyValues()
實現 Autowired
註解。關於 BeanPostProcessor
的類圖如下:
BeanPostProcessor
bean
初始化之前和初始化之後的處理器操作
package com.niocoder.beans.factory.config; import com.niocoder.beans.BeansException; /** * bean的後置處理器 * * @author zhenglongfei */ public interface BeanPostProcessor { /** * 初始化之前的操作 * * @param bean * @param beanName * @return * @throws BeansException */ default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } /** * 初始化之後的操作 * * @param bean * @param beanName * @return * @throws BeansException */ default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { return bean; } }
InstantiationAwareBeanPostProcessor
bean
例項化之前和例項化之後的處理器操作
package com.niocoder.beans.factory.config; import com.niocoder.beans.BeansException; /** * 例項化的後處理器 * * @author zhenglongfei */ public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor { /** * 例項化之前 * * @param beanClass * @param beanName * @return * @throws BeansException */ default Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException { return null; } /** * 例項化之後 * * @param bean * @param beanName * @return * @throws BeansException */ default boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException { return true; } /** * @param bean * @param beanName * @throws BeansException */ default void postProcessPropertyValues(Object bean, String beanName) throws BeansException { } }
AutowiredAnnotationBeanPostProcessor
實現 InstantiationAwareBeanPostProcessor
,在 postProcessPropertyValues
方法實現屬性注入
/** * 註解注入的後處理器 * * @author zhenglongfei */ public class AutowiredAnnotationBeanPostProcessor implements InstantiationAwareBeanPostProcessor { ...... @Override public void postProcessPropertyValues(Object bean, String beanName) throws BeansException { InjectionMetadata metadata = buildAutowiringMetadata(bean.getClass()); try { metadata.inject(bean); } catch (Throwable ex) { throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex); } } }
ConfigurableBeanFactory
新增 ConfigurableBeanFactory
來處理 BeanPostProcessor
package com.niocoder.beans.factory.config; import java.util.List; /** * 可配置的beanFactory * * @author zhenglongfei */ public interface ConfigurableBeanFactory extends AutowireCapableBeanFactory { /** * 增加BeanPostProcessor * * @param postProcessor */ void addBeanPostProcessor(BeanPostProcessor postProcessor); /** * 獲取 BeanPostProcessor * * @return */ List<BeanPostProcessor> getBeanPostProcessors(); }
DefaultBeanFactory
DefaultBeanFactory
從實現 AutowireCapableBeanFactory
修改為 ConfigurableBeanFactory
,增加 beanPostProcessors
屬性, 在 populateBean
方法時處理 BeanPostProcessor
。
/** * BeanFactory的預設實現類 * * @author zhenglongfei */ public class DefaultBeanFactory extends DefaultSingletonBeanRegistry implements ConfigurableBeanFactory, BeanDefinitionRegistry { /** * 存放BeanPostProcessor */ private List<BeanPostProcessor> beanPostProcessors = new ArrayList<>(); private void populateBean(BeanDefinition bd, Object bean) { for (BeanPostProcessor postProcessor : this.getBeanPostProcessors()) { if (postProcessor instanceof InstantiationAwareBeanPostProcessor) { ((InstantiationAwareBeanPostProcessor) postProcessor).postProcessPropertyValues(bean, bd.getId()); } } ...... } @Override public void addBeanPostProcessor(BeanPostProcessor postProcessor) { this.beanPostProcessors.add(postProcessor); } @Override public List<BeanPostProcessor> getBeanPostProcessors() { return this.beanPostProcessors; } }
AbstractApplicationContext
構造方法時註冊 AutowiredAnnotationBeanPostProcessor
實現註解注入
public abstract class AbstractApplicationContext implements ApplicationContext { private DefaultBeanFactory factory = null; public AbstractApplicationContext(String configFile) { factory = new DefaultBeanFactory(); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory); Resource resource = this.getResourceByPath(configFile); reader.loadBeanDefinition(resource); registerBeanPostProcessors(factory); } /** * 具體由子類實現 * * @param configFile * @return */ protected abstract Resource getResourceByPath(String configFile); @Override public Object getBean(String beanId) { return factory.getBean(beanId); } protected void registerBeanPostProcessors(ConfigurableBeanFactory beanFactory) { AutowiredAnnotationBeanPostProcessor processor = new AutowiredAnnotationBeanPostProcessor(); processor.setBeanFactory(factory); beanFactory.addBeanPostProcessor(processor); } }
ApplicationContextTestV4
測試註解注入
/** * */ public class ApplicationContextTestV4 { @Test public void testGetBeanProperty() { ApplicationContext ctx = new ClassPathXmlApplicationContext("bean-v4.xml"); NioCoderService nioCoder = (NioCoderService) ctx.getBean("nioCoder"); Assert.assertNotNull(nioCoder.getAccountDao()); Assert.assertNotNull(nioCoder.getItemDao()); } }