目录
1.自定义Tomcat
1.1 创建日志对象
创建lib文件夹,导入dom4j-1.1.jar包和log4j-1.2.12.jar包,创建日志工具类MyLogger
public class MyLogger {
private static Logger logger =Logger.getLogger(MyLogger.class);
public static void log(String msg){
logger.info(msg);
}
}
1.2 创建自定义服务器类HttpServer
在类中创建开启服务器方法,在方法中实现服务器的开启,接着用while(true)循环实现不中断地使用服务器,且在方法中实现多线程的开启。
public class HttpServer {
public static void main(String[] args) throws DocumentException {
start();
}
public static void start() throws DocumentException {
ServerSocket serverSocket = null;
int port = ServerParse.getPort();
try {
serverSocket = new ServerSocket(port);
MyLogger.log("服务器已启动");
while (true){
Socket client = serverSocket.accept();
System.out.println("连接成功");
//启动线程
new Thread(new RequestHandler(client)).start();
}
} catch (IOException e) {
throw new RuntimeException(e);
}finally {
if(serverSocket != null) {
try {
serverSocket.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
1.3 创建处理请求类
在类中创建带客户端参数的构造函数,在类中创建实现多线程的run()方法,判断请求是否成功,成功,输出响应200页面;否则,输出404错误页面。
public class RequestHandler implements Runnable{
private Socket client;
public RequestHandler(Socket client){
this.client = client;
}
@Override
public void run() {
BufferedReader reader = null;
PrintWriter writer = null;
try {
MyLogger.log("当前线程" + Thread.currentThread().getName());
reader = new BufferedReader(new InputStreamReader(client.getInputStream()));
writer = new PrintWriter(client.getOutputStream());
String requestLine = reader.readLine();
String responseURI = requestLine.split(" ")[1];
if(responseURI.endsWith("html") || responseURI.endsWith("htm")){
responseStaticPage(responseURI,writer);
}else{
String servletPath = responseURI;
ResponseObj responseObj = new ResponseObj();
responseObj.setWrite(writer);
RequestObj requestObj = new RequestObj(servletPath);
if(servletPath.contains("?")){
servletPath = servletPath.split("[?]")[0];
}
if(servletPath.split("/").length >0 && servletPath.indexOf("favicon.ico") == -1){
String webAppName = servletPath.split("/")[1];
Map<String, String> servletMap = WebParse.parse(webAppName);
String urlParttern = servletPath.substring(1 + webAppName.length());
String servletClass = servletMap.get(urlParttern);
if(servletClass != null){
//通过Class去缓冲池获取servlet
Servlet servlet = ServletCache.get(servletClass);
if(servlet == null){
Class aClass = Class.forName(servletClass);
servlet = (Servlet) aClass.newInstance();
ServletCache.put(servletClass,servlet);
}
servlet.service(responseObj,requestObj);
}
}
}
writer.flush();
} catch (IOException e) {
throw new RuntimeException(e);
} catch (Exception e){
e.printStackTrace();
}finally {
if(reader != null){
try {
reader.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
if(client != null){
try {
client.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
if(writer !=null){
writer.close();
}
}
}
private void responseStaticPage(String responseURI, PrintWriter writer) {
String htmlPath = responseURI.substring(1);
try {
BufferedReader reader = new BufferedReader(new FileReader(htmlPath));
StringBuilder sb = new StringBuilder();
sb.append("HTTP/1.1 200 ok\n");
sb.append("Content-type:text/html;charset=utf-8\n\n");
String str = null;
while ((str = reader.readLine()) != null){
sb.append(str);
}
writer.print(sb);
} catch (FileNotFoundException e) {
StringBuilder sb = new StringBuilder();
sb.append("HTTP/1.1 404 NotFound\n");
sb.append("Content-type:text/html;charset=utf-8\n\n");
sb.append("<html>");
sb.append("<head><meta content='text.html;charset=utf-8'></head>");
sb.append("<body><h1>404错误</h1></body>");
sb.append("</html>");
writer.print(sb);
} catch (Exception e) {
e.printStackTrace();
}
}
}
1.4 创建初始化并获取端口号的类
初始化端口号为8080,通过读取本地文件获取端口号。
public class ServerParse {
public static int getPort() throws DocumentException {
Integer port = 8080;
//创建解析器
SAXReader reader = new SAXReader();
Document document = reader.read("./conf/server.xml");
Element connectorEl = (Element) document.selectSingleNode("//connector");
port = Integer.valueOf(connectorEl.attributeValue("port"));
return port;
}
}
1.5 创建响应登录的类,并配置对应的xml文件。
public class LoginServlet implements Servlet {
@Override
public void service(ServletResponse response, ServletRequest request) {
StringBuilder sb = new StringBuilder();
sb.append("HTTP/1.1 200 ok\n");
sb.append("Content-type:text/html;charset=utf-8\n\n");
sb.append("<html>");
sb.append("<head><meta content='text/html;charset=utf-8' /></head>");
sb.append("<body><h1>正在校验登录,请稍后.......</h1></body>");
sb.append("</html>");
PrintWriter write = response.getWrite();
write.print(sb);
}
}
1.6 创建解析xml文件的类
创建解析xml文件的方法,解析xml的子元素,以键值对的方式存入map集合中,并返回map集合。
public class WebParse {
public static void main(String[] args) throws DocumentException {
Map<String, String> map = parse("oa");
Set<Map.Entry<String,String>> entries = map.entrySet();
for (Map.Entry<String, String> entry : entries) {
System.out.println(entry.getKey() + ":::" + entry.getValue());
}
}
public static Map<String,String> parse(String webAppName) throws DocumentException {
webAppName = webAppName + "/WEB-INF/web.xml";
SAXReader reader = new SAXReader();
Document document = reader.read(webAppName);
List<Element> servletEl = document.selectNodes("/web-app/servlet");
Map<String,String> servletMapInfo = new HashMap<>();
for (Element el:servletEl) {
Element servletNameEl = (Element) el.selectSingleNode("servlet-name");
Element servletClassEl = (Element) el.selectSingleNode("servlet-class");
String servletName = servletNameEl.getStringValue();
String servletClass = servletClassEl.getStringValue();
servletMapInfo.put(servletName,servletClass);
}
List<Element> servletMappingEl = document.selectNodes("/web-app/servlet-mapping");
Map<String,String> servletMappingInfo = new HashMap<>();
for(Element el:servletMappingEl){
Element servletNameEl = (Element) el.selectSingleNode("servlet-name");
Element urlEl = (Element) el.selectSingleNode("url-parttern");
String servletName = servletNameEl.getStringValue();
String url = urlEl.getStringValue();
servletMappingInfo.put(servletName,url);
}
Set<String> keySet = servletMapInfo.keySet();
Map<String,String> servletMap = new HashMap<>();
for (String servletName:keySet) {
String servletClass = servletMapInfo.get(servletName);
String urlParttern = servletMappingInfo.get(servletName);
servletMap.put(urlParttern,servletClass);
}
return servletMap;
}
}
1.7 创建带响应和请求参数的接口
public interface Servlet {
public void service(ServletResponse response,ServletRequest request);
}
1.8 创建缓冲池
将解析到的地址和类路径储存到缓冲池中,使用时再在缓冲池中取值。
public class ServletCache {
private static Map<String, Servlet> map = new HashMap<>();
//存servlet
public static void put(String name,Servlet servlet){
map.put(name,servlet);
}
//取servlet
public static Servlet get(String name){
return map.get(name);
}
}
1.9 创建服务器响应接口和实现该接口的类
//接口
public interface ServletResponse {
public void setWrite(PrintWriter write);
public PrintWriter getWrite();
}
//实现类
public class ResponseObj implements ServletResponse {
private PrintWriter out;
@Override
public void setWrite(PrintWriter out) {
this.out = out;
}
@Override
public PrintWriter getWrite() {
return out;
}
}
1.10 创建处理请求的接口
接口中提供获取单个参数的方法和获取列表多个参数的方法。
public interface ServletRequest {
String getParameterValue(String key);
String[] getParameterValues(String key);
}
1.11 创建处理用户输入表单的信息
获取用户输入表单的提交的信息,通过处理请求,将信息处理,并将信息输出到网页上
public class SaveUserServlet implements Servlet {
@Override
public void service(ServletResponse response, ServletRequest request) {
//处理表单的servlet
String username = request.getParameterValue("username");
String gender = request.getParameterValue("gender");
String[] interests = request.getParameterValues("interest");
StringBuilder sb = new StringBuilder();
sb.append("HTTP/1.1 200 ok\n");
sb.append("content-type:text/html;charset=utf-8\n\n");
sb.append("<html>");
sb.append("<head><meta charset='utf-8'></head>");
sb.append("<body>");
sb.append("用户名:" + username + "<br/>");
sb.append("性别:" + gender + "<br/>");
sb.append("爱好");
for (String interest:interests) {
sb.append(interest).append(" ");
}
sb.append("<br/>");
sb.append("</body>");
sb.append("</html>");
response.getWrite().print(sb);
}
1.12 创建处理需求类,实现需求接口
类中重写实现接口的方法,分别为获取单个参数的方法和获取列表的多个参数的方法,创建根据用户输入信息后提交的路径地址,获取用户输入的信息,将信息以键值对的方式存入map集合中的构造函数。
public RequestObj(String path){
if(path.contains("?")){
//参数部分
String dataAndParam = path.split("[?]")[1];
if(dataAndParam.length() > 1){
//多个参数
if(dataAndParam.contains("&")){
String[] nameAndValues = dataAndParam.split("[&]");
for (String nameAndValue:nameAndValues) {
String[] param = nameAndValue.split("[=]");
if(paramMap.containsKey(param[0])){
//多选框
//获取原来的值
String[] old_value = paramMap.get(param[0]);
//当前的值 定义一个新的数组,新的数组的长度比原来的数组长度+1
String[] newValues = new String[old_value.length+1];
//将原来的数组的值传给新数组
System.arraycopy(old_value,0,newValues,0,old_value.length);
//判断当前参数
if(param.length > 1) {
newValues[newValues.length-1] = param[1];
}else {
newValues[newValues.length-1] = "";
}
paramMap.put(param[0],newValues);
}else{
//普通输入框
if(param.length > 1){
paramMap.put(param[0],new String[]{param[1]});
}else{
paramMap.put(param[0],new String[]{""});
}
}
}
}else{
//单个参数
String[] param = dataAndParam.split("[=]");
if(param.length > 1){
paramMap.put(param[0],new String[]{param[1]});
}
}
}
}
}
@Override
public String getParameterValue(String key) {
String[] values = paramMap.get(key);
return (values != null && values.length != 0) ? values[0] : null;
}
@Override
public String[] getParameterValues(String key) {
return paramMap.get(key);
}
2.Tomcat的使用
2.1 创建实现Servlet接口的实现类
(1)新建一个空项目,并选择web服务器和选择Web Application ;
(2)创建完成后,创建项目模块,鼠标左键点击项目模块,在上侧栏选择Edit Configurations配置tomcat服务器;
(3)修改xml文件
(4)创建实现Servlet接口的实现类,并在类中重写实现接口的方法
public class HelloServlet implements Servlet {
public HelloServlet(){
System.out.println("构造函数.....");
}
@Override
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("init......");
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("helloServlet.......");
HttpServletRequest request = (HttpServletRequest) servletRequest;
String method = request.getMethod();
if("GET".equals(method)){
System.out.println("GET处理");
}else {
System.out.println("POST处理");
}
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
}
}
2.2 创建继承HttpServlet父类的子类
(1)新建一个空项目,并选择web服务器和选择Web Application ;
(2)创建完成后,创建项目模块,鼠标左键点击项目模块,在上侧栏选择Edit Configurations配置tomcat服务器;
(3)修改xml文件
(4)创建继承HttpServlet父类的子类,在子类中重写父类的doGet()方法和doPost()方法
public class RequestServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//getRequestURI() 获取请求的资源路径
System.out.println("uri:" + req.getRequestURI());
//getRequestURL() 获取请求的统一资源定位符(绝对路径)
System.out.println("url:" + req.getRequestURL());
//getRemoteHost() 获取客户端的 ip 地址
System.out.println("ip:" + req.getRemoteHost());
//getHeader() 获取请求头
System.out.println("header:" + req.getHeader("User-Agent"));
//getMethod() 获取请求的方式 GET 或 POST
System.out.println("method:" + req.getMethod());
//getParameter() 获取请求的参数
System.out.println(req.getParameter("username"));
System.out.println(req.getParameter("password"));
//getParameterValues() 获取请求的参数(多个值的时候使用)
System.out.println(Arrays.toString(req.getParameterValues("hobby")));
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req,resp);
}
}
2.3 直接在空项目模块中创建Servlet
(1)新建一个空项目,并选择web服务器和选择Web Application ;
(2)创建完成后,创建项目模块,鼠标右击项目模块,快捷创建Servlet;
(3)创建完成后,修改xml文件,接着,创建继承HttpServlet父类的子类,在子类中重写父类的doGet()方法和doPost()方法
public class ServletContext3 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext servletContext = getServletContext();
Integer count = (Integer) servletContext.getAttribute("count");
if(count == null){
count = 1;
}else {
count++;
}
resp.setContentType("text/html;charset=utf-8");
resp.getWriter().print("<h1>本页面一共被访问" + count + "次</h1>");
servletContext.setAttribute("count",count);
}
}
3.总结
自定义tomcat服务器,让我们能更好地理解tomcat各种api的作用和使用方式以及原理,让我们能更直观的了解和掌握tomcat,能让我们能更高效地使用tomcat去完成项目。tomcat的使用的三种方式,实现Servlet接口的方式比较麻烦,但能让我们了解Serlet接口内的api;继承HttpServlet的方法能减少不使用但要重写的方法,减少代码冗余;直接创建Servlet的方式,能更便捷的使用tomcat,只需要添加xml中对应的servletMapping。
4.建议采纳
如有建议或者错误请私信我进行修改,感谢!!!