Spring-Framework 源碼閱讀之AnnotationBeanUtils
Java程序員,就是要學會一個名字叫做“春”的東西,這玩意運用的非常的廣泛,現在如果你的業務系統或者軟件沒有在這個東西上開發,都不要意思拿出來。因為你更不上時代了。在平時的工作的中基本都是簡單的運用,沒有深入的了解內部的肌理。這次我一定可以滿滿的看完裏面的骨架。加油!加油!加油!
在之前我也看過一些講Spring的書籍,比如<<Spring揭秘>>,《Spring技術內幕》。大體知道了Spring的工作流程,但是還是有些迷茫。有一點一知半解的感覺。接下來我準備以自己一知半解的半桶水的知識去閱讀Spring的源碼。根據自己的模模糊糊的感覺和去網上搜索來幫助閱讀。這一次要地毯式的搜索閱讀。可是有時也會覺得Spring 框架這麽大,如此的閱讀是不是浪費時間。但是又有另一種想法,只要我們把每個類的作用都了解清楚了,每個類都自己寫一下單元測試。都跟進源碼閱讀。這樣等我大部分的內容都閱讀了,Spring的了解深度會更加清晰咯。
首先第一步就是下載Spring源碼,然後導入Idea中,如下圖所示。
Spring 最最最核心的地方就是Bean,所以我準備從spring-beans這個工程看起。首先第一個package: org.springframework.beans.annotation,這個包下就只有一個類AnnotationBeanUtils,該類就只有一個核心的方法,就是拷貝註解值到指定的類中。
public abstract class AnnotationBeanUtils { /** * Copy the properties of the supplied {@link Annotation} to the supplied target bean. * Any properties defined in {@code excludedProperties} will not be copied. * @param ann the annotation to copy from * @param bean the bean instance to copy to * @param excludedProperties the names of excluded properties, if any * @see org.springframework.beans.BeanWrapper */ public static void copyPropertiesToBean(Annotation ann, Object bean, String... excludedProperties) { copyPropertiesToBean(ann, bean,null, excludedProperties); } /** * Copy the properties of the supplied {@link Annotation} to the supplied target bean. * Any properties defined in {@code excludedProperties} will not be copied. * <p>A specified value resolver may resolve placeholders in property values, for example. * @param ann the annotation to copy from * @param bean the bean instance to copy to * @param valueResolver a resolve to post-process String property values (may be {@code null}) * @param excludedProperties the names of excluded properties, if any * @see org.springframework.beans.BeanWrapper */ public static void copyPropertiesToBean(Annotation ann, Object bean, @Nullable StringValueResolver valueResolver, String... excludedProperties) { Set<String> excluded = new HashSet<>(Arrays.asList(excludedProperties)); Method[] annotationProperties = ann.annotationType().getDeclaredMethods(); //獲取註解上的方法 BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(bean); //同過bean對象獲取bean的定義BeanDefinition for (Method annotationProperty : annotationProperties) { //遍歷方法 String propertyName = annotationProperty.getName(); //獲取方法名 if (!excluded.contains(propertyName) && bw.isWritableProperty(propertyName)) { Object value = ReflectionUtils.invokeMethod(annotationProperty, ann); //獲取註解方法上的值 if (valueResolver != null && value instanceof String) { value = valueResolver.resolveStringValue((String) value); //處理value的值,StringValueResolver的作用比如處理占位符${} } bw.setPropertyValue(propertyName, value);//把該值設置到bean定義上。 } } } }
從上面的解釋,應該非常清楚該工具類的作用,現在我們來寫一個例子來驗證。
package com.qee.beans.annotation; import java.lang.annotation.*; @Retention(RetentionPolicy.RUNTIME) @Documented public @interface FooAnnotation { String name(); int age(); }
package com.qee.beans.annotation; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Foo2Annotation { String accountId(); }
package com.qee.beans.annotation; @FooAnnotation(name = "xiao ming", age = 23) public class Foo { private String names; private int age; @Foo2Annotation(accountId = "123456") private String id; public String getNames() { return names; } public void setName(String names) { this.names = names; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getId() { return id; } public void setId(String id) { this.id = id; } public void setAccountId(String id){ setId(id); } }
package com.qee.beans.annotation; import org.springframework.beans.annotation.AnnotationBeanUtils; public class AnnotationBeanUtilsTest { public static void main(String[] args) throws NoSuchFieldException { testCopyProperties(); testCopyProperties2(); testCopyPropertiesWithIgnore(); } public static void testCopyProperties2() throws NoSuchFieldException { Foo2Annotation annotation = Foo.class.getDeclaredField("id").getAnnotation(Foo2Annotation.class); Foo foo = new Foo(); AnnotationBeanUtils.copyPropertiesToBean(annotation, foo); System.out.println(foo.getId()); } public static void testCopyProperties() { FooAnnotation annotation = Foo.class.getAnnotation(FooAnnotation.class); Foo foo = new Foo(); AnnotationBeanUtils.copyPropertiesToBean(annotation, foo); System.out.println("Name : " + annotation.name() + " " + foo.getNames()); System.out.println("Age : " + annotation.age() + " " + foo.getAge()); } public static void testCopyPropertiesWithIgnore() { FooAnnotation annotation = Foo.class.getAnnotation(FooAnnotation.class); Foo foo = new Foo(); foo.setName("Juergen Hoeller"); foo.setAge(30); AnnotationBeanUtils.copyPropertiesToBean(annotation, foo, "name", "age"); System.out.println("Name : " + annotation.name() + " " + foo.getNames()); System.out.println("Age : " + annotation.age() + " " + foo.getAge()); } }
從上面測試例子可以知道,AnnotationBeanUtils.copyPropertiesToBean把註解上的值拷貝給某個對象,只有某個對象有這個註解方法的setXX方法。並且如果該對象的某個屬性已經有值了,就不會在拷貝註解上的值到該屬性上。
從上面AnnotationBeanUtils的源碼上,我們知道引入了新的Spring對象--StringValueResolver,BeanWrapper,ReflectionUtils。在之後的源碼閱讀中,在進行解析。在這裏做一下紅色標記,哇哈哈,哇哈哈,哇哈哈!!!!!!
待看的,等看看到這些包的時候,在繼續分析下面的內容
org.springframework.util.StringValueResolver
org.springframework.beans.BeanWrapper
org.springframework.util.ReflectionUtils
Spring-Framework 源碼閱讀之AnnotationBeanUtils