一:編寫注解
Controller注解
開發(fā)Controller注解稍途,這個注解只有一個value屬性,默認值為空字符串倘潜,代碼如下:
package annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 自定義Controller注解
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Controller {
public String value() default "";
}
RequestMapping注解
開發(fā)RequestMapping注解绷柒,用于定義請求路徑,這個注解只有一個value屬性涮因,默認值為空字符串废睦,代碼如下:
package annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 自動以RequestMapping注解
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RequestMapping {
public String value()default "";
}
二、編寫核心的注解處理器
開發(fā)AnnotationHandleServlet
這里使用一個Servlet來作為注解處理器养泡,編寫一個AnnotationHandleServlet嗜湃,代碼如下:
package handler;
import annotation.Controller;
import annotation.RequestMapping;
import utils.BeanUtils;
import utils.DispatchActionConstant;
import utils.RequestMapingMap;
import utils.ScanClassUtil;
import utils.View;
import utils.WebContext;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Set;
/**
* 自定義注解的核心處理器,負責調用目標業(yè)務方法處理用戶請求,類似于SpringMvc的DespatcherServlet
*
* @author itguang
* @create 2018-04-05 21:54
**/
public class AnnotationHandleServlet extends HttpServlet {
/**
* 從HttpRequest中解析出 請求路徑,即 RequestMapping() 的value值.
*
* @param request
* @return
*/
private String pareRequestURI(HttpServletRequest request) {
String path = request.getContextPath() + "/";
String requestUri = request.getRequestURI();
String midUrl = requestUri.replace(path, "");
String lastUrl = midUrl.substring(0, midUrl.lastIndexOf("."));
return lastUrl;
}
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
System.out.println("AnnotationHandlerServlet-->doGet....");
this.excute(request, response);
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println("AnnotationHandlerServlet-->doPost....");
this.excute(request, response);
}
private void excute(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.將當前 HttpRequest 放到ThreadLocal中,方便在Controller中使用
WebContext.requestHodler.set(request);
//將 HttpReponse 放到ThreadLocal中,方便在Controller中使用
WebContext.responseHodler.set(response);
//2.解析請求的url
String requestUrl = pareRequestURI(request);
//3.根據 請求的url獲取要使用的類
Class<?> clazz = RequestMapingMap.getClassName(requestUrl);
//4.創(chuàng)建類的實例
Object classInstance = BeanUtils.instanceClass(clazz);
//5.獲取類中定義的方法
Method[] methods = BeanUtils.findDeclaredMethods(clazz);
//遍歷所有方法,找出url與RequestMapping注解的value值相匹配的方法
Method method = null;
for (Method m : methods) {
if (m.isAnnotationPresent(RequestMapping.class)) {
String value = m.getAnnotation(RequestMapping.class).value();
if (value != null && !"".equals(value.trim()) && requestUrl.equals(value.trim())) {
//找到要執(zhí)行的目標方法
method = m;
break;
}
}
}
//6.執(zhí)行url對應的方法,處理用戶請求
if (method != null) {
Object retObject = null;
try {
//利用反射執(zhí)行這個方法
retObject = method.invoke(classInstance);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
//如果有返回值,就代表用戶需要返回視圖
if (retObject != null) {
View view = (View) retObject;
//判斷要使用的跳轉方式
if (view.getDispathAction().equals(DispatchActionConstant.FORWARD)) {
//使用服務器端跳轉方式
request.getRequestDispatcher(view.getUrl()).forward(request, response);
} else if (view.getDispathAction().equals(DispatchActionConstant.REDIRECT)) {
//使用客戶端跳轉方式
response.sendRedirect(request.getContextPath() + view.getUrl());
} else {
request.getRequestDispatcher(view.getUrl()).forward(request, response);
}
}
}
}
@Override
public void init(ServletConfig config) throws ServletException {
/**
* 重寫了Servlet的init方法后一定要記得調用父類的init方法,
* 否則在service/doGet/doPost方法中使用getServletContext()方法獲取ServletContext對象時
* 就會出現java.lang.NullPointerException異常
*/
super.init(config);
System.out.println("---初始化開始---");
//獲取web.xml中配置的要掃描的包
String basePackage = config.getInitParameter("basePackage");
//如果配置了多個包澜掩,例如:<param-value>me.gacl.web.controller,me.gacl.web.UI</param-value>
if (basePackage.indexOf(",")>0) {
//按逗號進行分隔
String[] packageNameArr = basePackage.split(",");
for (String packageName : packageNameArr) {
initRequestMapingMap(packageName);
}
}else {
initRequestMapingMap(basePackage);
}
System.out.println("----初始化結束---");
}
/**
* @Method: initRequestMapingMap
* @Description:添加使用了Controller注解的Class到RequestMapingMap中
*/
private void initRequestMapingMap(String packageName){
//得到掃描包下的class
Set<Class<?>> setClasses = ScanClassUtil.getClasses(packageName);
for (Class<?> clazz :setClasses) {
if (clazz.isAnnotationPresent(Controller.class)) {
Method [] methods = BeanUtils.findDeclaredMethods(clazz);
for(Method m:methods){//循環(huán)方法购披,找匹配的方法進行執(zhí)行
if(m.isAnnotationPresent(RequestMapping.class)){
String anoPath = m.getAnnotation(RequestMapping.class).value();
if(anoPath!=null && !"".equals(anoPath.trim())){
if (RequestMapingMap.getRequesetMap().containsKey(anoPath)) {
throw new RuntimeException("RequestMapping映射的地址不允許重復!");
}
//把所有的映射地址存儲起來 映射路徑--類
RequestMapingMap.put(anoPath, clazz);
}
}
}
}
}
}
}
AnnotationHandleServlet的實現思路:
1.AnnotationHandleServlet初始化(init)時掃描指定的包下面使用了Controller注解的類输硝,
2.遍歷類中的方法今瀑,找到類中使用了RequestMapping注解標注的那些方法,
獲取RequestMapping注解的value屬性值点把,value屬性值指明了該方法的訪問路徑橘荠,以RequestMapping注解的value屬性值作為key,Class類作為value將存儲到一個靜態(tài)Map集合中郎逃。
3.當用戶請求時(無論是get還是post請求)哥童,會調用封裝好的execute方法 ,
execute會先獲取請求的url褒翰,然后解析該URL贮懈,根據解析好的URL從Map集合中取出要調用的目標類 ,再遍歷目標類中定義的所有方法优训,找到類中使用了RequestMapping注解的那些方法朵你,判斷方法上面的RequestMapping注解的value屬性值是否和解析出來的URL路徑一致,如果一致,說明了這個就是要調用的目標方法揣非,此時就可以利用java反射機制先實例化目標類對象抡医,然后再通過實例化對象調用要執(zhí)行的方法處理用戶請求。
在Web.xml文件中注冊AnnotationHandleServlet
就像使用SpringMvc一樣我們也需要在web.xml中進行配置:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>AnnotationHandleServlet</servlet-name>
<servlet-class>handler.AnnotationHandleServlet</servlet-class>
<init-param>
<description>配置要掃描包及其子包, 如果有多個包,以逗號分隔</description>
<param-name>basePackage</param-name>
<param-value>controller</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>AnnotationHandleServlet</servlet-name>
<!-- 攔截所有以.do后綴結尾的請求 -->
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app>
三, Controller注解和RequestMapping注解測試
新建一個controller包,在該包下面新建 LoginController.java,如:
package controller;
import annotation.Controller;
import annotation.RequestMapping;
import utils.View;
import utils.ViewData;
import utils.WebContext;
import javax.servlet.http.HttpServletRequest;
/**
* @author itguang
* @create 2018-04-06 09:26
**/
@Controller
public class LoginController {
//使用RequestMapping注解指明forward1方法的訪問路徑
@RequestMapping("login2")
public View forward1() {
System.out.println("login2...");
HttpServletRequest request = WebContext.requestHodler.get();
String username = request.getParameter("username");
String password = request.getParameter("password");
//執(zhí)行完forward1方法之后返回的視圖
return new View("/Login2.jsp");
}
/**
* 處理登錄請求,接受參數
* @return
*/
@RequestMapping("login")
public View login(){
System.out.println("login...");
//首先獲取當前線程的request對象
HttpServletRequest request = WebContext.requestHodler.get();
String username = request.getParameter("username");
String password = request.getParameter("password");
//將數據存儲到ViewData中
ViewData viewData = new ViewData();
viewData.put("msg","歡迎你"+username);
// 相當于
// request.setAttribute("msg","歡迎你"+username);
return new View("/index.jsp");
}
}
index.jsp:
<%--
Created by IntelliJ IDEA.
User: itguang
Date: 2018/4/5
Time: 15:24
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
<label style="color: red;">${msg}</label>
</body>
</html>
Login2.jsp
<%@ page language="java" pageEncoding="UTF-8"%>
<!DOCTYPE HTML>
<html>
<head>
<title>login2.jsp登錄頁面</title>
</head>
<body>
<fieldset>
<legend>用戶登錄</legend>
<form action="${pageContext.request.contextPath}/login.do" method="post">
用戶名:<input type="text" value="${param.usename}" name="username">
<br/>
密碼:<input type="text" value="${param.pwd}" name="password">
<br/>
<input type="submit" value="登錄"/>
</form>
</fieldset>
<hr/>
<label style="color: red;">${msg}</label>
</body>
</html>