`

很多初学或将学java web的朋友总是被一系列异于常规java project的流程结构所困惑,搞不清事情的本质,这里就以最简单的方式来让初出茅庐的新手对java web项目有个清晰明了的认识。

学java web的必定先行学过java基础,众所周知,java项目运行于一个public类中的一个pulblic static void main(String[])函数,然而java web中既没有了常规的main方法入口点同时各种纷乱的东西如jsp、html、tomcat以及servlet等也很容易让人陷入迷茫,就此使许多人在心中把java项目与web项目之间划起了天堑鸿沟。这里就带着这些问题看我给大家简单写上一个简单的“tomcat”来帮助初学者把java web与常规java项目统一起来,以利于朋友们在最初就能对java web项目有个较常规的认识。

首先,我们来研究下java web项目的运行过程。与普通java程序的main入口点不同,web似乎完全找不到了其入口点,然而需明确一点的是web项目中的servlet本身就是java类,同样是需要编译成.class被加载的,即使是jsp文件也是会经由jsp引擎转化为一个servlet被执行(html文件则仅被用来呆板的传输给双方浏览器解释)。所以web项目本质上还是一个java项目。那么它与传统java项目的差异又该作何解释?java项目根据程序流程触发,web项目则是基于网络访问事件触发,故本质上是一个事件驱动系统,然而任何事件驱动系统本身还是有一个确定的入口的,这点web也不例外。入口是有的,main也是有的,只是这些东西都被隐藏了起来,就是tomcat(亦或是其他web容器)的入口。这一点正如微软的MFC库封装了c++常规的main或WinMain入口一样,Tomcat也封装了java的main入口。

 下面我们就来动手写一个简单的tomcat仿真程序,此处必然绕不开java的反射机制(事实上所谓的Bean、控制反转、依赖注入等概念均离不开反射机制)。

1.首先我们来创建一个java项目Tomcat,创建一个Servlet.MyServletIn接口,添加一个service方法,以模拟servlet中的service方法。

2.将我们的Servlet.MyServletIn接口导出为ServletPkg.jar文件作为我们的servlet库。

3.在项目根目录下添加TomcatConf.txt文件作为Tomcat的配置文件,这里简单起见采用普通文本而非Xml文件,此文件中存放两行内容 ,第一行所要部署servlet项目目录,第二行你自己的真实Servlet类名(包含包路径)。

4.创建一个Core.TomcatCore类,并在其中添加main,作为整个容器的入口,在main中完成初始化tomcat本身、通过TomcatConf.txt配置文件下的servlet文件系统路径及类包路径信息,加载所部署servlet等工作。

5.另创建一个java项目MyWeb(此项目不需要main,用来模拟我们的web项目,当然这里只有servlet而已。)

6.将我们的”Servlet库“ServletPkg.jar导入我们的”Web项目“。

7.在MyWeb中添加一个实现了MyServletIn接口的类MyServlet。

8.实现MyServlet的抽象方法service模仿真实Servlet的行为。

9.部署我们的"web项目“到我们的Tomcat,即将我们的"web项目"根下的bin路径写入Tomcat根下的配置文件(TomcatConf.txt)的第一行,并将我们的Servlet类名写入配置文件第二行。如下:

F:\Users\smy\workspace-eclipse\MyServlet\bin
MyWeb.MyServlet

10.运行我们的"tomcat"。

下面看具体代码实现:

Tomcat项目:

自己动手写一个Tomcat_其他自己动手写一个Tomcat_其他_02TomcatCore.java :

package Core;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;

import Servlet.MyServletIn;
public class TomcatCore {

  public static void main(String[] args) throws InterruptedException, IOException, ClassNotFoundException, InstantiationException, IllegalAccessException {
    // TODO Auto-generated method stub
    System.out.println("Hellow,I'm MyTomcat ,and I'm in the initialization... ");
    Thread.sleep(3000);
    System.out.println("Now,I have finished the initialization .");
    Thread.sleep(1000);
    System.out.println("Now I will start  the Servlet.");
    //读取配置文件获取Servlet的项目路径名及类包路径名
    FileReader reader = new FileReader("./TomcatConf.txt");
        BufferedReader br = new BufferedReader(reader);
        String path = br.readLine();
        String className = br.readLine();
        br.close();
        reader.close();
        
        //根据已获取Servlet的项目路径名及类包路径名通过URL类加载器加载文件系统中的某个.class
        File file = new File(path);   
        URL url = file.toURI().toURL();//这里取文件系统的URL地址
        @SuppressWarnings("resource")
    URLClassLoader loader = new URLClassLoader(new URL[]{url});//创建持有我们所部署的"web项目"路径的URL类加载器,以使Tomcat之外的"web"纳入Tomcat的classpath之中。
        Class<?> tidyClazz = loader.loadClass(className); //利用反射加载类
        MyServletIn serv=(MyServletIn)tidyClazz.newInstance();//转化为Servlet接口执行service操作。
        serv.service();//当然,实际的tomcat并不在这里调用service,而仅仅是进入事件循环,在有浏览器请求时才调用service。  
  }
}

MyServletIn.java:

package Servlet;

public interface MyServletIn {
  public void service();

}

TomcatConf.txt:

F:\Users\smy\workspace-eclipse\MyWeb\bin
MyWeb.MyServlet

现在来看看我们的"Web项目"----MyWeb:

自己动手写一个Tomcat_其他_03

MyServlet.java:

package MyWeb;

import Servlet.MyServletIn;

public class MyServlet implements MyServletIn {


  public void service() {
    // TODO Auto-generated method stub
    System.out.println("Hellow I'm the servlet.");
    System.out.println("Now let's start our web http travel.");
    try {
      Thread.sleep(3000);
    } catch (InterruptedException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
    DAOOperator dao = new DAOOperator("Mysql");
    try {
      dao.operate();
    } catch (InterruptedException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
    System.out.println("Now I will beleave the web world...");
    System.out.println("Bye-bye.");
  }

}

DAOOperator.java:

package MyWeb; 
//这里只是模拟数据库操作
public class DAOOperator {
  private String name;
  public DAOOperator(String _name)
  {
    name = _name;
  }
  public void operate() throws InterruptedException
  {
    System.out.println("I will do much work...");
    Thread.sleep(3000);
    System.out.println(name + "DAOOperator exec succeed.");
  }
  
}

OK!

整个项目的代码也就是这个样子了,现在试运行Tomcat项目将会得到以下输出:

1.Hellow,I'm MyTomcat ,and I'm in the initialization... 
2.Now,I have finished the initialization .
3.Now I will start  the Servlet.
4.Hellow I'm the servlet.
5.Now let's start our web http travel.
6.I will do much work...
7.MysqlDAOOperator exec succeed.
8.Now I will beleave the web world...
9.Bye-bye.

方便起见,我为每一行编上号码。

首先第一行输出显示Tomcat会做一些自身的初始化,虽然这里实质上只是打印这么一句话而已但真正的容器的确是在这里做了许多初始化的。

第二行第三行显示tomcat完成初始化并将进入servlet,注意这里我们的代码还运行在我们的Tomcat项目中。

而第四行开始我们的代码就进入了我们的MyWeb项目之中开始了我们的Web之旅,并在之后的数行做其他much work并操作数据库直至最后退出web之旅。

然而实际的Tomcat并不会直接在这里运行我们的service而是初始化web项目并在一个等待状态中伺机触发service方法,并进入其中的doXXX等函数处理实际请求,这里仅本着突出特点的角度也是足够了的。

可以看到整个Tomcat是完全没有与Web项目产生直接的关联的,这一点正符合实际的Tomcat与常规web项目的工作方式,两者之间的联系仅在TomcatConf.txt中配置而已,我们的Tomcat通过此配置文件而找到了我们的MyWeb项目并进入其中加载MyWeb的.class中另外实现的MyServlet类(这里Tomcat本身是完全察觉不到此类的信息的,这也正是反射的优雅之处,看到这里聪明的朋友也许已经想到Bean以及依赖注入技术了吧,其实本质上是一回事,只是做了些巧妙地规定罢了)。

实际的Tomcat的配置文件中将包含更丰富而未必复杂的配置,但本质还是雷同的。

我想到了这里java项目与java web项目的内涵应该已经统一起来了吧,简而言之,你可以写java project,我也可以写,只是某天恰巧有个猫猫爱好者写个java project,而这个java project又恰好能理解所谓的jsp文件并能承载servlet类以及其他一些复杂而强大的功能,随后他又恰好为这个java project起了一个相当cute的名字tomcat,而我们也就可以基于此专门写一些能被他加载的servlet,我们所写的这些东西也就是所谓的Java Web项目了。

6.将我们的”Servlet库“ServletPkg.jar导入我们的”Web项目“。