介绍:
servlet:处理 http 请求
tomcat:服务器
Servlet
servlet 接口:
- 定义 Servlet 声明周期
- 初始化:init
- 服务:service
- 销毁:destory
- 继承链:
Tomcat
Tomcat 和 servlet 原理:
- 浏览器向服务器发送 http 请求
- socket 接收请求,发送给请求解析器
- 请求解析器再解析自己想要的信息
- 请求地址
- 请求方式
- 浏览器类型
- Cookie
- ··········
- 解析器解析到的信息发送给映射器
- 映射器中存放:
- Web 地址
- 内存地址
- 根据请求解析器中解析的信息,找到映射器中相对应的网络地址和内存地址,根据请求方式去访问对应的程序
Socket 交互以及解析阶段:
java
package com.Tomcat;
import com.sun.corba.se.spi.activation.Server;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class myTomcat {
Request request = new Request(); //创建解析请求的对象
public void startUP() throws IOException {
//监听端口号
ServerSocket serverSocket = new ServerSocket(7421);
while(true){
Socket socket = serverSocket.accept(); //阻塞监听
System.out.println("有请求!!!!!!!");
//将每个请求开启一个线程
new Thread(new Runnable() {
@Override
public void run() {
try {
handler(socket); //调用处理信息方法
}catch (Exception e){
e.printStackTrace();
}
}
}).start();
}
}
//处理信息
public void handler(Socket socket) throws IOException {
InputStream inputStream = socket.getInputStream();
//将bit流转换为文字信息
int count = 0;
while(count == 0){
count = inputStream.available(); //统计输入流的长度
}
//打印数据
byte[] bytes = new byte[count];
inputStream.read(bytes); //将bit信息写入到byte数组
String Context = new String(bytes); //将 byte 数组转换为字符串
System.out.println(Context); //输出信息
//拆解字符串,获取想要的信息
String[] list = Context.split("\\n"); //根据换行切割字符串
String Methed = list[0].split(" ")[0]; //在拆分的第一行中以空格再次拆分,获取第一个数据
String Path = list[0].split(" ")[1]; //在拆分的第一行中以空格再次拆分,获取第二个数据
//把截取的数据传给 Request 类
request.setMethod(Methed);
request.setPath(Path);
}
}
Request 存储解析信息 ==> 继承 HttpservletRequest,为方便访问同一变量
java
public class Request implements HttpservletRequast {
private String Method;
private String Path;
public String getMethod() {
return Method;
}
public void setMethod(String method) {
Method = method;
}
public String getPath() {
return Path;
}
public void setPath(String path) {
Path = path;
}
@Override
public String toString() {
return "Request{" +
"Method='" + Method + '\'' +
", Path='" + Path + '\'' +
'}';
}
}
扫包:
java
/**
* 扫描指定包,获取该包下所有的类的全路径信息
*/
public class SearchClassUtil {
//存放文件的绝对路径
public static List<String> classPaths = new ArrayList<String>();
/**
* 扫描固定包下面的类
* @return
*/
public static List<String> searchClass(){
//需要扫描的包名
String basePack = "com.servlet"; //写需要获取包名的路径
//将获取到的包名转换为路径
//getResource():是获取类对象的方法, "/" :表示在根目录开始
//getPath():是将对象的路径转为字符串
String classPath = SearchClassUtil.class.getResource("/").getPath();
//将包名转换为文件系统路径 ---> 把 "." 替换成 系统的路径分隔符(系统不一样,分隔符也不一样)
basePack = basePack.replace(".", File.separator);
//把两个路径合并为文件的 绝对路径
String searchPath = classPath + basePack;
//调用doPath()方法,把路径写入路径数组中
doPath(new File(searchPath),classPath);
//这个时候我们已经得到了指定包下所有的类的绝对路径了。我们现在利用这些绝对路径和java的反射机制得到他们的类对象
return classPaths;
}
/**
* 该方法会得到所有的类,将类的绝对路径写入到classPaths中
* @param file
*/
private static void doPath(File file,String classpath) {
if (file.isDirectory()) {//当前为文件夹
//文件夹我们就递归 ---> 筛出文件夹
File[] files = file.listFiles();
for (File f1 : files) {
doPath(f1,classpath);
}
} else {//标准文件
//标准文件我们就判断是否是class文件
if (file.getName().endsWith(".class")) {
//各级拆解字符串,替换分隔符
String path = file.getPath().replace(classpath.replace("/","\\").
replaceFirst("\\\\",""),"").replace("\\",".").
replace(".class","");
//如果是class文件我们就放入我们的集合中。
classPaths.add(path);
}
}
}
public static void main(String[] args) {
List<String> classes = SearchClassUtil.searchClass();
for (String s: classes) {
//System.out.println(s);
}
}
}
注解:设置文件的访问地址 ==> HashMap 中的 key 值
java
@Retention(RetentionPolicy.RUNTIME) //在运行期间保留
@Target(ElementType.TYPE) //作用于类上面
public @interface Webservlet {
String url() default "";
}
创建 Httpservlet 实现 Service 服务:
java
public abstract class HttpServlet { //HttpServerlet只实现了父类的service服务,其他方法没有实现,此时该类为抽象类
//子类需要使用doGet或doPost方法,在这里直接让子类去实现两个发给发
//这里需要获取Request中被解析出来的数据,
//要想访问的是同一个Request对象,这里用到接口,让Request实现这个接口,传参时就会向上转型,此时request对象为同一个对象
public abstract void doGet(HttpServletRequast requast, HttpServletResponse response) throws Exception;
public abstract void doPost(HttpServletRequast requast,HttpServletResponse response);
//在服务中判断用户的请求方式,让子类实现向对应的方法
public void service(HttpServletRequast requast,HttpServletResponse response) throws Exception {
if(requast.getMethod().equals("GET")){
doGet(requast,response);
}else if(requast.getMethod().equals("POST")){
doPost(requast,response);
}
}
}
HttpservletRequast:为 Httpservlet 访问对象为统一对象,让 Request 实现这个接口
java
public interface HttpservletRequast {
String getMethod();
void setMethod(String method);
String getPath();
void setPath(String path);
}
自己创建 servlet,继承 Httpservlet 实现 service 服务 ==> 实现相关的访问方式
java
@WebServerlet(url = "OneServerlet")
public class FirstServlet extends HttpServlet {
@Override
public void doGet(HttpServletRequast requast, HttpServletResponse response) throws Exception {
}
@Override
public void doPost(HttpServletRequast requast, HttpServletResponse response) {
}
}
获取访问地址:HashMap 中的 key 值
java
public class getMessageUtil {
public static String fund(String path) throws Exception {
//创建类对象
Class clazz = Class.forName(path);
//根据类对象调用 getDeclaredAnnotation() 方法找到该类的访问地址(@Webservlet()中的内容)
Webservlet webservlet = (Webservlet) clazz.getDeclaredAnnotation(Webservlet.class);
return webservlet.url();
}
public static void main(String[] args) throws Exception {
//fund();
}
}
-
映射器:底层由 HashMap 容器存储
javapublic class ServletConfigMapping { //将执行逻辑写入static代码块中,以便更好加载 //定义Servlet容器 public static Map<String,Class<HttpServlet>> classMap = new HashMap<>(); //该静态代码块应放在启动tomcat前运行 static { List<String> classPaths = SearchClassUtil.searchClass(); for (String classPath : classPaths){ try { InitClassMap(classPath); }catch (Exception e){ e.printStackTrace(); } } } //将key val 值插入到HashMap中 public static void InitClassMap(String classPath) throws Exception { //获取类对象 Class clazz = Class.forName(classPath); //获取访问地址 String url = getMessageUtil.fundUrl(classPath); //将值插入HashMap树当中 classMap.put(url,clazz); } }
Response 返回数据:
设置返回头工具类:
java
/**
* 设置返回头
*/
public class ResponseUtil {
public static final String responseHeader200 = "HTTP/1.1 200 \r\n"+
"Content-Type:text/html \r\n"+"\r\n";
public static final String responseHeader200JSON = "HTTP/1.1 200 \r\n"+
"Content-Type:application/json \r\n"+"\r\n";
public static String getResponseHeader404(){
return "HTTP/1.1 404 \r\n"+
"Content-Type:text/html \r\n"+"\r\n" + "404";
}
public static String getResponseHeader200(String context){
return "HTTP/1.1 200 \r\n"+
"Content-Type:text/html \r\n"+"\r\n" + context;
}
}
读取文件:根据提供的地址转化为文件完整地址
java
/**
* 该类的主要作用是进行读取文件
*/
public class FileUtil {
public static boolean witeFile(InputStream inputStream, OutputStream outputStream){
boolean success = false ;
BufferedInputStream bufferedInputStream ;
BufferedOutputStream bufferedOutputStream;
try {
bufferedInputStream = new BufferedInputStream(inputStream);
bufferedOutputStream = new BufferedOutputStream(outputStream);
bufferedOutputStream.write(ResponseUtil.responseHeader200.getBytes());
int count = 0;
while (count == 0){
count = inputStream.available();
}
int fileSize = inputStream.available();
long written = 0;
int beteSize = 1024;
byte[] bytes = new byte[beteSize];
while (written < fileSize){
if(written + beteSize > fileSize){
beteSize = (int)(fileSize - written);
bytes = new byte[beteSize];
}
bufferedInputStream.read(bytes);
bufferedOutputStream.write(bytes);
bufferedOutputStream.flush();
written += beteSize;
}
success = true;
} catch (IOException e) {
e.printStackTrace();
}
return success;
}
public static boolean writeFile(File file,OutputStream outputStream) throws Exception{
return witeFile(new FileInputStream(file),outputStream);
}
/**
* 根据提供的地址转换为文件完整地址
* @param path
* @return
*/
public static String getResoucePath(String path){
String resource = FileUtil.class.getResource("/").getPath();
return resource + "\\" + path;
}
}
response 返回数据:
java
public class Response implements HttpServletResponse {
//输出流
private OutputStream outputStream;
public Response(OutputStream outputStream){
this.outputStream = outputStream;
}
/***
* 返回动态文字信息
* @param context
* @throws IOException
*/
public void write(String context) throws IOException {
outputStream.write(context.getBytes()); //将文字信息转换为 byte流 形式
}
public void WriteHtml(String Path) throws Exception {
//得到文件全路径
String resoucePath = FileUtil.getResoucePath(Path);
//创建文件
File file = new File(resoucePath);
if(file.exists()){
System.out.println("静态资源存在");
//输出静态资源
FileUtil.writeFile(file,outputStream);
}else {
System.out.println("静态资源不存在");
}
}
}
HttpServletResponse 接口:
java
public interface HttpServletResponse {
void write(String context) throws IOException;
}
输出资源:
java
Response response = new Response(socket.getOutputStream());
if(request.getPath().equals("") || request.getPath().equals("/")){ //空访问
response.WriteHtml("404.html"); //抛出404页面
response.write(ResponseUtil.getResponseHeader404()); //抛出404文字信息
} else if (ServerletConfigMapping.classMap.get(request.getPath()) == null) { //静态资源
response.WriteHtml(request.getPath());
}else { //动态资源
Class<HttpServlet> httpServletClass = ServerletConfigMapping.classMap.get(request.getPath()); //获取类对象
if(httpServletClass != null){ //有类对象
HttpServlet httpServlet = httpServletClass.newInstance(); //多态创建对象
httpServlet.service(request,response); //启动service服务
}else{ //没有动态资源
response.WriteHtml("404.html"); //抛出 404页面
}
}
整合后 Tomcat:
java
public class myTomcat {
Request request = new Request(); //创建解析请求的对象
//提前加载容器(HashMap)
static {
List<String> classPaths = SearchClassUtil.searchClass();
for (String classPath : classPaths){
try {
ServerletConfigMapping.InitClassMap(classPath);
}catch (Exception e){
e.printStackTrace();
}
}
}
public static void main(String[] args) throws IOException {
myTomcat myTomcat = new myTomcat();
myTomcat.startUP();
}
public void startUP() throws IOException {
//监听端口号
ServerSocket serverSocket = new ServerSocket(8080 );
while(true){
Socket socket = serverSocket.accept(); //阻塞监听
System.out.println("有请求!!!!!!!");
//将每个请求开启一个线程
new Thread(new Runnable() {
@Override
public void run() {
try {
handler(socket); //调用处理信息方法
}catch (Exception e){
e.printStackTrace();
}
}
}).start();
}
}
//处理信息
public void handler(Socket socket) throws Exception {
InputStream inputStream = socket.getInputStream();
//将bit流转换为文字信息
int count = 0;
while(count == 0){
count = inputStream.available(); //统计输入流的长度
}
//打印数据
byte[] bytes = new byte[count];
inputStream.read(bytes); //将bit信息写入到byte数组
String Context = new String(bytes); //将 byte 数组转换为字符串
System.out.println(Context); //输出信息
//拆解字符串,获取想要的信息
String[] list = Context.split("\\n"); //根据换行切割字符串
String Methed = list[0].split(" ")[0]; //在拆分的第一行中以空格再次拆分,获取第一个数据
String Path = list[0].split(" ")[1]; //在拆分的第一行中以空格再次拆分,获取第二个数据
//把截取的数据传给 Request 类
request.setMethod(Methed);
request.setPath(Path);
//判断资源类型
Response response = new Response(socket.getOutputStream());
if(request.getPath().equals("") || request.getPath().equals("/")){ //空访问
response.WriteHtml("404.html"); //抛出404页面
response.write(ResponseUtil.getResponseHeader404()); //抛出404文字信息
} else if (ServerletConfigMapping.classMap.get(request.getPath()) == null) { //静态资源
response.WriteHtml(request.getPath());
}else { //动态资源
Class<HttpServlet> httpServletClass = ServerletConfigMapping.classMap.get(request.getPath()); //获取类对象
if(httpServletClass != null){ //有类对象
HttpServlet httpServlet = httpServletClass.newInstance(); //多态创建对象
httpServlet.service(request,response); //启动service服务
}else{ //没有动态资源
response.WriteHtml("404.html"); //抛出 404页面
}
}
}
}
Tomcat 运行原理:
原理:
- 浏览器发起请求
- Socket 解析输入流,获取请求头信息
- 分析请求的地址是动态资源还是静态资源
- 首先判断 HashMap 中有没有这个 Key 值
- 如果有就去访问动态资源,如果没有就去查看静态资源
- 如果也不是静态资源就返回 404
- Servlet 容器(HashMap):
- 将 @WebServlet 中的值作为 key 值,将对象作为 value 值,存入 HashMap 中
Servlet 容器加载时期:
- 在 Socket 启动之前启动 Servlet 容器
- 缺点:程序启动时间变长
- 优点:不易出现空指针
- 在 Socket 启动之后启动 Servlet 容器
- 在浏览器访问的同时启动 Servlet 容器