`
zhoujiangzi
  • 浏览: 91576 次
  • 性别: Icon_minigender_1
  • 来自: 成都
社区版块
存档分类
最新评论

Java Web框架编写(一)

阅读更多

   “框架”一词对我们来说并不陌生,比如建房子的时候,经常都是先把基本的结构弄好,然后再往里面添加门窗之类的,基本的结构就是框架。

   我觉得框架和工具是不同的作用,简单来说,工具就是我们使用它来帮我们完成某些工作,比如输入法。而框架是将我们写的东西给它调用来完成工作,两者是有区别的。

   下面就简单的来完成一个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框架

    从零开始搭建Java Web框架,使用的开发环境是IDEA15+tomcat8+jdk8+maven3; web.xml如何编写;pom.xml如何编写;将代码导入你的idea,部署一下tomcat,就可以运行

    架构探险—从零开始写Java Web框架

    架构探险—从零开始写Java Web框架架构探险—从零开始写Java Web框架

    Java Web Jquery表单验证

    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 mail实现邮件的收发。 基于B/S的邮件系统客户端软件,系统全部使用java编写,Web页面使用EasyJWeb框架开发,主要通过...

    Java EE Spring MVC框架编写的简单Web应用程序

    简单描述:java ee spring mvc框架,maven框架编写的简单web应用程序。 适用人群:适用于java ee初学者。 可以被应用在使用spring mvc框架创建java web应用程序的领域上。 其他说明:该资源为自己手敲,如有任何使用...

    Java Web网络程序设计详细教程(ssh框架)

    特别适合初学者 PPT教程非常详细 包括JDK安装配置 TOMCAT安装配置 MyEclipse运行环境搭建 数据库使用的是MySQL SSH框架的搭建 一步一步教你做!

    Java Web开发从初学到精通随书光盘源码

    《Java Web开发从初学到精通》介绍如何整合Web框架进行J2EE开发,所有实例都基于MyEclipse IDE开发,引领读者快速进入基于JaVa web的J2EE应用领域。《Java Web开发从初学到精通》开始主要介绍Servlet、JSP、JavaBean...

    JAVA.WEB服务.构建与运行

     《Java Web 服务:构建与运行(影印版)》提供了对Java的API的一个全面介绍,包括针对XML Web服务的JAX-WS和针对RESTful Web服务的JAX-RS。《Java Web服务:构建与运行》通过提供混合架构概述、完整的工作代码示例...

    基于java web技术实现学生成绩管理系统附项目源码+数据库文件+文档说明

    Spring是一种基于Java的开源框架,可以用于构建Web应用程序。利用Spring实现Java Web技术可以带来许多好处,包括: 1. 简化开发:Spring提供了一系列的模块和工具,可以帮助开发人员更快速、更高效地开发Web应用...

    java三大框架

    Web MVC和Spring Web提供了Java Web应用的框架或与其他流行的Web框架进行集成。 就是说可将两者一起使用,达到将两者自身的特点进行互补。 spring 框架介绍 : 它关注的领域是其他许多流行的Framework未曾关注的...

    Java Web实现登陆注册页面前端与后台数据连接(代码+sql文件)

    Java Web实现登陆注册页面前端与后台数据连接(代码+sql文件)。Java Web实现登陆注册页面前端与后台数据连接(代码+sql文件)。Java Web实现登陆注册页面前端与后台数据连接(代码+sql文件)。

    Java在WEB开发领域的革新

    各种框架其实在编写业务逻辑和服务方面并没有什么显著的不同,单从Controller和Model方面考虑,脚本语言web框架还是有一定的敏捷优势的,但是java框架已经和它们相差不多了,但是java在纵向扩展性,执行效率,静态...

    Java Web编程宝典-十年典藏版.pdf.part2(共2个)

    《Java Web编程宝典(十年典藏版)》是一本集技能、范例、项目和应用为一体的学习手册,书中介绍了应用Java Web进行程序开发的各种技术、技巧。全书分4篇,共24章,其中,第1篇为技能学习篇,主要包括Java Web开发环境...

    《Learn Java for Web Development》这本书是针对想要使用Java进行Web开发的读者编写的

    框架和工具: 它可能介绍常用的Java Web开发框架和工具,比如Spring Framework、Hibernate等,以及它们的使用方法和最佳实践。 数据库集成: 可能会讲解如何使用Java与数据库进行交互,包括连接数据库、执行查询和...

    Java Web程序设计教程

    &lt;&lt;Java Web程序设计教程.pdf&gt;&gt;人民邮电出版社的教程哦,所以,好书,你懂的!! 第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应用程序。利用Spring实现Java Web技术可以带来许多好处,包括: 1. 简化开发:Spring提供了一系列的模块和工具,可以帮助开发人员更快速、更高效地开发Web应用...

    Java Web标签应用开发

    Java Web程序员直接在JSP(Java Server Page,Java服务页面)页面中书写Java代码的做法,使得...可以使简单的问题简单化处理,复杂的问题也简单化处理,甚至于不必书写任何的Java代码就可以编写出基于Java的Web系统。.

    java web 案例

    自己编写的,java web编程实例 多媒体网站数据库系统,网页不是很漂亮,因为每个人的审美不同,可是技术还是很扎实的,struts框架搭建的DAO-service-web三层结构让继续开发成为一件很容易的事情,而与postgres数据库...

    基于Java Web的智慧医疗问诊系统设计与实现.pdf

    基于Java Web的智慧医疗问诊系统设计与实现.pdf

    JessMA Java Web 应用开发框架 (v3.2.2-20130815).pdf

    JessMA Java MVC & REST应用开发框架(简称 JessMA)是一套功能完备的高性能Full-Stack Web应用开发框架,内置稳定高效的MVC基础架构和DAO框架(已内置Hibernate、MyBatis和JDBC支持),集成 Action拦截、Form Bean ...

Global site tag (gtag.js) - Google Analytics