简单手写一个SpringMVC,了解SpringMVC源码。
首先让我们了解一下一个SpringMVC项目的工作流程,以及所用到的方法。
下来我们进入实战。
- 创建一个maven的Web项目,在pom.xml引入Servlet依赖。
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
- 项目的目录是这样的。
- Service接口
package com.enjoy.service;
public interface JamesService {
String query(String name,String age);
}
实现类
package com.enjoy.service.impl;
import com.enjoy.annotation.EnjoyService;
import com.enjoy.service.JamesService;
//tomcat启动
//spring底层 扫描找到特殊注解的类JamesService ==创建对象jamesServiceImpl
//默认首字母小写 但是可以自己定义
//map.put("jamesServiceImpl",jamesServiceImpl)
//
@EnjoyService("JamesServiceImpl")
public class JamesServiceImpl implements JamesService {
@Override
public String query(String name, String age) {
return "{name="+name+",age="+age+"}";
}
}
- controller类
package com.enjoy.controller;
import com.enjoy.annotation.EnjoyAutowired;
import com.enjoy.annotation.EnjoyController;
import com.enjoy.annotation.EnjoyRequestMapping;
import com.enjoy.annotation.EnjoyRequestParam;
import com.enjoy.service.JamesService;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
@EnjoyController
@EnjoyRequestMapping("/james")
public class lcController {
@EnjoyAutowired("JamesServiceImpl")
private JamesService jamesService;
@EnjoyRequestMapping("/query")
public void query(HttpServletRequest request, HttpServletResponse response,
@EnjoyRequestParam("name") String name,
@EnjoyRequestParam("age") String age){
try {
PrintWriter pw = response.getWriter();
String result=jamesService.query(name,age);
pw.write(result);
} catch (IOException e) {
e.printStackTrace();
}
}
}
- 在这里的注解肯定会报错所以我们要自定义注解。
package com.enjoy.annotation;
import java.lang.annotation.*;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME) //表示在运行时通过反射获取 载体
@Documented //javadoc
public @interface EnjoyAutowired {
String value() default "";
}
package com.enjoy.annotation;
import java.lang.annotation.*;
@Target(ElementType.TYPE) //只能在类上使用
@Retention(RetentionPolicy.RUNTIME) //表示在运行时通过反射获取 载体
@Documented //javadoc
public @interface EnjoyController {
String value() default "";
}
package com.enjoy.annotation;
import java.lang.annotation.*;
@Target({ElementType.TYPE,ElementType.METHOD}) //只能在类上使用
@Retention(RetentionPolicy.RUNTIME) //表示在运行时通过反射获取 载体
@Documented //javadoc
public @interface EnjoyRequestMapping {
String value() default "";
}
package com.enjoy.annotation;
import java.lang.annotation.*;
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME) //表示在运行时通过反射获取 载体
@Documented //javadoc
public @interface EnjoyRequestParam {
String value() default "";
}
package com.enjoy.annotation;
import java.lang.annotation.*;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME) //表示在运行时通过反射获取 载体
@Documented //javadoc
public @interface EnjoyService {
String value() default "";
}
- 自己动定义Servlet这里我命名为DisptcherServlet
package com.enjoy.servlet;
import com.enjoy.annotation.*;
import com.enjoy.controller.lcController;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class DispatcherServlet extends HttpServlet {
List<String> classNames = new ArrayList<String>();
Map<String, Object> beans = new HashMap<String, Object>();
Map<String, Object> handerMap = new HashMap<String, Object>();
//扫描路径service.impl.JamesServiceImpl.class
//创建对象,实列化(反射)
//autowired
//urlhandmapping 路径映射
public void init(ServletConfig config) throws ServletException {
System.out.println("hehe");
doBasePackage("com.enjoy");
doInstance();
doAutowired();
UrlHanding();
}
public void UrlHanding() {
for (Map.Entry<String, Object> entry : beans.entrySet()) {
Object instance = entry.getValue();
Class<?> clazz = instance.getClass();
if (clazz.isAnnotationPresent(EnjoyController.class)) {
EnjoyRequestMapping map1 = clazz.getAnnotation(EnjoyRequestMapping.class);
String classPath = map1.value();
Method[] methods = clazz.getMethods();
System.out.println("urlHanding");
for (Method method : methods) {
if (method.isAnnotationPresent(EnjoyRequestMapping.class)) {
EnjoyRequestMapping map2 = method.getAnnotation(EnjoyRequestMapping.class);
String methodPath = map2.value(); //query
handerMap.put(classPath + methodPath, method);
} else {
continue;
}
}
}
}
}
public void doAutowired() {
for (Map.Entry<String, Object> entry : beans.entrySet()) {
Object instance = entry.getValue();
Class<?> clazz = instance.getClass();
if (clazz.isAnnotationPresent(EnjoyController.class)) {
Field[] fields = clazz.getDeclaredFields();
System.out.println("autowired");
for (Field field : fields) {
if (field.isAnnotationPresent(EnjoyAutowired.class)) {
EnjoyAutowired ea = field.getAnnotation(EnjoyAutowired.class);
String key = ea.value();
Object ins = beans.get(key);
field.setAccessible(true);
try {
field.set(instance, ins);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
} else {
continue;
}
}
}
}
}
public void doInstance() {
for (String className : classNames) {
//className=com.enjoy...Service.class
String cn = className.replace(".class", "");
//cn=com.emjoy...Service
try {
Class<?> clazz = Class.forName(cn);
System.out.println("doInstance");
//判断一下 是不是引用了@Controller service
if (clazz.isAnnotationPresent(EnjoyController.class)) {
//控制类 实列化 map put key value
Object instance = clazz.newInstance();
//key 拿类上边的注解
EnjoyRequestMapping map1 = clazz.getAnnotation(EnjoyRequestMapping.class);
String key = map1.value();
//key value IOC容器==MAP
beans.put(key, instance);
} else if (clazz.isAnnotationPresent(EnjoyService.class)) {
//控制类 实列化 map put key value
Object instance = clazz.newInstance();
//key
EnjoyService map1 = clazz.getAnnotation(EnjoyService.class);
String key = map1.value();
//key value IOC容器==MAP
beans.put(key, instance);
} else {
continue;
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
//递归
public void doBasePackage(String basePackage) {
//e:/workspace/pro/enjoy
URL url = this.getClass().getClassLoader().getResource("/" + basePackage.replaceAll("\\.", "/"));
String fileStr = url.getFile(); //fileStr=e:/workspace/pro/enjoy
File file = new File(fileStr);
String[] fileStrs = file.list();
for (String path : fileStrs) {
System.out.println("package");
File filePath = new File(fileStr + path);
if (filePath.isDirectory()) {
doBasePackage(basePackage + "." + path);
} else {
//.class结束的文件?1 2
//.class 保存下来
//com.enjoy.xxx.JamesService.class?
classNames.add(basePackage + "." + filePath.getName());
}
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String uri = request.getRequestURI(); //pro /james/query
String context = request.getContextPath(); //pro
String path = uri.replace(context, "");
Method method = (Method) handerMap.get(path);
System.out.println("外边");
lcController instance = (lcController) beans.get("/" + path.split("/")[1]);
Object args[]=hand(request,response,method);
System.out.println("aaa");
for (int i = 0; i < args.length; i++) {
System.out.println(args[i]);
}
try {
method.invoke(instance,args);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
private static Object[] hand(HttpServletRequest request, HttpServletResponse response, Method method) {
//拿到当前待执行的方法有那些参数
Class<?>[] paramClazzs = method.getParameterTypes();
//根据参数的个数,new 一个参数的数组,将方法里的所有参数赋值到args来
Object[] args = new Object[paramClazzs.length];
int args_i = 0;
int index = 0;
for (Class<?> paramClazz : paramClazzs) {
if (ServletRequest.class.isAssignableFrom(paramClazz)) {
args[args_i++] = request;
}
if (ServletResponse.class.isAssignableFrom(paramClazz)) {
args[args_i++] = response;
}
//从0-3判断有没有Requestparam注解,很明显paramClazz为0和1时,不是
//当为2和3时为@RequesyParam需要解析
//{@com.enjoy.annotation.EnjoyRequestparam()[index];
Annotation[] paramAns = method.getParameterAnnotations()[index];
if (paramAns.length > 0) {
for (Annotation paramAn : paramAns) {
if (EnjoyRequestParam.class.isAssignableFrom(paramAn.getClass())) {
EnjoyRequestParam rp = (EnjoyRequestParam) paramAn;
//找到注解里的name和age
args[args_i++] = request.getParameter(rp.value());
}
}
}
index++;
}
return args;
}
}
- 在web.xml配置
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>commvc</display-name>
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>com.enjoy.servlet.DispatcherServlet</servlet-class>
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
- 启动项目,访问
完美运行,不理解的可以博客下评论。求关注一下或者?一个,感谢!!!!