手写SpringMVC 框架
上篇已经介绍过了SpringMVC的九大组件,本篇实战手写SpringMVC 框架。
手写SpringMVC 框架步骤
-
加载配置文件
-
扫描配置包
-
利用反射机制,实例化存放IOC容器
-
初始化 HandlerMapping
-
异常拦截
-
处理参数
-
HandlerMapping 通过url反射调用方法
手写SpringMVC
配置文件:
<!-- 在web.xml 配置资源文件 -->
<servlet>
<servlet-name>DWSpringMVC</servlet-name>
<servlet-class>com.davidwang.servlet.DWDispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>application.properties</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>DWSpringMVC</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
//application.properties 里面存放Controller 路径
scan_package = com.davidwang.controller
注解接口:
//接口、类、枚举、注解
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DWController {
//Controller 注册别名
String value() default "";
}
//方法
@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestMapping {
// 方法注解别名
String value() default "";
}
//字段、枚举的常量
@Target({ ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Quatifier {
// 注解注解别名
String value() default "";
}
//参数
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DWRequestParam {
String value();
}
继承 HttpServlet :
public class MyDispatcherServlet extends HttpServlet{
private Properties properties = new Properties();
private List<String> classNames = new ArrayList<>();
private Map<String, Object> ioc = new HashMap<>();
private Map<String, Method> handlerMapping = new HashMap<>();
private Map<String, Object> controllerMap =new HashMap<>();
@Override
public void init(ServletConfig config) throws ServletException {
//1.加载配置文件
doLoadConfig(config.getInitParameter("contextConfigLocation"));
//2.初始化所有相关联的类,扫描用户设定的包下面所有的类
doScanner(properties.getProperty("scan_package"));
//3.拿到扫描到的类,通过反射机制,实例化,并且放到ioc容器中(k-v beanName-bean) beanName默认是首字母小写
doInstance();
//4.初始化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(e);
}
}
private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {
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];
//方法的参数列表
for (int i = 0; i<parameterTypes.length; i++){
//根据参数名称,做某些处理
String requestParam = parameterTypes[i].getSimpleName();
if (requestParam.equals("HttpServletRequest")){
//参数类型已明确,这边强转类型
paramValues[i]=req;
continue;
}
if (requestParam.equals("HttpServletResponse")){
paramValues[i]=resp;
continue;
}
if(requestParam.equals("String")){
for (Entry<String, String[]> param : parameterMap.entrySet()) {
String value =Arrays.toString(param.getValue()).replaceAll("\\[|\\]", "").replaceAll(",\\s", ",");
paramValues[i]=value;
}
}
}
//利用反射机制来调用
try {
method.invoke(this.controllerMap.get(url), paramValues);//第一个参数是method所对应的实例 在ioc容器中
} catch (Exception e) {
e.printStackTrace();
}
}
private void doLoadConfig(String location){
//把web.xml中的contextConfigLocation对应value值的文件加载到流里面
InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(location);
try {
//用Properties文件加载文件里的内容
properties.load(resourceAsStream);
} catch (IOException e) {
e.printStackTrace();
}finally {
//关流
if(null!=resourceAsStream){
try {
resourceAsStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private void doScanner(String packageName) {
//把所有的.替换成/
URL url =this.getClass().getClassLoader().getResource("/"+packageName.replaceAll("\\.", "/"));
File dir = new File(url.getFile());
for (File file : dir.listFiles()) {
if(file.isDirectory()){
//递归读取包
doScanner(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 {
//把类搞出来,反射来实例化(只有加@DWController需要实例化)
Class<?> clazz =Class.forName(className);
if(clazz.isAnnotationPresent(DWController.class)){
ioc.put(toLowerFirstWord(clazz.getSimpleName()),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(RequestMapping.class)){
RequestMapping annotation = clazz.getAnnotation(RequestMapping.class);
baseUrl=annotation.value();
}
Method[] methods = clazz.getMethods();
for (Method method : methods) {
if(!method.isAnnotationPresent(RequestMapping.class)){
continue;
}
RequestMapping annotation = method.getAnnotation(RequestMapping.class);
String url = annotation.value();
url =(baseUrl+"/"+url).replaceAll("/+", "/");
handlerMapping.put(url,method);
controllerMap.put(url,clazz.newInstance());
System.out.println(url+","+method);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 把字符串的首字母小写
* @param name
* @return
*/
private String toLowerFirstWord(String name){
char[] charArray = name.toCharArray();
charArray[0] += 32;
return String.valueOf(charArray);
}
测试Controller
@DWController
@RequestMapping("/api")
public class TestController {
@Quatifier("TestServiceImpl")
TestService testService;
@RequestMapping("/test1")
public void test1(HttpServletRequest request, HttpServletResponse response,@RequestParam("name") String name){
System.out.println(param);
testService.insert(name);
}
@RequestMapping("/test2")
public void test2(HttpServletRequest request, HttpServletResponse response,@RequestParam("name") String name){
System.out.println(param);
testService.update(name);
}
}