一、前言
本课程的难度较高,需要将Servlet原理和IO课程全部学完。
二、当前项目使用方式
(1).自定义servlet
自定义servlet需要实现@WebServlet并且实现name和urlMapping
重启进行访问
http://localhost:8090/myServlet
(2).自定义html
重启进行访问
http://localhost:8090/index.html
(3).关于servlet位置
在SearchClassUtil类当中可以设置servlet包的位置
三、关于web须知
我们本次设计的tomcat能够将用户请求的资源进行返回
资源分类
1.静态资源:所有用户访问后,得到的结果都是一样的,称为静态资源。
静态资源可以直接被浏览器解析。
*例如:html/css/jpg/js..
2.动态资源:每个用户访问相同的资源后,得到的结果可能不一样,称为动态资源。
动态资源被访问后需要先转化为静态资源,再返回给浏览器,浏览器进行解析
*例如:servlet/jsp ...
四、tomcat设计原理
五、实现tomcat对静态资源的访问
(1).创建maven项目
(2).tomcat启动阶段
配置HttpServlet
创建HttpServletRequest接口
public interface HttpServletRequest {
public String getUrl();
public void setUrl(String url);
public String getMethod();
public void setMethod(String method);
}
创建HttpServletResponse接口
public interface HttpServletResponse {
void write(String context) throws IOException;
}
创建HttpServlet
/**
* class HttpServlet
*/
public abstract class HttpServlet {
public abstract void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException;
public abstract void doPost(HttpServletRequest request,HttpServletResponse response);
/**
* HttpServlet 实现service方法
*/
public void service(HttpServletRequest request,HttpServletResponse response) throws IOException {
if("GET".equals(request.getMethod())){
doGet(request,response);
}else if("POST".equals(request.getMethod())){
doPost(request,response);
}
}
}
创建工具类,描述返回的信息
/**
* 返回信息工具类
*/
public class ResponseUtil {
public static final String responseHeader200 = "HTTP/1.1 200 \r\n"+
"Content-Type:text/html \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;
}
}
配置注解信息
@Retention(RetentionPolicy.RUNTIME) //注解的生命周期,运行期间保留
@Target(value = {ElementType.TYPE,ElementType.FIELD}) // 该注解作用在类上边
public @interface WebServlet {
String urlMapping() default ""; //自定义的servlet路径
}
配置servlet容器
创建ServletConfig存储注解信息
/**
* 注解上的信息
*/
public class ServletConfig {
private String urlMapping; //2.url
private String classpath; //3.类的全路径名
public ServletConfig(String urlMapping,String classpath){
this.classpath = classpath;
this.urlMapping = urlMapping;
}
public String getUrlMapping() {
return urlMapping;
}
public void setUrlMapping(String urlMapping) {
this.urlMapping = urlMapping;
}
public String getClasspath() {
return classpath;
}
public void setClasspath(String classpath) {
this.classpath = classpath;
}
}
工具类,获取servlet的全路径名
/**
* 扫描指定包,获取该包下所有的类的全路径信息
*/
public class SearchClassUtil {
public static List<String> classPaths = new ArrayList<String>();
public static List<String> searchClass(){
//需要扫描的包名
String basePack = "com.qcby.webapp";
//将获取到的包名转换为路径
String classPath = SearchClassUtil.class.getResource("/").getPath();
basePack = basePack.replace(".", File.separator);
String searchPath = classPath + basePack;
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);
}
}
}
创建ServletConfigMapping生成servlet容器
/**
* servlet容器
*/
public class ServletConfigMapping {
//定义一个集合用来存储自定义servlet的配置信息
private static List<ServletConfig> configs = new ArrayList<>();
//定义servlet容器
public static Map<String,Class<HttpServlet>> classMap = new HashMap<>();
//解析注解 ---- 为了实现当mytomcat类启动的时候就将webapp下边所有的类的注解信息获取到我们需要写一个static代码块
static {
//1.获取webapp包下有哪些类
List<String> classPaths = SearchClassUtil.searchClass();
//2.获取类的注解信息
for (String classpath: classPaths) {
try {
//利用反射获取类的注解信息
getMessage(classpath);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
//利用反射获取类的注解信息
public static void getMessage(String classPath) throws ClassNotFoundException {
Class clazz = Class.forName(classPath);
//注解对象
WebServlet webServlet = (WebServlet) clazz.getDeclaredAnnotation(WebServlet.class);
//将解析的信息放入到集合当中
configs.add(new ServletConfig(webServlet.urlMapping(),classPath));
}
//初始化类容器
public static void initServlet() throws ClassNotFoundException {
for (ServletConfig servletConfig: configs) {
// 将servlet对象和 url请求地址放入到 map集合当中去
classMap.put(servletConfig.getUrlMapping(), (Class<HttpServlet>) Class.forName(servletConfig.getClasspath()));
}
}
}
(3).接收前端请求
创建Request类接收前端数据并实现HttpServletRequest接口
public class Request implements HttpServletRequest {
//请求的地址
private String url;
//请求的方式
private String Method;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getMethod() {
return Method;
}
public void setMethod(String method) {
Method = method;
}
}
创建Response类用来实现HttpServletResponse
public class Response implements HttpServletResponse {
//输出流
private OutputStream outputStream;
public Response(OutputStream outputStream){
this.outputStream = outputStream;
}
/**
* 返回动态资源
* @param context
*/
public void write(String context) throws IOException {
outputStream.write(context.getBytes());
}
/**
* 返回静态资源
*/
public void writeHtml(String path) throws Exception {
String resourcesPath = FileUtil.getResoucePath(path);
File file = new File(resourcesPath);
if(file.exists()){
//静态文件存在
System.out.println("静态文件存在");
FileUtil.writeFile(file,outputStream);
}else {
System.out.println("静态文件不存在");
write(ResponseUtil.getResponseHeader404());
}
}
}
工具类获取静态资源
/**
* 该类的主要作用是进行读取文件
*/
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);
}
public static String getResoucePath(String path){
String resource = FileUtil.class.getResource("/").getPath();
return resource + "\\" + path;
}
}
获取输入流信息,获取访问方式和访问地址
public class MyTomcat {
Request request = new Request();
//启动tomcat主方法
public void startUp() throws IOException, ClassNotFoundException {
//1.定义socket对象,监听8080端口
ServerSocket serverSocket = new ServerSocket(8080);
while (true){
Socket socket = serverSocket.accept();//等待接收 BIO
System.out.println("有用户请求过来了.....");
// 给每一个请求都开启一个线程处理信息
new Thread(new Runnable() {
@Override
public void run() {
try {
杜凯(socket);
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
}
//2.创建出入流,读取用户请求信息
public void 杜凯(Socket socket) throws Exception {
//创建输入流
InputStream inputStream = socket.getInputStream();
//解析输入流
getInputStream(inputStream);
socket.close();
}
public void getInputStream(InputStream inputStream) throws IOException {
//将bit流转为文字信息
int count = 0;
while (count == 0){
count = inputStream.available();
}
byte[] bytes = new byte[count];
inputStream.read(bytes);
String Context = new String(bytes);
System.out.println(Context);
//解析数据
if(Context.equals("")){
System.out.println("你输入了一个空请求");
}else {
String firstLine = Context.split("\\n")[0]; //根据换行来获取第一行数据
request.setUrl(firstLine.split("\\s")[1]);
request.setMethod(firstLine.split("\\s")[0]);
}
}
public static void main(String[] args) throws IOException, ClassNotFoundException {
MyTomcat myTomcat = new MyTomcat();
myTomcat.startUp();
}
}
加载tomcat启动配置,判断访问内容时否是静态资源
public class MyTomcat {
Request request = new Request();
/**
* servlet分发器
* @param request
* @throws InstantiationException
* @throws IllegalAccessException
*/
public void dispatch(Request request, Response response) throws Exception {
//根据请求的信息来获取servlet类
Class<HttpServlet> servletClass = ServletConfigMapping.classMap.get(request.getUrl());
//真实的创建servlet对象
if(servletClass !=null){
HttpServlet servlet = servletClass.newInstance();
servlet.service(request,response);
}else {
response.write(ResponseUtil.getResponseHeader404());
}
}
//启动tomcat主方法
public void startUp() throws IOException, ClassNotFoundException {
//加载servlet信息
ServletConfigMapping.initServlet();
//1.定义socket对象,监听8080端口
ServerSocket serverSocket = new ServerSocket(8080);
while (true){
Socket socket = serverSocket.accept();//等待接收 BIO
System.out.println("有用户请求过来了.....");
// 给每一个请求都开启一个线程处理信息
new Thread(new Runnable() {
@Override
public void run() {
try {
杜凯(socket);
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
}
//2.创建出入流,读取用户请求信息
public void 杜凯(Socket socket) throws Exception {
//创建输入流
InputStream inputStream = socket.getInputStream();
//解析输入流
getInputStream(inputStream);
//输出流
Response response = new Response(socket.getOutputStream());
//根据url判断是静态资源还是动态资源
if(request.getUrl().equals("")){
//没有访问数据
response.write(ResponseUtil.getResponseHeader404());
}else if(ServletConfigMapping.classMap.get(request.getUrl()) == null){
//访问静态资源
response.writeHtml(request.getUrl());
}else {
//访问动态资源
dispatch(request,response);
}
socket.close();
}
public void getInputStream(InputStream inputStream) throws IOException {
//将bit流转为文字信息
int count = 0;
while (count == 0){
count = inputStream.available();
}
byte[] bytes = new byte[count];
inputStream.read(bytes);
String Context = new String(bytes);
System.out.println(Context);
//解析数据
if(Context.equals("")){
System.out.println("你输入了一个空请求");
}else {
String firstLine = Context.split("\\n")[0]; //根据换行来获取第一行数据
request.setUrl(firstLine.split("\\s")[1]);
request.setMethod(firstLine.split("\\s")[0]);
}
}
public static void main(String[] args) throws IOException, ClassNotFoundException {
MyTomcat myTomcat = new MyTomcat();
myTomcat.startUp();
}
}