梦想之路(三):自制IOC

来源:互联网 发布:怎么注册手机淘宝网店 编辑:程序博客网 时间:2024/04/28 18:46

(一)功能设计细化

1.支持JSON配置Bean初始化:

        a. JSON读取出的内容必然需要一个容器进行装载,所以需要提供一个配置类;

        b. 既然支持JSON配置,那么必然会涉及读写文件。读写文件实现上虽然很简单,但是为了节省时间,先使用ApacheIO包;

        c. Bean配置必然牵扯引用,这个相对复杂一点,考虑全部Bean初始化完毕后在装载对应需要注入的对象;

2.支持注解配置Bean与注入对象:

        a. 使用注解则必须用到包扫描与注解定义,不考虑使用标准注解。

        b. 考虑的使用配置方式生成的Bean与注解方式的Bean的不同,不太好处理。暂定为无区别,但是配置方式的Bean会更早初始化。

3.支持单例与原始两种Bean获取方式:

        a. 这个是必须考虑的问题,比如用作Action类时,如果是单例则可能出现线程安全的问题。

(二)功能实现

功能细化完毕,一一实现!

配置类:

我们需要支持直接方式,则需要留一个配置扫描路径的接口,我选择在JSON配置文件中出现。既然是配置类,所以肯定是根据配置的格式所驱动的类,所以我们要先列出我们的配置文件。

JSON配置文件内容如下:

{    scanPath:'com.mxsl',    beans:[{        id:'userService',        classes:'com.mxsl.framework.test.UserService',        values:{            id:'001'        }    },{        id:'userAction',        classes:'com.mxsl.framework.test.UserAction',        members:{            userService:'userService'        }    }]}

scanPath用于配制扫描路径;

beans则用来存放所有配置的Bean属性;

每一个BeanId就是其唯一标示;

Beanclasses是对应的类,因为class是对应关键字,所以用classes避开;

values为对应成员变量的值,而memerbs则为对应需要注入的对象引用;

规则已经定义完毕,根据以上规则整理出对应的配置类文件如下:

InjectBean类用于存放Bean的配置信息:

package com.mxsl.framework.beans;import java.io.Serializable;import java.util.Map;/** * 用于存放配置Bean信息 * @author gr * */public class InjectBean implements Serializable {/** * Bean的唯一标示 */private String id;/** * Bean获取方式 */private String type;/** * 对应Class字符串 */private String classes;/** * 存放所有对象引用 */private Map<String, String> members;/** * 存放所有初始化值 */private Map<String, Object> values;/*get---set*/}



另外还有用于存放整体配置信息的类,Config



package com.mxsl.framework.web.context;import java.util.List;import com.mxsl.framework.beans.InjectBean;/** * 配置文件类 * @author gr * */public class Config {/** * 包扫描路径 */private String scanPath;/** * 对应配置的所有Bean信息 */private List<InjectBean> beans;/*get---set*/}

我们之前也有说过需要提供单例与原始两种Bean获取方式,所以我们需要一个枚举类,用于存放获取方式。

package com.mxsl.framework.beans;/** * Bean的作用域 * @author gr * */public enum BeanType {/** * 原始模式 */PROTOTYPE,/** * 单例模式 */SINGLETON//,/** * 请求模式(暂未实现) *///REQUEST,/** * 会话模式(暂未实现) *///SESSION}

当然,既然要支持路径扫描,那么这里提供了一个工具类,用于返回对应路径下的所有类名:

package com.mxsl.framework.util;import java.io.File;import java.net.URISyntaxException;import java.net.URL;import java.util.ArrayList;import java.util.List;/** * 工具类 * @author gr * */public class Utils {/** * 根据包名扫描其下所有类名 *  * @param packageName * @return */public static List<Class<?>> getClassName(String packageName) {List<Class<?>> classNames = new ArrayList<Class<?>>();ClassLoader loader = Thread.currentThread().getContextClassLoader();try {String resourceName = packageName.replaceAll("\\.", "/");URL url = loader.getResource(resourceName);File urlFile = new File(url.toURI());File[] files = urlFile.listFiles();for (File f : files)getClassName(packageName, f, classNames);} catch (URISyntaxException e) {e.printStackTrace();}return classNames;}private static void getClassName(String packageName, File packageFile,List<Class<?>> list) {if (packageFile.isFile()) {String className = packageName + "." + packageFile.getName().replace(".class", "");try {list.add(Class.forName(className));} catch (ClassNotFoundException e) {e.printStackTrace();}} else {File[] files = packageFile.listFiles();String tmPackageName = packageName + "." + packageFile.getName();for (File f : files) {getClassName(tmPackageName, f, list);}}}}

基础Bean容器类,用于存放Bean初始化后的所有信息:

package com.mxsl.framework.beans;import java.lang.reflect.Field;import java.util.HashMap;import java.util.Map;import org.apache.commons.beanutils.BeanUtils;import com.mxsl.framework.annotation.Inject;/** * 基础Bean容器 *  * @author gr *  */public class BaseBean {/** * 存放单例Bean对象 */private Object bean;/** * Bean对应Class */private Class<?> classes;/** * 对应需要注入的成员变量 */private final Map<Field, BaseBean> members = new HashMap<Field, BaseBean>();;private final Map<String, Object> values = new HashMap<String, Object>();/** * Bean类型 */private BeanType type;/** * 注解型构造函数 * @param classes * @param type */public BaseBean(Class<?> classes, BeanType type) {this.classes = classes;this.type = type;initBean();}/** * 配置型构造函数 * @param classes * @param type */public BaseBean(InjectBean injectBean, BeanType type) {try {this.classes = Class.forName(injectBean.getClasses());this.type = type;initInjectBean(injectBean);} catch (ClassNotFoundException e) {e.printStackTrace();}}/** * 获取Bean对象 *  * @return */public Object getBean() {if (BeanType.SINGLETON.equals(type)) {if (bean == null) {bean = createBean();}return bean;} else if (BeanType.PROTOTYPE.equals(type)) {return createBean();} else {return null;}}/** * 实例化Bean对象 *  * @return */private Object createBean() {Object bean = null;try {bean = classes.newInstance();for (Field field : members.keySet()) {Object member = members.get(field).getBean();BeanUtils.setProperty(bean, field.getName(), member);}for (String key : values.keySet()) {BeanUtils.setProperty(bean, key, values.get(key));}} catch (Exception e) {e.printStackTrace();}return bean;}/** * 初始化Bean */public void initBean() {Field[] fields = classes.getDeclaredFields();for (Field field : fields) {addMembers(field);}}/** * 增加Bean中需要注入的成员变量 * @param field */private void addMembers(Field field) {Inject inject = field.getAnnotation(Inject.class);if(inject!=null){String beanId;if ("".equals(inject.value())) {beanId = field.getName();} else {beanId = inject.value();}members.put(field, BeanFactory.getBaseBean(beanId));}}/** * 初始化InjectBean */public void initInjectBean(InjectBean injectBean) {initBean();initMembers(injectBean.getMembers());initValues(injectBean.getValues());}/** * 初始化所有需要注入的成员变量引用,指需要注入的其他Bean * @param injectMembers */private void initMembers(Map<String, String> injectMembers) {if (injectMembers != null) {for (String fieldName : injectMembers.keySet()) {try {Field field = classes.getDeclaredField(fieldName);members.put(field, BeanFactory.getBaseBean(injectMembers.get(fieldName)));} catch (NoSuchFieldException e) {e.printStackTrace();} catch (SecurityException e) {e.printStackTrace();}}}}/** * 初始化所有需要注入的成员变量 * @param injectMembers */private void initValues(Map<String, Object> injectValues) {if (injectValues != null) {for (String fieldName : injectValues.keySet()) {this.values.put(fieldName, injectValues.get(fieldName));}}}}

我们有了Bean的容器,那肯定也需要有一个存放Bean容器的工厂类:

package com.mxsl.framework.beans;import java.util.Collections;import java.util.HashMap;import java.util.List;import java.util.Map;import com.mxsl.framework.annotation.Action;import com.mxsl.framework.annotation.Bean;import com.mxsl.framework.util.Utils;import com.mxsl.framework.web.context.ContextLoader;/** * Bean工厂类 *  * @author gr *  */public class BeanFactory {/** * 静态Bean容器,利用synchronizedMap规避线程安全问题 */private final static Map<String, BaseBean> beans = Collections.synchronizedMap(new HashMap<String, BaseBean>());/** * 获取Bean包装容器 *  * @param id * @return */static BaseBean getBaseBean(String id) {return beans.get(id);}/** * 获取Bean包装容器 *  * @param id * @return */static Boolean containsBean(String id) {return beans.containsKey(id);}/** * 获取Bean对象 *  * @param id * @return */public static Object getBean(String id) {return getBaseBean(id).getBean();}/** * 初始化Bean * @param id * @param classes * @param beanType */private void initBean(String id, Class<?> classes, BeanType beanType) {if (!beans.containsKey(id)) {BaseBean bean = new BaseBean(classes, beanType);beans.put(id, bean);} else {System.err.println(id + "在容器中已存在!");}}/** * 初始化配置方式Bean * @param injectBean */private void initBean(InjectBean injectBean) {if (!beans.containsKey(injectBean.getId())) {BeanType type;if (injectBean.getType() == null) {type = BeanType.SINGLETON;} else if ("SINGLETON".equals(injectBean.getType().toUpperCase())) {type = BeanType.SINGLETON;} else if ("PROTOTYPE".equals(injectBean.getType().toUpperCase())) {type = BeanType.PROTOTYPE;} else {type = BeanType.SINGLETON;}BaseBean bean = new BaseBean(injectBean, type);beans.put(injectBean.getId(), bean);} else {System.err.println(injectBean.getId() + "在容器中已存在!");}}/** * 初始化BeanFactory *  * @param context */public void initFactory(ContextLoader loader) {initBeans(loader);initScon(loader);}/** * 初始化所有配置类Bean * @param loader */private void initBeans(ContextLoader loader) {List<InjectBean> injectBeans = loader.getConfig().getBeans();for (InjectBean injectBean : injectBeans) {this.initBean(injectBean);}}/** * 初始化所有注解类Bean * @param loader */private void initScon(ContextLoader loader) {List<Class<?>> beansClass = Utils.getClassName(loader.getConfig().getScanPath());for (Class<?> classes : beansClass) {Bean bean = classes.getAnnotation(Bean.class);Action action = classes.getAnnotation(Action.class);if (action != null) {String beanId;if("".equals(action.id())){beanId = action.value() + classes;this.initBean(beanId,classes,BeanType.PROTOTYPE);}else{beanId = action.id();if(!BeanFactory.containsBean(beanId)){BeanFactory.getBaseBean(beanId);this.initBean(beanId,classes,BeanType.PROTOTYPE);}}loader.getActionMapper().addActionBean(action.value(),BeanFactory.getBaseBean(beanId));} else if (bean != null) {String id;if ("".equals(bean.value())) {id = classes.toString();} else {id = bean.value();}this.initBean(id, classes, bean.type());}}}}

拥有了BeanFactory后,我们当然需要一个用于全局加载的类,用于初始化BeanFactory:

package com.mxsl.framework.web.context;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.IOException;import java.io.InputStream;import java.util.Date;import java.util.List;import javax.servlet.ServletContext;import org.apache.commons.io.IOUtils;import com.alibaba.fastjson.JSON;import com.mxsl.framework.beans.BeanFactory;import com.mxsl.framework.web.ActionMapper;public class ContextLoader {private ServletContext context;private final BeanFactory beanFactory = new BeanFactory();private final ActionMapper actionMapper = new ActionMapper();private Config config;public ContextLoader(ServletContext context) {this.context = context;initConfig();beanFactory.initFactory(this);}/** * 初始化配置类 */private void initConfig() {try {String configPath = context.getInitParameter("configPath");if (configPath == null) {configPath = "/WEB-INF/Config.json";}else{configPath = configPath.replaceFirst("classpath:", "/WEB-INF/classes/");}InputStream input;input = new FileInputStream(context.getRealPath(configPath));List<String> list = IOUtils.readLines(input);StringBuffer json = new StringBuffer();for (String str : list) {json.append(str);}config = JSON.parseObject(json.toString(), Config.class);} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}public Config getConfig() {return config;}public void setConfig(Config config) {this.config = config;}public ServletContext getContext() {return context;}public void setContext(ServletContext context) {this.context = context;}public BeanFactory getBeanFactory() {return beanFactory;}public ActionMapper getActionMapper() {return actionMapper;}}

最后,就是增加一个监听,用于系统启动时,加载对应的ContextLoader:

package com.mxsl.framework.web.context;import javax.servlet.ServletContextEvent;import javax.servlet.ServletContextListener;import javax.servlet.annotation.WebListener;/** * 上下文监听,用于初始化整个系统 * @author gr * */@WebListenerpublic class ContextListener implements ServletContextListener {private ContextLoader loader;public void contextInitialized(ServletContextEvent event) {loader = new ContextLoader(event.getServletContext());event.getServletContext().setAttribute("contextLoader", loader);}public void contextDestroyed(ServletContextEvent event) {}public ContextLoader getLoader() {return loader;}}

        所有的类已经列举完毕,因为在代码中穿插逻辑介绍的话肯定会引起大家的反感。所以在最后,我进行一下整体代码的逻辑:

                首先,在系统启动时,ContextLisenter会调用初始化方法,这是会实例化一个ContextLoader类;

               在该类构造参数中,会读取对应JSON配置文件,将文件转化为配置类对象,然后根据此对象初始化BeanFactory。

               BeanFactory中会先初始化Config中配置的所有Bean生成对应的BaseBean对象,放入Factory的容器之内,然后根据配置路径扫描的Bean,初始化后同样加入到容器之内。

               而在需要获取Bean的实例时,会根据BaseBean中属性自动装载需要引用的Bean或属性。

       以上为我所实现的简单IOC,因为篇幅问题,不在单个介绍方法细节与我所遇到的问题。如果大家有疑问或更好的建议,欢迎评论,多多交流。

	
				
		
原创粉丝点击