2007-05-10
Guice项目实战(一)
关键字: Guice AromaRI引言
公元二零零七年,开源领域各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应用。
- 01:29
- 浏览 (3619)
- 论坛浏览 (4579)
- 评论 (8)
- 分类: 软件架构
- 相关推荐
评论
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;
}
}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";
}
}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;
}
}
}
}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);
}
}
···
}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();
}
}几个重要类的细节(一)
DefaultContainer
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;
}
}发表评论
该博客是同时发布到论坛的,无法评论在论坛已被锁定的帖子
我的相册
AromaRI
共 1 张
共 1 张
最近加入圈子
链接
最新评论
-
Guice项目实战(一)
http://ecsoftcn.javaeye.com/
-- by phantom -
Guice项目实战(一)
ActionSupport /* * EsayJF.com Inc. ...
-- by ecsoftcn -
Guice项目实战(一)
ActionProxy /* * EsayJF.com Inc. ...
-- by ecsoftcn -
Guice项目实战(一)
ActionEnhancer /* * EsayJF.com Inc. ...
-- by ecsoftcn -
Guice项目实战(一)
AromaServlet /* * EsayJF.com Inc. ...
-- by ecsoftcn









评论排行榜