key point
- Servlet GenericServlet HttpServlet HttpRequestServlet HttpResponseServlet
- 保持状态的方法
- Servlet 之间的消息共享级别
- Context Listener(application)
- ServletContextListener
- ServletContextAttributeListener
- Session Listener session
- HttpSessionListener
- HttpSessionAttributeListener
- HttpSessionActivationListener
- HttpSessionBindingListener
- Filter
Java 和PHP体系对比
体系 | 负载均衡 | 中间件 | 框架 | 数据库 |
---|---|---|---|---|
JAVA | nginx | tomcat/jboss | spring/play | mysql/postgre... |
PHP | nginx | php-cgi | thinkphp/laravel | mysql/postgre... |
基本信息
servlet 的全称是 Server Applet。 是用Java编写的服务器端程序。其主要功能在于交互式地浏览和修改数据,生成动态内容。servlet不仅能处理http协议还能处理ftp等协议。
在servlet3.0之前一个servlet应用由一系列处理请求的Java编译文件(*.class)和一个告诉Java编译文件是怎么组织的部署描述符(web.xml)构成。
在3.0之后 部署描述可以通过Java编码的方式进行,web.xml可以省略。
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright 2004 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package javax.servlet;
import java.io.IOException;
/**
* Defines methods that all servlets must implement.
*
* <p>A servlet is a small Java program that runs within a Web server.
* Servlets receive and respond to requests from Web clients,
* usually across HTTP, the HyperText Transfer Protocol.
*
* <p>To implement this interface, you can write a generic servlet
* that extends
* <code>javax.servlet.GenericServlet</code> or an HTTP servlet that
* extends <code>javax.servlet.http.HttpServlet</code>.
*
* <p>This interface defines methods to initialize a servlet,
* to service requests, and to remove a servlet from the server.
* These are known as life-cycle methods and are called in the
* following sequence:
* <ol>
* <li>The servlet is constructed, then initialized with the <code>init</code> method.
* <li>Any calls from clients to the <code>service</code> method are handled.
* <li>The servlet is taken out of service, then destroyed with the
* <code>destroy</code> method, then garbage collected and finalized.
* </ol>
*
* <p>In addition to the life-cycle methods, this interface
* provides the <code>getServletConfig</code> method, which the servlet
* can use to get any startup information, and the <code>getServletInfo</code>
* method, which allows the servlet to return basic information about itself,
* such as author, version, and copyright.
*
* @author Various
*
* @see GenericServlet
* @see javax.servlet.http.HttpServlet
*
*/
public interface Servlet {
/**
* Called by the servlet container to indicate to a servlet that the
* servlet is being placed into service.
*
* <p>The servlet container calls the <code>init</code>
* method exactly once after instantiating the servlet.
* The <code>init</code> method must complete successfully
* before the servlet can receive any requests.
*
* <p>The servlet container cannot place the servlet into service
* if the <code>init</code> method
* <ol>
* <li>Throws a <code>ServletException</code>
* <li>Does not return within a time period defined by the Web server
* </ol>
*
*
* @param config a <code>ServletConfig</code> object
* containing the servlet's
* configuration and initialization parameters
*
* @exception ServletException if an exception has occurred that
* interferes with the servlet's normal
* operation
*
* @see UnavailableException
* @see #getServletConfig
*
*/
public void init(ServletConfig config) throws ServletException;
/**
*
* Returns a {@link ServletConfig} object, which contains
* initialization and startup parameters for this servlet.
* The <code>ServletConfig</code> object returned is the one
* passed to the <code>init</code> method.
*
* <p>Implementations of this interface are responsible for storing the
* <code>ServletConfig</code> object so that this
* method can return it. The {@link GenericServlet}
* class, which implements this interface, already does this.
*
* @return the <code>ServletConfig</code> object
* that initializes this servlet
*
* @see #init
*
*/
public ServletConfig getServletConfig();
/**
* Called by the servlet container to allow the servlet to respond to
* a request.
*
* <p>This method is only called after the servlet's <code>init()</code>
* method has completed successfully.
*
* <p> The status code of the response always should be set for a servlet
* that throws or sends an error.
*
*
* <p>Servlets typically run inside multithreaded servlet containers
* that can handle multiple requests concurrently. Developers must
* be aware to synchronize access to any shared resources such as files,
* network connections, and as well as the servlet's class and instance
* variables.
* More information on multithreaded programming in Java is available in
* <a href="http://java.sun.com/Series/Tutorial/java/threads/multithreaded.html">
* the Java tutorial on multi-threaded programming</a>.
*
*
* @param req the <code>ServletRequest</code> object that contains
* the client's request
*
* @param res the <code>ServletResponse</code> object that contains
* the servlet's response
*
* @exception ServletException if an exception occurs that interferes
* with the servlet's normal operation
*
* @exception IOException if an input or output exception occurs
*
*/
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException;
/**
* Returns information about the servlet, such
* as author, version, and copyright.
*
* <p>The string that this method returns should
* be plain text and not markup of any kind (such as HTML, XML,
* etc.).
*
* @return a <code>String</code> containing servlet information
*
*/
public String getServletInfo();
/**
*
* Called by the servlet container to indicate to a servlet that the
* servlet is being taken out of service. This method is
* only called once all threads within the servlet's
* <code>service</code> method have exited or after a timeout
* period has passed. After the servlet container calls this
* method, it will not call the <code>service</code> method again
* on this servlet.
*
* <p>This method gives the servlet an opportunity
* to clean up any resources that are being held (for example, memory,
* file handles, threads) and make sure that any persistent state is
* synchronized with the servlet's current state in memory.
*
*/
public void destroy();
}
public void init(ServletConfig servletConfig) throws ServletException ;
init 方法在整个类的生命周期内只执行一次(不是每一次用户请求一次、这个类就会被生成一次,而是当用户第一次请求这个类的时候这个类会被生成,然后这个类会放在JVM的heap里面,直到被GC)
ServletConfig servletConf
容器从部署文件中读出Servlet初始化参数,并把这些参数交给ServletConfig,然后把ServletConfig传递给servlet的init(ServletConfig config)方法。从而避免了在类里硬编码
URI和servlet对应的这个部分可以用注解来代替
@WebServlet(name = "test", urlPatterns = {"/test1/test2/test3/"})
@WebInitParam(name = "color", value = "red")
public class MyServlet2 extends HttpServlet{
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException{
PrintWriter printWriter = response.getWriter();
printWriter.printf(getServletConfig().getInitParameter("color"));
}
}
对于之前的servlet接口有两个可以改进的地方
- 对于我们没有功能的方法也要进行实现
- servletConfig要我们自己去定义
于是就有了
GenericServlet 这个类里面有ServletConfig属性,实现了servlet接口,我们的类继承GenericServlet这个接口,只用对有必要的函数进行重写。
为了便于http的处理,JEE提供了HttpServlet。HttpServlet继承于GenericServlet。
在实现Servlet接口的service方法的时候将入参向下转型为HttpServletRequest 和HttpServletRequest。这样我们可以使用HTTP协议的一些特性。
HttpServletRequest
- String getContextPath();//返回context(上下文的根)的路径
- Cookie[] getCookies();//获得cookie
- String getHeader(String name);//根据HTTP头的key获得value
- String getMethod();//获得请求的方法
- String getQueryString(); //返回URI?后面的全部部分
- HttpSession getSession();//找到和这个请求的有关的session。如果没有找到,创建新的session。
HttpServletResponse
- addCookie(Cookie cookie);
- addHeader();
- sendRedirect(); 向浏览器response 状态码为302 location为sendRedirect里的地址
HttpService 重载service方法 根据HttpRequest.method 的不同将请求分发到不同的函数中,后续继承HttpService的类只要重写要进行处理的Http方法的方法就好。比如我要处理http的get,我继承于HttpService的类只要重写doGet方法就好。
Servlet保持状态的4种方法
- 网址重写 ?a=b&c=d
- form-data
- cookie
- HttpSession
第1种和第2种方法可以通过request.getParameter("")的方式 通过key来访问值
第3种 cookie是放在http头部中的 每一次向服务器发送信息的时候都会包括该域名下所有的cookie,cookie的个数是有限制的最大数量为20个。通过HttpServletReuest 的getCookies()方法可以获得所有的cookie 然后通过比较cookie名称和我们预想的cookie的name是否一致得到我们想要的cookie。
第4种方法 一般向服务器发送消息的时候会有一个叫做JESSIONID Cookie 里面包含了在内存这次访问session的key,服务器可以根据这个key反序列化出相关的信息。在Servlet中可以通过HttpServlet的getSession() 方法得到本次访问的HttpSession 对象。
Servlet之间的消息共享
Servlet 之间通过Attribute(属性)的方式进行消息共享。消息分享的层级有以下三个
- request(当个访问者当次请求)
- session (当个访问者,所有的请求)
- application (全部访问者,所有的请求)
在request 级别的attirbute 的使用方法一般是
Map<String, String[]> bigCities = new HashLinkedMap<>();
bigCities.put("China",{"Beijing", "Shanghai", "Hongkong"});
bigCities.put("USA",{"New York", "Boston", "Washington, D.C."})
request.setAttrribuate("bigCities",bigCities);
RequestDispatcher rd = request.getRequestDispatcher("/show.jsp");
rd.forward(request, response);
将属性放到request中,把这个属性转交给jsp页面进行页面的渲染
<c:forEach item = "${requestScope.bigCities}" var = "country">
${country.key}
<c:forEach item= "${country.value}" var ="city" varStatus = "status">
${city}
<c:if test = "${! status.last}">
<br/>
</c:if>
</c:forEach>
</c:forEach>
监听器
进行事件驱动编程,当什么事情发生的时候我们要做出合适的反应
定义监听器有两个方法
@WebListener
public class ListenerClass implements ListenerInterface{
}
或者
<listen>
<listener-class>org.cor.projectName.something</listener-class>
</listen>
Context Listener 上下文级别的监听器
ServletContextLister
当一个application的context被容器所创建的时候,容器会调用实现这个接口的对象的特定方法
- void contextInitialized(ServletContextEvent event);
对环境的初始化做出响应 - void contextDestoryed(ServletContextEvent event);
对环境的析构做出响应 - ServletContext getServletContext();
得到上下文对象
for example
@WebListener
public class AppContextListener implements ServletContextListener{
@Override
public void contextInitialized(ServletContextEvent event){
Map<String, String> contries = new HashMap<String, String>();
contries.put("ca", "Canada");
contries.put("us", "USA");
}
}
当一个application的context被容器所创建的时候,我们给容器增加一个Attribute
ServletContextAttributeListener
当application范围内的attribute发生改变的时候,容器会调用实现这个接口的对象的特定方法
- void attributeAdded(ServletContextAttributeEvent event);
增加属性 - void attributeRemoved(ServletContextAttributeEvent event);
删除属性 - void attributeReplaced(ServletContextAttributeEvent event);
替换属性的内容
Session Listener session级别的监听器
HttpSessionListener
这个接口的作用是和上面的ServletContextLister相类似只不过是作用于session范围
- sessionCreated(HttpSessionEvent event);
当一个session被创建时会调用这个方法 - sessionDestroyed(HttpSessionEvent event);
当一个session被销毁时调用这个方法 - HttpSession getSession();
获得要被操作的session
HttpSessionAttributeListener
这个接口的作用和上面的ServletContextAttributeListener 相类似,作用于session范围
- void attributeAdded(HttpSessionBindingEvent event)
属性添加 - void attributedRemoved(HttpSessionBindingEvent event);
属性删除 - void attributeReplace(HttpSessionBindingEvent event);
HttpSessionActivationListener
session 是保存在内存中,当内存不够的时候容器会尝试着将session进行迁移或者序列化,通过这个接口能对做出容器的行为做出反应
- void sessionDidActivate(HttpSessionEvent event);(容器尝试将session触发)
- void sessionWillPassivate(HttpSessionEvent event);(容器尝试着将session钝化)
HttpSessionBindingListener
如果一个类想知道什么时候被绑定到HttpSesison上,那么这个类要实现HttpSessionBindingListener接口,在绑定的时候或者不绑定的时候,这个类会做出合适反应
public BigCity implements HttpSessionBindingListener{
...
@Override
public void valueBound(HttpSessionBindingEvent event){
}
@Override
public void valueUnbound(HttpSessionBindingEvent event){
}
}
在把这个类的对象放到session的setAttribute的时候,valueBound会被调用。
过滤器
过滤器是指请求过来的时候先拦截请求,处理一波之后在交给servlet,在验证登陆状态字符处理等的方面都有应用。
定义过滤器有两个方法
@WebFilter(filterName = "***Filter", urlPatterns = {"/*"})
//过滤器必须要以Filter结尾
public class ***Filter implements Filter{
}
或者在部署文件中
<filter>
<filter-name>***Filter</filter-name>
<filter-class>test.andrew.io.***Filter</filter-class>
</filter>
<filter-mapping>
<filter-name>***Filter</filter-name>
<url-patten>/*</url-patten>
</filter-mapping>
一个典型的Filter的应用,设置字符
public class UTF8Filter implements Filter{
@Override
public void destroy() {
// TODO Auto-generated method stub
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain fchain) throws IOException, ServletException {
// TODO Auto-generated method stub
//System.out.println("先执行过滤器里面的内容。。。。。");
request.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
fchain.doFilter(request, response);
}
@Override
public void init(FilterConfig arg0) throws ServletException {
// TODO Auto-generated method stub
}
}
Spring MVC
就和其他的框架一样 Sring 的出现也是为了简化开发
- Spring 环境的构建
1.1 old school
step1: 在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_3_1.xsd" version="3.1"> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <servlet> <servlet-name>app</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>app</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
- contextLoaderListener
ContextLoaderListener的作用就是启动Web容器时,自动装配ApplicationContext的配置信息。因为它实现了ServletContextListener这个接口,在web.xml配置这个监听器,启动容器时,就会默认执行它实现的方法。至于ApplicationContext.xml这个配置文件部署在哪,如何配置多个xml文件,书上都没怎么详细说明。现在的方法就是查看它的API文档。在ContextLoaderListener中关联了ContextLoader这个类,所以整个加载配置过程由ContextLoader来完成。看看它的API说明- 第一段
ContextLoaderServlet的API,可以看到它继承了ContextLoader这个类而且它实现了ServletContextListener。
主要的实现是基于ContextLoader 这个类. - 第二段
ContextLoader创建的是 XmlWebApplicationContext这样一个类,它实现的接口是WebApplicationContext->ConfigurableWebApplicationContext->ApplicationContext->
BeanFactory这样一来spring中的所有bean都由这个类来创建 - 第三段
讲如何部署applicationContext的xml文件,如果在web.xml中不写任何参数配置信息,默认的路径是"/WEB-INF/applicationContext.xml,在WEB-INF目录下创建的xml文件的名称必须是applicationContext.xml。如果是要自定义文件名可以在web.xml里加入contextConfigLocation这个context参数:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/classes/applicationContext-.xml
</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/classes/applicationContext-.xml
</param-value>
</context-param>
在<param-value> </param-value>里指定相应的xml文件名,如果有多个xml文件,可以写在一起并一“,”号分隔。上面的applicationContext-.xml采用通配符,比如这那个目录下有applicationContext-ibatis-base.xml,applicationContext-action.xml,applicationContext-ibatis-dao.xml等文件,都会一同被载入。
由此可见applicationContext.xml的文件位置就可以有两种默认实现:
第一种:直接将之放到/WEB-INF下
第二种:将之放到classpath下,但是此时要在web.xml中加入<context-param>,用它来指明你的applicationContext.xml的位置以供web容器来加载。按照Struts2 整合spring的官方给出的档案,写成:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext-.xml,classpath:applicationContext-.xml</param-value>
</context-param>
- 第一段
但是也不一定要定义applicationContext.xml, 如果不定义 applicationContext
1.2 使用config方bo
- Spring对请求的处理
- Spring 的ORM
- Spring的不同环境
spring part
该注解用于读取Request请求的body部分数据
资料1
资料2
资料3
Spring 环境的构建
每种语言采用自动化构建的方式不尽相同,Python用的是pip,PHP用的是Composer,现在Java社区比较常用的是gradle和maven。
下面介绍maven的依赖构建
首先用ide构建一个maven的项目
一个pom.xml的样例
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>andrew.io</groupId><!--组织名称-->
<artifactId>spring</artifactId><!--项目名称-->
<version>1.0-SNAPSHOT</version><!--版本名称-->
<dependencies>
<!--在这个位置添加依赖的包-->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.3.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.data/spring-data-jpa -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>1.10.5.RELEASE</version>
</dependency>
</dependencies>
</project>
一个简易的Spring MVC的分层
和其他语言的项目一样,一个简单的spring mvc project也会对项目进行分层。分的层次大致如下
- controller
主要用来组装客户端发过来的信息然后给业务层进行处理
捕捉异常,对异常进行合适处理
将结果反馈给请求方 - service
负责主要的业务逻辑的操作 - dao
数据库CRUD - domian
对象和数据库中的实体对应 - config spring
配置类和spring配置文件(xml,yum,property)
这样可以明确每个层次的边界、减少每个层次之间的耦合度、增强复用性。
通过Spring MVC响应一个请求
当一个请求被容器响应的时候,首先由spring框架的DispatcherServlet进行处理。它是一个前端控制器,在Spring框架中通过单例模式实现,所有的请求都会通过它将任务派发被Controller。典型的应用程序中会有多个控制器,DispatcherServlet会查询一个或者多个控制器映射(handler mapping)来确定要派发给谁。Controller的职责主要是将发过来的request做一个合适的处理(网址重写?a=b,还是通过表单提交(form-data)的post中的数据,或者是application-* 形式的),在把数据发送给server层,server层根据业务的要求对数据进行加工,在加工过程的CRUD由Dao层负责,之后将处理好的结果返回给Contorller。Controller将得到的数据进行打包,并且标示出用于渲染输出的视图名,同时将这些数据返回给DispatcherServlet。DispatcherServlet会将使用视图解析器(view resolver)来将逻辑视图名匹配为一个特定的视图实现。视图将使用模型数据渲染输出。由此一次请求完成。
配置DispatcherServlet
配置可以通过web.xml 实现,但是xml毕竟不如代码清晰,现在比较推荐通过java类进行配置以下为一个配置类
public class WebAppInitializer
extends AbstractAnnotationConfigDispatcherServletInitializer{
//
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[0];
}
//配置文件类
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[0];
}
//指定上下文的根
protected String[] getServletMappings() {
return new String["/"];
}
}
怎么发挥作用的?
st=>start: 开始
servlet=>operation: 容器查找实现 ServletContainerInitializer接口的类
spring=>operation: spring提供了这个接口的实现,但是它会找实现了WebApplicationInitailizer的类
spring3=>operation: spring3.2提供了一个WebApplicationInitailizer的基础实现
,叫做AbstractAnnotationConfigDispatcherServletInitializer,
但是正如名字显示的是Abstract(抽象的)需要通过开发者进行实现
e=>end: 结束
st->servlet->spring->spring3->e
st=>start: 开始
e=>end: 结束