相信大家都用过struts或者spring mvc这样的mvc框架,先来介绍一下mvc吧,
MVC是三个单词的缩写,分别为: 模型(Model),视图(View)和控制Controller)。 MVC模式的目的就是实现Web系统的职能分工。 Model层实现系统中的业务逻辑,通常可以用JavaBean或EJB来实现。 View层用于与用户的交互,通常用JSP来实现。 Controller层是Model与View之间沟通的桥梁,它可以分派用户的请求并选择恰当的视图以用于显示,同时它也可以解释用户的输入并将它们映射为模型层可执行的操作。
mvc带来的好处 ,google一下一大箩筐,这里就不说了。
其实没个mvc底层的实现就是一个大的servlet ,来拦截我们得请求 ,分发到不同处理操作,然后跳转到相应的视图,当然这些都可配置的,mvc这个project我们也会机遇java annotation的方式来实现的
我们知道spring 通过java annotation就可以注释一个类为action ,在方法上添加上一个java annotation 就可以配置请求的路径了 ,那么我们得实现也参考这个过程来完成我们要做的事情
①定义请求路径的java annotation
RequestMapping.java
package com.ajunframework.servlet.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
*
* @author ajun
* @http://blog.youkuaiyun.com/ajun_studio
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface RequestMapping {
public String value() default "";
}
此时请求的路径 ,我们可以做一个解析 ,然后找到你的action类中方法上的注释和你解析出来的路径一致,说名就是这个方法,接着我们利用java反射机制调用这个类得方法,就可以了,接着这个方法会返回一个路径,就是我们跳转的视图返回给我们总的servlet ,就行跳转。
这些功能改怎么实现呢?
首先考虑你返回的视图的路径,其中还有包含的数据怎么办呢?还有就是服务器端跳转还是客户端跳转呢?
此时我们要定义一个视图类,对这些操作属性进行封装,其中包括条状的路径 、展现到页面的数据(这里只是做request范围内的)、跳转方式。
下面是视图类得封装代码:
package com.ajunframework.servlet.view;
import com.ajunframework.servlet.constant.DispatchActionConstant;
/**
* @author ajun
* @http://blog.youkuaiyun.com/ajun_studio
**/
public class View {
private String url;//跳转路径
private String dispathAction = DispatchActionConstant.FORWARD;//跳转方式
public View(String url) {
this.url = url;
}
public View(String url,String name,Object value) {
this.url = url;
ViewData view = new ViewData();
view.put(name, value);
}
public View(String url,String name,String dispathAction ,Object value) {
this.dispathAction = dispathAction;
this.url = url;
ViewData view = new ViewData();//请看后面的代码
view.put(name, value);
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getDispathAction() {
return dispathAction;
}
public void setDispathAction(String dispathAction) {
this.dispathAction = dispathAction;
}
}
request范围的数据存储类ViewData.java
package com.ajunframework.servlet.view;
import javax.servlet.http.HttpServletRequest;
import com.ajunframework.servlet.WebContext;
/**
* @author ajun
* @http://blog.youkuaiyun.com/ajun_studio
**/
public class ViewData {
private HttpServletRequest request;
public ViewData() {
initRequestAndResponse();
}
private void initRequestAndResponse(){
this.request = WebContext.requestHodler.get();//下面会介绍
}
public void put(String name,Object value){
this.request.setAttribute(name, value);
}
}
还有就是我们在每次请求的时候,到达action里的时候 我们会用ViewData.put()往request中存数据,而action中并没有request的情况如何处理呢?
下面我提供一个在进入action之前进行初始化request的类WebContext.java
package com.ajunframework.servlet;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
/**
* @author ajun
* @http://blog.youkuaiyun.com/ajun_studio
**/
public class WebContext {
public static ThreadLocal<HttpServletRequest> requestHodler = new ThreadLocal<HttpServletRequest>();
public HttpServletRequest getRequest(){
return requestHodler.get();
}
public HttpSession getSession(){
return requestHodler.get().getSession();
}
public ServletContext getServletContext(){
return requestHodler.get().getSession().getServletContext();
}
}
通过这个类 ,你可以当前请求的request或者session相关请求类的实例变量,线程间互不干扰的,因为用到了ThreadLocal这个类。
下面来介绍一下servlet的实现,这是无论是get post请求都会调用我封转的方法 ,这个方法,会根据你请求的url,找到你对象的action ,然后调用其中方法注释和改url配置的方法,同事返回View视图类,接着根据View中的路径,返回到相应的视图。
为了方便这里我用到了我们写的ioc,用于在servlet初始化的过程中,实例化和注入相关的class,供我们调用。这里耦合在一起了 ,不是很好。。。
DispatchServlet.java
package com.ajunframework.servlet;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.ajunframework.beans.applicationContext.AnnotationClassPathApplicationContext;
import com.ajunframework.beans.factory.AnnotationBeanFactory;
import com.ajunframework.beans.factory.RequestMapingMap;
import com.ajunframework.beans.utils.BeanUtils;
import com.ajunframework.servlet.annotation.RequestMapping;
import com.ajunframework.servlet.constant.DispatchActionConstant;
import com.ajunframework.servlet.view.View;
/**
* @author ajun
* @http://blog.youkuaiyun.com/ajun_studio
**/
public class DispatchServlet extends HttpServlet {
private static final long serialVersionUID = 5325307163972641802L;
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
/*String configPth=config.getInitParameter("configFile");
InputStream in =config.getServletContext().getResourceAsStream(configPth);
Properties p = new Properties();
try {
p.load(in);
} catch (IOException e) {
e.printStackTrace();
} */
AnnotationClassPathApplicationContext.getAnnotationClassPathApplicationContext().init();//初始化bean
}
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
this.excute(request, response);
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
this.excute(request, response);
}
private void excute(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
WebContext.requestHodler.set(request);//初始化当前请求的request
String lasturl = pareRequestURI(request);//person/list,解析url
String className = RequestMapingMap.getRequesetMap().get(lasturl);
Object actionClass = AnnotationBeanFactory.getBeanFactory().getBean(className);
Method [] methods = BeanUtils.findDeclaredMethods(actionClass.getClass());
Method method = null;
for(Method m:methods){//循环方法,找匹配的方法进行执行
if(m.isAnnotationPresent(RequestMapping.class)){
String anoPath = m.getAnnotation(RequestMapping.class).value();
if(anoPath!=null && !"".equals(anoPath.trim()) && lasturl.equals(anoPath.trim())){
method = m;
break;
}
}
}
try {
if(method!=null){
View view = (View)method.invoke(actionClass, request,response);//执行action的方法
if(view.getDispathAction().equals(DispatchActionConstant.FORWARD)){//不同的跳转方式
request.getRequestDispatcher(view.getUrl()).forward(request, response);
}else if(view.getDispathAction().equals(DispatchActionConstant.REDIRECT)){
response.sendRedirect(view.getUrl());
}else{
request.getRequestDispatcher(view.getUrl()).forward(request, response);
}
}
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
private String pareRequestURI(HttpServletRequest request){
String path = request.getContextPath()+"/";// /ajun
String requestUri = request.getRequestURI();// ajun/person/list.ajun
String midUrl = requestUri.replaceFirst(path, "");
String lasturl = midUrl.substring(0, midUrl.lastIndexOf("."));//person/list
return lasturl;
}
}
web.xml对这个servlet的配置
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<servlet>
<servlet-name>DispatchServlet</servlet-name>
<servlet-class>com.ajunframework.servlet.DispatchServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>DispatchServlet</servlet-name>
<url-pattern>*.ajun</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
这里还用到了一个跳转方式的常量类介绍给大家DispatchActionConstant.java
package com.ajunframework.servlet;
/**
* 跳转常量
* @author ajun
*
*/
public class DispatchActionConstant {
public static String FORWARD = "forward";//服务器跳转
public static String REDIRECT = "redirect";//客户端跳转
}
现在这个简单的mvc已经实现了,下一节做个测试。。。