模仿寫一個簡單的mvc框架
阿新 • • 發佈:2018-12-08
手寫一個簡單的mvc框架
前言
- 為了更好的理解springmvc,前段時間寫了一個簡單的mvc,借鑑了網上找到的一些程式碼不少都是有bug的,今天把程式碼分享出來,希望能夠幫助和我一樣需要這方面學習的人。
- 在期間遇到的一些坑我會在其他篇部落格上闡述。
github連結
專案結構
程式碼
- 各個註解類
package com.mymvc.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.Target; import java.lang.annotation.RetentionPolicy; @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface MyAutowired { /** * 進行依賴注入的時候尋找filed * @return */ String value() default ""; }
package com.mymvc.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.Target; import java.lang.annotation.RetentionPolicy; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface MyController { /** * 表示給controller註冊別名 * @return */ String value() default ""; }
package com.mymvc.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.TYPE,ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface MyRequestMapping { /** * 表示訪問該方法的url * @return */ String value() default ""; }
package com.mymvc.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyRequestParam {
/**
* 表示引數的別名,必填
* @return
*/
String value();
}
package com.mymvc.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyResponsebody {
String value() default "";
}
package com.mymvc.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.annotation.RetentionPolicy;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyService {
/**
* 表示給service註冊別名
* @return
*/
String value() default "";
}
- controller
package com.mymvc.core.controller;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.alibaba.fastjson.JSONObject;
import com.mymvc.annotation.MyAutowired;
import com.mymvc.annotation.MyController;
import com.mymvc.annotation.MyRequestMapping;
import com.mymvc.annotation.MyRequestParam;
import com.mymvc.annotation.MyResponsebody;
import com.mymvc.core.pojo.User;
import com.mymvc.core.serviceImpl.UserServiceImpl;
@MyController
@MyRequestMapping("/test")
public class TestController {
@MyAutowired("user")
public UserServiceImpl userServiceImpl;
@MyRequestMapping("/string")
//@MyResponsebody
public String stringTest(HttpServletRequest request, HttpServletResponse response){
Map<String, String> map = new HashMap<String, String>();
map.put("123", "454");
return "MyHtml";
}
@MyRequestMapping("/json")
@MyResponsebody
public Map<String, Object> jsonTest(HttpServletRequest request, HttpServletResponse response){
Map<String, Object> map = new HashMap<String, Object>();
map.put("123", "454");
User user = new User();
map.put("user", user);
return map;
}
@MyRequestMapping("/userservice")
@MyResponsebody
public User userservice(HttpServletRequest request, HttpServletResponse response){
User user = userServiceImpl.getUser();
return user;
}
@MyRequestMapping("param")
@MyResponsebody
public Map paramtest(@MyRequestParam("d")String b, Integer a, Integer c,HttpServletRequest request){
Map<String ,Object> map = new HashMap<String, Object>();
map.put("a", a);
map.put("b", b);
map.put("c", c);
return map;
}
}
- pojo
package com.mymvc.core.pojo;
public class User {
public String name = "1111";
public String age = "111";
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
@Override
public String toString() {
return "User [name=" + name + ", age=" + age + "]";
}
}
- service
package com.mymvc.core.service;
import com.mymvc.core.pojo.User;
public interface UserService {
public User getUser();
}
package com.mymvc.core.serviceImpl;
import com.mymvc.annotation.MyService;
import com.mymvc.core.pojo.User;
import com.mymvc.core.service.UserService;
@MyService("user")
public class UserServiceImpl implements UserService{
@Override
public User getUser(){
User user = new User();
user.setAge("11");
user.setName("11");
return user;
}
}
- servlet
package com.mymvc.servlet;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.Type;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.filefilter.PrefixFileFilter;
import com.alibaba.fastjson.JSONObject;
import com.mymvc.annotation.MyAutowired;
import com.mymvc.annotation.MyController;
import com.mymvc.annotation.MyRequestMapping;
import com.mymvc.annotation.MyRequestParam;
import com.mymvc.annotation.MyResponsebody;
import com.mymvc.annotation.MyService;
import com.mymvc.core.controller.TestController;
import com.mymvc.core.serviceImpl.UserServiceImpl;
public class MyDispatcherServlet extends HttpServlet{
//放類名
private List<String> classNames = new ArrayList<String>();
//spring的ioc容器
private Map<String, Object> ioc = new HashMap<String, Object>();
//將掃描帶有特定註解的方法放在該map中
private Map<String, Method> handlerMapping = new HashMap<String, Method>();
//將掃描帶有contoller註解的類放在該map中
private Map<String, Object> controllerMap =new HashMap<String, Object>();
//尋找該專案靜態資源時,使用者設定的字首
private String suffix ;
//尋找該專案靜態資源時,使用者設定的字尾
private String prefix ;
//需要掃描的包名(controllr)
private String packages ;
@Override
public void init(ServletConfig config) throws ServletException {
//1.初始化基本資訊
getBasicConfig(config);
//2.初始化所有相關聯的類,掃描使用者設定的包下面所有的類
scanPackages(this.packages);
//3.拿到掃描到的類,通過反射機制,例項化,並且放到ioc容器中(k-v beanName-bean) beanName預設是首字母小寫
doInstance();
//4.進行依賴注入
doIoc();
//5.初始化HandlerMapping(將url和method對應上)
initHandlerMapping();
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try {
//處理請求
doDispatch(req,resp);
} catch (Exception e) {
resp.getWriter().write("500!! Server Exception");
}
}
private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {
//判斷是否url對映的方法是否為空
if(handlerMapping.isEmpty()){
return;
}
String url =req.getRequestURI();
String contextPath = req.getContextPath();
url=url.replace(contextPath, "").replaceAll("/+", "/");
if(!this.handlerMapping.containsKey(url)){
resp.getWriter().write("404 NOT FOUND!");
return;
}
Method method =this.handlerMapping.get(url);
//獲取方法的引數列表
Class<?>[] parameterTypes = method.getParameterTypes();
//獲取請求的引數
Map<String, String[]> parameterMap = req.getParameterMap();
//儲存引數值
Object [] paramValues= new Object[parameterTypes.length];
Parameter[] parameters = method.getParameters();
for (int i = 0; i<parameterTypes.length; i++){
String requestParam = parameterTypes[i].getSimpleName();
if (requestParam.equals("HttpServletRequest")){
//引數型別已明確,這邊強轉型別
paramValues[i]=req;
System.out.println("request :" + i);
continue;
}
if (requestParam.equals("HttpServletResponse")){
paramValues[i]=resp;
System.out.println("response :" + i);
continue;
}
for(Map.Entry<String, String[]> entry : parameterMap.entrySet()){
if(parameters[i].isAnnotationPresent(MyRequestParam.class)){
String annotationValue = ((MyRequestParam)parameters[i].getAnnotation(MyRequestParam.class)).value();
if(annotationValue.equals(entry.getKey())){
String value =Arrays.toString(entry.getValue()).replaceAll("\\[|\\]", "").replaceAll(",\\s", ",");
paramValues[i] = value;
if(parameters[i].getType().toString().equals("class java.lang.Integer"))
paramValues[i] =Integer.parseInt(value) ;
continue;
}
}
if(parameters[i].getName().equals(entry.getKey())){
String value =Arrays.toString(entry.getValue()).replaceAll("\\[|\\]", "").replaceAll(",\\s", ",");
paramValues[i] = value;
if(parameters[i].getType().toString().equals("class java.lang.Integer"))
paramValues[i] =Integer.parseInt(value) ;
continue;
}
}
}
for(Object prm : paramValues){
}
try {
Object object = method.invoke(this.controllerMap.get(url), paramValues);//第一個引數是method所對應的例項 在ioc容器中
if(method.isAnnotationPresent(MyResponsebody.class)){
if(!(null == object)){
resp.getWriter().write( JSONObject.toJSONString(object) );
}
}else {
Type type = method.getGenericReturnType();
if("class java.lang.String".equals(type.toString())){
outputTheFile(req,resp,object.toString());
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
private void getBasicConfig(ServletConfig config){
this.packages = config.getInitParameter("packagesToScan");
this.suffix = config.getInitParameter("suffix");
this.prefix = config.getInitParameter("prefix");
}
private void scanPackages(String packageName) {
//把所有的.替換成/
URL url =this.getClass().getClassLoader().getResource("/"+packageName.replaceAll("\\.", "/"));
File dir = new File(url.getFile());
for (File file : dir.listFiles()) {
if(file.isDirectory()){
//遞迴讀取包
scanPackages(packageName+"."+file.getName());
}else{
String className =packageName +"." +file.getName().replace(".class", "");
classNames.add(className);
}
}
}
private void doInstance() {
if (classNames.isEmpty()) {
return;
}
for (String className : classNames) {
try {
//通過反射機制,將controllr註解和service註解的類獲得,放在map上
Class<?> clazz =Class.forName(className);
if(clazz.isAnnotationPresent(MyController.class)){
ioc.put(toLowerFirstWord(clazz.getSimpleName()),clazz.newInstance());
}else if(clazz.isAnnotationPresent(MyService.class)){
String serviceName = clazz.getAnnotation(MyService.class).value();
if(serviceName.equals("") || serviceName == null){
ioc.put(toLowerFirstWord(clazz.getSimpleName()),clazz.newInstance());
}else {
ioc.put(serviceName,clazz.newInstance());
}
}else{
continue;
}
} catch (Exception e) {
e.printStackTrace();
continue;
}
}
}
private void initHandlerMapping(){
if(ioc.isEmpty()){
return;
}
try {
for (Entry<String, Object> entry: ioc.entrySet()) {
Class<? extends Object> clazz = entry.getValue().getClass();
if(!clazz.isAnnotationPresent(MyController.class)){
continue;
}
//拼url時,是controller頭的url拼上方法上的url
String baseUrl ="";
if(clazz.isAnnotationPresent(MyRequestMapping.class)){
MyRequestMapping annotation = clazz.getAnnotation(MyRequestMapping.class);
baseUrl=annotation.value();
}
Method[] methods = clazz.getMethods();
for (Method method : methods) {
if(!method.isAnnotationPresent(MyRequestMapping.class)){
continue;
}
MyRequestMapping annotation = method.getAnnotation(MyRequestMapping.class);
String url = annotation.value();
url =(baseUrl+"/"+url).replaceAll("/+", "/");
handlerMapping.put(url,method);
controllerMap.put(url,entry.getValue());
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
private void doIoc(){
if(ioc.isEmpty()) return ;
for(Entry<String, Object> entry : ioc.entrySet()){
Field[] fields = entry.getValue().getClass().getDeclaredFields();
for(Field field : fields){
field.setAccessible(true);
if(field.isAnnotationPresent(MyAutowired.class)){
String autowiredValueName = field.getAnnotation(MyAutowired.class).value();
if(autowiredValueName == null || autowiredValueName.equals("")){
autowiredValueName = toLowerFirstWord(field.getType().getSimpleName());
}
field.setAccessible(true);
try {
field.set(entry.getValue(), ioc.get(autowiredValueName));
} catch (IllegalArgumentException e) {
// TODO: handle exception
e.printStackTrace();
}catch (IllegalAccessException e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
}
}
/**
* 把字串的首字母小寫
* @param name
* @return
*/
private String toLowerFirstWord(String name){
char[] charArray = name.toCharArray();
charArray[0] += 32;
return String.valueOf(charArray);
}
/**
* 將檔案輸出到前端
*/
private void outputTheFile(HttpServletRequest request,HttpServletResponse response,
String filePath){
String uri =request.getRequestURI();
String dir = request.getSession(true).getServletContext().getRealPath("/");
dir = dir + prefix + "/" +filePath + suffix;
response.setContentType("text/html; charset=UTF-8");
try {
InputStream inputStream = new FileInputStream(dir);
String html = IOUtils.toString(inputStream,"UTF-8");
PrintWriter out = response.getWriter();
out.println(html);
out.flush();
out.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}catch (IOException e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
- 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" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<servlet>
<servlet-name>MySpringMVC</servlet-name>
<servlet-class>com.mymvc.servlet.MyDispatcherServlet</servlet-class>
<init-param>
<param-name>packagesToScan</param-name>
<param-value>com.mymvc.core</param-value>
</init-param>
<init-param>
<param-name>suffix</param-name>
<param-value>.html</param-value>
</init-param>
<init-param>
<param-name>prefix</param-name>
<param-value>html</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>MySpringMVC</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
- pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>mymvc</groupId>
<artifactId>mymvc</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>mymvc</name>
<description/>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>bean-validator</artifactId>
<version>3.0-JBoss-4.0.2</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.41</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.enterprise.deploy</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.jms</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.management.j2ee</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>javax.persistence</artifactId>
<version>2.0.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.resource</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.security.auth.message</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.security.jacc</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.servlet</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.servlet.jsp</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.servlet.jsp.jstl</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api-osgi</artifactId>
<version>2.2.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>jsr311-api</artifactId>
<version>1.1.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>jstl-impl</artifactId>
<version>1.2</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
<version>1.4.3</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.xml</groupId>
<artifactId>webservices-api-osgi</artifactId>
<version>2.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.weld</groupId>
<artifactId>weld-osgi-bundle</artifactId>
<version>1.0.1-SP3</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>javax.servlet.jsp.jstl</artifactId>
<version>1.2.1</version>
</dependency>
</dependencies>
<build>
<sourceDirectory>src</sourceDirectory>
<resources>
<resource>
<directory>src</directory>
<excludes>
<exclude>**/*.java</exclude>
</excludes>
</resource>
</resources>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>2.2</version>
<configuration>
<warSourceDirectory>${basedir}/WebRoot</warSourceDirectory>
<version>3.0</version>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
</plugins>
</build>
</project>