- 手撕簡易SpringMVC框架
mvc 整體架構
新建一個動態 Web 工程。整體機構如下:
-
- 手寫自己的Dispather 類,繼承 HttpServlet
@PackageScann
public class MyDispather extends HttpServlet {
private static final long serialVersionUID = -7154047248664853493L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doget" + req.getRequestURI() + ":==:" + req.getRequestURL());
resp.sendRedirect("/pages/index.html");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.err.println("dispather service()" + Thread.currentThread().getName());
String requestURI = req.getRequestURI();
if (requestURI.endsWith("/")) {
requestURI = requestURI.substring(0, requestURI.length() - 1);
}
if (IOCUtils.getUrlMaps().containsKey(requestURI)) {
Method method = IOCUtils.getUrlMaps().get(requestURI);
try {
method.invoke(IOCUtils.newInstance(method), req, resp);
} catch (Exception e) {
e.printStackTrace();
}
} else {
resp.getWriter().write("The URL NOT Found");
}
}
@Override
public void destroy() {
System.err.println("dispather destory()");
}
/**
* 獲取所有的自定義註解類,並且實例化,並且收入BeanMap 中
*/
@Override
public void init() throws ServletException {
System.out.println("dispather init()");
// 獲取掃描路徑下所有的class文件,實例化註解的bean.
IOCUtils.getBasePackage(this.getClass());
IOCUtils.createBeanMaps();
}
}
-
- 自定義過濾器,處理請求亂碼問題
public class MyServletFilter implements Filter {
@Override
public void destroy() {
System.out.println("=========過濾器銷燬了=================");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest)request;
HttpServletResponse res = (HttpServletResponse)response;
System.out.println("過濾器執行操作" + req.getQueryString());
req.setCharacterEncoding("utf-8");
// 解決瀏覽器中文亂碼
res.setHeader("content-type", "text/html;charset=utf-8");
// 解決get請求亂碼
MyRequestWarp reqs = new MyRequestWarp(req);
chain.doFilter(reqs, res);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("過濾器初始化");
}
}
-
- 自定義增強 HttpServletRequestWrapper 類,對請求的參數進行解碼
public class MyRequestWarp extends HttpServletRequestWrapper {
private HttpServletRequest request;
public MyRequestWarp(HttpServletRequest request) {
super(request);
this.request = request;
}
// 對請求request 對象的數據進行統一編碼
@Override
public Map<String, String[]> getParameterMap() {
// 獲取請求方式
String method = request.getMethod();
if ("POST".equalsIgnoreCase(method)) {
try {
request.setCharacterEncoding("utf-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
Map<String, String[]> map = request.getParameterMap();
return map;
} else if ("get".equalsIgnoreCase(method)) {
// get請求單獨對請求參數單個裝換
Map<String, String[]> map = request.getParameterMap();
System.out.println(map + ">>>>");
if (map == null) {
return super.getParameterMap();
}
Set<String> keySet = map.keySet();
String[] value = null;
String utf = null;
try {
for (String string : keySet) {
value = map.get(string);
for (int i = 0; i < value.length; i++) {
// 字符串轉碼
utf = new String(value[i].getBytes("iso-8859-1"), "utf-8");
value[i] = utf;
}
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
return super.getParameterMap();
}
@Override
public String getParameter(String name) {
// 通過value獲取值
String[] values = getParameterValues(name);
if (values != null) {
return values[0];
}
return super.getParameter(name);
}
@Override
public String[] getParameterValues(String name) {
// 通過map 集合獲取值
Map<String, String[]> parameterMap = getParameterMap();
if (parameterMap != null) {
return parameterMap.get(name);
}
return super.getParameterValues(name);
}
}
-
- 自定義 註解 Controller ,Service,RequestMapping,AutoWired,packScann
– MyController 控制層註解
/**
* 自定義控制層註解
*
* @author Donald 2019/03/23 21:08:55
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyController {
String value() default "";
}
– MyService 服務層註解
/**
* 自動定義控制層
*
* @author Donald 2019/03/23 21:58:49
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyService {
String value() default "";
}
– MyRequestMapping 請求註解
/**
* 自定義請求url 映射
*
* @author Donald 2019/03/23 22:07:26
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyRequestMapping {
String value() default "";
}
– PackageScann包掃描註解
/**
* 包掃描註解
*
* @author Donald 2019/03/23 22:38:33
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PackageScann {
String value() default "";
}
– 參數自動注入註解
/**
* 自定義自動注入
*
* @author Donald 2019/03/23 22:07:39
*/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyAutowired {
String value() default "";
}
-
- 核心IOC 容易類 處理對bean 的管理,請求映射
public class IOCUtils {
private IOCUtils() {}
private static HashMap<String, Object> beanMaps = new HashMap<>();
private static HashMap<String, Class<?>> classMaps = new HashMap<>();
private static HashMap<String, Method> urlMaps = new HashMap<>();
private static String basePackage = null;
private static Class<?> clazz = null;
public static HashMap<String, Object> getBeanMaps() {
return beanMaps;
}
public static HashMap<String, Method> getUrlMaps() {
return urlMaps;
}
public static HashMap<String, Class<?>> getClassMaps() {
return classMaps;
}
/**
* 獲取掃描的包名。默認爲dispather所在的包名
*
* @return
* @throws IOException
*/
public static void getBasePackage(Class<?> clazz) {
IOCUtils.clazz = clazz;
PackageScann annotation = clazz.getAnnotation(PackageScann.class);
if (annotation == null) {
return;
}
String className = clazz.getTypeName();
basePackage =
"".equals(annotation.value()) ? className.substring(0, className.lastIndexOf(".")) : annotation.value();
// 獲取指定包下面的所有的class 文件
getClassFiles();
}
/**
* 獲取包名及其子包下面的所有的class文件
*/
private static void getClassFiles() {
// 將com.feifan 轉換成文件夾形式com/feifan 再類加載讀取文件
String fileString = basePackage.replaceAll("\\.", "/");
// 得到com/feifan的絕對地址 、D:xxx/com/feifan
String rootPath = clazz.getClassLoader().getResource(fileString).getPath();
// 直留着com/feifan,拼接類全名
if (rootPath != null)
rootPath = rootPath.substring(rootPath.indexOf(fileString));
try {
Enumeration<URL> enumeration = clazz.getClassLoader().getResources(fileString);
while (enumeration.hasMoreElements()) {
URL url = (URL)enumeration.nextElement();
if (url.getProtocol().equals("file")) {
// 如果是文件
File file = new File(url.getPath().substring(1));
Scanner(file, rootPath);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 封裝類名,字節碼 classMaps
*
* @param file
* @param rootPath
*/
private static void Scanner(File file, String rootPath) {
File[] files = file.listFiles();
for (int i = 0; files != null && i < files.length; i++) {
File file2 = files[i];
if (file2.isDirectory()) {
Scanner(file2, rootPath + file2.getName() + "/");
} else {
if (file2.getName().endsWith(".class")) {
String classname = (rootPath + file2.getName()).replaceAll("/", ".");
// 去掉.class 後綴字節碼存入
classname = classname.substring(0, classname.lastIndexOf("."));
try {
classMaps.put(classname, Class.forName(classname));
} catch (ClassNotFoundException e) {
// TODO 獲取字節碼文件異常
e.printStackTrace();
}
}
}
}
}
/**
* 生成自定義註解註解的bean
*/
public static void createBeanMaps() {
if (classMaps == null) {
return;
}
Class<?> clazz = null;
String pathRoot = "";
String path = "";
String beanKey = "";
for (Map.Entry<String, Class<?>> entry : classMaps.entrySet()) {
clazz = entry.getValue();
// 控制層註解
if (clazz.isAnnotationPresent(MyController.class)) {
if (clazz.isAnnotationPresent(MyRequestMapping.class)) {
pathRoot = clazz.getAnnotation(MyRequestMapping.class).value();
if (!pathRoot.startsWith("/")) {
pathRoot = "/" + pathRoot;
}
if (!pathRoot.endsWith("/")) {
pathRoot = pathRoot + "/";
}
}
// url 映射路徑
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
if (method.isAnnotationPresent(MyRequestMapping.class)) {
path = method.getAnnotation(MyRequestMapping.class).value();
if (path.startsWith("/")) {
if (path.length() > 1) {
path = path.substring(path.indexOf("/") + 1);
} else {
path = "";
}
}
urlMaps.put(pathRoot + path, method);
}
}
beanKey = clazz.getSimpleName();
beanKey = beanKey.substring(0, 1).toLowerCase() + beanKey.substring(1);
try {
beanMaps.put(beanKey, clazz.newInstance());
} catch (Exception e) {
e.printStackTrace();
}
}
// 服務層註解
if (clazz.isAnnotationPresent(MyService.class)) {
Class<?>[] classes = clazz.getInterfaces();
try {
for (Class<?> class1 : classes) {
beanKey = class1.getSimpleName();
beanKey = beanKey.substring(0, 1).toLowerCase() + beanKey.substring(1);
beanMaps.put(beanKey, clazz.newInstance());
}
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
createAutowried();
}
/**
* 自動注入參數
*/
private static void createAutowried() {
if (classMaps == null) {
return;
}
Class<?> clazz = null;
String parm = "";
String beanName = "";
for (Map.Entry<String, Class<?>> entry : classMaps.entrySet()) {
clazz = entry.getValue();
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(MyAutowired.class)) {
parm = field.getType().toString();
parm = parm.substring(parm.lastIndexOf(".") + 1);
parm = parm.substring(0, 1).toLowerCase() + parm.substring(1);
beanName = clazz.getSimpleName();
beanName = beanName.substring(0, 1).toLowerCase() + beanName.substring(1);
try {
field.setAccessible(true);
field.set(beanMaps.get(beanName), beanMaps.get(parm));
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
/**
* 獲取當前方法的主類
*
* @param method
* @return
*/
public static Object newInstance(Method method) {
String simpleName = method.getDeclaringClass().getSimpleName();
simpleName = simpleName.substring(0, 1).toLowerCase() + simpleName.substring(1);
return beanMaps.get(simpleName);
}
}
-
- 業務邏輯實現
– service 接口
- 業務邏輯實現
public interface UserService {
void printf();
}
– service 實現類
@MyService
public class UserServiceImpl implements UserService {
@Override
public void printf() {
System.err.println("UserServiceImpl>>>>>>");
}
}
– controller 類
BaseController 用來獲取請求裏面的requst,response
public class BaseController {
protected HttpServletRequest request;
protected HttpServletResponse response;
protected void init(HttpServletRequest request, HttpServletResponse response) {
this.request = request;
this.response = response;
}
protected HttpServletRequest getRequest() {
return request;
}
protected HttpServletResponse getResponse() {
return response;
}
}
– 控制層
@MyController
@MyRequestMapping("my")
public class UserController extends BaseController {
@MyAutowired
private UserService UserService;
@MyRequestMapping("my")
public String getMy(HttpServletRequest req, HttpServletResponse resp) throws Exception {
System.out.println("進來了" + req.getRequestURL());
UserService.printf();
// resp.setHeader("content-type", "text/html;charset=utf-8");
resp.getWriter().write("恭喜你訪問到控制層了>>>>" + req.getParameter("name"));
return "my";
}
}
-
- 配置Web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
<display-name>SmsInterface</display-name>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>springMVC</servlet-name>
<servlet-class>com.feifan.MyDispather</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springMVC</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<filter>
<filter-name>myFilter</filter-name>
<filter-class>com.feifan.Filter.MyServletFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>myFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
啓動測試:
**
至此一波簡易的mvc 弄完
**
鏈接:https://pan.baidu.com/s/1SzcMIrcOSxo7-S--UO3hWQ
提取碼:ujg4