最近在看springMvc的源碼,看到了該框架的注入註解的部分覺的有點吃力,可能仍是對註解的方面的知識還認識的不夠深入,因此特地去學習註解方面的知識。因爲本人也是抱着學習的態度來閱讀源碼,若文章在表述和代碼方面若有不妥之處,歡迎批評指正。留下你的腳印,歡迎評論!但願能互相學習。java
1,首先定義三個經常使用的註解Service,Autowired,Contrller;(主要的解釋都在代碼中有,在這裏就很少陳述)web
Service:spring
package com.lishun.Annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /*Description: * @Target:指定註解的使用範圍(指的是,在哪些類型可使用該註解:Service註解只能在類,接口(包括註解類型)或enum等使用) * 可選值: * 可選的值在枚舉類 ElemenetType 中,包括: ElemenetType.CONSTRUCTOR 構造器聲明 ElemenetType.FIELD 域聲明(包括 enum 實例) ElemenetType.LOCAL_VARIABLE 局部變量聲明 ElemenetType.ANNOTATION_TYPE 做用於註解量聲明 ElemenetType.METHOD 方法聲明 ElemenetType.PACKAGE 包聲明 ElemenetType.PARAMETER 參數聲明 ElemenetType.TYPE 類,接口(包括註解類型)或enum聲明 * */ @Target(ElementType.TYPE) /*Description: * @Retention :表示在什麼級別保存該註解信息 * 可選的參數值在枚舉類型 RetentionPolicy 中,包括: RetentionPolicy.SOURCE 註解將被編譯器丟棄 RetentionPolicy.CLASS 註解在class文件中可用,但會被VM丟棄 RetentionPolicy.RUNTIME VM將在運行期也保留註釋,所以能夠經過反射機制讀取註解的信息。 * */ @Retention(RetentionPolicy.RUNTIME) /*@Documented 將此註解包含在 javadoc 中 ,它表明着此註解會被javadoc工具提取成文檔。 * 在doc文檔中的內容會由於此註解的信息內容不一樣而不一樣。至關與@see,@param 等。 * */ @Documented public @interface Service { /* @interface用來聲明一個註解,其中的每個方法其實是聲明瞭一個配置參數。 * 方法的名稱就是參數的名稱,返回值類型就是參數的類型(返回值類型只能是基本類型、Class、String、enum)。 * 能夠經過default來聲明參數的默認值。 */ String value() default "this is service annotation"; }
Autowired:數據庫
package com.lishun.Annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.METHOD,ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) public @interface Autowired { public String value() default "no description"; }
Contrller:設計模式
package com.lishun.Annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Contrller { String value() default "this is contrller annotation"; }
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------網絡
2:javaBean數據池-BeanFactory:主要存放含有註解的類;框架
package com.lishun.factory; import java.util.HashMap; import java.util.Map; /** * Description:存放全部bean的數據池 * @author lishun * @since 2015-09-10 */ public class BeanFactory { private static Map<String, Object> map = new HashMap<String, Object>(); public static void addBean(String beanName, Object bean) { map.put(beanName, bean); } public static Object getBean(String beanName) throws Exception { Object o = map.get(beanName); if (o != null) { return o; } else { throw new Exception("未注入的類型:" + beanName); } } public static Boolean containsBean(String beanName){ return map.containsKey(beanName); } }
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ide
3:編寫處理註解的核心代碼(這裏涉及的主要知識是反射,若是反射知識不夠熟練的話建議先學習反射方面的知識),主要涉及的兩個類是註解驅動(AnnotationDriven)和註解掃描類(PackUtils-這個類主要的是掃描包名下全部的類(如com.lishun,就是掃描該包下全部的類),代碼主要是來自網絡)工具
AnnotationDriven:學習
package com.lishun.utils; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.util.List; import com.lishun.Annotation.Autowired; import com.lishun.Annotation.Contrller; import com.lishun.Annotation.Service; import com.lishun.factory.BeanFactory; /** * Description:注入驅動類,全部的註解注入都在這裏實現(這裏只實現了經過類型來注入值,其餘方式沒實現,其實代碼都是差很少了,有興趣的能夠自行腦補) * @author lishun * */ public class AnnotationDriven { public static void annotationDriven(String packName) throws Exception { //注入Service和Contrller List<Class<?>> classSaveServicePaths = PackUtils .getClassListByAnnotation(packName, Service.class); List<Class<?>> classSaveContrllerPaths = PackUtils .getClassListByAnnotation(packName, Contrller.class); saveBean(classSaveServicePaths); saveBean(classSaveContrllerPaths); //注入Autowired List<Class<?>> classInjectPaths = PackUtils.getClassListByAnnotation( packName, Autowired.class); inject(classInjectPaths); } private static void saveBean(List<Class<?>> classSavePaths) throws InstantiationException, IllegalAccessException { for (Class<?> classPath : classSavePaths) { try { Class c = Class.forName(classPath.getName()); Object o = c.newInstance(); //掃描的到的含有註解的類實例化後保存在池中 BeanFactory.addBean(classPath.getName(), o); } catch (ClassNotFoundException e) { e.printStackTrace(); } } } private static void inject(List<Class<?>> classInjectPaths) throws Exception { Object o = null; for (Class<?> classInjectPath : classInjectPaths) { Class c = Class.forName(classInjectPath.getName()); //判斷存放bean的池中是否存在該bean if (BeanFactory.containsBean(classInjectPath.getName())) { o = BeanFactory.getBean(classInjectPath.getName()); } else { o = c.newInstance(); } Field[] fields = c.getDeclaredFields(); for (Field field : fields) { Annotation[] annotations = field.getAnnotations(); for (Annotation annotation : annotations) { // 判斷是不是經過類型註解注入 if (annotation instanceof Autowired) { Class classField = field.getType(); Object clazz = BeanFactory .getBean(classField.getName()); field.set(o, clazz); BeanFactory.addBean(classInjectPath.getName(), o); } } } } } }
PackUtils:
package com.lishun.utils; import java.io.File; import java.io.FileFilter; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.net.JarURLConnection; import java.net.URL; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; import java.util.jar.JarEntry; import java.util.jar.JarFile; /** * Description:掃描指定包工具類的註解 * @author lishun * @since 2015-09-10 */ public class PackUtils { public static List<Class<?>> getClassList(String packageName, boolean isRecursive) { List<Class<?>> classList = new ArrayList<Class<?>>(); try { Enumeration<URL> urls = Thread.currentThread().getContextClassLoader().getResources(packageName.replaceAll("\\.", "/")); while (urls.hasMoreElements()) { URL url = urls.nextElement(); if (url != null) { String protocol = url.getProtocol(); if (protocol.equals("file")) { String packagePath = url.getPath(); addClass(classList, packagePath, packageName, isRecursive); } else if (protocol.equals("jar")) { JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection(); JarFile jarFile = jarURLConnection.getJarFile(); Enumeration<JarEntry> jarEntries = jarFile.entries(); while (jarEntries.hasMoreElements()) { JarEntry jarEntry = jarEntries.nextElement(); String jarEntryName = jarEntry.getName(); if (jarEntryName.endsWith(".class")) { String className = jarEntryName.substring(0, jarEntryName.lastIndexOf(".")).replaceAll("/", "."); if (isRecursive || className.substring(0, className.lastIndexOf(".")).equals(packageName)) { classList.add(Class.forName(className)); } } } } } } } catch (Exception e) { e.printStackTrace(); } return classList; } // 獲取指定包名下的全部類(可根據註解進行過濾) public static List<Class<?>> getClassListByAnnotation(String packageName, Class<? extends Annotation> annotationClass) { List<Class<?>> classList = new ArrayList<Class<?>>(); try { Enumeration<URL> urls = Thread.currentThread().getContextClassLoader().getResources(packageName.replaceAll("\\.", "/")); while (urls.hasMoreElements()) { URL url = urls.nextElement(); if (url != null) { String protocol = url.getProtocol(); if (protocol.equals("file")) { String packagePath = url.getPath(); addClassByAnnotation(classList, packagePath, packageName, annotationClass); } else if (protocol.equals("jar")) { JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection(); JarFile jarFile = jarURLConnection.getJarFile(); Enumeration<JarEntry> jarEntries = jarFile.entries(); while (jarEntries.hasMoreElements()) { JarEntry jarEntry = jarEntries.nextElement(); String jarEntryName = jarEntry.getName(); if (jarEntryName.endsWith(".class")) { String className = jarEntryName.substring(0, jarEntryName.lastIndexOf(".")).replaceAll("/", "."); Class<?> cls = Class.forName(className); if (cls.isAnnotationPresent(annotationClass)) { classList.add(cls); } } } } } } } catch (Exception e) { e.printStackTrace(); } return classList; } private static void addClass(List<Class<?>> classList, String packagePath, String packageName, boolean isRecursive) { try { File[] files = getClassFiles(packagePath); if (files != null) { for (File file : files) { String fileName = file.getName(); if (file.isFile()) { String className = getClassName(packageName, fileName); classList.add(Class.forName(className)); } else { if (isRecursive) { String subPackagePath = getSubPackagePath(packagePath, fileName); String subPackageName = getSubPackageName(packageName, fileName); addClass(classList, subPackagePath, subPackageName, isRecursive); } } } } } catch (Exception e) { e.printStackTrace(); } } private static File[] getClassFiles(String packagePath) { return new File(packagePath).listFiles(new FileFilter() { @Override public boolean accept(File file) { return (file.isFile() && file.getName().endsWith(".class")) || file.isDirectory(); } }); } private static String getClassName(String packageName, String fileName) { String className = fileName.substring(0, fileName.lastIndexOf(".")); if (!packageName.equals("")) { className = packageName + "." + className; } return className; } private static String getSubPackagePath(String packagePath, String filePath) { String subPackagePath = filePath; if (!packagePath.equals("")) { subPackagePath = packagePath + "/" + subPackagePath; } return subPackagePath; } private static String getSubPackageName(String packageName, String filePath) { String subPackageName = filePath; if (!packageName.equals("")) { subPackageName = packageName + "." + subPackageName; } return subPackageName; } private static void addClassByAnnotation(List<Class<?>> classList, String packagePath, String packageName, Class<? extends Annotation> annotationClass) { try { File[] files = getClassFiles(packagePath); if (files != null) { for (File file : files) { String fileName = file.getName(); if (file.isFile()) { String className = getClassName(packageName, fileName); Class<?> cls = Class.forName(className); if (cls.isAnnotationPresent(annotationClass)) { classList.add(cls); } Field[] fields=cls.getFields(); for (Field field : fields) { if(field.isAnnotationPresent(annotationClass)){ classList.add(cls); } } } else { String subPackagePath = getSubPackagePath(packagePath, fileName); String subPackageName = getSubPackageName(packageName, fileName); addClassByAnnotation(classList, subPackagePath, subPackageName, annotationClass); } } } } catch (Exception e) { e.printStackTrace(); } } }
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
4 最後編寫平時使用的設計模式來測試註解(Dao,Service,Contrller)【這裏主要是爲測試註解的注入,因此沒有使用實際的使用數據庫數據,側重點不在這裏】
Dao
package com.lishun.Dao; import com.lishun.Annotation.Service; @Service public class UserDao { public void run(){ System.out.println("測試成功"); } }
Service:
package com.lishun.Service; import com.lishun.Annotation.Autowired; import com.lishun.Annotation.Service; import com.lishun.Dao.UserDao; @Service public class UserService { @Autowired public UserDao userDao; public void run(){ userDao.run(); } }
Controller:
package com.lishun.controller; import com.lishun.Annotation.Autowired; import com.lishun.Annotation.Contrller; import com.lishun.Service.UserService; @Contrller public class UserContrller { @Autowired public UserService userService; public void login(){ userService.run(); } }
測試入口
package com.lishun.t; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.Arrays; import java.util.List; import org.junit.Test; import com.lishun.Annotation.Autowired; import com.lishun.Annotation.Contrller; import com.lishun.Annotation.Service; import com.lishun.Dao.UserDao; import com.lishun.Service.UserService; import com.lishun.controller.UserContrller; import com.lishun.factory.BeanFactory; import com.lishun.utils.AnnotationDriven; import com.lishun.utils.PackUtils; public class test { @Test public void main() throws Exception { //啓動時根據須要掃描的包名,來注入含有註解的類的字段值 AnnotationDriven.annotationDriven("com.lishun"); //這裏至關於web的訪問一次controller的一次請求 UserContrller user = (UserContrller) BeanFactory .getBean("com.lishun.controller.UserContrller"); user.login(); } }
最後運行,
控制檯輸出:測試成功
因爲本人水平有限,若文章在表述和代碼方面若有不妥之處,歡迎批評指正。留下你的腳印,歡迎評論!但願能互相學習。須要源碼的留下郵箱