架构探险-从零开始写Javaweb框架读书笔记(2)

来源:互联网 发布:seo关键词优化外包 编辑:程序博客网 时间:2024/05/22 19:52

实现Bean容器

bean容器是通过保存类与实例化的对象的映射关系。进行依赖注入(DI),又称为控制反转(IOC)

学习Java的反射

反射这个概念很美妙,反着射,我理解的意思就是不仅仅通过对象才能获取到属性,也可以根据类来设置对象的属性。

  1. 首先先看一下java的反射包(java.lang.reflect),使用官方的api来达到我们的目的
    (1)创建实例化对象
    (2) 为对象注入属性
    (3) 调用方法
    那么我们先写一个工具类reflect达到我们的目的
/** * 反射工具类 * * Created by xueaohui on 2016/6/22. */public final class ReflectionUtil {    private static final Logger LOGGER = LoggerFactory.getLogger(ReflectionUtil.class);    /**     * 创建实例     */    public static Object newInstance(Class<?> cls){        Object instance ;        try {            instance = cls.newInstance();        }catch (Exception e){            LOGGER.error("new instance failure" , e );            throw new RuntimeException(e);        }        return instance;    }    /**     * 调用方法     */    public static Object invokeMethod(Object obj , Method method , Object... args){        Object result ;        try {            method.setAccessible(true);            result = method.invoke(obj, args);        }catch (Exception e){            LOGGER.error("invoke method failure" , e );            throw new RuntimeException(e);        }        return result;    }    /**     * 设置成员变量的值     */    public static void setField(Object obj , Field field , Object value){        try {        //如果设置属性为private并且不调用这个方法,会抛出IllegalAccessException 异常            field.setAccessible(true);            field.set(obj,value);        } catch (IllegalAccessException e) {            LOGGER.error("set field failure" , e );            throw new RuntimeException(e);        }    }}

根据以上方法,那么我们知道,只要我们知道了类,那么我们就知道了一切。
我们需要扫描所有我们编写的类,开发一个类加载器。

开发一个类加载器

那么我们需要以下功能
(1) 获取类加载器
(2) 获取类
(3) 加载类

/** * Created by xueaohui on 2016/6/22. */public final class ClassUtil {    private static final Logger LOGGER = LoggerFactory.getLogger(ClassUtil.class);    /**     * 获取类加载器     */    public static ClassLoader getClassLoader(){        return Thread.currentThread().getContextClassLoader();    }    /**     * 加载类     */    public static Class<?>loadClass(String className , boolean isInitialized){        Class<?> cls;        try {            cls = Class.forName(className,isInitialized,getClassLoader());        } catch (ClassNotFoundException e) {            LOGGER.error("load class failure" , e);            throw new RuntimeException(e);        }        return cls;    }    /**     * 加载类(默认将初始化类)     */    public static Class<?> loadClass(String className) {        return loadClass(className, true);    }    /**     * 获取指定包下的所有类     */    public static Set<Class<?>> getClassSet(String packageName){        Set<Class<?>> classSet = new HashSet<Class<?>>();        try {            //今天才知道的枚举类            //Enumeration 比 Iterator 的遍历速度更快            //因为没有fast-fail机制            Enumeration<URL> urls = getClassLoader().getResources(packageName.replace(".","/"));            while(urls.hasMoreElements()){                URL url = urls.nextElement();                if(url != null){                    //获取环境 是文件环境 还是jar环境                    String protocol = url.getProtocol();                    if(protocol.equals("file")){                        String packagePath = url.getPath().replaceAll("%20", "");                        addClass(classSet,packagePath,packageName);                    }else if(protocol.equals("jar")){                        JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection();                        if(jarURLConnection != null){                            JarFile jarFile = jarURLConnection.getJarFile();                            if(jarFile != null){                                Enumeration<JarEntry> jarEntryEnumeration = jarFile.entries();                                while (jarEntryEnumeration.hasMoreElements()){                                    JarEntry jarEntry = jarEntryEnumeration.nextElement();                                    String jarEntryName = jarEntry.getName();                                    if(jarEntryName.endsWith(".class")){                                        String className = jarEntryName.substring(0,jarEntryName.lastIndexOf(".")).replaceAll("/",".");                                        doAddClass(classSet,className);                                    }                                }                            }                        }                    }                }            }        }catch (Exception e){            LOGGER.error("get classSet failure" , e );            throw new RuntimeException(e);        }        return classSet;    }    private static void addClass(Set<Class<?>> classSet, String packagePath, String packageName) {        File[] files = new File(packagePath).listFiles(new FileFilter() {            public boolean accept(File file) {                return (file.isFile() && file.getName().endsWith(".class")) || file.isDirectory();            }        });        for (File file : files){            String fileName = file.getName();            if(file.isFile()){                String className = fileName.substring(0,fileName.lastIndexOf("."));                if(StringUtil.isNotEmpty(packageName)){                    className = packageName + "." + className ;                }                doAddClass(classSet,className);            }else{                //如果是文件夹 递归处理                String subPackagePath = fileName;                if(StringUtil.isNotEmpty(packagePath)){                    subPackagePath = packagePath + "/" + subPackagePath;                }                String subPackageName = fileName;                if(StringUtil.isNotEmpty(packageName)){                    subPackageName = packageName + "." + subPackageName;                }                addClass(classSet,subPackagePath,subPackageName);            }        }    }    private static void doAddClass(Set<Class<?>> classSet, String className) {        Class<?> cls = loadClass(className,false);        classSet.add(cls);    }}

我们开发一个类操作助手来通过获取我们编写代码的包来加载类

/** * Created by xueaohui on 2016/6/22. * 类操作助手 */public final class ClassHelper {    /**     * 定义类集合(用于存放加载的类)     */    private static final Set<Class<?>> CLASS_SET;    static {        String beanPackage = ConfigHelper.getAppBasePackage();        CLASS_SET = ClassUtil.getClassSet(beanPackage);    }    /**     * 获取包下的所有类     */    public static Set<Class<?>> getClassSet(){        return CLASS_SET;    }    /**     * 获取应用包下所有的Service类     */    public static Set<Class<?>> getServiceClassSet(){        Set<Class<?>> set = new HashSet<Class<?>>();        for(Class<?> cls : CLASS_SET){            if(cls.isAnnotationPresent(Service.class)){                set.add(cls);            }        }        return set;    }    /**     * 获取应用包下所有的Controller类     */    public static Set<Class<?>> getControllerClassSet(){        Set<Class<?>> set = new HashSet<Class<?>>();        for(Class<?> cls : CLASS_SET){            if(cls.isAnnotationPresent(Controller.class)){                set.add(cls);            }        }        return set;    }    /**     * 获取包下所有的bean 包括 service 和 controller 等     */    public static Set<Class<?>> getBeanClassSet(){        Set<Class<?>> beanClassSet = new HashSet<Class<?>>();        beanClassSet.addAll(getControllerClassSet());        beanClassSet.addAll(getServiceClassSet());        return beanClassSet;    }    /**     * 获取应用包名下某父类(或接口)的所有接口(或实现类)     */    public static Set<Class<?>> getClassSetBySuper(Class<?> superClass){        Set<Class<?>> classSet = new HashSet<Class<?>>();        for(Class<?> cls : CLASS_SET){            if(superClass.isAssignableFrom(cls)&&!superClass.equals(cls)){                classSet.add(cls);            }        }        return classSet;    }    /**     * 获取应用包名下带有某注解的类     */    public static Set<Class<?>> getClassSetByAnnotation(Class<? extends Annotation> annotationClass){        Set<Class<?>> classSet = new HashSet<Class<?>>();        for(Class<?> cls : CLASS_SET){            if(cls.isAnnotationPresent(annotationClass)){                classSet.add(cls);            }        }        return classSet;    }}

以上可知我们可以获取到所有的class
那么我们就要开发这个bean容器,实现类-实例对象的映射
只需用Map容器即可实现

/** * Bean 助手类 * Created by xueaohui on 2016/6/22. */public final class BeanHelper {    /**     * 定义Bean映射 (用于存放 Bean 类 与 Bean 实例的映射关系)     */    private static final Map<Class<?>,Object>BEAN_MAP = new HashMap<Class<?>, Object>();    static {        Set<Class<?>> classSet = ClassHelper.getBeanClassSet();        for(Class<?>beanClass : classSet){            BEAN_MAP.put(beanClass, ReflectionUtil.newInstance(beanClass));        }    }    /**     * 获取Bean映射     */    public static Map<Class<?>,Object>getBeanMap(){        return BEAN_MAP;    }    /**     * 获取Bean实例     */    public static <T> T getBean(Class<T> cls){        if(!BEAN_MAP.containsKey(cls)){            throw new RuntimeException("can not get bean by class " + cls );        }        return (T)BEAN_MAP.get(cls);    }    /**     * 设置bean 实例     */    public static void setBean(Class<?> cls,Object obj){        BEAN_MAP.put(cls,obj);    }}

以上我们就实现bean容器,我们实现这个容器的作用就是可以通过框架来实现管理我们的对象。根据经验,对所有有状态的bean应该使用prototype作用域,而对无状态的bean则应该使用singleton作用域。比如controller中我设置了一个hashmap用来返回json对象,如果controller是单例的,那么每一次访问这个controller的借口时候hashmap原来put进去的值也会被返回。

0 0