1、SmartMVC是一个web mvc框架,实现了一个通用的控制器(DispatcherServlet),
利用该框架可以开发基于MVC架构的web应用。使用该框架,只需要写视图
和模型。
2、SmartMVC的主要组件及关系
3、SmartMVC执行流程
新建一个Maven工程,jar该为war,生成web.xml文件,为工程添加Tomcat服务器
4、导入依赖的包:在项目的pom.xml文件中添加以下代码
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
5、指定配置文件的位置及名称和配置启动加载,在项目的web.xml文件中的标签之前添加以下代码
<!-- 指定配置文件的位置及名称 -->
<!-- 可以使用初始化参数来指定配置文件的路径及文件名(以便读取配置文件不用写死)-->
<init-param>
<param-name>configLocation</param-name>
<param-value>smartmvc.xml</param-value>
</init-param>
<!-- 配置启动加载
即容器启动之后,会立即创建该servlet的实例, 接下来,会调用该实例的init方法、
参数是一个大于等于零的整数,越小,优先级越高,即有限创建该实例 -->
<load-on-startup>1</load-on-startup>
6、SmartMVC核心类
RequestMapping.java类
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestMapping {
String value();
}
Handler.java类
/**
* 为了方便利用Java反射机制去调用处理器的方法而设计的一个辅助类。
*/
public class Handler {
//method:处理器方法对应的Method对象
private Method method;
//obj:处理器实例
private Object obj;
public Method getMethod() {
return method;
}
public void setMethod(Method method) {
this.method = method;
}
public Object getObj() {
return obj;
}
public void setObj(Object obj) {
this.obj = obj;
}
}
HandlerMapping.java类
/**
* 映射处理器类:负责提供请求路径与处理器的对应关系:比如:"/hello.do"这个请求应该
* 有HelloController 的hello方法来处理。
*/
public class HandlerMapping {
/*
* maps:存放请求路径与处理器的对应关系。
* 注:
* Handler封装了处理器实例与方法对象, 这样方便利用Java反射去调用处理器方法。
*/
private Map<String, Handler> maps=new HashMap<String, Handler>();
/*
* 依据请求路径返回Handler对象。
* @param path 请求路径,比如"/hello.do"
* @return Handler 对象(封装有处理器实例及方法)
*/
public Handler getHandler(String path) {
return maps.get(path);
}
/*
* 负责建立请求路劲与处理器的对应关系
* @param beans 处理器实例组成的集合
*/
public void process(List beans) {
for (Object bean : beans) {
//获得Class对象
Class clazz=bean.getClass();
//获得处理器的所有方法
Method[] methods=clazz.getDeclaredMethods();
//遍历所有方法
for (Method method : methods) {
//获得加在方法前的@RequestMapping
RequestMapping rm=method.getDeclaredAnnotation(RequestMapping.class);
//获得请求路径
String path=rm.value();
System.out.println("path:"+path);
/*
* 以请求路径作为key,以处理实例及方法
* 的封装(Handler)作为value,将这个
* 对应关系存放到maps里面。
*/
Handler handler=new Handler();
handler.setMethod(method);
handler.setObj(bean);
maps.put(path, handler);
}
}
System.out.println("maps:"+maps);
}
}
DispatcherServlet.java类
public class DispatcherServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private HandlerMapping handlerMapping;
public DispatcherServlet() {
System.out.println("DispatcherServlet's constructor");
}
@Override
/**
* 读取smartmvc配置文件,依据读取到的类名创建处理器对象,然后偶将处理器对对象交给
* HandlerMapping来处理。
*/
public void init() throws ServletException {
SAXReader saxReader=new SAXReader();
//读取配置文件的位置及文件名
String configLocation = getInitParameter("configLocation");
/*
* 利用类加载器(ClassLoader)提供的方法来获得一个输入流(该流只需要读取的配置文件)
*/
InputStream in=getClass().getClassLoader().getResourceAsStream(configLocation);
try {
//解析配置文件
Document doc=saxReader.read(in);
//获取根元素
Element root=doc.getRootElement();
//beans集合用于存放处理器对象
List beans=new ArrayList();
//获取根元素下面的所有子元素
List<Element> elements=root.elements();
//遍历所有子元素
for (Element element : elements) {
//获得处理器类名
String className=element.attributeValue("class");
System.out.println("className:"+className);
//将处理器实例化(即创建处理器对象)
Object obj=Class.forName(className).newInstance();
beans.add(obj);
}
System.out.println("beans:"+beans);
/*
* 创建HandlerMapping对象,然后将
* 处理器实例组成的集合交给该对象
* 进行处理
*/
handlerMapping=new HandlerMapping();
handlerMapping.process(beans);
} catch (Exception e) {
e.printStackTrace();
System.out.println("读取配置文件失败");
}
}
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取请求路径
String uri=request.getRequestURI();
//获取应用名
String contextPath=request.getContextPath();
//将请求资源中的应用名除掉
String path=uri.substring(contextPath.length());
System.out.println("path:"+path);
/*
* 以请求路径(path)作为参数,调用HandlerMapping对象方法,获得与该请求路径对应的Handler对象,
* 接下来,就可以通过Handler对象来调用处理器的方法
*/
Handler handler=handlerMapping.getHandler(path);
System.out.println("handler:"+handler);
if(handler==null) {
response.sendError(404);
System.out.println("没有对应的处理器");
return;
}
//利用Handler对象来调用处理器的方法
Method method=handler.getMethod();
Object obj=handler.getObj();
Object rv=null;
try {
/*
* 先看处理器的方法带不带参, 如果带有参数(暂时只支持两种类型的参数,
* 分别是HttpServletRequest和HttpServletResponse), 则按照带有参数的方式调用,
* 即method.invoke(obj,params)
*/
//获得处理器方法的参数类型信息
Class[] types=method.getParameterTypes();
if(types.length==0) {
//处理器方法不带参
rv=method.invoke(obj);
}else {
//params用于存放实际参数值
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;
}
}
rv=method.invoke(obj, params);
}
//viewName:视图名
String viewName=rv.toString();
System.out.println("viewName:"+viewName);
/*
* 处理视图名
* 默认转发到"/WEB-INF/"+视图名+".jsp", 如果视图名是以"redirect:"开头,则重定向
*/
if(viewName.startsWith("redirect:")) {
//生成重定向地址
String redirectPath=contextPath+"/"+viewName.substring("redirect:".length());
//重定向
response.sendRedirect(redirectPath);
}else {
//转发
String forwardPath="/WEB-INF/"+viewName+".jsp";
request.getRequestDispatcher(forwardPath).forward(request, response);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
计算bmi指数实现
BmiController.java类
public class BmiController {
@RequestMapping("/toBmi.do")
public String toBmi() {
System.out.println("BmiController's toBmi()");
return "bmi";
}
@RequestMapping("/bmi.do")
public String bmi(HttpServletRequest request) {
System.out.println("BmiController's bmi()");
//读取升高、体重
String height=request.getParameter("height");
String weight=request.getParameter("weight");
System.out.println("height:"+height+"weight:"+weight);
BMIService bService=new BMIService();
String status=bService.bmi(Double.parseDouble(height), Double.parseDouble(weight));
//转发
request.setAttribute("status", status);
return "view";
}
}
BMIService.java类
public class BMIService {
public String bmi(double height,double weight) {
String status="正常";
double bmi=weight/height/height;
if(bmi<19) {
status="过轻";
}
if(bmi>25) {
status="过重";
}
return status;
}
7、配置BmiController组件:新建一个以xml结尾的配置文件,添加以下内容:
<beans>
<!-- 配置处理器 -->
<bean class="包名+类名"/>
</beans>
8、在项目中的WEB-INF文件夹中建对应的jsp文件
bmi.jsp
<%@ page contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<!DOCTYPE html>
<html>
<head>
<title>Insert title here</title>
</head>
<body style="font-size:30px;">
<form action="bmi.do" method="post">
<fieldset>
<legend>计算BMI指数</legend>
体重(公斤):<input name="weight"/><br/>
身高(米):<input name="height"/><br/>
<input type="submit" value="确定"/>
</fieldset>
</form>
</body>
</html>
view.jsp
<%@ page contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<!DOCTYPE html>
<html>
<head>
<title>Insert title here</title>
</head>
<body style="font-size:30px;">
体重状况:${status}
</body>
</html>