zoukankan      html  css  js  c++  java
  • 一起写框架-Ioc内核容器的实现-基础功能-ComponentScan(四)

    功能说明

    该步骤实现的功能包括:

    1. 启动程序时,将@ComponentScan加载的类,创建对象并放在容器里面。

    2. 通过ApplicatoinContext的getBean()方法获得容器里面的对象。 (放在下一篇文实现)

    实现步骤

    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.定义容器操作接口ApplicationContext

     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.实现容器

     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.实现扫描包。获得包以及该包子包的所有类的类全名。

    --使用到一个工具类从包中读取包和其子包类名--

      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的对象。成功!

  • 相关阅读:
    装饰者模式
    Linux top命令
    Java基础--单例类创建和测试
    Mybatis动态sql
    bmp图片格式及读取
    自然语言处理--nltk安装及wordnet使用详解
    Spring注解
    struts2 + spring + mybatis 框架整合
    Java基础--ThreadLocal
    Java基础--压缩和解压缩gz包
  • 原文地址:https://www.cnblogs.com/zhuyuejiu/p/7819107.html
Copyright © 2011-2022 走看看