“框架”一词对我们来说并不陌生,比如建房子的时候,经常都是先把基本的结构弄好,然后再往里面添加门窗之类的,基本的结构就是框架。
我觉得框架和工具是不同的作用,简单来说,工具就是我们使用它来帮我们完成某些工作,比如输入法。而框架是将我们写的东西给它调用来完成工作,两者是有区别的。
下面就简单的来完成一个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,就可以运行
3. **Struts框架**:Apache Struts是另一个流行的Java EE Web框架,基于MVC模式。它提供了Action和Form Bean的概念,以及一系列拦截器(Interceptor)来处理请求和响应。 4. **Hibernate ORM**:在处理数据库交互时...
《架构探险-从零开始写Java Web框架》是一本深入探讨Java Web开发的书籍,作者黄勇通过本书向读者展示了如何从零构建一个完整的Web框架。全书源码的提供,使得读者能够更加直观地理解并实践书中的理论知识,这对于...
总结起来,Java Web框架开发涉及MVC模式、ORM技术、依赖注入、AOP等多个方面,学习并熟练掌握这些知识点对于成为一名合格的Java Web开发者至关重要。无论是初学者还是经验丰富的开发者,都需要持续学习和实践,以...
在本系列中,我们将一起踏上一次架构探险之旅,目标是从零构建一个完整的Java Web框架。这个过程将涵盖多个关键知识点,让我们逐步深入了解Java Web开发的核心技术,并了解如何将它们整合成一个高效、灵活的框架。 ...
SSH框架(Struts、Spring、Hibernate)是Java Web开发中的一个经典组合,为开发者提供了一个高效、灵活且可扩展的开发环境。本教程面向初学者,通过PPT形式详细讲解了从基础环境搭建到SSH框架的实战应用,旨在帮助...
2. 基于Step的Java Web框架可以减少Java代码的编写量,并提高开发效率。 3. 动态语言概念可以提高系统的灵活性和可维护性。 4. framework的设计可以采用MVC和软件流程化设计理念,以提高系统的可维护性和可扩展性。 ...
本PDF教程旨在帮助初学者和有一定经验的开发者深入理解Java Web开发和JavaScript脚本编写,以便更好地设计和实现功能丰富的网页应用程序。 在Java Web开发中,最基础的是Servlet和JSP(JavaServer Pages)。Servlet...
在Java Web开发中,SSH(Spring、Struts2、Hibernate)是一个常见的企业级应用框架组合。这个"Java Web SSH框架搭建小案例"旨在通过一个简单的登录退出功能来演示如何集成和使用这些框架。下面我们将详细讲解SSH框架...
【Java Web框架详解】 Java Web框架是用于构建Web应用程序的工具集合,它们简化了开发流程,提供了丰富的功能,以及良好的可扩展性和维护性。本文将详细介绍8种流行的Java Web框架,包括Grails、GWT、JSF、Play、...
接着,Spring MVC是Spring框架的一个模块,专门用于构建Web应用。它提供了模型-视图-控制器(MVC)的架构模式,使得开发更易于组织和测试。在Spring MVC中,接口通常由Controller类的特定方法表示,这些方法通过注解...
标题中的“基于openapi2.0接口生成对应的java web框架代码”表明了这是一个关于使用OpenAPI 2.0规范来自动生成Java Web应用程序框架代码的主题。OpenAPI(原名Swagger)是一个用于描述RESTful API的开放标准,它允许...
Java Web应用框架是开发Web应用程序的关键工具,它们提供了一种结构化的方法,使得开发者能够更高效、高质量地构建Web应用。本论文主要探讨了Java Web应用框架中的SSH框架,包括Struts2、Hibernate和Spring这三大...
本压缩包“Java框架API大全.rar”包含了三大主流Java Web框架——Spring、Hibernate、Struts的完整API文档,为开发者提供了详尽的参考资料。 首先,Spring框架是Java企业级应用的核心框架,以其依赖注入(DI)和...
4. Web框架:现代Java Web开发通常使用成熟的Web框架来简化开发流程。这本书可能会介绍一些流行的Java Web框架,比如Spring MVC,以及如何在这些框架中实现MVC(模型-视图-控制器)设计模式。 5. 服务器和部署:一...
- Struts2框架技术应用:Struts2是用于简化Java EE Web应用开发的框架,提供了一种模型-视图-控制器(MVC)的架构模式,本书从Struts2的基础概念入手,逐步引导读者了解其高级应用和框架整合。 - Hibernate框架...
SSM框架是Java Web开发中常用的三大框架集成,包括Spring MVC、Spring和MyBatis。它们各自负责不同的职责,共同构建了一个高效、灵活的后端开发解决方案。 **Spring MVC** 是Spring框架的一部分,主要处理HTTP请求...
本项目“java web不使用框架,直接mvc dao实现网站”旨在展示如何在不依赖任何特定框架的情况下,手动搭建一个基于MVC(Model-View-Controller)模式和DAO(Data Access Object)设计模式的Web应用,实现基本的分页...