1.提出问题
2.问题一:Servlet和Jsp为什么是一个东西?用类之间的关系来说明。
这是一个用于简单计算的Jsp页面,如下图:
Jsp文件首先翻译为Java文件,再将java文件编译为class文件,最后执行class文件。Jap翻译的Java文件如下图:
在Java文件中可以看到如下重要代码:
public final class index_jsp extends org.apache.jasper.runtime.HttpJspBase
implements org.apache.jasper.runtime.JspSourceDependent,
org.apache.jasper.runtime.JspSourceImports{
}
可以看到index_jsp类继承了HttpJspBase类,使用API查看HttpJspBase类源码:
可以看到官方文档显示HttpJspBase继承HttpServlet,所以由此说明Jsp和Servlet是同一个东西,且Servlet是Jsp的父类。
3.问题二: 怎么样才可以让servlet在服务器启动时实例化。并且那个标签中的数字大或小有什么意义?
使用<load-on-startup>标签使servlet在服务器启动时实例化。
标签内的的数字代表执行的先后次序,越小越先执行
修改web.xml文件:
<servlet>
<servlet-name>DoComputerServlet</servlet-name>
<servlet-class>com.wpf.jsp.servlet.DoComputerServlet</servlet-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
启动服务器不发送请求:
尝试多个servlet自动实例化修改web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<context-param>
<param-name>comm</param-name>
<param-value>全局变量</param-value>
</context-param>
<servlet>
<servlet-name>DoComputerServlet</servlet-name>
<servlet-class>com.wpf.jsp.servlet.DoComputerServlet</servlet-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>DoComputerServlet</servlet-name>
<url-pattern>/doComputer</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>OtherServlet</servlet-name>
<servlet-class>com.wpf.jsp.servlet.OtherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>OtherServlet</servlet-name>
<url-pattern>/doOther</url-pattern>
</servlet-mapping>
</web-app>
重新启动服务器不发送请求:
4.问题三:说明servlet的生命周期。方法的先后调用?
首先自创建的DoComputerServlet类继承了HttpServlet类,其中父类存在的一些关于Servlet生命周期的方法,如下代码:
package com.wpf.jsp.servlet;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
public class DoComputerServlet extends HttpServlet {
//服务方法
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
//服务方法
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("doPostInit");
Double num1 = Double.parseDouble(request.getParameter("num1"));
Double num2 = Double.parseDouble(request.getParameter("num2"));
String op = request.getParameter("op");
Double num3 = null;
if ("+".equals(op)) {
num3 = num1 + num2;
} else if ("-".equals(op)) {
num3 = num1 - num2;
} else if ("*".equals(op)) {
num3 = num1 * num2;
} else {
if (num2 != 0)
num3 = num1 / num2;
}
request.setAttribute("num3", num3);
request.getRequestDispatcher("index.jsp").forward(request, response);
}
//摧毁方法
@Override
public void destroy() {
System.out.println("DoComputerServletDestroy");
}
//初始方法
@Override
public void init() throws ServletException {
System.out.println("DoComputerServletInit");
}
//构造方法
public DoComputerServlet() {
System.out.println("DoComputerServletConstructor");
}
}
分别有服务方法,构造方法,初始方法,摧毁方法中添加了输出。启动服务器,并发送多次请求,关闭游览器后重新打开游览器访问,最后关闭服务。命令台的输出为如下图:
可以看出Servlet的生命周期中,实例化一次初始化一次,服务多次,销毁一次。
5.问题四:servlet是线程安全的吗﹖为什么请说明?
浏览器每发送一次请求,服务器提供一个线程去控制请求数据,所以在这次请求中request的数据是线程安全的。
对于动用servlet中的属性值时是不安全的。
Servlet不是线程安全的。
要解释为什么Servlet为什么不是线程安全的,需要了解Servlet容器(即Tomcat)使如何响应HTTP请求的。
当Tomcat接收到Client的HTTP请求时,Tomcat从线程池中取出一个线程,之后找到该请求对应的Servlet对象并进行初始化,之后调用service()方法。要注意的是每一个Servlet对象再Tomcat容器中只有一个实例对象,即是单例模式。如果多个HTTP请求请求的是同一个Servlet,那么着两个HTTP请求对应的线程将并发调用Servlet的service()方法。
上图中的Thread1和Thread2调用了同一个Servlet1,所以此时如果Servlet1中定义了实例变量或静态变量,那么可能会发生线程安全问题(因为所有的线程都可能使用这些变量)。
servlet不是线程安全的
6.问题五:如何在多个servlet中共享配置的数据,用哪个标签?
首先在web.xml中修改代码:
<servlet>
<servlet-name>DoComputerServlet</servlet-name>
<servlet-class>com.wpf.jsp.servlet.DoComputerServlet</servlet-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>DoComputerServlet</servlet-name>
<url-pattern>/doComputer</url-pattern>
</servlet-mapping>
修改DoComputerServlet代码:
//初始方法
@Override
public void init() throws ServletException {
String encoding = this.getInitParameter("encoding");
System.out.println("DoComputerServletInit————encoding" + encoding);
}
启动服务器发送请求输出结果:
生成另有一个Servlet
package com.wpf.jsp.servlet;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
public class OtherServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("doPost");
}
public OtherServlet() {
System.out.println("OtherServletConstructor");
}
@Override
public void destroy() {
System.out.println("OtherServletDestroy");
}
@Override
public void init() throws ServletException {
String encoding = this.getInitParameter("encoding");
System.out.println("OtherServletInit————encoding" + encoding);
}
}
重新启动发送请求:
OtherServlet无数据
在web.xml中加入全局变量
<context-param>
<param-name>comm</param-name>
<param-value>全局变量</param-value>
</context-param>
修改Servlet
//初始方法
@Override
public void init() throws ServletException {
String encoding = this.getInitParameter("encoding");
System.out.println("DoComputerServletInit————encoding" + encoding);
String comm = this.getServletContext().getInitParameter("comm");
System.out.println("DoComputerServletInit————comm" + comm);
}
@Override
public void init() throws ServletException {
String encoding = this.getInitParameter("encoding");
System.out.println("OtherServletInit————encoding" + encoding);
String comm = this.getServletContext().getInitParameter("comm");
System.out.println("OtherServletInit————comm" + comm);
}
重启服务器并发送请求:
多个servlet中共享配置的数据,使用<context-param>标签。
7.问题六:代码实现来说明,filter, servlet,ServletContextListener这三个的启动顺序。
新建一个Filter:
package com.wpf.jsp.filter;
import com.wpf.jsp.domain.User;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
public class MyFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("MyFilterInit");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
System.out.println("MyFilterDestroy");
}
}
新建一个Listener:
package com.wpf.jsp.listener;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import java.util.ArrayList;
import java.util.List;
public class MyListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
List<String> strings = new ArrayList<>();
for (int i = 0; i < 10; i++) {
strings.add("abc" + i);
}
System.out.println("context---init---");
ServletContext application = sce.getServletContext();
application.setAttribute("strings", strings);
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("contextDestroyed");
}
}
修改web.xml:
<filter>
<filter-name>MyFilter</filter-name>
<filter-class>com.wpf.jsp.filter.MyFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>MyFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
<listener-class>com.wpf.jsp.listener.MyListener</listener-class>
</listener>
重启服务器:
8.问题七:代码实现监听session属性添加了,或session属性删除了。
新建一个Listener:
package com.wpf.jsp.listener;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
public class SessionListener implements HttpSessionListener, HttpSessionAttributeListener {
@Override
public void attributeAdded(HttpSessionBindingEvent se) {
System.out.println("attributeAdded");
}
@Override
public void attributeRemoved(HttpSessionBindingEvent se) {
System.out.println("attributeRemoved");
}
@Override
public void attributeReplaced(HttpSessionBindingEvent se) {
System.out.println("attributeReplaced");
}
@Override
public void sessionCreated(HttpSessionEvent se) {
System.out.println("sessionCreated");
}
@Override
public void sessionDestroyed(HttpSessionEvent se) {
System.out.println("sessionDestroyed");
}
}
修改web.xml:
<listener>
<listener-class>com.wpf.jsp.listener.SessionListener</listener-class>
</listener>
修改servlet:
修改jsp:
重启服务器发送请求:
9.问题八:请实现,tomcat启动时,将数据库中用户表中的所有数据,缓存在系统的userList中。
新建一个Listener:
package com.wpf.jsp.listener;
import com.wpf.jsp.domain.User;
import com.wpf.jsp.service.UserService;
import com.wpf.jsp.service.impl.UserServiceImpl;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import java.util.List;
public class UserListListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
UserService userService = UserServiceImpl.getUserService();
List<User> users = userService.get();
sce.getServletContext().setAttribute("users", users);
}
}
修改web.xml:
<listener>
<listener-class>com.wpf.jsp.listener.UserListListener</listener-class>
</listener>
修改index.jsp:
<%
List<User> userList = (List<User>) request.getServletContext().getAttribute("users");
for (User user : userList) {
%>
<tr align="center">
<td><%=user.getId()%>
</td>
<td><%=user.getUserName()%>
</td>
<td><%=user.getPassword()%>
</td>
</tr>
<%
}
%>
运行服务器: