“框架”一词对我们来说并不陌生,比如建房子的时候,经常都是先把基本的结构弄好,然后再往里面添加门窗之类的,基本的结构就是框架。
我觉得框架和工具是不同的作用,简单来说,工具就是我们使用它来帮我们完成某些工作,比如输入法。而框架是将我们写的东西给它调用来完成工作,两者是有区别的。
下面就简单的来完成一个WEB框架,基于注解方式完成。当然一个人的力量有限,所以很多问题没有考虑进去,比如上传文件,异常处理等等,只是一个非常简单初级的框架,也请大家多多提意见。
1. 配置servlet
对于WEB应用来说,主要有两种形式来进行用户请求拦截操作,一个是Filter,一个是servlet,这里我采用servlet来完成,所以应该在web.xml中对servlet进行配置,并且要在tomcat启动的时候完成初始化工作。
<!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>Archetype Created Web Application</display-name> <servlet> <servlet-name>dispater</servlet-name> <servlet-class>com.jacksoft.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispater</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping> </web-app>
这里我的servlet的名称是不是有点熟悉?嘿嘿
2. 导入jar
我的项目采用maven来管理,所以需要导入servlet-api的包,在pom.xml里面进行配置,将该jar导入
<properties> <servlet-api.version>2.5</servlet-api.version> </properties> <dependencies> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>${servlet-api.version}</version> </dependency> </dependencies>
3. 编码
这里先进行分析具体的流程
当客户端发起请求的时候,比如:http://127.0.0.1:8080/web/test.do,其中web是项目的path,而test.do是访问的导航路径,所以我们需要根据这个test.do来查找对应的类,然后进行处理,最后再返回相应的页面得到结果,这就是一个大概的流程,当然这之间问题很多,都一一简单处理。
(1)这里采用注解的形式,所以得先写个注解来说明某个类是专门处理请求的,Controller
package com.jacksoft.web.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 指定controller配置 * 可以配置路径,默认为空 * @author Jack * */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface Controller { String value() default ""; }
该注解名称是controller,用来定义处理请求的类
(2)定义URL 访问路径对应注解
package com.jacksoft.web.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface UrlValue { /** * 指定具体路径信息 * @return */ String url(); Method method() default Method.GET; }
注意: 这里的method方法返回的是一个枚举类型数据,也就是我们提交请求对应的方法,所以还需要创建一个枚举,便于管理数据
package com.jacksoft.web.annotation; public enum Method { GET, POST, DELETE, PUT }
简单定义几种请求方式。
(3)编写DispatcherServlet
这里采用扫描的形式来查找那些类是标记了Controller注解的,这里就全部采用硬编码的形式来设置变量,由于初始扫描包的路径可能还要子包,所以需要递归的形式来读取所以的文件信息
private void initContext(){ try{ List<String> classList = new ArrayList<String>(); String filePath = SCAN_PACKAGE.replace(".", "/"); URL url = Thread.currentThread().getContextClassLoader().getResource(filePath); File f = new File(url.toURI()); File[] files = f.listFiles(); for(File file : files){ listFile(file, classList, SCAN_PACKAGE); } for(String className : classList){ System.out.println(className); } }catch(Exception ex){ ex.printStackTrace(); } } /** * 递归查找文件,发现是文件夹,那么继续查找 * @param f * @param classList * @param filePath */ private void listFile(File f,List<String> classList,String filePath){ if(f.isFile() && f.getName().endsWith(".class")){ classList.add(filePath + "." + f.getName().replace(".class", "")); }else{ File[] files = f.listFiles(); String tmpFilePath = filePath + "." +f.getName(); for(File file : files){ listFile(file,classList,tmpFilePath); } } }
首先将包名转换为路径,然后查找下面的文件,如果是目录,那么继续查找,同时路径名称也要变化。
这样就找到了指定包下面的所以class名称,然后再通过反射来查找那些类是我们需要的。
这里创建个Controller类
package com.jacksoft.web.controller; import com.jacksoft.web.annotation.Controller; import com.jacksoft.web.annotation.Method; import com.jacksoft.web.annotation.UrlValue; @Controller public class TestController { @UrlValue(url="test.do",method=Method.GET) public String test(){ System.out.println("------TestController.test正在处理----------------"); System.out.println(req.getParameter("username")); return "test"; } }
该类已经加了controller标记,同时也添加了urlvalue的标记,先来处理controller标记,这里我们希望将访问路径,Controller以及对应的方法关联起来,所以定义了一个java bean
package com.jacksoft.web.utils; import java.lang.reflect.Method; public class MethodUtils { private String path; private String requestMethod; private Object controller; private Method method; public String getPath() { return path; } public void setPath(String path) { this.path = path; } public String getRequestMethod() { return requestMethod; } public void setRequestMethod(String requestMethod) { this.requestMethod = requestMethod; } public Object getController() { return controller; } public void setController(Object controller) { this.controller = controller; } public Method getMethod() { return method; } public void setMethod(Method method) { this.method = method; } @Override public String toString() { return "path=" + path + " requestMethod=" + requestMethod + " controller=" + controller + " method=" + method; } }
这样我们查找到需要的数据时,可以对象化后再放入map中,其中key为我们访问的servletUrl,value为刚刚创建的MethodUtils.
private static Map<String,MethodUtils> controllerMap = null;
在initContext方法中进行初始化操作
controllerMap = new HashMap<String, MethodUtils>();
将查找了又controller和urlvalue标记的信息放入mao中,因此initContext方法如下:
private void initContext(){ try{ controllerMap = new HashMap<String, MethodUtils>(); List<String> classList = new ArrayList<String>(); String filePath = SCAN_PACKAGE.replace(".", "/"); URL url = Thread.currentThread().getContextClassLoader().getResource(filePath); File f = new File(url.toURI()); File[] files = f.listFiles(); for(File file : files){ listFile(file, classList, SCAN_PACKAGE); } for(String className : classList){ Class<?> clazz = Class.forName(className); Object obj = clazz.newInstance(); //判断是否加了controller的注解 //只处理该注解,如果是,那么接着将其方法中的urlvalue注解拿到 if(clazz.isAnnotationPresent(Controller.class)){ Method[] methods = clazz.getMethods(); for(Method method : methods){ //判断方法是否加了UrlValue的注解,如果加了,那么获取对应的url和method方法 if(method.isAnnotationPresent(UrlValue.class)){ UrlValue value = method.getAnnotation(UrlValue.class); MethodUtils util = new MethodUtils(); util.setPath(value.url()); util.setRequestMethod(value.method().name()); util.setMethod(method); util.setController(obj); controllerMap.put(value.url(), util); } } } } }catch(Exception ex){ ex.printStackTrace(); } }
这样就把信息连在一起了,当用户访问test.do的时候,那么取该map中查找key=test.do,找出来之后,再通过反射就可以执行对应的方法,同时返回数据。该方法只需要在启动tomcat的时候执行,所以把他给init方法调用
@Override public void init() throws ServletException { initContext(); }
接下来就是service方法了,将接受请求的路径到map中去获取,如果能拿得到数据,那么就可以正常调用,否则返回错误页面
@Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { try{ String servletUrl = req.getServletPath().substring(1); MethodUtils utils = controllerMap.get(servletUrl); if(utils == null){ req.getRequestDispatcher("/WEB-INF/jsp/error/error404.jsp"); return; } Method method = utils.getMethod(); Object obj = method.invoke(utils.getController()); req.getRequestDispatcher("/WEB-INF/jsp/"+obj+".jsp").forward(req, resp); }catch(Exception ex){ ex.printStackTrace(); } }
到这里一个简单的框架就算完成了,启动tomcat来进行访问,访问test.do 能够正确返回test.jsp页面
4. 加上service注解
请求分发给controller后,controller还需要调用service来处理具体的事情,这里也通过注解的形式来为字段赋值,设计原理就是Service注解的名称和controller字段注解的值一样,那么就进行赋值处理
(1)定义Service注解
package com.jacksoft.web.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Service { String value() default ""; }
该注解用于service上面
(2)定义myservice,在controller中标记字段
package com.jacksoft.web.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface MyService { String value(); }
(3)创建service
既然用到service,那么就创建个,简单的打印一句话
package com.jacksoft.web.service; import com.jacksoft.web.annotation.Service; @Service("testService") public class TestService { public void sayHello(){ System.out.println("----------TestService.sayHello()--------------------------"); } }
(4) 接下来接着处理DispaterServlet,将Service注解的类也找出来存储起来
定义一个Map<String,Object>来存储数据
private Map<String,Object> serviceMap = null;
(5)在initContext方法处理controller之前,就需要处理service注解类
/** * 处理server注解 */ for(String str : classList){ Class<?> clazz = Class.forName(str); if(clazz.isAnnotationPresent(Service.class)){ Service service = clazz.getAnnotation(Service.class); serviceMap.put(service.value(), clazz.newInstance()); } }
这样我们在处理controller时,如果发现字段上面有MyService注解,并且名称一样,那么我们就为其设值
修改Controller,添加service字段
package com.jacksoft.web.controller; import com.jacksoft.web.annotation.Controller; import com.jacksoft.web.annotation.Method; import com.jacksoft.web.annotation.MyService; import com.jacksoft.web.annotation.UrlValue; import com.jacksoft.web.service.TestService; @Controller public class TestController { @MyService("testService") private TestService service; @UrlValue(url="test.do",method=Method.GET) public String test(){ System.out.println("------TestController.test正在处理----------------"); service.sayHello(); return "test"; } }
(6)处理Service注解
// 处理service注解 Field[] fields = clazz.getDeclaredFields(); for(Field field : fields){ if(field.isAnnotationPresent(MyService.class)){ MyService service = field.getAnnotation(MyService.class); String serverName = service.value(); Object serverParam = serviceMap.get(serverName); if(serverParam != null){ //为标记有注解 且名字相同的字段赋值 field.setAccessible(true); field.set(obj, serverParam); } } }
将Controller中标记有MyService注解的字段,看名称是否在map中存在,如果存在,那么就赋值
(7) 测试
其他的就不修改了,启动tomcat,进行访问test.do,调用service处理也能工作了。
5. 封装参数
在分发到controller对应方法时,还需要设置参数,在service中处理的时候,通过method对象来设置参数信息,最后通过反射将参数传递过去。
Method method = utils.getMethod(); Class<?>[] parameterClass = method.getParameterTypes(); Object[] parameter = new Object[parameterClass.length]; int i = 0; for(Class<?> clazz : parameterClass){ if(clazz.equals(HttpServletRequest.class)){ parameter[i] = req; }else if(clazz.equals(HttpServletResponse.class)){ parameter[i] = resp; } i++; } Object page = utils.getMethod().invoke(utils.getController(), parameter);
这里简单的写两种类型的参数,当然参数很多,可以通过javabean的特性来赋值。
到这里一个简单的框架就差不多了,接下来就是不断的去修改,添加,尽可能的完美。
附上DispaterServlet全部代码:
package com.jacksoft.web.servlet; import java.io.File; import java.io.IOException; import java.lang.reflect.Field; 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; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.jacksoft.web.annotation.Controller; import com.jacksoft.web.annotation.MyService; import com.jacksoft.web.annotation.Service; import com.jacksoft.web.annotation.UrlValue; import com.jacksoft.web.utils.MethodUtils; public class DispatcherServlet extends HttpServlet { /** * */ private static final long serialVersionUID = 1L; private static final String SCAN_PACKAGE = "com.jacksoft.web"; private static Map<String,MethodUtils> controllerMap = null; private Map<String,Object> serviceMap = null; @Override public void init() throws ServletException { initContext(); } private void initContext(){ try{ controllerMap = new HashMap<String, MethodUtils>(); serviceMap = new HashMap<String, Object>(); List<String> classList = new ArrayList<String>(); String filePath = SCAN_PACKAGE.replace(".", "/"); URL url = Thread.currentThread().getContextClassLoader().getResource(filePath); File f = new File(url.toURI()); File[] files = f.listFiles(); for(File file : files){ listFile(file, classList, SCAN_PACKAGE); } /** * 处理server注解 */ for(String str : classList){ Class<?> clazz = Class.forName(str); if(clazz.isAnnotationPresent(Service.class)){ Service service = clazz.getAnnotation(Service.class); serviceMap.put(service.value(), clazz.newInstance()); } } for(String className : classList){ Class<?> clazz = Class.forName(className); //判断是否加了controller的注解 //只处理该注解,如果是,那么接着将其方法中的urlvalue注解拿到 if(clazz.isAnnotationPresent(Controller.class)){ Object obj = clazz.newInstance(); // 处理service注解 Field[] fields = clazz.getDeclaredFields(); for(Field field : fields){ if(field.isAnnotationPresent(MyService.class)){ MyService service = field.getAnnotation(MyService.class); String serverName = service.value(); Object serverParam = serviceMap.get(serverName); if(serverParam != null){ //为标记有注解 且名字相同的字段赋值 field.setAccessible(true); field.set(obj, serverParam); } } } Method[] methods = clazz.getMethods(); for(Method method : methods){ //判断方法是否加了UrlValue的注解,如果加了,那么获取对应的url和method方法 if(method.isAnnotationPresent(UrlValue.class)){ UrlValue value = method.getAnnotation(UrlValue.class); MethodUtils util = new MethodUtils(); util.setPath(value.url()); util.setRequestMethod(value.method().name()); util.setMethod(method); util.setController(obj); controllerMap.put(value.url(), util); } } } } }catch(Exception ex){ ex.printStackTrace(); } } /** * 递归查找文件,发现是文件夹,那么继续查找 * @param f * @param classList * @param filePath */ private void listFile(File f,List<String> classList,String filePath){ if(f.isFile() && f.getName().endsWith(".class")){ classList.add(filePath + "." + f.getName().replace(".class", "")); }else{ File[] files = f.listFiles(); String tmpFilePath = filePath + "." +f.getName(); for(File file : files){ listFile(file,classList,tmpFilePath); } } } @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { try{ String servletUrl = req.getServletPath().substring(1); MethodUtils utils = controllerMap.get(servletUrl); if(utils == null){ req.getRequestDispatcher("/WEB-INF/jsp/error/error404.jsp"); return; } Method method = utils.getMethod(); Object obj = method.invoke(utils.getController()); req.getRequestDispatcher("/WEB-INF/jsp/"+obj+".jsp").forward(req, resp); }catch(Exception ex){ ex.printStackTrace(); } } }
相关推荐
从零开始搭建Java Web框架,使用的开发环境是IDEA15+tomcat8+jdk8+maven3; web.xml如何编写;pom.xml如何编写;将代码导入你的idea,部署一下tomcat,就可以运行
架构探险—从零开始写Java Web框架架构探险—从零开始写Java Web框架
Java Web Jquery表单验证 jQuery是一个流行的JavaScript库,可用于在网页上进行各种操作,包括表单验证。 1、将基于Jquery的表单验证的调查问卷分为四个部分:FrontPage.html、write.html、end.html、style.css。 1...
基于B/S的邮件系统客户端软件,系统全部使用java编写,Web页面使用EasyJWeb框架开发,主要通过调用java mail实现邮件的收发。 基于B/S的邮件系统客户端软件,系统全部使用java编写,Web页面使用EasyJWeb框架开发,主要通过...
简单描述:java ee spring mvc框架,maven框架编写的简单web应用程序。 适用人群:适用于java ee初学者。 可以被应用在使用spring mvc框架创建java web应用程序的领域上。 其他说明:该资源为自己手敲,如有任何使用...
特别适合初学者 PPT教程非常详细 包括JDK安装配置 TOMCAT安装配置 MyEclipse运行环境搭建 数据库使用的是MySQL SSH框架的搭建 一步一步教你做!
《Java Web开发从初学到精通》介绍如何整合Web框架进行J2EE开发,所有实例都基于MyEclipse IDE开发,引领读者快速进入基于JaVa web的J2EE应用领域。《Java Web开发从初学到精通》开始主要介绍Servlet、JSP、JavaBean...
《Java Web 服务:构建与运行(影印版)》提供了对Java的API的一个全面介绍,包括针对XML Web服务的JAX-WS和针对RESTful Web服务的JAX-RS。《Java Web服务:构建与运行》通过提供混合架构概述、完整的工作代码示例...
Spring是一种基于Java的开源框架,可以用于构建Web应用程序。利用Spring实现Java Web技术可以带来许多好处,包括: 1. 简化开发:Spring提供了一系列的模块和工具,可以帮助开发人员更快速、更高效地开发Web应用...
Web MVC和Spring Web提供了Java Web应用的框架或与其他流行的Web框架进行集成。 就是说可将两者一起使用,达到将两者自身的特点进行互补。 spring 框架介绍 : 它关注的领域是其他许多流行的Framework未曾关注的...
Java Web实现登陆注册页面前端与后台数据连接(代码+sql文件)。Java Web实现登陆注册页面前端与后台数据连接(代码+sql文件)。Java Web实现登陆注册页面前端与后台数据连接(代码+sql文件)。
各种框架其实在编写业务逻辑和服务方面并没有什么显著的不同,单从Controller和Model方面考虑,脚本语言web框架还是有一定的敏捷优势的,但是java框架已经和它们相差不多了,但是java在纵向扩展性,执行效率,静态...
《Java Web编程宝典(十年典藏版)》是一本集技能、范例、项目和应用为一体的学习手册,书中介绍了应用Java Web进行程序开发的各种技术、技巧。全书分4篇,共24章,其中,第1篇为技能学习篇,主要包括Java Web开发环境...
框架和工具: 它可能介绍常用的Java Web开发框架和工具,比如Spring Framework、Hibernate等,以及它们的使用方法和最佳实践。 数据库集成: 可能会讲解如何使用Java与数据库进行交互,包括连接数据库、执行查询和...
<<Java Web程序设计教程.pdf>>人民邮电出版社的教程哦,所以,好书,你懂的!! 第1章web应用开发简介 1 1.1何为web应用 1 1.1.1web的概念及发展 1 1.1.2web应用程序 2 1.2使用java开发web应用 3 1.2.1面向对象...
Spring是一种基于Java的开源框架,可以用于构建Web应用程序。利用Spring实现Java Web技术可以带来许多好处,包括: 1. 简化开发:Spring提供了一系列的模块和工具,可以帮助开发人员更快速、更高效地开发Web应用...
Java Web程序员直接在JSP(Java Server Page,Java服务页面)页面中书写Java代码的做法,使得...可以使简单的问题简单化处理,复杂的问题也简单化处理,甚至于不必书写任何的Java代码就可以编写出基于Java的Web系统。.
自己编写的,java web编程实例 多媒体网站数据库系统,网页不是很漂亮,因为每个人的审美不同,可是技术还是很扎实的,struts框架搭建的DAO-service-web三层结构让继续开发成为一件很容易的事情,而与postgres数据库...
基于Java Web的智慧医疗问诊系统设计与实现.pdf
JessMA Java MVC & REST应用开发框架(简称 JessMA)是一套功能完备的高性能Full-Stack Web应用开发框架,内置稳定高效的MVC基础架构和DAO框架(已内置Hibernate、MyBatis和JDBC支持),集成 Action拦截、Form Bean ...