一、Web项目结构
|- WebRoot : web应用的根目录
|- 静态资源(html+css+js+image+vedio)
|- WEB-INF : 固定写法。
|-classes: (可选)固定写法。存放class字节码文件
|-lib: (可选)固定写法。存放jar包文件。
|-jsp:java服务器页面
|-web.xml (必选)核心web配置,后端代码入口
注意:1)WEB-INF目录里面的资源不能通过浏览器直接访问。
2)如果希望访问到WEB-INF里面的资源,就必须把资源配置到web.xml的文件中。
二、动态资源开发技术
- 静态资源和动态资源的区别:
静态资源: 当用户多次访问这个资源,资源的源代码永远不会改变的资源。
动态资源:当用户多次访问这个资源,资源的源代码可能会发送改变。 - 动态资源的开发技术:
-
Servlet : 用java语言来编写动态资源的开发技术,许多框架如:SpringMVC、Structs等都是servlet改造的。
-
Servlet特点:
- 普通的java类,继承HttpServlet类,覆盖doGet、doPost等方法。
- Servlet类只能交给tomcat服务器运行。
-
Servlet 注解版本:
Servlet3.0以上使用注解自动映射@WebServlet -
Servlet代码实现步骤:
- 继承HttpServlet这个类
- 重写请求方法
- 在web.xml中使用<servlet><servlet-mapping>、使用注解@WebServlet定义servlet
-
Sevlet的生命周期:
Servlet程序的生命周期由tomcat服务器控制的.
Servlet重要的四个生命周期方法:
1. 构造方法:创建servlet对象的时候调用,默认情况下,第一次访问servlet的时候创建servlet对象,只调用1次,证明servlet对象在tomcat是单实例的。
2. init方法:创建完servlet对象的时候调用,只调用1次。
3. service方法:每次发出请求时调用,调用n次。
4. destroy方法:销毁servlet对象的时候调用。停止服务器或者重新部署web应用时销毁servlet对象,只调用1次。
代码实现:package chauncy.servlet; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * 多个请求同时访问servlet 会被实例化多少次? 1次 证明:通过反射创建对象会访问构造函数,只要证明构造函数执行一次 * init() 初始化,只会执行一次 * service() 处理请求,doGet、doPost、doPut、doDelete等 * destory() 销毁方法,只会执行一次,在容器停止的时候把对象从堆空间删除 * 总结:Servlet默认是单例的且在第一次请求被执行的时候才创建,而且永远在jvm中只有一个实例。 * 如何保证在容器启动的时候创建Servlet而不是第一次请求再创建?在web.xml中使用<load-on-startup>配置servlet的自动加载,数值越大优先级越低,设置成1当容器启动的时候就会创建并且执行init方法 * @classDesc: 功能描述(servlet生命周期) * @author: ChauncyWang * @version: 1.0 */ @WebServlet("/ServletLifecycle") public class ServletLifecycle extends HttpServlet { public ServletLifecycle() { System.out.println("ServletLifecycle 构造函数被执行。。。"); } @Override public void init() throws ServletException { System.out.println("执行Servlet初始化init方法"); } /** * doGet是被service执行的 */ @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("doGet方法被执行"); } @Override public void destroy() { System.out.println("执行Servlet销毁destroy方法"); } }
-
Servlet的自动加载:
默认情况下,第一次访问servlet的时候创建servlet对象。如果servlet的构造方法或init方法中执行了比较多的逻辑代码,那么导致用户第一次访问sevrlet的时候比较慢。
改变servlet创建对象的时机: 提前到加载web应用的时候!
在servlet的配置信息中,加上一个<load-on-startup>即可,整数值越大,创建优先级越低。 -
Servlet的多线程并发问题:
servlet对象在tomcat服务器是单实例多线程的。
因为servlet是多线程的,所以当多个servlet的线程同时访问了servlet的共享数据,如成员变量,可能会引发线程安全问题。
解决办法:- 把使用到共享数据的代码块进行同步(使用synchronized关键字进行同步)
- 建议在servlet类中尽量不要使用成员变量。如果确实要使用成员,必须同步。而且尽量缩小同步代码块的范围。(哪里使用到了成员变量,就同步哪里!!),以避免因为同步而导致并发效率降低。
代码实现:
package chauncy.servlet; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * @classDesc: 功能描述:(证明servlet线程安不安全) * servlet加载流程: * 1.tomcat加载web.xml配置 * 2.当发生请求来源时,先匹配<url-pattern>标签,然后寻找<servlet>配置 * 3.解析<servlet>配置<servlet-class> * 4.通过java反射机制,调用Class.forName class.newInstance();无参构造函数创建对象 * 5.先执行servlet无参构造函数 * 6.然后执行init方法 * 7.之后访问service方法,进行判断请求类型,确定访问执行doGet、doPost等 * 8.最后当服务器停止,执行销毁destroy方法 * @author: ChauncyWang * @verssion: v1.0 */ @WebServlet("/ServletThreadSecurity") public class ServletThreadSecurity extends HttpServlet { private int i=1; public ServletThreadSecurity(){ System.out.println("ServletThreadSecurity无参构造函数被执行"); } /** * 当多个请求触发时,如果servlet的构造函数只执行一次,说明servlet时单例的。 * */ @Override public void init() throws ServletException { System.out.println("This is init()"); } /** * 因为servlet线程不安全,所以应该尽量避免使用全局变量。 * 面试中如果问到哪里遇到线程安全,举例servlet默认是单例的线程不安全。HashMap和HashTable区别,HashMap是非线程安全、非synchronized可以接受null的键值,HashTable与之相反。 * jdk1.5提供了ConcurrentHashMap替代HashTable,ConcurrentHashMap具有更强的可扩展性。 * 因为HashMap是线程不安全非synchronized而HashTable是线程安全synchronized的,所以单线程下HashMap比HashTable快。 * HashMap可以通过语句:Map m = Collections.synchronizeMap(hashMap);进行同步 * StringBuilder是线程不安全的,而StringBuffer是线程安全的。 * 如果一个StringBuffer对象在字符串缓冲区被多个线程使用时,StringBuffer中很多方法可以带有synchronized关键字,所以可以保证线程是安全的,但StringBuilder的方法则没有该关键字,所以不能保证线程安全。 * 但是如果在单线程情况下不保证线程安全,StringBuilder比StringBuffer快。 */ @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("This is doGet()"); resp.setCharacterEncoding("utf-8");//内容编码,防止出现中文乱码 resp.setContentType("text/html;charset=utf-8");//向浏览器输出内容 synchronized (ServletThreadSecurity.class) { resp.getWriter().write("第"+i+"次"); try { Thread.sleep(1000); } catch (InterruptedException e) { } i++; } } }
-
- ServletContext对象:
- 得到web应用上下文路径:
java.lang.String getContextPath(),用在请求重定向的资源名称中。
代码实现:package chauncy.servlet; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * @classDesc: 功能描述(servlet上下文的使用) * @author: ChauncyWang * @version: 1.0 */ @WebServlet("/ServletContext") public class ServletContext extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { javax.servlet.ServletContext servletContext = this.getServletContext(); // 获取当前项目上下文路径 String contextPath = servletContext.getContextPath(); // 跳转网页使用:转发或者重定向 resp.sendRedirect(contextPath + "/ServletLifecycle");// 重定向 } }
- 域对象有关的方法:
域对象:作用是用于保存数据,获取数据,可以在不同的动态资源之间共享数据。
可以通过传递参数的形式,共享数据,但是局限:只能传递字符串类型,也可以使用域对象共享数据,好处:可以共享任何类型的数据,ServletContext就是一个域对象,ServletContext域对象:作用范围在整个web应用中有效。- 保存数据:void setAttribute(java.lang.String name, java.lang.Object object)
- 获取数据: java.lang.Object getAttribute(java.lang.String name)
- 删除数据: void removeAttribute(java.lang.String name)
ServletContext域对象的使用,代码实现:package chauncy.servlet; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * 验证servlet上下文作用域 <br/> * servlet上下文作用域保存在服务器端,多用户共享使用,类似于全局变量,不建议使用。 * @classDesc: 功能描述(创建servlet上下文属性值) * @author: ChauncyWang * @version: 1.0 */ @WebServlet("/ServletContextActionScope") public class ServletContextActionScope extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this.getServletContext().setAttribute("userName", "ChauncyWang"); } }
所有域对象:package chauncy.servlet; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * 验证servlet上下文作用域 * @classDesc: 功能描述(调用servlet上下文属性值) * @author: ChauncyWang * @version: 1.0 */ @WebServlet("/ServletContextActionScopeCall") public class ServletContextActionScopeCall extends HttpServlet{ @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String userName = (String) this.getServletContext().getAttribute("userName"); System.out.println("userName:"+userName); } }
- HttpServletRequet 域对象
- ServletContext域对象
- HttpSession 域对象
- PageContext域对象
- 转发与重定向:
重定向非常耗费资源,会发送两次请求,且使用request域会失效。一般来说,转发在本服务器内部使用,重定向在外部服务器使用(跳转到外部服务器)。
通用获取数据的类:package chauncy.servlet; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * @classDesc: 功能描述(获取参数) * @author: ChauncyWang * @version: 1.0 */ @WebServlet("/GetDataServlet") public class GetDataServlet extends HttpServlet{ @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String userName = (String) req.getAttribute("userName"); System.out.println("userName:"+userName); } }
- 转发:
- 地址栏不会改变
- 转发只能转发到当前web应用内的资源
- 可以在转发过程中,把数据保存到request域对象中。
- 效率高,不需要等待服务器返回302状态后再request。
代码实现:package chauncy.servlet; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * @classDesc: 功能描述(转发请求servlet) * @author: ChauncyWang * @version: 1.0 */ @WebServlet("/ForwardServlet") public class ForwardServlet extends HttpServlet{ @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setAttribute("userName", "ChauncWang"); //跳转有两种方式,一种是转发一种是重定向,此处使用转发。 req.getRequestDispatcher("/GetDataServlet").forward(req, resp); } }
- 重定向:
- 地址栏会改变,变成重定向到地址。
- 重定向可以跳转到当前web应用,或其他web应用,甚至是外部域名网站。
- 不能在重定向的过程,把数据保存到request中。
- 效率低,需要当客户端request给服务器,服务器返回302状态给客户端后,客户端收到状态为302,本地解析Location值,后再次request服务器的Location值地址。
代码实现:
手动实现Servlet的Redirect:package chauncy.servlet; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * @classDesc: 功能描述(重定向请求servlet) * @author: ChauncyWang * @version: 1.0 */ @WebServlet("/RedirectServlet") public class RedirectServlet extends HttpServlet{ @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setAttribute("userName", "ChauncWang"); //跳转有两种方式,一种是转发一种是重定向,此处使用重定向。 resp.sendRedirect(this.getServletContext().getContextPath()+"/GetDataServlet"); } }
package chauncy.servlet; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * @classDesc: 功能描述(手写实现Servlet的Redirect) * @author: ChauncyWang * @version: 1.0 */ @WebServlet("/RedirectImplement") public class RedirectImplement extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setStatus(302); resp.setHeader("Location", this.getServletContext().getContextPath()+"/ServletLifecycle"); } }
- 转发:
- 得到web应用上下文路径: