浏览 11224 次
锁定老帖子 主题:Guice项目实战(一)
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2007-05-10
引言
Guice是由Bob lee设计的基于Java5 Annotation的轻量级IoC容器,它把组织对象依赖关系的逻辑从XML文件转移到对象内部,巧妙的实现了DI模式。本文并不打算详细讲解Guice的语法,如果你尚且不知道Guice是什么,可以先阅读附录中提供的文章了解Guice相关信息。本文余下部分将用一个以Guice为基础的简单的MVC模式的参考实现(实在是不知道应该叫什么,暂且就称之为参考实现吧)以及在该实现基础上开发的XXX示例来深入Guice应用开发实战。 完整内容请参阅附件中的文档,以及AromaRI的实现代码。文档正在完善中···
附件AromaRI项目源码是一个完整的Eclipse工程,运行ant脚本可以编译、测试和打包web应用。 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2007-05-11
附件一和附件三的内容是一样的,因为我在上传的时候重复了,但是不能删除
下面把AromaRI的工作原理结构图发上来:
AromaRI只是一个简单的MVC层,由Guice充当IoC容器的角色。 目前文档只完成了一小部分,其他章节会陆续发布。 |
|
返回顶楼 | |
发表时间:2007-05-11
几个重要类的细节(一)
DefaultContainer /* * 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 Log logger = LogFactory.getLog(DefaultContainer.class); /* Goole Guice's Container */ private Injector injector; /* 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 boolean initialized = false; private Stage stage = 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; } } |
|
返回顶楼 | |
发表时间:2007-05-11
SingletonContainerLoader
/* * 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 boolean initialized = 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(); } } |
|
返回顶楼 | |
发表时间:2007-05-11
AromaServlet
/* * 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 Log logger = LogFactory.getLog(AromaServlet.class); private static final long serialVersionUID = 5858340596717871501L; private Container container; /* * @see javax.servlet.GenericServlet#init(javax.servlet.ServletConfig) */ @Override public 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() */ @Override public 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) */ @Override protected 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); } } ··· } |
|
返回顶楼 | |
发表时间:2007-05-11
ActionEnhancer
/* * 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 Log logger = 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; } } } } |
|
返回顶楼 | |
发表时间:2007-05-11
ActionProxy
/* * 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 Log logger = LogFactory.getLog(ActionProxy.class); private static final Class[] COMMAND_METHOD_PARAM = new Class[] { Map.class }; private String command; private Object proxy; 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"; } } |
|
返回顶楼 | |
发表时间:2007-05-11
ActionSupport
/* * 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 Log logger = LogFactory.getLog(ActionSupport.class); private static final Class[] COMMAND_METHOD_PARAM = new Class[] { ActionContext.class }; private String command; /* * @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; } } |
|
返回顶楼 | |
发表时间:2007-06-15
http://ecsoftcn.iteye.com/ |
|
返回顶楼 | |