一塊兒寫框架-Ioc內核容器的實現-基礎功能-ComponentScan(四)

功能說明

該步驟實現的功能包括:java

1. 啓動程序時,將@ComponentScan加載的類,建立對象並放在容器裏面。框架

2. 經過ApplicatoinContext的getBean()方法得到容器裏面的對象。 (放在下一篇文實現)ide

實現步驟

1.定義一個掃描註解@ComponentScan函數

 

 1 package ioc.core.annotation;
 2 
 3 import java.lang.annotation.Documented;
 4 import java.lang.annotation.ElementType;
 5 import java.lang.annotation.Retention;
 6 import java.lang.annotation.RetentionPolicy;
 7 import java.lang.annotation.Target;
 8 //表示用於運行時的註解
 9 @Retention(RetentionPolicy.RUNTIME)
10 //表示只能在類或者接口的上面使用
11 @Target(value=ElementType.TYPE)
12 @Documented
13 public @interface ComponentScan {
14     
15     /**
16      * 聲明一個註解屬性用於接收掃描的包路徑
17      * @return
18      */
19     String[] basePackages() default {} ;
20 
21 }

 

2.定義一個@Configuration標識配置類工具

package ioc.core.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;
/**
 * 標識配置類註解的定義
 * @author ranger
 *
 */
//表示用於運行時的註解
@Retention(RetentionPolicy.RUNTIME)
//表示只能在類或者接口的上面使用
@Target(value=ElementType.TYPE)
@Documented
public @interface Configuration {

}

 

 

 

2.定義容器 Context接口測試

 

 1 package ioc.core;
 2 import java.util.Map;
 3 
 4 /**
 5  * Ioc框架的容器接口
 6  * @author ranger
 7  *
 8  */
 9 public interface Context {
10     
11      /**
12       * 用於得到容器中的全部對象
13       * @return
14       */
15      Map<String,Object> getObjects();
16      
17      /**
18       * 用於增長容器中的對象
19       * @param key
20       * @param value
21       */
22      void addObject(String key, Object value);
23 
24 }

 

 

 

 

3.定義容器操做接口ApplicationContextthis

 

 1 package ioc.core;
 2 /**
 3  * Ioc框架的容器操做接口
 4  * @author ranger
 5  *
 6  */
 7 public interface ApplicationContext {
 8 
 9     
10     /**
11      * 經過容器裏面的對象名,返回容器中的對象
12      * @param objectName
13      * @return
14      */
15     Object getBean(String objectName);
16 }

 

4.實現容器url

 1 package ioc.core.impl;
 2 
 3 import java.util.HashMap;
 4 import java.util.Map;
 5 
 6 import ioc.core.Context;
 7 
 8 /**
 9  * 實現框架容器,用於存儲掃描註解建立的全部對象。
10  * @author ranger
11  *
12  */
13 public class ContextImpl implements Context {
14     
15     //使用Map來存儲對象,爲何使用Map對象呢?由於預留對象名能夠設置的須要。
16     Map<String,Object> objects=new HashMap<String,Object>();
17 
18     @Override
19     public Map<String,Object> getObjects() {
20 
21         return this.objects;
22     }
23 
24     @Override
25     public void addObject(String key,Object value) {
26         objects.put(key, value);
27     }
28 }

5.實現掃描包。得到包以及該包子包的全部類的類全名。spa

--使用到一個工具類從包中讀取包和其子包類名--.net

 

  1 package ioc.core.utils;
  2 
  3 import java.io.File;
  4 import java.io.IOException;
  5 import java.net.JarURLConnection;
  6 import java.net.URL;
  7 import java.net.URLClassLoader;
  8 import java.util.Enumeration;
  9 import java.util.HashSet;
 10 import java.util.Set;
 11 import java.util.jar.JarEntry;
 12 import java.util.jar.JarFile;
 13 
 14 /**
 15  * 本類用於讀取包下面的類名
 16  * 來自博客
 17  * http://blog.csdn.net/aust_glj/article/details/53385651
 18  *
 19  */
 20 public class PackageUtils {
 21     public static void main(String[] args) throws Exception {
 22         String packageName = "ioc.core.annotation";
 23         Set<String> classNames = getClassName(packageName, true);
 24         if (classNames != null) {
 25             for (String className : classNames) {
 26                 System.out.println(className);
 27             }
 28         }
 29     }
 30 
 31     /**
 32      * 獲取某包下全部類
 33      * @param packageName 包名
 34      * @param isRecursion 是否遍歷子包
 35      * @return 類的完整名稱
 36      */
 37     public static Set<String> getClassName(String packageName, boolean isRecursion) {
 38         Set<String> classNames = null;
 39         ClassLoader loader = Thread.currentThread().getContextClassLoader();
 40         String packagePath = packageName.replace(".", "/");
 41 
 42         URL url = loader.getResource(packagePath);
 43         if (url != null) {
 44             String protocol = url.getProtocol();
 45             if (protocol.equals("file")) {
 46                 classNames = getClassNameFromDir(url.getPath(), packageName, isRecursion);
 47             } else if (protocol.equals("jar")) {
 48                 JarFile jarFile = null;
 49                 try{
 50                     jarFile = ((JarURLConnection) url.openConnection()).getJarFile();
 51                 } catch(Exception e){
 52                     e.printStackTrace();
 53                 }
 54                 
 55                 if(jarFile != null){
 56                     getClassNameFromJar(jarFile.entries(), packageName, isRecursion);
 57                 }
 58             }
 59         } else {
 60             /*從全部的jar包中查找包名*/
 61             classNames = getClassNameFromJars(((URLClassLoader)loader).getURLs(), packageName, isRecursion);
 62         }
 63         
 64         return classNames;
 65     }
 66 
 67     /**
 68      * 從項目文件獲取某包下全部類
 69      * @param filePath 文件路徑
 70      * @param className 類名集合
 71      * @param isRecursion 是否遍歷子包
 72      * @return 類的完整名稱
 73      */
 74     private static Set<String> getClassNameFromDir(String filePath, String packageName, boolean isRecursion) {
 75         Set<String> className = new HashSet<String>();
 76         File file = new File(filePath);
 77         File[] files = file.listFiles();
 78         for (File childFile : files) {
 79             if (childFile.isDirectory()) {
 80                 if (isRecursion) {
 81                     className.addAll(getClassNameFromDir(childFile.getPath(), packageName+"."+childFile.getName(), isRecursion));
 82                 }
 83             } else {
 84                 String fileName = childFile.getName();
 85                 if (fileName.endsWith(".class") && !fileName.contains("$")) {
 86                     className.add(packageName+ "." + fileName.replace(".class", ""));
 87                 }
 88             }
 89         }
 90 
 91         return className;
 92     }
 93 
 94     
 95     /**
 96      * @param jarEntries
 97      * @param packageName
 98      * @param isRecursion
 99      * @return
100      */
101     private static Set<String> getClassNameFromJar(Enumeration<JarEntry> jarEntries, String packageName, boolean isRecursion){
102         Set<String> classNames = new HashSet<String>();
103         
104         while (jarEntries.hasMoreElements()) {
105             JarEntry jarEntry = jarEntries.nextElement();
106             if(!jarEntry.isDirectory()){
107                 /*
108                  * 這裏是爲了方便,先把"/" 轉成 "." 再判斷 ".class" 的作法可能會有bug
109                  * (FIXME: 先把"/" 轉成 "." 再判斷 ".class" 的作法可能會有bug)
110                  */
111                 String entryName = jarEntry.getName().replace("/", ".");
112                 if (entryName.endsWith(".class") && !entryName.contains("$") && entryName.startsWith(packageName)) {
113                     entryName = entryName.replace(".class", "");
114                     if(isRecursion){
115                         classNames.add(entryName);
116                     } else if(!entryName.replace(packageName+".", "").contains(".")){
117                         classNames.add(entryName);
118                     }
119                 }
120             }
121         }
122         
123         return classNames;
124     }
125     
126     /**
127      * 從全部jar中搜索該包,並獲取該包下全部類
128      * @param urls URL集合
129      * @param packageName 包路徑
130      * @param isRecursion 是否遍歷子包
131      * @return 類的完整名稱
132      */
133     private static Set<String> getClassNameFromJars(URL[] urls, String packageName, boolean isRecursion) {
134         Set<String> classNames = new HashSet<String>();
135         
136         for (int i = 0; i < urls.length; i++) {
137             String classPath = urls[i].getPath();
138             
139             //沒必要搜索classes文件夾
140             if (classPath.endsWith("classes/")) {continue;}
141 
142             JarFile jarFile = null;
143             try {
144                 jarFile = new JarFile(classPath.substring(classPath.indexOf("/")));
145             } catch (IOException e) {
146                 e.printStackTrace();
147             }
148 
149             if (jarFile != null) {
150                 classNames.addAll(getClassNameFromJar(jarFile.entries(), packageName, isRecursion));
151             }
152         }
153         
154         return classNames;
155     }
156 }

 

--容器操做類的公用代碼寫在AbstractApplicationContext抽象類的構造函數裏面,方便之後擴展其餘的加載狀況--

 

 1 package ioc.core.impl;
 2 
 3 import java.util.Iterator;
 4 import java.util.Set;
 5 
 6 import ioc.core.ApplicationContext;
 7 import ioc.core.Context;
 8 import ioc.core.annotation.ComponentScan;
 9 import ioc.core.annotation.Configuration;
10 import ioc.core.utils.PackageUtils;
11 
12 public abstract class AbstractApplicationContext implements ApplicationContext{
13     //聲明一個線程變量,存儲容器對象,表示同一條線程,一個ApplicationContext只操做一個容器對象。
14     private ThreadLocal<Context> contexts=new ThreadLocal<Context>();
15     
16      
17     protected String[] basePackage=null;
18      /**
19       * 將容器操做加載建立對象的代碼寫抽象類裏面,這樣能夠方便之後擴展多種實現。
20       * @param classType
21       */
22     public AbstractApplicationContext(Class<?> classType) {
23          //判斷配置類是否有Configuration註解
24          Configuration annotation = classType.getAnnotation(Configuration.class);
25          if(annotation!=null){
26              //得到組件掃描註解
27              ComponentScan componentScan = classType.getDeclaredAnnotation(ComponentScan.class);
28              //得到包名
29              this.basePackage = componentScan.basePackages();
30              //根據包名得到類全限制名
31              Set<String> classNames = PackageUtils.getClassName(this.basePackage[0], true);
32              //經過類名建立對象
33              Iterator<String> iteratorClassName = classNames.iterator();
34              while(iteratorClassName.hasNext()){
35                  String className = iteratorClassName.next();
36                  try {
37                      //經過類全名建立對象
38                     Object instance = Class.forName(className).newInstance();
39                     //將對象加到容器中,對象名就類全名
40                     this.getContext().addObject(instance.getClass().getSimpleName(),instance);
41                 } catch (InstantiationException e) {
42                     e.printStackTrace();
43                 } catch (IllegalAccessException e) {
44                     e.printStackTrace();
45                 } catch (ClassNotFoundException e) {
46                     e.printStackTrace();
47                 }
48              }
49          }
50     }
51     
52     public String[] getBasePackage() {
53         return basePackage;
54     }
55     
56     
57     public Context getContext(){
58         if(contexts.get()==null){
59              //調用容器
60              Context context=new ContextImpl();
61              contexts.set(context);
62         }
63         return contexts.get();
64     }
65     
66     
67 
68 }

 

----實現AnnotationApplicationContext註解操做類,繼承bstractApplicationContext---

注意:這裏尚未實現getBean方法。先實現啓動程序時,包下面的全部類有沒有加入到容器裏了--

 

 1 package ioc.core.impl;
 2 
 3 public class AnntationApplicationContext extends AbstractApplicationContext {
 4 
 5     public AnntationApplicationContext(Class<?> classType) {
 6         super(classType);
 7     }
 8 
 9     @Override
10     public Object getBean(String objectName) {
11         
12         return null;
13     }
14 }

 

測試代碼

測試是否能夠得到指定掃描包下的類的對象

1.建立一個測試源碼包存放測試代碼

 

2. Config類,是一個配置類。裏面定義掃描包的路徑

 

 1 package ioc.core.test.config;
 2 
 3 import ioc.core.annotation.ComponentScan;
 4 import ioc.core.annotation.Configuration;
 5 
 6 //使用定義@Configuration定義該類是一個配置類
 7 @Configuration
 8 //使用ComponentScan設置掃描包的路徑
 9 @ComponentScan(basePackages="ioc.core.test")
10 public class Config {
11 
12 }

 

3. 編寫一個普通的UserService類測試

 

package ioc.core.test.service;

/**
 * 一個普通的類,用於測試是否能夠建立對象
 * @author ranger
 *
 */
public class UserService {
    
    public void login(){
        System.out.println("-登陸-");
    }
}

 

4. 建立一個AnntationApplicationContextTest測試類

 

package ioc.core.test;

import org.junit.Test;

import ioc.core.impl.AnntationApplicationContext;
import ioc.core.test.config.Config;

public class AnntationApplicationContextTest {
    
    @Test
    public void constructor(){
        try {
            AnntationApplicationContext context=new AnntationApplicationContext(Config.class);
            //若是能夠打印出容器裏面的對象,說明成功
            System.out.println(context.getContext().getObjects());
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

}

 

5.測試結果,得到UserService的對象。成功!

相關文章
相關標籤/搜索