Servlet进阶
Servlet3.0注解配置
注解配置的使用
我们删除之前的web.xml的配置文件,在我们要配置URL路径的Servlet类上添加@WebServlet()注解,括号里面传入我们要处理的请求的URL路径,以/
开头
@WebServlet("/hello")
public class ServletDemo1 implements Servlet {
...
}
原来的xml,我们看到别名是冗余的只是为了识别,一个servlet就8行,阅读性很差。
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>com.kaikeba.course06.ServletDemo1</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
URL路径
我们看一下@WebServlet注解的源码,我们知道如果注解中只有一个属性,那么默认就是value()属性。其中value()属性指的就是urlPatterns(),我们看到其实传入的是一个数组,所以我们的Servlet3.0的注解配置支持一个Servlet处理多个请求。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WebServlet {
String name() default "";
String[] value() default {};
String[] urlPatterns() default {};
int loadOnStartup() default -1;
WebInitParam[] initParams() default {};
boolean asyncSupported() default false;
String smallIcon() default "";
String largeIcon() default "";
String description() default "";
String displayName() default "";
}
如果要指定load-on-startup,我么可添加该参数,我们看到默认是-1,和web.xml的文件配置是一样的。
@WebServlet(urlPatterns = "/hello",loadOnStartup = 1)
public class ServletDemo1 implements Servlet {
...
}
URL的使用方式
- 普通方式
/hello
:处理URL路径为/hello的请求- ``/user/hello`:处理URL路径为/hello的请求
很好理解,我们的URL和请求一一对应,不区分大小写。
- 添加通配符
/*
:处理所有请求/user/*
:处理第一层为hello的所有请求*.do
:处理任何以.do结尾的请求
注意事项:
通配符的优先级是非常低的,所以默认情况下。用户发来一个请求,如果我们不是使用通配符的URL也能处理请求,则通配符是不生效的,但是如果没有一个Servlet可以处理一个请求,则通配符*
才会生效的。
特殊情况演示:
请求:/hello
执行顺序:/hello/*
> /hello
> /*
请求:/user/hello
执行顺序:/user/hello/*
> /user/hello
> /user/*
> /*
>
我们如果定义了多级目录,比如/a/b
就不要使用/a
这样的请求了!
- 错误方式
/*.do
-
/*/*
这种方式会失去匹配功能,也根据浏览器而异,一般情况下,该URL什么都无法接收。
-
/he*
:
- 特殊方式
-
/
在不同的浏览器对
/
请求处理不同。谷歌浏览器不会将/
作为请求,而在火狐浏览器的/
和/*
有相同的作用,但是如果处理/
和/*
的Servlet都存在,则优先执行/*
,所以我们尽量减少
HttpServlet
HttpServlet是GenericServlet的实现类,GenericServlet是Servlet接口的空实现抽象类,HttpServlet是我么最常用的Servlet了。
常量
HttpServlt定义了8个常量,对应浏览器的7种请求(少了一个HEAD请求)。
public abstract class HttpServlet extends GenericServlet {
private static final String METHOD_DELETE = "DELETE";
private static final String METHOD_HEAD = "HEAD";
private static final String METHOD_GET = "GET";
private static final String METHOD_OPTIONS = "OPTIONS";
private static final String METHOD_POST = "POST";
private static final String METHOD_PUT = "PUT";
private static final String METHOD_TRACE = "TRACE";
......
}
处理HTTP请求
源码比较长,简单看一下:
- 使用request.getMethod()方法获取请求类型,值是“POST”,"GET"这样的大写。
- 使用一堆if – else if 来判断是哪种请求,然后执行对应方法,比如POST请求对应doPost()方法。
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String method = req.getMethod();
long lastModified;
if (method.equals("GET")) {
lastModified = this.getLastModified(req);
if (lastModified == -1L) {
this.doGet(req, resp);
} else {
long ifModifiedSince;
try {
ifModifiedSince = req.getDateHeader("If-Modified-Since");
} catch (IllegalArgumentException var9) {
ifModifiedSince = -1L;
}
if (ifModifiedSince < lastModified / 1000L * 1000L) {
this.maybeSetLastModified(resp, lastModified);
this.doGet(req, resp);
} else {
resp.setStatus(304);
}
}
} else if (method.equals("HEAD")) {
lastModified = this.getLastModified(req);
this.maybeSetLastModified(resp, lastModified);
this.doHead(req, resp);
} else if (method.equals("POST")) {
this.doPost(req, resp);
} else if (method.equals("PUT")) {
this.doPut(req, resp);
} else if (method.equals("DELETE")) {
this.doDelete(req, resp);
} else if (method.equals("OPTIONS")) {
this.doOptions(req, resp);
} else if (method.equals("TRACE")) {
this.doTrace(req, resp);
} else {
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[]{method};
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(501, errMsg);
}
}
一般操作
我们只要复写对应的doGet(),doPost()方法即可,Servlet收到请求时,会根据requset.getMethod()获得请求类型,我们不要去干重写service方法的迷惑操作。
@WebServlet("/service")
public class HttpServletDemo2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("get方法");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("post方法");
}
}
中文乱码
Http之间传输数据可能会出现乱码问题,这需要我们统一编码:
接收用户数据乱码问题:
String username=request.getParameter("username");
//转码
username = new String(username.getBytes("ISO-8859-1") , "UTF-8");
System.out.println("userName="+username+"==password="+password);
发送数据给用户乱码问题:
//告诉服务器用编码来解析什么来解析
response.setCharacterEncoding("UTF-8");
//告诉客户端用什么编码
response.setHeader("content-type", "text/html;charset=UTF-8");
或者使用这个,一个顶两
response.setContentType("text/html;charset=UTF-8")