自己实现的简单MVC框架(类似Struts2+Spring)

1 篇文章 0 订阅
1 篇文章 0 订阅

一、框架简介

本框架是一个类似于Struts2+Spring的框架,目的在于个人钻研和技术分享,将流行技术框架Struts2、Spring中使用到的主要技术以较为简化的方式实现出来,给大家一个更直观的呈现。(注意:本框架本身不够完善,还不足以用于商用业务开发,代码可能存在缺陷,部分功能还有优化空间;同时要说明,Struts2、Spring的实际实现较为复杂(自然功能也强大),本框架借鉴中他们本来的一些实现方式,但也做了较大改动和简化)

二、涉及到的主要技术点

1.MVC三层分离(参考资料:Struts2与MVC基础入门

 1)M(Model)为业务处理逻辑处理及持久化操作等

 2)V (View)为页面呈现,本架构中通过JSP呈现,Struts2中还可以通过VelocityFreemarker来做展示。

 3)C (Controller) 为action控制对各逻辑模块的调用和到视图层的跳转。

      实现方式:

      采用了类似struts2的配置文件格式,用于定义action、调用方法及跳转。

       页面可以提交数据到Action中使用,Action中可以将数据写入Request或Session中,在JSP页面可以通过request.getAttribute或session.getAttribute方式获取到。

2.IOC容器(参考资料:IOC容器

  实现方式:采用了类似Spring的配置文件格式,用于定义各种bean及bean间依赖,支持单实例配置。

3.AOP (参考资料:Spring AOP 详解java动态代理)

  实现方式:采用了不同于Spring的aop配置,做了简化,可以用于定义切面和对应的切面操作类。说实话,AOP这些概念说起来非常晦涩难懂,简单点说,就是利用java里的动态代理机制,对指定的一些类中的一些方法进行拦截,在这些方法执行前后插入自定义的一些操作。如在所有action类中add方法前增加一个记录日志的操作,对于所有update方法记录执行所需要的时间。除了日志的记录,比较常用的还有事务。 不过始终铭记于心,这样的拦截对我们自己的代码都是有要求的,如你指定对add开头的方法进行拦截(方法关键字支持模糊匹配,*代表多个字符),那么自己所开发的所有代码在命名时必须要满足这个格式,如果命名为Add(a变成了大写的了),那就拦截不到了。当然,如果你把拦截的范围设置的大了,则有可能误伤,把一些本不应拦截的也给拦截了。

4.与Mybatis集成

  实现方式:与mybatis 3.2.7进行集成,支持一个工厂类来产生SqlSessionFactory对象,用于DAO类中进行调用。

三、本框架与Struts2、Spring框架差异之处

1)没有支持Struts2中带有的拦截器功能

2)AOP实现方式与Spring的实现方式有差别,配置文件有较大变化

还有其他的差异,这里不一一赘述。

四、源代码下载:

https://github.com/jerrymousecn/miniMVC

其中mybatis_demo目录为与mybatis集成的样例,里面使用的数据库是mysql。

mybatis_demo\miniMVC_mybatis\mysql.sql文件为数据库表及数据创建语句;

如果需要使用此样例,需要检查ibatisConfiguration.xml中数据库配置是否正确(文件位于:mybatis_demo\miniMVC_mybatis\src\cn\jerry\mini_mvc\example\config\);

如果要自己编写其他的样例,则需要注意StudentMapper.xml、ibatisConfiguration.xml中各种路径配置是否正确(具体参考mybatis相关资料,本框架未对mybatis配置做出任何改变)

本文编写时,对于的代码发布版本:https://github.com/jerrymousecn/miniMVC/archive/1.7.zip

注:导入eclipse项目时要注意修改项目属性中"Java Build Path"对应的JRE路径,修改"Targeted Runtimes"对应的容器配置。

五、主要源代码:

1.Action控制类,入口过滤器

package cn.jerry.mini_mvc;
import java.io.File;
import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.dom4j.DocumentException;

import cn.jerry.mini_mvc.aop.AopProxyFactory;


public class MiniMVCFilter implements Filter {
	private ActionMappings actionMappings;
	@Override
	public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
			FilterChain filterChain) throws IOException, ServletException {
		HttpServletRequest request = (HttpServletRequest)servletRequest;
		HttpServletResponse response = (HttpServletResponse)servletResponse;
		ActionContext actionContext = new ActionContext(request,response);
		ActionContext.setContext(actionContext);
		try {
			String shortUri = getShortURI(request);
			if(isAcceptedAction(shortUri))
			{
				String actionName = getActionName(shortUri);
				String redirectPagePath = actionMappings.execute(request.getParameterMap(), actionName);
				RequestDispatcher dispatcher = request.getRequestDispatcher(redirectPagePath);
				dispatcher.forward(request, response);
			}
			else
			{
				filterChain.doFilter(servletRequest, servletResponse);
			}
		}catch(Exception e)
		{
			e.printStackTrace();
		}
		finally {
			ActionContext.clearUp();
		}
	}
	
	
	private boolean isAcceptedAction(String shortURI)
	{
		if(shortURI.endsWith(Constants.DEFAULT_ACTION_SUFFIX))
			return true;
		return false;
	}
	
	private String getShortURI(HttpServletRequest request)
	{
		String totalURI = request.getRequestURI();
		String contextPath = request.getContextPath();
		String shortURI = totalURI.substring(contextPath.length());
		return shortURI;
	}

	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
		ObjectFactory objectFactory =initObjectFactory(filterConfig);
		initActionMapping(filterConfig,objectFactory);
	}

	private ObjectFactory initObjectFactory(FilterConfig filterConfig)
	{
		String beanConfigPath = filterConfig.getInitParameter("bean-config");
		String beanConfigFullPath = getFullPath(filterConfig,beanConfigPath);
//		objectFactory = BeanFactory.getInstance();
		ObjectFactory objectFactory = AopProxyFactory.getInstance();
		try {
			objectFactory.init(beanConfigFullPath);
		} catch (DocumentException e) {
			e.printStackTrace();
		}
		return objectFactory;
	}
	private void initActionMapping(FilterConfig filterConfig,ObjectFactory objectFactory)
	{
		String configPath = filterConfig.getInitParameter("config");
		String configFullPath = getFullPath(filterConfig,configPath);
		actionMappings = ActionMappings.getInstance();
		try {
			actionMappings.init(configFullPath);
			actionMappings.setObjectFactory(objectFactory);
		} catch (DocumentException e) {
			e.printStackTrace();
		}
	}
	private String getFullPath(FilterConfig filterConfig,String relativePath)
	{
		String realPath = filterConfig.getServletContext().getRealPath("/");
		return realPath +"WEB-INF"+File.separatorChar+"classes"+File.separatorChar+relativePath;
	}
	private String getActionName(String shortUri)
	{
		String actionName = shortUri.substring(1,shortUri.length()-Constants.DEFAULT_ACTION_SUFFIX.length());
		return actionName;
	}
	@Override
	public void destroy() {

	}


}

2.对象工厂类,用于实现IOC容器,生成各种bean,支持单例模式

package cn.jerry.mini_mvc;

import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import org.dom4j.DocumentException;

import cn.jerry.mini_mvc.parser.XMLBean;
import cn.jerry.mini_mvc.parser.BeanParser;
import cn.jerry.mini_mvc.parser.XMLBeanProperty;

public class BeanFactory implements ObjectFactory{
	private static BeanFactory beanFactory = new BeanFactory();
	private Map
  
  
   
    singletonMap = new HashMap
   
   
    
    ();
	protected BeanFactory() {
	}
	public static BeanFactory getInstance() {
		return beanFactory;
	}
	private Map
    
    
     
      beanMap = new HashMap
     
     
      
      ();
	
	@Override
	public void init(String configFile) throws DocumentException
	{
		BeanParser beanParser = new BeanParser();
		beanParser.init(configFile);
		beanMap = beanParser.getBeanMap();
	}
	public XMLBean getBean(String beanName) throws Exception {
		XMLBean bean = beanMap.get(beanName);
		return bean;
	}
	@Override
	public Object getInstance(Object obj) throws Exception {
		return obj;
	}
	@Override
	public Object getInstanceByBeanName(String beanName) throws Exception
	{
		if(singletonMap.get(beanName)!=null)
			return singletonMap.get(beanName);
		XMLBean bean = beanMap.get(beanName);
		String className = bean.getClassName();
		Object obj = Class.forName(className).newInstance();
		
		injectObj(bean, obj);
		
		if(bean.isSingleton())
		{
			saveSingletonBean(beanName, obj);
		}
		return obj;
	}
	public void injectObj(XMLBean bean,Object obj) throws Exception
	{
		if(bean.hasProperties())
		{
			Map
      
      
        map = bean.getPropertyMap(); for(Entry 
       
         entry : map.entrySet()) { String propertyName = entry.getKey(); XMLBeanProperty beanProperty = entry.getValue(); Object beanInProperty; if(beanProperty.hasRefToOtherBean()) { beanInProperty = getInstanceByBeanName(beanProperty.getRefBeanName()); } else { beanInProperty = beanProperty.getValue(); } setBean(obj,propertyName,beanInProperty); } } } private void saveSingletonBean(String beanName,Object obj) { singletonMap.put(beanName, obj); } public void setBean(Object obj,String fieldName,Object fieldValue) throws IllegalArgumentException, IllegalAccessException, SecurityException, NoSuchFieldException { BeanUtil.setBeanProperty(obj, fieldName, fieldValue); } } 
        
      
     
     
    
    
   
   
  
  

3.AOP代理生成类(通过cglib方式生成)

package cn.jerry.mini_mvc.aop;

import java.lang.reflect.Method;
import java.util.Map;

import cn.jerry.mini_mvc.BeanUtil;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class CgLibProxy extends BaseProxy implements MethodInterceptor {

	public CgLibProxy(Map
   
   
    
     aopAspectMap) {
		super(aopAspectMap);
	}
	
	public Object getInstance(Object targetObj) {
		setTargetObj(targetObj);
		Enhancer enhancer = new Enhancer();
		enhancer.setSuperclass(targetObj.getClass());
		enhancer.setCallback(this);
		Object proxyObj =  enhancer.create();
		BeanUtil.copyBeanProperties(targetObj, proxyObj);
		return proxyObj;
	}
	public Object getInstance(Class targetClass) {
		setTargetClass(targetClass);
		Enhancer enhancer = new Enhancer();
		enhancer.setSuperclass(targetClass);
		enhancer.setCallback(this);
		Object proxyObj =  enhancer.create();
		return proxyObj;
	}

	@Override
	public Object intercept(Object proxyObj, Method method, Object[] args,
			MethodProxy proxy) throws Throwable {
		initAdvices(getTargetClass(), proxyObj, method, args);
		
		execBeforeAdvice(getTargetClass(), proxyObj, method, args);
		execAroundBeforeAdvice(getTargetClass(), proxyObj, method, args);
		Object resultObj = proxy.invokeSuper(proxyObj, args);
		execAroundAfterAdvice(getTargetClass(), proxyObj, method, args);
		execAfterAdvice(getTargetClass(), proxyObj, method, args);
		return resultObj;
	}
}
package cn.jerry.mini_mvc.aop;

import java.lang.reflect.Method;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import cn.jerry.mini_mvc.BeanFactory;

public abstract class BaseProxy {
	private Class targetClass;
	private Object targetObj;
	protected BeforeAdvice beforeAdvice;
	protected AfterAdvice afterAdvice;
	protected AroundAdvice aroundAdvice;
	protected Map
    
    
     
      aopAspectMap;

	public BaseProxy(Map
     
     
      
       aopAspectMap) {
		this.aopAspectMap = aopAspectMap;
	}
	private void resetAdvices()
	{
		beforeAdvice = null;
		afterAdvice = null;
		aroundAdvice = null;
	}

	protected void initAdvices(Class targetClass, Object proxyObj,
			Method method, Object[] args) throws Exception {
		resetAdvices();
		
		String classFullPath = targetClass.getCanonicalName();
		String methodName = method.getName();
		for (Entry
      
      
       
        entry : aopAspectMap.entrySet()) {
			AopAspect aopAspect = entry.getValue();
			String classPattern = preparePattern(aopAspect.getClasses());
			String methodPattern = preparePattern(aopAspect.getMethod());
			if(isMatch(classFullPath, methodName, classPattern, methodPattern))
			{
				BeanFactory beanFactory = BeanFactory.getInstance();
				beforeAdvice = (BeforeAdvice)beanFactory.getInstanceByBeanName(aopAspect.getBeforeAdvice());
				afterAdvice = (AfterAdvice)beanFactory.getInstanceByBeanName(aopAspect.getAfterAdvice());
				aroundAdvice = (AroundAdvice)beanFactory.getInstanceByBeanName(aopAspect.getAroundAdvice());
				break;
			}
		}
	}
	public boolean isClassAccepted(Class clazz)
	{
		String classFullPath = clazz.getCanonicalName();
		for (Entry
       
       
         entry : aopAspectMap.entrySet()) { AopAspect aopAspect = entry.getValue(); String classPattern = preparePattern(aopAspect.getClasses()); if(isMatch(classFullPath, classPattern)) { return true; } } return false; } private boolean isMatch(String classFullPath, String methodName, String classPattern, String methodPattern) { if (isMatch(classFullPath, classPattern) && isMatch(methodName, methodPattern)) { return true; } else { return false; } } private boolean isMatch(String srcStr, String pattern) { Pattern p = Pattern.compile(pattern); Matcher m = p.matcher(srcStr); if (m.find()) { return true; } else { return false; } } private String preparePattern(String inputPattern) { inputPattern = "^"+inputPattern+"$"; return inputPattern.replaceAll("\\*", ".*"); } public abstract Object getInstance(Class targetClass); public abstract Object getInstance(Object obj); protected void execBeforeAdvice(Class targetClass, Object proxyObj, Method method, Object[] args) { try { if (beforeAdvice != null) beforeAdvice.before(targetClass, proxyObj, method, args); } catch (Exception e) { e.printStackTrace(); } } protected boolean isToIntercept() { return false; } protected void execAfterAdvice(Class targetClass, Object proxyObj, Method method, Object[] args) { try { if (afterAdvice != null) afterAdvice.after(targetClass, proxyObj, method, args); } catch (Exception e) { e.printStackTrace(); } } protected void execAroundBeforeAdvice(Class targetClass, Object proxyObj, Method method, Object[] args) { try { if (aroundAdvice != null) aroundAdvice.before(targetClass, proxyObj, method, args); } catch (Exception e) { e.printStackTrace(); } } protected void execAroundAfterAdvice(Class targetClass, Object proxyObj, Method method, Object[] args) { try { if (aroundAdvice != null) aroundAdvice.after(targetClass, proxyObj, method, args); } catch (Exception e) { e.printStackTrace(); } } public void setBeforeAdvice(BeforeAdvice beforeAdvice) { this.beforeAdvice = beforeAdvice; } public void setAfterAdvice(AfterAdvice afterAdvice) { this.afterAdvice = afterAdvice; } public void setAroundAdvice(AroundAdvice aroundAdvice) { this.aroundAdvice = aroundAdvice; } protected Class getTargetClass() { return targetClass; } protected void setTargetClass(Class targetClass) { this.targetClass = targetClass; } protected Object getTargetObj() { return targetObj; } protected void setTargetObj(Object targetObj) { this.targetObj = targetObj; } } 
       
      
      
     
     
    
    
   
   

截图:

1.输入框页面,输入姓名

2.结果页面,显示一个欢迎信息,其中的姓名来自前一个提交的页面

后台打印信息(大部分是AOP类打印的,用于展示AOP操作):

BeforeAdviceImpl1 targetObj: TestAction method: execute
AroundAdviceImpl1 targetObj: TestAction method: execute
test1 in TestDao ...
AroundAdviceImpl1 targetObj: TestAction method: execute
Time Elapsed: 1 ms
AfterAdviceImpl1 targetObj: TestAction method: execute





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值