是一种软件架构思想、其核心思想是,要将数据处理与数据展现分开,按照这种思想,可以将一个软件划分成三种不同类型的模块,分别是模型、视图和控制器。
模型负责数据处理(业务逻辑)、视图负责数据展现(表示逻辑)、控制器负责协调模型和视图(请求要先发送给控制器,由控制器选择对应的模型来处理;模型返回的处理结果也要先发送给控制器,由控制器选择对应的视图来展现)。
使用Servlet来充当控制器,使用jsp充当视图,使用java类来充当模型。
三者的关系如下图所示:
a.方便代码的维护。
比如,视图发生变化(修改了视图的代码或者增加了新的视图,不影响模型)。
b.方便测试。
比如,将代码写在java类里面,可以直接测试,不需要部署整个应用。
c.方便分工协作。
是一个简单的MVC框架,实现了一个通用的控制器,开发人员利用该框架,只需要写业务逻辑和表示逻辑。
//告诉JVM注解的存在时间到运行期
@Retention(value=RetentionPolicy.RUNTIME)
public @interface RequestMapping {
//声明一个属性,注意要用public,而且后面由小括号。用来储存请求路径
public String value();
}
public class LoginController {
@RequestMapping("/toLogin.do")
public String toLogin() {
System.out.println("LoginController.toLogin()");
return "login";
}
@RequestMapping("/login.do")
public String login(HttpServletRequest request) {
System.out.println("LoginController.login()");
String username = request.getParameter("username");
String password = request.getParameter("password");
if("tom".equals(username)&& "1234".equals(password)) {
System.out.println("正确,重定向成功页面");
/*
* 如果视图名是以redirect:开头,表示重定向
*/
//因为浏览器无法直接访问jsp文件,所以无法直接重定向,这里返回一个请求地址,由下面的方法处理
return "redirect:toSuccess.do";
}else {
request.setAttribute("login_failed", "用户名或密码错误");
System.out.println("错误,转发登录页面");
return "login";
}
}
@RequestMapping("/toSuccess.do")
public String toSuccess(){
return "success";//视图名
}
<beans>
<bean class="controller.LoginController"/>
<bean class="..."/>
beans>
public class DispatcherServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private HandlerMapping handlerMapping;
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("DispatcherServlet.service()");
request.setCharacterEncoding("utf-8");
/*
* 先获得请求资源路径,然后截取请求资源路径的一部分,生成请求路径(path),然后调用handlerMapping的getHandler方法
* 获得Handler对象,利用Handler对象调用处理器的方法
*/
String uri = request.getRequestURI();
System.out.println("uri: "+uri);
String contextPath = request.getContextPath();
System.out.println("contextPath: "+ contextPath);
//截取请求资源路径的一部分(除掉应用名),生成请求路径
String path = uri.substring(contextPath.length());
System.out.println("path: "+path);
Handler handler = handlerMapping.getHandler(path);
System.out.println("handler: "+handler);
Method m = handler.getMh();
Object obj = handler.getObj();
Object rv = null;
try {
//获取参数的类型数组。有几个参数,数组就有几个元素
Class[] types = m.getParameterTypes();
if(types.length<=0) {
//0个元素说明是无参方法
System.out.println("调用无参方法");
rv = m.invoke(obj);//获得方法的返回值
}else {
//有参方法,参数用一个数组来存放,参数的类型自有response和request两种。方法都是从他们里面获取数据
Object[] params = new Object[types.length];
for(int i=0;i<types.length;i++) {
if(types[i]==HttpServletRequest.class) {
params[i] = request;
}
if(types[i]==HttpServletResponse.class) {
params[i]=response;
}
}
System.out.println("调用有参方法");
rv = m.invoke(obj, params);
}
System.out.println("rv: "+rv);
String viewName = (String) rv;
System.out.println("viewName: "+viewName);
if(viewName.startsWith("redirect:")) {//重定向
String redirectPath = contextPath+"/"+viewName.substring("redirect:".length());
System.out.println("redirectPath:"+redirectPath);
response.sendRedirect(redirectPath);
}else {
//转发
/*
* 依据视图名,定位到某个jsp
* "/WEB-INF/"+视图名+".jsp"
*/
String jspPath = "/WEB-INF/"+viewName+".jsp";
System.out.println("jspPath:"+jspPath);
request.getRequestDispatcher(jspPath).forward(request, response);
}
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//因为所有的处理器都只需要实例化一次,所以就在初始化的时候实例化一次就可以
public void init() throws ServletException {
System.out.println("DispatcherServlet.init()");
/*
* 读取smartmvc配置文件中处理器的配置信息,然后利用java反射将处理器实例化
*/
SAXReader reader = new SAXReader();
InputStream in = getClass().getClassLoader().getResourceAsStream("smartmvc.xml");
try {
Document doc = reader.read(in);
Element root = doc.getRootElement();
List<Element> elements = root.elements();
//beans用来存放处理器实例
List<Object> beans = new ArrayList<Object>();
for(Element bean:elements) {
String className = bean.attributeValue("class");
System.out.println(className);
//将处理器实例化
Object obj = Class.forName(className).newInstance();
beans.add(obj);
}
System.out.println("beans: "+beans);
//创建映射处理器实例
handlerMapping = new HandlerMapping();
//调用映射处理器的process方法,该方法利用java反射读取beans元素的@RequestMapping的路径信息,然后建立请求路径与处理器的对应关系
handlerMapping.process(beans);
} catch (DocumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 映射处理器
* 负责 提供请求路径与处理器的对应关系,比如"/hello.do"应该由HelloController的hello方法来处理
* @author soft01
*
*/
public class HandlerMapping {
private String path;
/*
* handlerMap用于存放请求路径与处理器的对应关系。处理器还需要对应的方法,所以用Handler类来封装
*/
private Map<String, Handler> handlerMap = new HashMap<String, Handler>();
/*
* 依据请求路径返回Handler对象。
* 注: Handler对象封装了处理器及Method对象
*/
public Handler getHandler(String path) {
return handlerMap.get(path);
}
/*
* 负责建立请求路径与Handler的对应关系。
* 首先遍历beans集合,然后利用java反射读取处理器中的@RequestMapping配置信息的请求路径
* 然后,以请求路径作为key,以Handler作为value,Handler封装了处理器及Method对象,
* 将这对关系存放到handlerMap里面
*/
public void process(List<Object> beans) {
for(Object bean:beans) {
Class cla = bean.getClass();//class对象可以看作是反射的入口,要做反射相关操作先要拿到class对象
Method[] ms = cla.getDeclaredMethods();
for(Method m:ms) {
//获得加在方法前的@RequestMapping注解
RequestMapping rm = m.getDeclaredAnnotation(RequestMapping.class);
if(rm!=null) {
//读取注解的属性值,即请求路径
String path = rm.value();
System.out.println(path);
//以请求路径为key,以Handler对象为value,将对应关系存放到handlerMap
handlerMap.put(path, new Handler(m,bean));
}
}
}
System.out.println("handlerMap: "+handlerMap);
}
step9.添加Handler(base.common包下)。
/**
* 因为调用方法需要用Method类才行,不能用类名和方法名拼接在一起,这样只是该字符串
*/
import java.lang.reflect.Method;
public class Handler {
private Method mh;
private Object obj;
public Handler(Method mh, Object obj) {
super();
this.mh = mh;
this.obj = obj;
}
public Method getMh() {
return mh;
}
public void setMh(Method mh) {
this.mh = mh;
}
public Object getObj() {
return obj;
}
public void setObj(Object obj) {
this.obj = obj;
}