首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 软件管理 > 软件架构设计 >

Guice项目实战(1)

2012-11-08 
Guice项目实战(一)引言?????? 公元二零零七年,开源领域各IoC框架战火不断。Spring大红大紫,独领风骚;PicoCo

Guice项目实战(一)

引言


?????? 公元二零零七年,开源领域各IoC框架战火不断。Spring大红大紫,独领风骚;PicoContainer、Hivemind紧随其后,穷追不舍。正当各路豪杰稳步发展之时,一匹黑马悄悄杀进江湖,这就是号称比Spring快100倍的Guice,从此江湖又起风云!

?????? Guice是由Bob lee设计的基于Java5 Annotation的轻量级IoC容器,它把组织对象依赖关系的逻辑从XML文件转移到对象内部,巧妙的实现了DI模式。本文并不打算详细讲解Guice的语法,如果你尚且不知道Guice是什么,可以先阅读附录中提供的文章了解Guice相关信息。本文余下部分将用一个以Guice为基础的简单的MVC模式的参考实现(实在是不知道应该叫什么,暂且就称之为参考实现吧)以及在该实现基础上开发的XXX示例来深入Guice应用开发实战。

完整内容请参阅附件中的文档,以及AromaRI的实现代码。文档正在完善中···

附件AromaRI项目源码是一个完整的Eclipse工程,运行ant脚本可以编译、测试和打包web应用。

/* * EsayJF.com Inc. * * Copyright (c) 2006-2008 All Rights Reserved. */package com.aroma.core.container.support;import static com.google.inject.Scopes.SINGLETON;import static com.google.inject.name.Names.named;import static com.google.inject.servlet.AromaServletModule.APPLICATION;import static com.google.inject.servlet.ServletScopes.REQUEST;import static com.google.inject.servlet.ServletScopes.SESSION;import java.lang.annotation.Annotation;import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map;import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;import com.aroma.core.Globals;import com.aroma.core.config.Component;import com.aroma.core.container.Container;import com.aroma.core.util.AromaUtil;import com.google.inject.AbstractModule;import com.google.inject.Guice;import com.google.inject.Injector;import com.google.inject.Key;import com.google.inject.Scope;import com.google.inject.Stage;import com.google.inject.servlet.AromaServletModule;/* * An Container as a wrapper of Guice's {@link Injector}. * * <p> * <b>some examples:</b> <code> * AdminAction adminAction = container.getBean(AdminAction.class); * * UserAction userAction = (UserAction)container.getBean("user"); * * LoginService adminLogin = container.getBean(LoginService.class,new MyAnnotation()); * </code> * * @author ecsoftcn@hotmail.com * * @version $Id: DefaultContainer.java,v 1.0 Apr 18, 2007 1:48:30 PM Tony Exp $ */public class DefaultContainer implements Container {/* Logging */private static final Loglogger= LogFactory.getLog(DefaultContainer.class);/* Goole Guice's Container */private Injectorinjector;/* Action container's array */private Component[]components= new Component[] {};/* Module's list */private List<AbstractModule>modules= new ArrayList<AbstractModule>();/* Current support scopes */private static final Map<String, Scope>scopes= new HashMap<String, Scope>();/* Guice's running stages */private static final Map<String, Stage>stages= new HashMap<String, Stage>();/* Initializing flag */private booleaninitialized= false;private Stagestage= Stage.PRODUCTION;static {scopes.put(Globals.SCOPES_REQUEST, REQUEST);scopes.put(Globals.SCOPES_SESSION, SESSION);scopes.put(Globals.SCOPES_APPLICATION, APPLICATION);scopes.put(Globals.SCOPES_SINGLETON, SINGLETON);stages.put(Stage.PRODUCTION.name(), Stage.PRODUCTION);stages.put(Stage.DEVELOPMENT.name(), Stage.DEVELOPMENT);}public DefaultContainer(Component[] components) {this(components, null);}public DefaultContainer(Component[] components, String stage) {this(components, stage, null);}public DefaultContainer(Component[] components, String stage, String[] moduleArray) {if (!AromaUtil.isEmptyOrNull(components)) {this.components = components;}if (!AromaUtil.isEmptyOrNull(stage) && !AromaUtil.isEmptyOrNull(scopes.get(stage))) {this.stage = stages.get(stage);}if (moduleArray != null && moduleArray.length > 0) {for (String name : moduleArray) {try {Class module = AromaUtil.loadClass(name);this.modules.add((AbstractModule) module.newInstance());} catch (Exception e) {logger.error("Error occured while regist module [" + name + "]", e);}}}}/* * * @see com.aroma.core.container.Container#init() */public void init() {if (!initialized) {if (logger.isInfoEnabled()) {logger.info("***************************************************************");logger.info("* *");logger.info("* AromaRI Container *");logger.info("* *");logger.info("***************************************************************");}long start = System.currentTimeMillis();injector = Guice.createInjector(stage, new AbstractModule() {/* * @see com.google.inject.AbstractModule#configure() */@Override@SuppressWarnings("unchecked")protected void configure() {if (logger.isInfoEnabled()) {logger.info("Install ServletModule [" + AromaServletModule.class.getName() + "]");}install(new AromaServletModule());for (AbstractModule module : modules) {if (logger.isInfoEnabled()) {logger.info("Install CustomModule [" + module.getClass().getName() + "]");}install(module);}for (Component component : components) {if (!AromaUtil.isEmptyOrNull(component.getScope())) {Scope scope = scopes.get(component.getScope());if (!AromaUtil.isEmptyOrNull(scope)) {if (logger.isInfoEnabled()) {logger.info("Binding Component [" + component.getAction().getName() + "] in Scope [" + component.getScope()+ "]");}bind(component.getAction()).annotatedWith(named(component.getName())).to(component.getAction()).in(scope);continue;}}if (logger.isInfoEnabled()) {logger.info("Binding Component [" + component.getAction().getName() + "]");}bind(component.getAction()).annotatedWith(named(component.getName())).to(component.getAction());}}});initialized = true;long time = System.currentTimeMillis() - start;if (logger.isInfoEnabled()) {logger.info("Container finish initialized! Cost " + time + "ms.");}}}/* * * @see com.aroma.core.container.Container#destroy() */public void destroy() {if (injector != null) {injector = null;}}/* * Obtain an object from container by a special name. * * <p> * <b>Warning</b> * You can call this method only when get a {@link Action} instance from Container, * * because only {@link Action} instance have name attribute in container. * * @param name bean's name * * @return Object */@SuppressWarnings("unchecked")public Object getBean(String name) {for (Component component : components) {if (component.getName().equals(name)) {return getBean(component.getAction(), named(name));}}return null;}/* * Obtain an instance of Class<T> . * * @param <T> generic type * * @param type instance type * * @return T */public <T> T getBean(Class<T> type) {T instance = null;try {instance = injector.getInstance(type);if (logger.isDebugEnabled()) {logger.debug("Get an instance from container [" + instance.getClass().getName() + "]");}} catch (Throwable e) {if (logger.isWarnEnabled()) {logger.warn("An exception has occured while obtain an instance of " + type.getSimpleName(), e);}}return instance;}/* * Obtain an instance of Class<T> . * * @param <T> generic type * * @param type instance type * * @param annotation annotation instance * * @return T */public <T> T getBean(Class<T> type, Annotation annotation) {T instance = null;try {instance = injector.getInstance(Key.get(type, annotation));if (logger.isDebugEnabled()) {logger.debug("Get an instance from container [" + instance.getClass().getName() + "]");}} catch (Throwable e) {if (logger.isWarnEnabled()) {logger.warn("An exception has occured while obtain an instance of " + type.getSimpleName(), e);}}return instance;}}/* * EsayJF.com Inc. * * Copyright (c) 2006-2008 All Rights Reserved. */package com.aroma.core.container.support;import java.io.InputStream;import com.aroma.core.config.ConfigParser;import com.aroma.core.config.support.DefaultConfigParser;import com.aroma.core.container.Container;/** * Singleton and Thread-safe Container Loader. * * @author ecsoftcn@hotmail.com * * @version $Id: ContainerFactory.java, 2007-4-26 下午10:05:28 Tony Exp $ */public class SingletonContainerLoader {static final ThreadLocal<Container>localContext= new ThreadLocal<Container>();private static booleaninitialized= false;private SingletonContainerLoader() {}/** * Create a {@link Container} instance and put it in {@link ThreadLocal<Container>} * * @param in config file's inputstream * * @return Container * * @throws Exception */public static Container create(InputStream in) throws Exception {return create(in, null);}/** * Create a {@link Container} instance and put it in {@link ThreadLocal<Container>} * * @param in config file's inputstream * * @param stage Guice's running stage * * @return Container * * @throws Exception */public static Container create(InputStream in, String stage) throws Exception {return create(in, stage, null);}/** * Create a {@link Container} instance and put it in {@link ThreadLocal<Container>} * * @param in config file's inputstream * * @param stage Guice's running stage * * @param modules custom modules * * @return Container * * @throws Exception */public static Container create(InputStream in, String stage, String[] moduleArray) throws Exception {if (!initialized) {ConfigParser configParser = new DefaultConfigParser();Container container = new DefaultContainer(configParser.parse(in), stage, moduleArray);container.init();if (container == null) {throw new Exception("AromaRI Container setup failure!");}localContext.set(container);initialized = true;}return getCurrentThreadContainer();}/** * Get current thread's container. * * <p> * {@link Action} and any other classes can obtain current thread's {@link Container} instance through this method. * * <b>Example:</b> Container container = SingletonContainerLoader.getCurrentThreadContainer(); * * @return */public static Container getCurrentThreadContainer() {return localContext.get();}}/* * EsayJF.com Inc. * * Copyright (c) 2006-2008 All Rights Reserved. */package com.aroma.core;import java.io.IOException;import java.util.Iterator;import java.util.Map;import javax.servlet.ServletConfig;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;import com.aroma.core.container.Container;import com.aroma.core.container.support.SingletonContainerLoader;import com.aroma.core.util.AromaUtil;/** * @author ecsoftcn@hotmail.com * * @version $Id: AromaServlet.java,v 1.0 Apr 19, 2007 10:56:18 AM Tony Exp $ */public class AromaServlet extends HttpServlet {/** Logging */private static final Loglogger= LogFactory.getLog(AromaServlet.class);private static final longserialVersionUID= 5858340596717871501L;private Containercontainer;/* * @see javax.servlet.GenericServlet#init(javax.servlet.ServletConfig) */@Overridepublic void init(ServletConfig context) throws ServletException {super.init(context);if (container == null) {container = (Container) context.getServletContext().getAttribute(Globals.AROMA_RI_CONTAINER);if (container == null) {String modules = context.getInitParameter(Globals.AROMA_CUSTOM_MODULES);String stage = context.getInitParameter(Globals.AROMA_STAGE);String configFile = context.getInitParameter(Globals.AROMA_CUSTOM_CONFIG_FILE);String[] moduleArray = null;if (!AromaUtil.isEmptyOrNull(modules)) {moduleArray = modules.split(",");}if (AromaUtil.isEmptyOrNull(configFile)) {configFile = Globals.AROMA_DEFALUT_CONFIG_FILE;}try {container = SingletonContainerLoader.create(context.getServletContext().getResourceAsStream(configFile), stage, moduleArray);} catch (Exception e) {throw new ServletException(e);}context.getServletContext().setAttribute(Globals.AROMA_RI_CONTAINER, container);}}}/* * @see javax.servlet.GenericServlet#destroy() */@Overridepublic void destroy() {super.destroy();if (container != null) {container.destroy();container = null;}}/* * @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) */@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {ActionContext context = new DefaultActionContext();try {if (!getRequestPath(req, res).endsWith(Globals.EXTENTAION_DO)) {req.getRequestDispatcher(getRequestPath(req, res)).forward(req, res);} else {beforeProcess(req, res, context);Action action = null;Object target = ActionEnhancer.enhance(container.getBean(getActionName(req, res)), context.getParameters());if (target instanceof Action) {action = (Action) target;} else {action = new ActionProxy(target, (String) context.getParameter(Globals.DEFAULT_COMMAND_NAME));}if (AromaUtil.isEmptyOrNull(action)) {handleError(req, res, "The action that you just request dose not exist!");}action.execute(context);afterProcess(req, res, context);req.getRequestDispatcher(getResultPage(req, res, context)).forward(req, res);}} catch (Throwable e) {handleException(req, res, "Error occured while processing request !", e);}}···}/* * EsayJF.com Inc. * * Copyright (c) 2006-2008 All Rights Reserved. */package com.aroma.core;import java.lang.reflect.Field;import java.lang.reflect.InvocationTargetException;import java.util.Map;import org.apache.commons.beanutils.BeanUtils;import org.apache.commons.beanutils.PropertyUtils;import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;import com.aroma.core.util.AromaUtil;/** * * @author ecsoftcn@hotmail.com * * @version $Id: ActionEnhancer.java, 2007-4-28 下午07:16:44 Tony Exp $ */public class ActionEnhancer {/** Logging */private static final Loglogger= LogFactory.getLog(ActionEnhancer.class);private ActionEnhancer() {}/** * * @param object * @param data * @return */public static Object enhance(Object object, Map data) {if (AromaUtil.isEmptyOrNull(object) || AromaUtil.isEmptyOrNull(data)) {if (logger.isDebugEnabled()) {logger.debug("Action or ActionContext may be null , so ignore it!");}return object;}autowireActionProperties(object, data);return object;}private static void autowireActionProperties(Object object, Map data) {Class superClass = object.getClass();Class currentClass;while (true) {currentClass = superClass;Field[] fields = currentClass.getDeclaredFields();if (logger.isDebugEnabled()) {logger.debug("Start to inject [" + currentClass.getSimpleName() + "]'s instance variable! ");}for (Field field : fields) {String name = field.getName();Object value = data.get(name);try {Autowire autowire = field.getAnnotation(Autowire.class);if (AromaUtil.isEmptyOrNull(autowire)) {if (logger.isDebugEnabled()) {logger.debug("[" + currentClass.getSimpleName() + "] variable '" + name+ "' didn't have [Autowire] annotation , so inject it by it's setter method!");}if (PropertyUtils.isWriteable(object, name)) {BeanUtils.setProperty(object, name, value);if (logger.isDebugEnabled()) {logger.debug("[" + currentClass.getSimpleName() + "] set variable '" + name + "' by value '" + value + "'");}}} else {if (logger.isDebugEnabled()) {logger.debug("[" + currentClass.getSimpleName() + "] variable '" + name+ "' has a [Autowire] annotation , so inject it directly!");}if (!AromaUtil.isEmptyOrNull(value)) {field.setAccessible(true);field.set(object, value);if (logger.isDebugEnabled()) {logger.debug("[" + currentClass.getSimpleName() + "] set variable '" + name + "' by value '" + value + "'");}}}} catch (IllegalArgumentException e) {if (logger.isWarnEnabled()) {logger.warn("[" + currentClass.getSimpleName() + "] exception occured while inject variable '" + name + "'", e);}} catch (IllegalAccessException e) {if (logger.isWarnEnabled()) {logger.warn("[" + currentClass.getSimpleName() + "] exception occured while inject variable '" + name + "'", e);}} catch (InvocationTargetException e) {if (logger.isWarnEnabled()) {logger.warn("[" + currentClass.getSimpleName() + "] exception occured while inject variable '" + name + "'", e);}}}superClass = currentClass.getSuperclass();if (superClass == null || superClass == Object.class) {break;}}}}/* * EsayJF.com Inc. * * Copyright (c) 2006-2008 All Rights Reserved. */package com.aroma.core;import java.lang.reflect.Method;import java.util.Iterator;import java.util.Map;import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;import com.aroma.core.util.AromaUtil;/** * * @author ecsoftcn@hotmail.com * * @version $Id: ActionProxy.java, 2007-5-1 下午04:39:48 Tony Exp $ */public class ActionProxy implements Action {/** Logging */private static final Loglogger= LogFactory.getLog(ActionProxy.class);private static final Class[]COMMAND_METHOD_PARAM= new Class[] { Map.class };private Stringcommand;private Objectproxy;public ActionProxy(Object proxy) {this(proxy, null);}public ActionProxy(Object proxy, String command) {this.command = command;this.proxy = proxy;}/* * @see com.aroma.core.Action#execute(com.aroma.core.ActionContext) */public void execute(ActionContext context) {if (AromaUtil.isEmptyOrNull(proxy)) {throw new RuntimeException("[ActionProxy] proxy object can't be null!");}String command = getCommand();String methodName = getMethodName(command);Method method = null;try {method = proxy.getClass().getDeclaredMethod(methodName, COMMAND_METHOD_PARAM);} catch (NoSuchMethodException e) {logger.error("[execute] no method named '" + methodName + "' in " + proxy.getClass().getSimpleName(), e);throw new RuntimeException("Can't find '" + methodName + "' method!", e);}try {if (logger.isDebugEnabled()) {logger.debug("[execute] method '" + methodName + "' in " + proxy.getClass().getSimpleName() + " have been invoked!");}Object results = method.invoke(proxy, new Object[] { context.getParameters() });if (!AromaUtil.isEmptyOrNull(results)) {if (results instanceof Map) {Map resultMap = (Map) results;for (Iterator iter = resultMap.keySet().iterator(); iter.hasNext();) {Object key = iter.next();Object value = resultMap.get(key);if (key instanceof String) {String keyStr = (String) key;if (Globals.NEXT_PAGE.equals(keyStr) && value instanceof String) {context.setNextPage((String) value);}else{context.addResult(keyStr, value);}}}}}} catch (Exception e) {logger.error("[execute] error occured while invoke '" + methodName + "' in" + proxy.getClass().getSimpleName(), e);throw new RuntimeException("Fail to execute '" + methodName + "' method!", e);}}/** * Obtain the real method by user command and default command prefix. * * @param command * * @return current request method */private String getMethodName(String command) {String first = command.substring(0, 1);return Globals.COMMAND_METHOD_PREFIX + first.toUpperCase() + command.substring(1);}/** * @return the command */public String getCommand() {if (!AromaUtil.isEmptyOrNull(command)) {return command;}return "execute";}}/* * EsayJF.com Inc. * * Copyright (c) 2006-2008 All Rights Reserved. */package com.aroma.core;import java.lang.reflect.Method;import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;import com.aroma.core.util.AromaUtil;/** * An implementation of {@link Action}. * * <p> * Extends from this class , you can difine more than one method in a single Action. * * @author ecsoftcn@hotmail.com * * @version $Id: ActionSupport.java, 2007-4-28 上午12:48:13 Tony Exp $ */public abstract class ActionSupport implements Action {/** Logging */private static final Loglogger= LogFactory.getLog(ActionSupport.class);private static final Class[]COMMAND_METHOD_PARAM= new Class[] { ActionContext.class };private Stringcommand;/* * @see com.aroma.core.Action#execute(com.aroma.core.ActionContext) */public final void execute(ActionContext context) {String command = getCommand();String methodName = getMethodName(command);Method method = null;try {method = getClass().getDeclaredMethod(methodName, COMMAND_METHOD_PARAM);} catch (NoSuchMethodException e) {logger.error("[execute] no method named '" + methodName + "' in " + getClass().getSimpleName(), e);throw new RuntimeException("Can't find '" + methodName + "' method!", e);}try {if (logger.isDebugEnabled()) {logger.debug("[execute] method '" + methodName + "' in " + getClass().getSimpleName() + " have been invoked!");}method.invoke(this, new Object[] { context });} catch (Exception e) {logger.error("[execute] error occured while invoke '" + methodName + "' in" + getClass().getSimpleName(), e);throw new RuntimeException("Fail to execute '" + methodName + "' method!", e);}}/** * By default,user may override this method directly. * * <p> * If you want to add more than one method in a action , just do it follow this pattern: * * <b>public void doXxxXxxx(ActionCotext context){}</b> * * @param context */public void doExecute(ActionContext context) {}/** * Obtain the real method by user command and default command prefix. * * @param command * * @return current request method */private String getMethodName(String command) {String first = command.substring(0, 1);return Globals.COMMAND_METHOD_PREFIX + first.toUpperCase() + command.substring(1);}/** * @return the command */public final String getCommand() {if (!AromaUtil.isEmptyOrNull(command)) {return command;}return "execute";}/** * @param command the command to set */public final void setCommand(String command) {this.command = command;}}http://ecsoftcn.iteye.com/

热点排行