Spring

1. 配置文件

  src/main/resources 下创建XX.properties 文件,文件里加入数据库配置信息,base_package,jsp_path,asset_path(静态资源文件的基础路径)等信息。

2. 加载配置

  加载配置文件中的配置信息,将jsp_path,asset_path配置项提供默认值,以配置文件中配置的信息优先。

3. 类加载器

  来加载基础包名下的所有类,比如使用了某注解的类,或实现了某个接口的类,或继承了某父类的所有子类等。获取类加载器,加载类,获取指定包下的所有类。

  获取类加载器只需获取当前线程中的classLoader即可,加载类需要提供类名和是否初始化的标记,这里的初始化是指执行类的静态代码块。获取指定包名下的所有类,需要将包名转换为文件路径,读取class文件或jar包,获取指定的类名去加载类,将类放到Set中。

  

public static ClassLoadder getClassLoader(){
    return Thread.currentThread().getContextClassLoader();
}
public static Class<?> loadClass(String className, boolean isInitialized) {
        Class<?> cls;
        try {
            cls = Class.forName(className, isInitialized, getClassLoader()); // 使用给定的类加载器加载类并返回Class对象
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
        return cls;
}
public static Set<Class<?>> getClassSet(String packageName) {
        // 存放Class类的Set集合
        Set<Class<?>> classSet = new HashSet<Class<?>>();
        try {
            // getResource(String name) 查找具有给定名称的资源
            // Enumeration 接口的对象,它生成一系列元素,一次生成一个。连续调用 nextElement 方法将返回一系列的连续元素
            Enumeration<URL> urls = getClassLoader().getResources(packageName.replace(".", "/"));  
            while (urls.hasMoreElements()) { // 判断此枚举是否包含更多的元素
                URL url = urls.nextElement(); // 返回此枚举的下一个元素
                if (url != null) {
                    String protocol = url.getProtocol(); // 获取此 URL的协议名称
                    if(protocol.equals("file")) {
                        String packagePath = url.getPath().replaceAll("%20", ""); // getPath() 获取此 URL 的路径部分
                        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> 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("/", ".");
                                        doAddClass(classSet, className);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return classSet;
}
/**
     * 递归遍历类或文件,判断是否是.class文件或者标准文件
     * @param classSet
     * @param packagePath
     * @param packageName
     */
    public 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;
                } 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);
                }
            }
        }
    }
    /**
     * 将类放入 Set<Class<?>> 中
     * @param classSet 存放Class类的Set集合
     * @param className 类名
     */
    public static void doAddClass(Set<Class<?>> classSet, String className) {
        Class<?> cls = loadClass(className, false);
        classSet.add(cls);
}

ClassHelper.class用于获取应用包名下所有Service类,Controller类,将带有@Controller,@Service注解的类所产生的对象,理解为由框架所管理的bean,获取应用包名下所有bean的方法,同样将bean放到Set中,先由上述代码获取所有类,然后从类中查找包含固定注解的类加入到Set中:

/**
     * 定义Class类的Set集合(用于存放所加载的类)
     */
    private static final Set<Class<?>> CLASS_SET;
    
    static {
        String basePackage = ConfigHelper.getAppBasePackage();
        CLASS_SET = ClassUtil.getClassSet(basePackage);
    }
    
    /**
     * 获取应用包名下的所有的类
     * @return Set集合泛型为Class类
     */
    public static Set<Class<?>> getClassSet() {
        return CLASS_SET;
    }
    
    /**
     * 获取应用包名下的所有Service类
     * @return Set集合泛型为Class类
     */
    public static Set<Class<?>> getServiceClassSet() {
        Set<Class<?>> classSet = new HashSet<Class<?>>();
        for (Class<?> cls : CLASS_SET) {
            if (cls.isAnnotationPresent(Service.class)) {
                classSet.add(cls);
            }
        }
        return classSet;
    }
    
    /**
     * 获取应用包名下的所有Controller类
     * @return Set集合泛型为Class类
     */
    public static Set<Class<?>> getControllerClassSet() {
        Set<Class<?>> classSet = new HashSet<Class<?>>();
        for (Class<?> cls : CLASS_SET) {
            if (cls.isAnnotationPresent(Controller.class)) {
                classSet.add(cls);
            }
        }
        return classSet;
    }
    
    /**
     * 获取应用包名下所有Bean类(包括:Service、Controller等)
     * @return Set集合泛型为Class类
     */
    public static Set<Class<?>> getBeanClassSet() {
        Set<Class<?>> beanClassSet = new HashSet<Class<?>>();
        beanClassSet.addAll(getServiceClassSet());
        beanClassSet.addAll(getControllerClassSet());
        return beanClassSet;
}

4. bean容器

  前边获取了所加载的类,但是无法通过类来实例化对象,因此要通过反射来对所加载的类进行实例化,bean类与bean实例的映射关系放到Map中。

public final class ReflectionUtil {
    /**
     * 创建类的实例
     * @param cls Class类的实例
     * @return Object 类型的对象
     */
    public static Object newInstance(Class<?> cls) {
        Object instance = null;
        try {
            instance = cls.newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return instance;
    } 
    /**
     * 调用方法
     * @param obj Object类的对象
     * @param method Method类的对象
     * @param args 可变的Object类型的参数
     * @return
     */
    public static Object invokeMethod(Object obj, Method method, Object... args) {
        Object result = null;
        method.setAccessible(true); // 如果获取到的method是private,不设置为true,就会报错
        try {
            method.invoke(obj, args);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return result;
    }
    /**
     * 设置成员变量的值    
     * @param obj Object类的对象
     * @param field Field类的对象
     * @param value Object类的对象
     */
    public static void setField(Object obj, Field field, Object value) {
        field.setAccessible(true); // 如果获取到的field是private,不设置为true,就会报错
        try {
            field.set(obj, value);
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

 

/**
     * 定义Bean映射(用于存放Bean类与Bean实例的映射关系)
     */
    private static final Map<Class<?>, Object> BEAN_MAP = new HashMap<Class<?>, Object>();
    
    static {
        Set<Class<?>> beanClassSet = ClassHelper.getBeanClassSet();
        for (Class<?> beanClass : beanClassSet) {
            Object obj = ReflectionUtil.newInstance(beanClass);
            BEAN_MAP.put(beanClass, obj);
        }
    }
    /**
     * 获取Bean映射
     * @return Map<Class<?>, Object> 对象
     */
    public static Map<Class<?>, Object> getBeanMap() {
        return BEAN_MAP;
    }
    /**
     * 获取Bean实例
     * @param cls Class类
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> T getBean(Class<T> cls) { // <T>表明T是一个泛型,没有明确的类型
        if (!BEAN_MAP.containsKey(cls)) {
            throw new RuntimeException("can not get bean by class:" + cls);
        }
        return (T) BEAN_MAP.get(cls);
}

在bean map中存放了bean类与bean实例的映射关系,只需要调用getBean方法,传入一个bean类就能获取bean实例,这就是bean容器了。

5. 依赖注入

  在Controller中定义了Service变量,如何在Controller的方法中调用Service成员变量的方法呢?如何实例化Service成员变量呢?通过框架自身来实例化,这种实例化过程就是IOC啦,先通过Bean Map(类和对象的映射关系),遍历这个映射关系,取出bean和bean实例,然后通过反射获取类中所有的成员变量,在循环中遍历当前成员变量,判断是否带有自定义注解,若带有自定义注解,则从bean map中取出bean实例,最后通过反射修改当前成员变量的值。

public final class IocHelper {
    static {
        // 获取所有的 Bean 类与 Bean 实例之间的映射关系(简称 Bean Map)
        Map<Class<?>, Object> beanMap = BeanHelper.getBeanMap();
        if (CollectionUtil.isNotEmpty(beanMap)) {
            // 遍历 Bean Map
            for (Map.Entry<Class<?>, Object> beanEntry : beanMap.entrySet()) {
                // 从 BeanMap 中获取 Bean 类与 Bean 实例
                Class<?> beanClass = beanEntry.getKey();
                Object beanInstance = beanEntry.getValue();
                // 获取 Bean 类定义的所有成员变量(简称 Bean Field)
                Field[] beanFields = beanClass.getDeclaredFields();
                if (ArrayUtil.isNotEmpty(beanFields)) {
                    for (Field beanField : beanFields) {
                        // 判断当前 Bean Field 是否带有 Inject 注解
                        if (beanField.isAnnotationPresent(Inject.class)) {
                            // 在 Bean Map 中获取 Bean Field 对应的实例
                            Class<?> beanFieldClass = beanField.getType();
                            Object beanFieldInstance = beanMap.get(beanFieldClass);
                            if (beanFieldInstance != null) {
                                // 通过反射初始化 BeanField 的值
                                ReflectionUtil.setField(beanInstance, beanField, beanFieldInstance);
                            }
                        } 
                    }
                }
            }
        }
    }
}

在静态块中实现相关逻辑,完成IOC容器的初始化工作,当这个类被加载的时候,就应该加载它的静态块,IOC框架所管理的bean都是单例的,由于bean map 中的对象都是事先创建好并放入这个bean容器的,所以所有的bean都是单例的。

5. 加载Controller

 

 

  

posted @ 2017-07-19 17:57  yangfei969  阅读(200)  评论(0编辑  收藏  举报