java 基础 是 servlet 的基础 servlet 是 jsp 的基础,Java本身不适合web 开发,但是 servlet 可以做到,jsp 适合做页面
java –> servlet –> jsp
JBOSS
WebLogic
Tomcat
主要负责 tomcat 的启动和停止(二进制核心文件)
tomcat 的配置文件目录,奇珍有三个 xml 文件非常重要,分别是 server.xml 、 tomcat-users.xml 、 web.xml
主要用于配置和server 相关的信息,比如 tomcat 的启动端口以及 Host
主要配置和 web 应用(web站点)相关的,比如我们规定hello.jsp 为网站的默认首页,我们就可以在这个页面里面加
<welcome-file-list> <welcome-file>hello.jsp</welcome-file> </welcome-file-list>
配置用户名密码和用户的权限,包括默认页面的manager
该目录放置 tomcat 运行所需的 jar 包
日志目录
该目录下放置我们的web应用
用于存放java 文件被访问后生成的 .class 和 servlet 文件
classes 目录存放的是 class文件,lib 目录存放的是该项目由需要的jar 文件
如果我们的 webapps 所在的磁盘已经空间不足,那么我们能不能实现将web 应用放在别的地方但是还让tomcat 来管理呢?当然是可以的,我们这个时候就要配置虚拟目录
我们只要在 server.xml 里面的 <HOST>
中间添加一个 <Context>
节点,格式如下:
<Context docBase="E:/jspstudy/WWW" path=""></Context>
path 是待会要在url 中显示的那个目录,可以随意指定(视觉效果)
docBase 是绝对路径
当然除了这两个参数以外,还有两个参数需要了解一下
1.reloadable 这个参数设为true 以后 Tomcat 就会及时的发现文件的变化,然后更新,开销较大,建议只在开发时开启
2.upackWAR 这个参数设置为 true 以后我们通过tomcat 上传的 war 就能自动的解压,并放在webapps 目录下
HOST 标签可以有很多个,有一个host就有一个虚拟主机
如果我们不想看到loclhost ,根据网页的请求原理,我们可以在本地的hosts 文件中配置主机名,配置好了以后可以在 server.xml 中 找到 HOST 标签修改 name 参数
tomcat 拿到请求以后会先解析主机(一个tomcat 能管理多个主机),然后会解析web 应用,因为一个主机可能会有多个web 应用,就如我们之前说的可以设置,如下
<Context docBase="E:/jspstudy/WWW" path=""></Context>
最后才是解析资源并获取资源
在引擎(engine)中可以配置默认主机
(1)实现 servlet 接口
(2)继承 GeneraicServlet
(3)继承 HttpSerlvet
下面的代码实际上是实现了一个接口,就是把所有的方法都实现了
package com.test; import javax.servlet.*; import javax.servlet.http.*; import java.io.*; class myServlet implements Servlet{ //用于初始化 servlet,就是把 servlet 装载到内存中 //该函数只会被调用一次 public void init(ServletConfig config) throws ServletException{ } //得到 ServletConfig 对象 public ServletConfig getServletConfig(){ return null; } //该函数是服务函数,我们的业务逻辑代码写在这里 //浏览器每请求一次就会被调用一次 public void service(ServletRequest req, ServletResponse res) throws ServletException, java.io.IOException{ System.out.println("hello,world"); } // 该函数得到 servlet 的配置信息 public java.lang.String getServletInfo(){ return null; } // 销毁 servlet 从内存中清除该servlet public void destroy(){ } }
根据 servlet 的规范我们还要在 WEB-INF/web.xml 中部署servlet
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0" metadata-complete="true"> <servlet> <servlet-name>myServlet</servlet-name> <servlet-class>com.test.myServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>myServlet</servlet-name> <url-pattern>/ABC</url-pattern> </servlet-mapping> </web-app>
<servlet>
标签下的子标签 1.servlet-name 自定义该servlet 的名字,默认使用该servlet 的文件名
2.servlet-class 指明该 servlet 是放在哪个包下面的,形式:com.test.myServlet
<servlet-mapping>
标签(servlet 的映射)下的子标签 1.servlet-name 和上面的保持一致
2.url-pattern 是将来访问这个 servlet的URL 的一部分,默认是这个servlet 的名字 的资源名(当做出一个请求以后,首先找这个对应的,然后根据名字找到servlet文件)
因为我的web 目录是在 testweb ,于是我们访问 localhost:8080/testweb/ABC 就能在控制台输出 hello,world
现在我们想在网页上看到返回信息,而不是在控制台,那我们只要在 service 这个函数中使用 res.getWriter().println()方法
//该函数是服务函数,我们的业务逻辑代码写在这里 //浏览器每请求一次就会被调用一次 public void service(ServletRequest req, ServletResponse res) throws ServletException, java.io.IOException{ System.out.println("hello ,world"); res.getWriter().println("hello ,world"); }
如果用 javac 去编译一个 java 文件,需要带上.(点号)
javac -d . 文件路径
1.当 servlet 第一次被调用的时候,会触发 init 函数,然后将servlet实例加载到内存,这个函数只会被调用一次
2.当第一次以后(包括第一次)就会去调用 service 函数,服务器已经将我们的请求变成一个对象传递给这个函数,并将返回结果以一个对象打包(当然还会对其进行解析,将必要的结果返回给浏览器)
3.当站点 reload 或者 服务器 restart 的时候 调用 destory 函数,销毁
这个是因为觉得 servlet 实现五个方法比较烦,于是把其他四个不是很重要的方法隐藏了,就留了一个service
package com.test; import javax.servlet.*; import javax.servlet.http.*; import java.io.*; public class myHttpServlet extends HttpServlet{ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, java.io.IOException{//响应 get 方式的提交 resp.getWriter().println("调用 doGet 方法"); } protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, java.io.IOException{//响应 post 方式的提交 resp.getWriter().println("调用 doPost 方法"); } }
<servlet> <servlet-name>myHttpServlet</servlet-name> <servlet-class>com.test.myHttpServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>myHttpServlet</servlet-name> <url-pattern>/myHttpServlet</url-pattern> </servlet-mapping>
servlet 映射到 URL 也可以使用通配符,通配符只能有两种格式,一种就是: .扩展名 另一种是 `/xxx/ `,这种通配可以起到屏蔽信息,或者统一报错回复上
注意:在匹配的时候参考的标准
(1)如果同时匹配到多个,谁长得最像就是访问谁
(2)*.do 这种形式的优先级最低
servlet 是一个供 servlet 引擎(web服务器)调用的java 类,他不能独立运行,他的运行完全是由servlet 引擎控制和调度的
servlet 在被第一次调用后就加载到内存,然后内存中的 srvlert 就会对各个请求进行服务,不会重新创建,这也就是说 servlet 是单例,可能会出现线程安全问题(类变量),于是应该加同步机制 synchromized(this){}
<load-on-stratup>
配合线程 解决网站启动时初始化或者定时完成任务的问题 我们在 web.xml 的servlet 标签中使用
<servlet> <servlet-name>myServlet</servlet-name> <servlet-class>com.test2.myServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet>
这里的数字代表该servlet 的启动顺序
我们在 servlet 文件中 写上
@Override public void init() throws ServletException { super.init(); System.out.println("init 函数被调用 "); }
在启动服务的时候就会被调用,而不用特地的去访问这个servlet ,这就能完成一些类似数据库之类的东西的初始化操作
在 servlet 的配置文件中可以使用 <init-param>
标签为 servlet 配置一些初始化的参数
我们在 web.xml 这样写
<servlet> <servlet-name>ServletConfigTest</servlet-name> <servlet-class>com.test2.ServletConfigTest</servlet-class> <init-param> <param-name>encoding</param-name> <param-value>utf-8</param-value> </init-param> </servlet>
servlet 文件中就能用下面的语句去加载这个配置,这样就不是写死的了
response.getCharacterEncoding(this.getServletConfig().getInitParameter("encoding"));
当然上面的配置只是针对某一个 servlet 使用,如果想对多个 servlet 都应用的话我们可以
<context-param> <param-name></param-name> <param-value></param-value> </context-param>
web 服务器收到客户端的 http 请求以后,会针对每一次请求创建一个代表请求的request 对象 和 代表相应的 response 对象,因此我们要获取客户机提交上来的数据只要找request 对象就可以了,如果我们想要向客户机输出数据只要找 response 对象就可以了
1.getWriter() 得到的是 PrintWriter 对象,用于向客户机会送字符数据
2.getOutputStream() 得到的是 OutputStream对象,用于向客户机会送字符数据或者二进制数据
这两个流不能同时使用,因为一旦你在一个流中做出了返回,服务器就会去检测和这个 response 相关的流有没有关闭,如果没有关,则强制关闭,于是下一次再去接收相同的信息是接收不到的
实际上这个重定向也是一个 get 请求,也就是说我们能通过? 利用 get方式传参
if("admin".equals(name) && "123456".equals(pass)){ response.sendRedirect("/UserManager/MainFrameServlet?uname="+name); }else{ response.sendRedirect("/UserLogin/LoginServlet"); }
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=utf-8"); PrintWriter out = response.getWriter(); String name = request.getParameter("uname"); out.println("欢迎"+name+"进入管理界面</br>"); out.println("<a href='/UserLogin/LoginServlet'>返回重新登陆</a>"); }
当然这种方法只能传递字符串,不能传递对象,如果想传递对象可以用 session 的方式
if("admin".equals(name) && "123456".equals(pass)){ request.getSession().setAttribute("loginpass",pass); response.sendRedirect("/UserManager/MainFrameServlet?uname="+name); }else{ response.sendRedirect("/UserLogin/LoginServlet"); }
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=utf-8"); PrintWriter out = response.getWriter(); String name = request.getParameter("uname"); String pass = (String) request.getSession().getAttribute("loginpass"); out.println("欢迎"+name+"进入管理界面,你的密码是:"+ pass + "</br>"); out.println("<a href='/UserLogin/LoginServlet'>返回重新登陆</a>"); }
只要是在有数据传递的地方就会出现中文乱码
(1)form 表单(get/post)
(2)URL传参
我们的UTF-8 的请求首先是服务器接收,服务器是外国人编写的,默认编码一般为 ISO-8859-1,两者编码不一致就会出现乱码
可以使用下面的方式修改请求体中的编码
request.setCharacterEncoding("utf-8");
但是这个设置对通过 get 方式在请行中传递的参数是不起作用的
String u = new String(request.getParameter("username").getBytes("iso-8859-1"),"UTF-8");
由于也是通过 get 方式传参,因此和get 的处理方式一样
相当于返回到浏览器重新发起了 get 请求也是通过 get 请求的方式处理
(1)getRequestURL()
(2)getRequestURI()
(3)getQueryString()
(4)getRemoteAddr()
(5)getRemoteHost()
(6)getRemotePort()
(7)getLocalPort()
(8)getLocalAddr()
(9)getLocalName()
(10)getHeader()
(11)getHeaderNames()
(12)getParameter()
(13)getParameterValues()
(14)getParameterNames()
请求转发:一个web 资源受到服务器的请求以后,通知服务器去调用另一个web 资源进行处理的过程
我们可以通过他实现我们之前实现过得跳转并传递参数的功能
request.getRequestDispatcher("转向的地址").forward(request,response);
上面的代码实际上是使用了转向的方法将 request 和 response 对象转发到了下一个地址
这句话和 sendRedirect最本质的区别就是,他的转向不是打回给浏览器重新发起请求,而是打回给服务器