做JavaWeb开发的 不可能不知道Servlet 知道Servlet是JavaWeb应用的基础
不过搞懂的恐怕不多 毕竟实际开发多使用框架 Servlet被封装在最底层
但是Servlet版本不断演进 新的变化总应该学习和知晓
传统的Servlet 是使用web.xml文件配置的
众所周知十分的繁琐 而和此保持一致的框架
比如Spring (3.0之前) 因为配置太过繁杂
而被讥讽为xml地狱
建立一个web项目
项目结构
–src
|–java
|–resource
|–webapp
在java目录下源码目录新建servlet类
public class HelloServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("Get Http Connect...");
response.getWriter().println("Get Http Connect Success");
}
}
在webapp目录下的WEB-INF
建立web应用配置文件web.xml
<display-name>First Web Applicationdisplay-name>
<description>第一个网络服务应用description>
<welcome-file-list>
<welcome-file>index.jspwelcome-file>
welcome-file-list>
<servlet>
<servlet-name>Firstservlet-name>
<servlet-class>tv.ciyuan2.HelloServletservlet-class>
servlet>
<servlet-mapping>
<servlet-name>Firstservlet-name>
<url-pattern>/Hellourl-pattern>
servlet-mapping>
每个Servlet首先必须注册
使用标签进行注册 子标签指定servlet名
指定servlet的全限定路径 表明是哪个具体的类
然后要把Servlet映射到URL地址
使用标签进行映射
子标签指定要映射的Servlet 此名名称要和之前注册的相同
子标签映射URL地址 此处必须加上 / 符号 否则访问不到
其他标签如 可以指定欢迎页面
一个Servlet时还可以接受 能想象到多个Servlet实例时配置将会多么繁琐
在前面的基础上再添加多个Servlet
如登录 注册 提示等等
<display-name>First Web Applicationdisplay-name>
<description>第一个网络服务应用description>
<welcome-file-list>
<welcome-file>index.jspwelcome-file>
welcome-file-list>
<servlet>
<servlet-name>Firstservlet-name>
<servlet-class>tv.ciyuan2.HelloServletservlet-class>
servlet>
<servlet-mapping>
<servlet-name>Firstservlet-name>
<url-pattern>/Hellourl-pattern>
servlet-mapping>
<servlet>
<servlet-name>Loginservlet-name>
<servlet-class>tv.ciyuan2.LoginServletservlet-class>
servlet>
<servlet-mapping>
<servlet-name>Loginservlet-name>
<url-pattern>/Loginurl-pattern>
servlet-mapping>
<servlet>
<servlet-name>Registservlet-name>
<servlet-class>tv.ciyuan2.RegistServletservlet-class>
servlet>
<servlet-mapping>
<servlet-name>Registservlet-name>
<url-pattern>/Registurl-pattern>
servlet-mapping>
<servlet>
<servlet-name>Promptservlet-name>
<servlet-class>tv.ciyuan2.PromptServletservlet-class>
servlet>
<servlet-mapping>
<servlet-name>Promptservlet-name>
<url-pattern>/Prompturl-pattern>
servlet-mapping>
web程序 过滤器必不可少
再添加上编码过滤器
<display-name>First Web Applicationdisplay-name>
<description>第一个网络服务应用description>
<welcome-file-list>
<welcome-file>index.jspwelcome-file>
welcome-file-list>
<filter>
<filter-name>CharactorEncodingFilterfilter-name>
<filter-class>filter.CharactorEncodingFilterfilter-class>
filter>
<filter>
<filter-name>HtmlFilterfilter-name>
<filter-class>filter.HtmlFilterfilter-class>
filter>
<filter-mapping>
<filter-name>CharactorEncodingFilterfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
<filter-mapping>
<filter-name>HtmlFilterfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
<servlet>
<servlet-name>Firstservlet-name>
<servlet-class>tv.ciyuan2.HelloServletservlet-class>
servlet>
<servlet-mapping>
<servlet-name>Firstservlet-name>
<url-pattern>/Hellourl-pattern>
servlet-mapping>
<servlet>
<servlet-name>Loginservlet-name>
<servlet-class>tv.ciyuan2.LoginServletservlet-class>
servlet>
<servlet-mapping>
<servlet-name>Loginservlet-name>
<url-pattern>/Loginurl-pattern>
servlet-mapping>
<servlet>
<servlet-name>Registservlet-name>
<servlet-class>tv.ciyuan2.RegistServletservlet-class>
servlet>
<servlet-mapping>
<servlet-name>Registservlet-name>
<url-pattern>/Registurl-pattern>
servlet-mapping>
<servlet>
<servlet-name>Promptservlet-name>
<servlet-class>tv.ciyuan2.PromptServletservlet-class>
servlet>
<servlet-mapping>
<servlet-name>Promptservlet-name>
<url-pattern>/Prompturl-pattern>
servlet-mapping>
非常的繁杂 可以说是接近xml地狱了
所以JavaEE 6的Servlet 3.0标准中 提供了新的解决方案
使用注解初始化Servlet
Servlet 3.0 之后可以使用注解来配置 Servlet Filter Listener等Java Web组件
Servlet 3.0的注解支持 在web.xml中是可以设置的 web.xml的顶层标签 < web-app > 有一个 metadata-complete 属性 设置为false时则启用注解和web插件支持
true时则只初始化web.xml配置中的内容
默认当然是false 也就是启用注解和插件支持
@WebServlet 注解,用于代替在web.xml注册Servlet类的< servlet >,< servlet-mapping >
标签 可以方便的定义一个Servlet
还是刚才的HelloServlet 使用注解来配置
@WebServlet(name = "HelloServlet",urlPatterns="/hello")
public class HelloServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("Get Http Connect...");
response.getWriter().println("Get Http Connect Success");
}
}
简单了许多 只需一个注解 就可以配置成功
用过SpringMVC,Jersey等框架的 可以发现非常类似
都是通过注解标志 来指定请求处理 路径映射的
属性名 | 类型 | 描述 |
---|---|---|
name | String | 指定 Servlet 的 name 属性,等价于 < servlet-name>。如果没有显式指定,则该 Servlet 的取值即为类的全限定名。 |
value | String[] | 该属性等价于 urlPatterns 属性。两个属性不能同时使用。 |
urlPatterns | String[] | 指定一组 Servlet 的 URL 匹配模式。等价于 < url-pattern> 标签。 |
loadOnStartup | int | 指定 Servlet 的加载顺序,等价于 < load-on-startup> 标签。 |
initParams | WebInitParam[] | 指定一组 Servlet 初始化参数,等价于 < init-param> 标签。 |
asyncSupported | boolean | 声明 Servlet 是否支持异步操作模式,等价于 < async-supported> 标签。 |
description | String | 该 Servlet 的描述信息,等价于 < description> 标签。 |
displayName | String | 该 Servlet 的显示名,通常配合工具使用,等价于 < display-name> 标签。 |
常用的就是映射路径的urlPatterns属性
初始化参数的inti-param属性
加载顺序的loadOnStartup 属性
很多教程里面把启动框架的servlet(如Spring)设置为loadOnStartup=1
就是为了第一个加载框架以便首先启动
(当然现在一般都是加载框架启动监听器 在Servlet初始化之前就启动框架)
@WebFilter 用于将一个类声明为过滤器,该注解将会在部署时被容器处理,容器将根据具体的属性配置将相应的类部署为过滤器,等同于web.xml中< filter>标签
属性名 | 类型 | 描述 |
---|---|---|
filterName | String | 指定过滤器的 name 属性,等价于 < filter-name> |
value | String[] | 该属性等价于 urlPatterns 属性。但是两者不应该同时使用。 |
urlPatterns | String[] | 指定一组过滤器的 URL 匹配模式。等价于 < url-pattern> 标签。 |
servletNames | String[] | 指定过滤器将应用于哪些 Servlet。取值是 @WebServlet 中的 name 属性的取值,或者是 web.xml 中 < servlet-name> 的取值。 |
dispatcherTypes | DispatcherType | 指定过滤器的转发模式。具体取值包括: |
ASYNC、ERROR、FORWARD、INCLUDE、REQUEST。 | ||
initParams | WebInitParam[] | 指定一组过滤器初始化参数,等价于 < init-param> 标签。 |
asyncSupported | boolean | 声明过滤器是否支持异步操作模式,等价于 < async-supported> 标签。 |
description | String | 该过滤器的描述信息,等价于 < description> 标签。 |
displayName | String | 该过滤器的显示名,通常配合工具使用,等价于 < display-name> 标签。 |
一个登录过滤器
@WebFilter(filterName="LoginFilter",urlPatterns = "/hello") public class LoginFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("LoginFilter Strat");
}
@Override
public void doFilter(ServletRequest request,
ServletResponse response,FilterChain chain)
throws IOException, ServletException
{
HttpServletRequest httpServletRequest=(HttpServletRequest)request;
String userid=Optional.ofNullable
((String)httpServletRequest.getSession().getAttribute("userid"))
.orElseGet(()->"");
if(userid.isEmpty())
{
response.getWriter().println("未登录 登陆后再试");
}
else
{
chain.doFilter(request,response);
}
}
@Override
public void destroy() { }
}
该注解通常不单独使用,而是配合 @WebServlet 或者 @WebFilter 使用。它的作用是为 Servlet 或者过滤器指定初始化参数,这等价于 web.xml 中 < servlet> 和 < filter> 的 < init-param> 子标签。
属性名 | 类型 | 是否可选 | 描述 |
---|---|---|---|
name | String | 否 | 指定参数的名字,等价于 < param-name>。 |
value | String | 否 | 指定参数的值,等价于 < param-value>。 |
description | String | 是 | 关于参数的描述,等价于 < description>。 |
这个标签是和@WebServlet 或者 @WebFilter 配合使用的
比如 使用阿里巴巴的Druid数据库连接池时
配置管理界面时
配置Druid的登录和管理界面
@WebServlet(urlPatterns = "/druid/*",
initParams={
@WebInitParam(name="allow",value=""),// IP白名单 (没有配置或者为空,则允许所有访问)
@WebInitParam(name="loginUsername",value="admin"),// 用户名
@WebInitParam(name="loginPassword",value="admin"),// 密码
@WebInitParam(name="resetEnable",value="false")// 禁用HTML页面上的“Reset All”功能
}) public class DruidStatViewServlet extends StatViewServlet { }
从上可知 @WebInitParam是编写在@WebServler注解的initParam标签之中的
配置Druid的资源过滤器
@WebFilter(filterName="druidWebStatFilter",urlPatterns="/*",
initParams={
@WebInitParam(name="exclusions",value="*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*")
// 忽略资源
}) public class DruidFilter extends WebStatFilter { }
同样 @WebInitParam也可以编写在@WebFilter注解的initParam标签之中的
该注解用于将类声明为监听器,被 @WebListener 标注的类必须实现以下至少一个接口:
该注解使用非常简单,其属性如下:
属性名 | 类型 | 是否可选 | 描述 |
---|---|---|---|
value | String | 是 | 该监听器的描述信息。 |
一个监听来访客户的网络地址的监听器
@WebListener(value="VisitListener")`
public class VisitListener implements ServletRequestListener { @Override
public void requestInitialized(ServletRequestEvent sre) {
String ipaddress=sre.getServletRequest().getRemoteHost();
System.out.println("id address="+ipaddress);
}
}
如此,则不需要在 web.xml 中配置 < listener> 标签了。它等价的 web.xml 中的配置形式如下:
<listener>
<listener-class>*.VisitListenerlistener-class>
listener>
使用@MultipartConfig注解 启用文件上传支持
过去需要借助第三方工具 Servlet3.0以后就不需要了
@WebServlet(name = "UploadServlet",urlPatterns = "/upload")
@MultipartConfig
public class UploadServlet extends HttpServlet {
@Override
public void init() throws ServletException {
System.out.println("UploadServlet Strat");
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Collection parts=request.getParts();
//此处一个Part对应一个上传的文件
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
属性名 | 类型 | 是否可选 | 描述 |
---|---|---|---|
fileSizeThreshold | int | 是 | 当数据量大于该值时,内容将被写入文件。 |
location | String | 是 | 存放生成的文件地址。 |
maxFileSize | long | 是 | 允许上传的文件最大值。默认值为 -1,表示没有限制。 |
maxRequestSize | long | 是 | 针对该 multipart/form-data 请求的最大数量,默认值为 -1,表示没有限制。 |
使用web.xml配置框架加载 大部分人应该都是很熟悉了
原理是在web.xml中配置监听器或者Servlet
这样在应用容器启动时会自动加载框架的启动器
那么 使用注解后 应该怎样加载框架呢
JavaEE 也提供了解决方案
那就是 ServletContainerInitializer
javax.servlet.ServletContainerInitializer
源码
public interface ServletContainerInitializer {
public void onStartup(Set> c, ServletContext ctx)
throws ServletException;
}
所有实现这接口的类 它的*OnStratip()*方法
将在web应用容器启动的时候被调用
在Srping中是SpringServletContainerInitializer
这个类实现了ServletContainerInitializer
所以容器 启动时会自动 加载SpringServletContainerInitializer的onStratup方法
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
//入参 webAppInitializerClasses 即包括所有webAppInitializer类型的Class
通常都是Spring的Java形式的配置文件
@Override
public void onStartup(@Nullable Set> webAppInitializerClasses,
ServletContext servletContext)throws ServletException {
List initializers = new LinkedList<>();
if (webAppInitializerClasses != null) {
for (Class> waiClass : webAppInitializerClasses) {
// 如果该Class 不是接口,不是抽象类,并且是WebApplicationInitializer类型的类
if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
try {
//实例化该类,并加入到initializers集合中
initializers.add((WebApplicationInitializer)
ReflectionUtils.accessibleConstructor(waiClass).newInstance());
}
catch (Throwable ex) {
throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
}
}
}
}
if (initializers.isEmpty()) {
servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
return;
}
servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
AnnotationAwareOrderComparator.sort(initializers);
//遍历initializers集合中的类
for (WebApplicationInitializer initializer : initializers) {
//调用其onStartup方法 启用其配置的内容
initializer.onStartup(servletContext);
}
}
}
而 我们需要重写的Spring配置类 AbstractAnnotationConfigDispatcherServletInitializer
public abstract class AbstractAnnotationConfigDispatcherServletInitializer
就是一个WebAppInitializerClass的子类
在SpringServletContainerInitializer 启动的时候
就会被加载并调用其方法
这样 框架就被启动起来了
服务端推送 很显然 这是一个为HTTP/2准备的功能
所以这是Servlet 4.0提供的新特性
服务器推送使服务器能预测客户端请求的资源需求。然后,在完成请求处理之前,它可以将这些资源发送到客户端。
客户端发出一个针对该网页的请求。服务器然后分析所请求的页面,确定呈现它所需的资源,并主动将这些资源发送到客户端的缓存(比如大量的图片 视频等流媒体)。
在执行所有这些操作的同时,服务器仍在处理原始网页请求。客户端收到响应时,它需要的资源已经位于缓存中,注意是存放于浏览器缓存。
要推送资源 首先得获取推送器
Servlet通过推送器向客户端发出推送
@Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
PushBuilder pushBuilder = request.newPushBuilder();
}
每次调用 newPushBuilder()
方法时,都将返回 PushBuilder
的新实例。如果服务器推送不可用,newPushBuilder()
将返回null
。在某些情况下,客户端可能会为请求事务拒绝服务器推送。如果客户端没有使用安全连接,服务器推送也不会起作用。因此,务必要在对 PushBuilder
实例调用方法之前,针对 null
返回值进行测试。
发送推送内容
PushBuilder pushBuilder = request.newPushBuilder();
if (pushBuilder != null) {
pushBuilder.path("images/bigBackground.png").push();
pushBuilder.path("video/demo.mp4").push();
}