`
ispring
  • 浏览: 355615 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

基于约定的Spring MVC扩展

阅读更多

闲来无事翻了下以前写的项目,发现一个还算不错的东西,就是基于约定的Spring MVC扩展,这段代码是好早以前的东西了(大概四五年了吧),那个时候Spring还远没有现在这么“强大”,哦不,应该说是杂,现在的Spring似乎无所不能,却再也不那么专注了,基于让我有点怀念Spring1.X时代了。本人是Spring的忠实用户,但思想却一直局限于1.X时代,基本上不用Annotation,看着网络上大家对Annotation褒贬不一,呵呵,本人虽不用,但也不掺和,每个人都有自己的习惯。

这个扩展是当时没有Annotation时代,为了解决XML配置文件膨胀而产生的,原理很简单,就是依据请求的urlPath,动态的解析到所对应的处理类,然后实例化处理类,注入所需要的依赖,再执行。记录一下当年Coding的影子,虽然青涩,却也值得怀念。

 

 

/**
 * 
 */
package cn.edu.ccnu.inc.webtemplate.spring.mvc;

import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
import org.springframework.web.servlet.mvc.multiaction.InternalPathMethodNameResolver;
import org.springframework.web.servlet.mvc.multiaction.MethodNameResolver;
import org.springframework.web.servlet.mvc.multiaction.MultiActionController;

import cn.edu.ccnu.inc.webtemplate.cache.SystemCacheManager;
import cn.edu.ccnu.inc.webtemplate.util.ClassUtils;

/**
 * Convention based url handler mapping, to find a handle class based on the url hierarchy if no
 * handler found with the mapping-configuration.
 * Example:
 * <bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
 * 		<property name="defaultHandler">
 * 			 <!-- It also could configure a reference to an exist handler -->
 * 			<bean class="org.springframework.web.servlet.mvc.UrlFilenameViewController" />
 * 		</property>
 * 		<property name="packages">
 * 				<value>cn.edu.ccnu.inc.webtemplate.controller, cn.edu.ccnu.inc.webtemplate.spring.controller</value>
 * 		</property>
 *		<property name="mappings">
 *			<value>
 *	 			/login.html = filenameController
 *				/index.html = filenameController
 *				/main.html = filenameController
 *				/security/userManage.html = filenameController
 *				/security/roleManage.html = filenameController
 *			</value>
 *		</property>
 * </bean>
 * the path "/security/methodPrivilegeManage.html" doesn't match any item of above, so it will be mapped
 * to the security.UserManager class under the package cn.edu.ccnu.inc.webtemplate.controller or under
 * cn.edu.ccnu.inc.webtemplate.spring.controller package to handle. When no controller found with this strategy,
 * a defaultHandler will be used if it is configured(eg..
 * @author <a href="mailto:huangfengjing@gmail.com">Ivan</a>
 * @created 2005-10-29 下午12:41:30
 */
public class ConventionBasedUrlHandlerMapping extends SimpleUrlHandlerMapping implements InitializingBean {
	
	SystemCacheManager systemCacheManager = null;
	private String CACHE_NAME_IN_SYSTEM = "_system_cache_handler";
	
	/** Default handler when no handler mapped */
	private Object defaultHandler;

	/** Packages to look for the Handler class */ 
	private String packages;
	
	/**
	 * Config the packages
	 * @param packages
	 */
	public void setPackages(String packages) {
		this.packages = packages;
	}

	/**
	 * @param defaultHandler the defaultHandler to set
	 */
	public void setDefaultHandler(Object defaultHandler) {
		this.defaultHandler = defaultHandler;
	}


	/**
	 * Override the default lookup strategy, when the parent can not find an appropriate one to handle.
	 * Lookup strategy:
	 * 1. ask parent mapping to look up handler.
	 * 2. if no handler found, then use url hierarchy build a short name to retrieve the bean as the handler.
	 * 3. if still no handler found, assemble a full class name with package and url hierarchy, then load and
	 *    initiate a class with reflection, inject all needed properties, finally use this object as the handler.
	 * 4. finally, if no handler found through all of above ways, use a default one.
	 * 5. if no default handler specified, return null.
	 */
	@Override
	protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
		Object handler = super.lookupHandler(urlPath, request);
		
		Map<Object, Object> handlerCache = systemCacheManager.getCache(CACHE_NAME_IN_SYSTEM);
		// get handler from the cache
		handler = handlerCache.get(urlPath);
		if(handler != null) {
			return handler;
		}
		
		// load the Controller as the handler
		String className;
		String[] pkgs = StringUtils.commaDelimitedListToStringArray(packages);
		
		for(String pkg : pkgs) {
			className = pkg + "." + ClassUtils.convertUrlPathToClassName(urlPath, true);
			handler = ClassUtils.loadModelWithApplicationContextFirst(className, getApplicationContext(), null);
			
			if((handler != null && handler instanceof MultiActionController) && ((MultiActionController)handler).getMethodNameResolver().getClass().equals(InternalPathMethodNameResolver.class)) {
				try {
					MethodNameResolver methodNameResolver = (MethodNameResolver)BeanFactoryUtils.beanOfTypeIncludingAncestors(getApplicationContext(), MethodNameResolver.class, true, true);
					((MultiActionController)handler).setMethodNameResolver(methodNameResolver);
				} catch (BeansException be) {
					// ignore
				}
				break;
			}
		}
			
		// use the default handler if it is specified
		if(handler == null && defaultHandler != null) {
			handler = defaultHandler;
		}
		
		// if find one, put it to the cache
		if(handler != null) {
			handlerCache.put(urlPath, handler);
		}
		return handler;
	}
	
	/* (non-Javadoc)
	 * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
	 */
	public void afterPropertiesSet() throws Exception {
		Assert.notNull(packages, "Packages can not be null.");
		if(systemCacheManager == null) {
			systemCacheManager = (SystemCacheManager)BeanFactoryUtils.beanOfTypeIncludingAncestors(getApplicationContext(), SystemCacheManager.class);
		}
	}
}

 

/**
 * 
 */
package cn.edu.ccnu.inc.webtemplate.util;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.util.ReflectionUtils.FieldFilter;

/**
 * 
 * @author <a href="mailto:huangfengjing@gmail.com">Ivan</a>
 * @created 2005-11-1 上午12:40:04
 */
public abstract class ClassUtils extends org.springframework.util.ClassUtils {

	/**
	 * Normalize the control/userManage.html/.htm/.jsp/.vm... name to the standard
	 * class name like control.UserManage 
	 * @param urlPath
	 * @return
	 */
	public static String convertUrlPathToClassName(String urlPath, boolean hasSuffix) {
		if(urlPath.startsWith("/")) {
			urlPath = urlPath.substring(1);
		}
		urlPath = urlPath.replaceAll("/", ".");
		
		int index = urlPath.length();
		if(hasSuffix) {
			// get rid of resource suffix such as .html/.htm/.jsp/.vm and so no
			index = urlPath.lastIndexOf(".");
			if(index < 0) {
				return "";
			}
		}
		
		String handlerName = urlPath.substring(0, index);
		index = handlerName.lastIndexOf(".");
		StringBuffer sb = new StringBuffer(handlerName);
		if(index < handlerName.length() && Character.isLowerCase(handlerName.charAt(index + 1))) {
			sb.setCharAt(index + 1, Character.toUpperCase(handlerName.charAt(index + 1)));
		}
		
		return sb.toString();
	}
	
	/**
	 * Load a module class from application context, if not found, then use reflection to initiate one
	 * and inject all it's properties which is not set yet and the value has been configured in context.
	 * @param fullname
	 * @param context
	 * @return
	 */
	public static Object loadModelWithApplicationContextFirst(String fullname, ApplicationContext context, FieldFilter filter) {
		Object model = null;
		
		String shortName = ClassUtils.getShortName(fullname);
		StringBuffer sb = new StringBuffer(shortName);
		sb.setCharAt(0, Character.toLowerCase(shortName.charAt(0)));
		try {
			model = context.getBean(sb.toString());
		} catch (BeansException be) {
			// ignore
		}
		if(model != null) {
			return model;
		}
		
		try {
			model = ClassUtils.forName(fullname).newInstance();
			injectDependencies(model, context, filter);
		} catch (ClassNotFoundException e) {
			// ignore, continue
		} catch (InstantiationException e) {
			// ignore, continue
		} catch (IllegalAccessException e) {
			// ignore, continue
		}
		
		return model;
	}
	
	/**
	 * Initialize all the properties which has Setter, the value comes from Spring ApplictionContext
	 * By default, the static/final/volatile/native field will no be injected.
	 * @param bean
	 */
	public static void injectDependencies(Object bean, ApplicationContext context, FieldFilter filter) {
		Method[] methods = ReflectionUtils.getAllDeclaredMethods(bean.getClass());
		Field[] fields = ReflectionUtils.getAllFields(bean.getClass());
		for(Field field : fields) {
			if(filter == null) {
				filter = new FieldFilter() {
					public boolean matches(Field field) {
						int modifier = field.getModifiers();
						if(Modifier.isStatic(modifier) || Modifier.isFinal(modifier)
								|| Modifier.isVolatile(modifier) || Modifier.isNative(modifier)) {
							return false;
						}
						return true;
					}
				};
			}
			if(!filter.matches(field)) {
				continue;
			}
			ReflectionUtils.makeAccessible(field);
			try {
				if(field.get(bean) != null) {
					continue;
				}
			} catch (Exception e) {
				continue;
			}
			
			// make sure it has a Setter or we will skip this field
			StringBuffer startdMethodName = new StringBuffer(field.getName());
			startdMethodName.setCharAt(0, Character.toUpperCase(field.getName().charAt(0)));
			startdMethodName.insert(0, "set");
			for(Method method : methods) {
				if((startdMethodName.toString()).equals(method.getName())) {
					try {
						ReflectionUtils.invokeMethod(method, bean, new Object[] {context.getBean(field.getName())});
						break;
					} catch (BeansException be) {
						// ignore
					}
				}
			}
		}
	}
}
 

0
0
分享到:
评论

相关推荐

    Webx3 Guide Book pdf

    Webx是基于经典MVC设计模式的WEB框架,推崇页面驱动和约定胜于配置的理念。 Webx是一个基于 Spring的组件框架。组件是一个软件包,它可以被其它组件扩展,也可以扩展其它组件。利用这些特性,Webx不仅能够用来开发...

    2019年毕业设计-一款情侣APP 附论文、作品视频演示、代码

    1、Spring Boot是目前主流的以java为开发语言的后端框架,以“约定优于配置”为约定,简化Spring项目的配置。Spring Boot并非一种新的框架,而是为方便整合Spring、Spring MVC以及各种持久化框架所编写。Spring能...

    SSM项目健身房众筹系统.zip

    健身房众筹系统是一款基于SSM框架(Spring、Spring MVC、MyBatis)开发的Web应用程序,旨在为健身爱好者和投资者提供一个在线协作平台,以筹集资金开设或扩展健身房。以下是该系统的主要特点: 1. **项目发布**:...

    ThinkPHP Increx数据库应用系统开发平台

    类比下,就是Spring MVC中实现Controller接口的那些AbstractController之类; 6. 在视图层,对页面元素(css/js/image)等,尤其是其路径,做了大量的约定以便于开发保持一致,具体看Public目录,(偶比较喜欢JQuery...

    java web 开发详解

     优点: 架构简单——易于扩展 标记库很容易利用FreeMarker或者Velocity来定制 基于控制器或者基于页面的导航 缺点: 文档组织得很差 对新特征过分关注 Tapestry  优点: 一旦学会它,将极大地提高生产率 HTML模板...

    etmvc:一个简易高效的web开发框架

    etmvc是基于Java技术,为WEB开发提供的一套简易MVC框架。 简易但不简单,其设计和实现借鉴业界众多的优秀框架,如struts,spring mvc,ror,grails等, 力图为Java开发人员提供一套高效的开发框架。 基于java技术开发,...

    webx3框架指南PDF教程附学习Demo

    • 扩展性 —— Webx 3.0对Spring做了扩展,使Spring Bean不再是“bean”,而是升级成“组件”。一个组件可以扩展另一个组件,也可以被其它组件扩展。这种机制造就了Webx的非常好的扩展性,且比未经扩展的Spring更易...

    单点登录源码

    SpringMVC | MVC框架 | [http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#mvc](http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#mvc) ...

    Java语言基础下载

    Java编码约定 56 运算符的优先级 58 升级和表达式的类型转换 62 独立实践 70 第五章:数组 71 学习目标 71 数组的描述 72 创建数组 72 多维数组 78 拷贝数组 80 内容总结 83 独立实践 84 第六章:继承 86 学习目标:...

    asp.net知识库

    asp.net2.0:扩展ImageButton控件定制自己需要的功能 ASP.NET 2.0 正式版中无刷新页面的开发(示例代码的补充) ASP.NET2.0中themes、Skins轻松实现网站换肤! ASP.NET 2.0 中的代码隐藏和编译 ASP.NET 2.0 Language ...

Global site tag (gtag.js) - Google Analytics