JavaEE复习笔记

JavaEE复习笔记

根据上课的笔记整理与补充。
contact: 405544641@qq.com

Spring的思想 控制翻转:让容器管理对象的创建与销毁,依赖注入:利用反射实现,动态地向一个对象提供其所需的其他对象

JavaEE三大组件: Servlet, Filter, Listener
组件必须有 init(conf) destroy()

web容器(servlet容器)

请求怎么来到servlet呢?答案是servlet容器,比如我们最常用的tomcat,同样,你可以随便谷歌一个servlet的hello world教程,里面肯定会让你把servlet部署到一个容器中,不然你的servlet压根不会起作用。
tomcat才是与客户端直接打交道的家伙,他监听了端口,请求过来后,根据url等信息,确定要将请求交给哪个servlet去处理,然后调用那个servlet的service方法,service方法返回一个response对象,tomcat再把这个response返回给客户端。

import javax.servlet servlet是个接口
HttpServlert实现Servlet
使用Servlet ServletRequest ServletRespond
1.首先要在web.xml中进行配置

<servlet>
    <servlet-name>A</servlet-name>   //servlet名
    <servlet-class>com.xxx.xx.xx.servlet类名</servlet-class>  //对应的类
    <init-param>   //初始化参数
        <param-name>p1</param-name><param-value>123</param-value>
    </init-param>
</servlet>
//! 到这为止已经定义好servlet,不会有任何错误。 当服务器启动后,也会创建好servlet. 但是无法访问因为没mapping
<servlet-mapping>
    <servlet-name>A</servlet-name> //要和上面对应
    <url-pattern>/login</url-pattern>     //注意 /代表的是web Content的位置
</servlet-mapping>


**url-pattern:  /是缺省匹配,若当前访问资源地址的所有 Servlet 都不匹配时,就由缺省的 Servlet 进行处理
               优先级:全路径匹配>部分路径匹配>拓展名匹配
               如果我们web应该中同时配置"/"和"/*",那么默认的Servelt永远都不会被匹配到,所有的路径最终都会匹配到"/*"上
               /不能匹配jsp,原因是jsp容器已经默认映射了一个*.jsp,优先级要高于/  /*可以
               精确路径匹配>最长路径匹配>扩展匹配     /a/*.xxx 这样方式会报错。不能混搭。
注:Filter不会像servlet只匹配一个。 顺序与定义顺序相同
/*和/** 后者会匹配更多的目录 (Ant path匹配。spring支持。还支持正则表达式,进行更复杂的匹配)

关于url-pattern的/, 同样在struts中,默认映射了.action .do , /无法覆盖他们
strut2的配置文件加载顺序为:

   Struts-default.xml---> struts-plugin.xml--> struts.xml-->   struts.properties--> web.xml

使用servlet

//AServlet.java
package a; //倒序 com.xxx,部门,项目:模块

////先引入这3个包
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
public class AServlet extends HttpServlet{


   /*  默认已经实现
   private ServletConfig config;
   @Ovrride
   public void init(ServletConfig config) throws ServletException{
      super(config);
      init();
    }*/

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws    IOException,ServletException{
    response.setContentType("text/html;charset=UTF-8");  //默认ISO8859-1
    response.setCharacterEncoding("UTF-8")//设置编码
    PrintWriter out=response.getWriter();
    out.println("<h1>this is servlet</h1>");
    //不要close()   谁创建的流谁负责关闭
        }

    @Ovrride
    protected void doPost(HttpServletRequest request,HttpServletResponse){
    }

/*
    @Override
    public void destroy(){
        super.destroy();
    }

    @Override
    public void service(SevletRequest req,ServletResponse res) throws ServletException,IOException{



       res.setContextType("text/html");
       PrintWriter out=response.getWriter();
       out.println("this is servlet");

       //获取config
       //getServletConfig();
        //!!!!考ServletConfig   的函数

       HttpServletRquest hsr=(HttpServeltRequest) req;
       HttpServletResponse hsres=(HttpServletResponse) res;
       //GET / HTTP/1.1\r\n            
       //GET /HTTP/a HTTP/1.1\r\n
       String method=res.getMethod();
       if(method.compareTo("POST")==0){
           doPost(req,res);
       }else if("GET".equals(method)){
            doGet(req,res);
       }

}      
*/
   }

注意事项

  • doPost doGet serice 一般抛2个异常 ServletException 和IOException
  • serve的两个参数是ServletRequest和ServletResponse
  • Context-Type:
    • text/html : HTML格式
      text/plain :纯文本格式
      text/xml : XML格式
      image/gif :gif图片格式
      image/jpeg :jpg图片格式
      image/png:png图片格式
      application/xhtml+xml :XHTML格式
      application/xml : XML数据格式
      application/atom+xml :Atom XML聚合格式
      application/json : JSON数据格式
      application/pdf :pdf格式
      application/msword : Word文档格式
      application/octet-stream : 二进制流数据(如常见的文件下载)
  • this.getServletConfig().getInitParameter(String); 获取servlet的初始化参数
    Alt text
    每个servlet有一个servletconfig,但是全局只有一个servletcontext (每个web应用仅有一个,servletcontext也可以叫applicationContext,是最大的web域对象)

使用response.setContentType(String);设置 如text/html;charset=UTF-8

  • 输出办法: 首先从response.getWriter();获取PrintWriter对象,之后利用其进行流操作输出 (println();)
  • HttpServlet 中已存在 service()方法.缺省的服务功能是调用与 HTTP 请求的方法相应的 do 功能。
    例如,如果 HTTP 请求方法为 GET,则缺省情况下就调用 doGet()。 super(req,res)

setContentType(String) 和 setCharacterEncoding()的区别:前者设置的是页面的静态文字编码(浏览器显示的编码),后者关系到动态参数的编码

HTTP缓存(课上说的必考,但是考纲好像又没有)

  • http缓存有强缓存,协商缓存
    步骤1. 浏览器先根据这个资源的http头信息来判断是否命中强缓存。如果命中则直接加在缓存中的资源,并不会将请求发送到服务器。 (返回200)
    2.如果未命中强缓存,则浏览器会将资源加载请求发送到服务器。服务器来判断浏览器本地缓存是否失效。若可以使用,则服务器并不会返回资源信息,浏览器继续从缓存加载资源。
    3.如果未命中协商缓存,则服务器会将完整的资源返回给浏览器,浏览器加载新资源,并更新缓存。

  • 强缓存: 利用http的返回头中的Expires或者Cache-Control两个字段来控制的。前者表示缓存过期时间(具体时间),后者表示相对时间。如Cache-Control:3600,代表着资源的有效期是3600秒。所以服务器与客户端时间偏差也不会导致问题。
    同时启用的时候Cache-Control优先级高

    • Cache-Control可包含的字段:
      1.max-age 单位s
      2.s-maxage 同1,只适用于共享缓存,私有缓存中被省略
      3.public 响应可以被任何对象缓存
      4.private 响应只能被单个用户,非共享。不能被代理服务器缓存
      5.no-cache:强制缓存了响应的用户,在使用数据前都要先发请求到服务器
      6.no-store:禁止缓存
  • 协商缓存:
    服务器根据http头信息中的Last-Modify/If-Modify-Since或Etag/If-None-Match来判断是否命中协商缓存。如果命中,则http返回码为304,浏览器从缓存中加载资源。

Last-Modify/If-Modify-Since
ETag/If-None-Match
与Last-Modify/If-Modify-Since不同的是,Etag/If-None-Match返回的是一个校验码(ETag: entity tag)。ETag可以保证每一个资源是唯一的,资源变化都会导致ETag变化*。ETag值的变更则说明资源状态已经被修改。服务器根据浏览器上发送的If-None-Match值来判断是否命中缓存。

Alt text

ServletRequest
>>getAttribute(String) //获取存在request的属性 是Object
>>getAttributeNames() //Enumeration 用hasMoreElements() nextElement()进行遍历
>>getParameter(String)//获取存在request的参数 只能是String (可以获得通过get post传进来的数据)
>>getParameterNames() //返回迭代器
>>getParameterValues() //返回String[]
Alt text

>>getProtocol()
>>getRealPath(String path) //寻找某个资源在服务器文件系统上的实际地址
>>getServletContext()

HttpServletResponse
>>addCookie(Cookie cookie) //添加一个cookies

new Cookie(String name, String value) 之后可以用setMaxAge设置有效时间(秒)

>>getHeader(String name)
>>addHeader(String name, String value)
//继承来的
>> getWriter()
>>setContentType(String type)

  • ServletConfig getServletConfig(); //必考

ServletConfig

  • getInitParameter(String name)
  • getServletName()
  • getServletContext()

!!!! 必考ServletContext
context可以直接在servlet里get,也可以从request.getSession().getServletContext() 也可以 request.getServletContext();
常见的有6种方式可以Get到serveletContext:

  • 在servlet类里get
  • reques.get //从request get (域对象 )
  • request.getSession().get //从http session get (域对象)
  • servletConfig.get //从servletconfig里get
  • ServletContextEvent对象里get
  • PageContext里get (域对象)

也就是从另外三大web域对象都可以get到,再加上从context事件还有servlet配置,还有servlet本身get

servletcontent.getRealPath(“/“)和servletcontent.getContextPath()的区别
前面是获取ServletContext 所在的文件系统上的实际位置。 后者是获取配置的servletContext路径

ServletContext官方叫servlet上下文。服务器会为每一个应用创建一个对象,这个对象就是ServletContext对象。这个对象全局唯一,而且工程内部的所有servlet都共享这个对象。所以叫全局应用程序共享对象。
凡是域对象都有如下3个方法:

setAttribute(name,value);name是String类型,value是Object类型;

往域对象里面添加数据,添加时以key-value形式添加

getAttribute(name);

根据指定的key读取域对象里面的数据

removeAttribute(name);

根据指定的key从域对象里面删除数据

同时因为都是web域对象。所以除servletcontext外,其他三个域都能通过getServletContext()获取到servletContext

可以获取web.xml中的全局参数

  <context-param>
         <param-name>param1</param-name>
         <param-value>value1</param-value>
  </context-param>
 用servletContext.getInitParameter(parameName);获取

>>getContextPath();
设置contextpath的方式
/META-INF/context.xml

!!可以获取servletContext()的方式

  • ServletConfig的getServletContext()
  • request.getSession().getServletContext()
  • 直接 getServletContext();

WEB应用结构

xxxxxxxxxxxx\first
-> WEB-INF
——> classes
——> lib
——> web.xml
-> META-INF
——> context.xml

!!!URL 三部分
1.schema 2.服务器位置 3.资源路径

<schema>://<user>:<password>@<host>:<port>/<path>:<params>?<query>#<frag>

http的两部分内容:header body ,header项用\r\n区分,区分 两次回车换行
Alt text

编码 base64:3个字节为一组 ucs:任何一个字符都是4字节

http状态码:

2xx OK
3xx 重定向
—>302 Object Moved Alt text 要根据Location重新发请求

4xx 客户端错误
—>401 (访问拒绝)客户端证书无效,账号密码错误
—>403 Access Forbidden 请求结构正确。但是服务器不想处理.403暗示了所请求的资源确实存在。跟401一样,若服务器不想透露此信息,它可以谎报一个404
—>404 资源不存在
—>405 请求方法错误。比如用了不支持的协议,或者不支持get,结果用了get

5xx:服务器端错误
502 代理错误
500 异常
501 服务器不能识别一个方法。类似405,但是405是能够被服务器识别,但是资源不支持该方法.
101表示服务器已经理解了客户端的请求,并将通过Upgrade消息头通知客户端采用不同的协议来完成这个请求。

web.xml

<welcome-file-list>  //可以设置多个,可以访问到web-inf内的文件,会返回第一个找到的文件,找不到就404,首页的路径只能是一个实际存在的物理文件地址,不能将首页设置成Servlet或Controller的地址,再通过来Servlet或Controller返回一个页面
     <welcome-file>a.jsp</welcome-file>
     <welcome-file>b.html</welcome-file>
     <welcome-file>c.html</welcome-file>
</welcome-file-list>

xml区分大小写

部署web application在Tomcat

<Host name="localhost"  appBase="webapps"
        unpackWARs="true" autoDeploy="true">


    <!-- 部署 "三个" web application  -->
    <Context path="/a" docBase="/somefoler/first"/>  //虚拟路径1 context path=/a
    <Context path="/s" docBase="/otherfolder/second"/> //虚拟路径2 context path=/s
    <Context path="" docBase="/otherfolder/second"/>   //虚拟路径3

    <!-- SingleSignOn valve, share authentication between web applications
         Documentation at: /docs/config/valve.html -->
    <!--
    <Valve className="org.apache.catalina.authenticator.SingleSignOn" />
    -->

    <!-- Access log processes all example.
         Documentation at: /docs/config/valve.html
         Note: The pattern used is equivalent to using pattern="common" -->
    <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
           prefix="localhost_access_log" suffix=".txt"
           pattern="%h %l %u %t &quot;%r&quot; %s %b" />

  </Host>

!!如何自己设计ServletContext().getRealPath();
A: ServletContext().getContextPath();可以用,然后去tomact的 server.xml中寻找对应docBase..然后就可以get到了,如果docBase是相对路径的话,也可以根据tomcat所在路径加上这个相对位置确定

//使用真实路径读取文件
String filename = getServletContext().getRealPath("/WEB-INF/aFolder/a.txt");
FileInputStream fis = new FileInputStream(filename);

web socket

现在前后端通讯的常用的方式有ajax,websocket,还有fetch

  //创建套接字
  Socket s = new Socket("www.baidu.com", 80);  //这是Java的Socket建立过程。直接访问底层的TCP协议。
  var socket = new WebSocket("ws://localhost:8080/websocket1/b");  //JavaScript代码。

websocket可以跨域访问。非常灵活。

Connection: Upgrade
Cookie: JSESSIONID=6FFBEA8EB6951EAD7D123A2D7ED70861
Host: localhost:8080
Origin: http://localhost:8080
Sec-WebSocket-Key: 6ZxylD/4f4qccKeapm3LEQ==
Sec-WebSocket-Version: 13
Upgrade: websocket
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko

响应代码:101,不是200, 101表示服务器已经理解了客户端的请求,并将通过Upgrade消息头通知客户端采用不同的协议来完成这个请求。
在发送完这个响应最后的空行后,服务器将会切换到 在Upgrade消息头中定义的那些协议。: 只有在切换新的协议更有好处的时候才应该采取类似措施。
例如,切换到新的HTTP版本比旧版本更有优势,或者切换到一个实时且同步的协议以传送利用此类特性的资源。

    Connection: upgrade
    Date: Thu, 12 Sep 2019 00:54:07 GMT
    Sec-WebSocket-Accept: 5uiTO0X9qutU+74Ugp/7eMHoXQk=
    Upgrade: websocket

和以往的http请求不同,增加了Connection: Upgrade, Upgrade: websocket等请求头,大家理解,客户端请求建立websocket.
服务器可以不支持。当然,客户端程序来自同一个服务器,在某个使用websocket的应用中,显然支持。我们的JavaEE版本8支持websocket.
至于请求中的Sec-WebSocket-Key, 是base64编码,源数据是16个字节的随机数。服务器收到这个数据, 并不做Base64解码,而是直接拼上一个magic字符串,
“258EAFA5-E914-47DA-95CA-C5AB0DC85B11”, 然后转换为utf-8编码的字节数组,最后做SHA1,(见MD5,CRC,SHA-1,SHA-256)。
对SHA1产生的20个字节(160个比特位)做BASE64,然后就是Sec-WebSocket-Accept: 5uiTO0X9qutU+74Ugp/7eMHoXQk=, 返回给客户端。
客户端当然做同样的算法,然后做比对,实际上就是完成websocket的握手过程。

握手完毕,http协议“转换”到websocket协议。
  • 1、客户端:申请协议升级
  • 2.服务端:响应协议升级 :状态代码101表示协议切换。到此完成协议升级,后续的数据交互都按照新的协议来。
  • 3、Sec-WebSocket-Accept的计算 :Sec-WebSocket-Accept根据客户端请求首部的Sec-WebSocket-Key计算出来。

Alt text

实例:
// Create WebSocket connection.
const socket = new WebSocket('ws://localhost:8080');

// Connection opened
socket.addEventListener('open', function (event) {
    socket.send('Hello Server!');
});

// Listen for messages
socket.addEventListener('message', function (event) {
    console.log('Message from server ', event.data);
});

//javaee
@ServerEndpoint("/chatroom/{roomId}/{nickName}")    //挂一个服务器资源
public class ChatRoomServer{
        @OnOpen//连接打开时
        public void onOpen(Session session,@PathParam("roomId") long roomId,
        @PathParam("nickName") String unDecName){
        }  //可以注入参数
        @OnMessage  //消息抵达

        public void onMessage(Session session, String message, @PathParam("roomId") long roomId, 
        @PathParam("nickName") String unDecName)
        {

        }

        @OnClose
        public void onClose(Session session, @PathParam("roomId") long roomId, 
            @PathParam("nickName") String unDecName)
        {}


}

Alt text

package a;

//Socket客户端
import java.io.*;
import java.net.*;

import javax.websocket.*;

@ClientEndpoint
public class WebSocketClienet {

//就是例子代码,只有一个session
public static Session session = null;

@OnOpen
public void onOpen(Session session) {
    try {
        this.session = session;
        System.out.println("create a session.");
        session.getBasicRemote().sendText("hello");
        System.out.println("send hello to server.");

    } catch (IOException ex) {

    }
}

@OnMessage
public void processMessage(Session session, String message, boolean last) {
    System.out.println(message);
}

@OnClose
public void end() {
    System.out.println("bye server.");
}

@OnError
public void onError(Throwable t) throws Throwable {
    System.out.println("meet some exception in client.");
    t.printStackTrace();
}



public static void main(String[] args) throws Exception {
    WebSocketContainer container = ContainerProvider.getWebSocketContainer();  //获取web容器
    container.connectToServer(WebSocketClienet.class, URI.create("ws://localhost:8080/websocket1/b"));   //连接到服务器

    for(int i = 0; i < 10; i++) {
        Thread.sleep(1000);
        session.getBasicRemote().sendText("" + new java.util.Date());
    }
    session.close();
}

}

Server Sent Event(SSE):服务器向客户端发送消息 (应该是只做了解)

一般服务器向客户端发送消息常用的方式就是用setInterval去做轮询或者是websocket。只是前者setInterval(//每60秒执行myFunction()一次 setInterval(“myFunction()”,60000); )轮询这个中间的时间间隔需要考虑好,如果事件间隔较短,这对服务器的压力比较大,如果事件较长,则有些需要立即立即做出响应的方式是做不到的。后者的websocket相对来说技技术要求有点高,实现起来比较复杂。所以就有现在的这种方式就是Server-sent Events我们只需要需要在服务器端将事件流发送给前端,然后前端接收到后端所传给的事件流,然后触发事件,这是事件的捕获和监听其实和前端的事件捕获和触发是一样的

用法:

var evtSource = new EventSource('/sse'); //创建es对象,/sse是服务器端给的向前端发送事件的接口地址。通过get方式进行发送事件。
//var evtSource = new EventSource('http://www.baidu.com', { withCredentials: true }); (如果跨域)
//至此接收器创建好了
evtSource.onmessage = function (e) {
      console.log(e.data)
}
//也就是当后端发送的内容之后就会进行触发,并且将data给打印出来,首先要提出的的是,Server-sent Events 所发送的内容都是字符串的流而且是通过utf-8的编码格式的,如果后端希望给前端发送一串json,也需要将json转化成相对应的字符串,然后在发送给前端,前端接收之后,再讲所发送的字符串转换成json,然后进行处理。

//然后就是监听后端发送给前端的事件内容,不如说,我们后端给前端sent一个testEvent事件,前端通过事件接收者,去触犯相对应的事件监听,因此就有了下面的工作流程
evtSource.addEventListener('testEvent', function (e) {
      console.log(e)
})
...
后端应该如何才能给我们发送事件呢,
首先我们发送内容是需要给前端设置一个mime为text/event-stream,然后在进行发送事件的内容
'Content-Type': 'text/event-stream'
  • 注解@Endpoint(“/b”) // @ServerEndpoint(“/b”)指定服务器资源
  • sse使用 javax.socket.endpoint

javax.websocket.Endpoint类

使用方法。一个类继承Endpoint,之后重写onOpen,onClose,onError
对于Socket来说,每一端都是一个EndPoint
还可以具体一点,用ServerEndpoint或者ClientEndpoint

Session 接口 (websocket的)

>> getBasicRemote() :返回RemoteEndpoint object,getAsyncRemote是非阻塞式的,getBasicRemote是阻塞式的

大部分情况下,推荐使用getAsyncRemote()。由于getBasicRemote()的同步特性,并且它支持部分消息的发送即sendText(xxx,boolean isLast). isLast的值表示是否一次发送消息中的部分消息,对于如下情况:
session.getBasicRemote().sendText(message, false);
session.getBasicRemote().sendBinary(data);
session.getBasicRemote().sendText(message, true);
由于同步特性,第二行的消息必须等待第一行的发送完成才能进行,而第一行的剩余部分消息要等第二行发送完才能继续发送,所以在第二行会抛出IllegalStateException异常。如果要使用getBasicRemote()同步发送消息,则避免尽量一次发送全部消息,使用部分消息来发送。

>>WebSocketContainer getContainer()

WebSocketContainer

connectToServer(class,URI) ,返回Session

所以…. session <-> WebSocketContainer 可以相互获取。 session是从container连接服务器后来的.

至于Endpoint,客户端和服务器都是一个Endpoint

HttpSession

import javax.servlet.http.*;

其是一个域对象。因此也具有
getAttribute(String),setAttribute(String,Object)等函数
同时有getID(),getServletContext()等函数
创建时间 getCreationTime()
set/getMaxInactiveInterval() //超时时间

设置超时时间
也可以在web.xml中

<session-config>
    <session-timeout>2</session-timeout>  //2分钟,默认为30
</session-config>
#也可以session.setMaxInactiveInterval(30*60);
  • 扔到Attribute中的对象要序列化

Q:如何识别一个request是某个会话的
tomcat有个全局的map,产生的session对象的id一定放在response中,返回给浏览器.下次请求会带上它

一般来说Session是基于Cookie实现的,
!!!会话追踪手段

  • 手段1:cookies.
  • 手段2: URL重写 如果浏览器不支持Cookie或用户阻止了所有Cookie,可以把会话ID附加在HTML页面中所有的URL上,这些页面作为响应发送给客户。这样,当用户单击URL时,会话ID被自动作为请求行的一部分而不是作为头行发送回服务器。这种方法称为URL重写(URL rewriting)。
  • 手段3:隐藏域

      使用隐藏域:<input type="hidden" name="…" value="…">
    

    jsessionID ssessionID …

刷新是个问题
有时不用httpsession, 把会话信息放在分布式存储系统。只要ID在就行

Cookies

response.addCookies(Cookies);
//Cookie(String name, String value)
还可以setMaxAge()设置生存时间
还有serDomain设置有效域 (于是可以跨域共享cookie)
setPath(String uri) 也和上面差不多,可以指定cookie有效的地址(支持通配符)
setHttpOnly(boolean isHttpOnly)

压缩(要考)

gzip是一种数据格式,默认且目前仅使用deflate算法压缩data部分;
Alt text
Alt text

RequestDispatcher

可以用requestDispatcher来分发请求到多个组件去处理。

从ServletRequest.getRequestDispatcher(java.lang.String)获取

rd.forward() //转发,让其他组件去处理,自己不管了
rd.include(request,response) 包含.是让其他组件去处理,但是还会返回来,自己继续处理

!!Interface SingleThreadMode() //保证不会有多个线程执行service函数

解决并发访问的两种方法:

  • 1.加锁
  • 2.数据复制

事务的ACID

  • A:原子性
  • C:一致性
  • I:隔离性
  • D:持久性

request.getContextPath(): 可以获取所配置的ServletContext路径

例:http://localhost:8080/dmsd-itoo-exam-log-web/course/index.jsp,在tomcat中配置了application context为 /dmsd-itoo-exam-log-web
request.getContextPath(),得到context路径:/dmsd-itoo-exam-log-web (取决于contextpath的配置);
request.getServletPath(),返回当前请求的资源相对于context的路径:/course/index.jsp;
request.getRequestURL(),返回请求资源的完整地址:http://localhost:8080/dmsd-itoo-exam-log-web/course/index.jsp;
request.getRequestURI() ,返回包含Context路径的资源路径:/dmsd-itoo-exam-log-web/course/index.jsp。

Filter

doFilter(ServletRequest request, ServletResponse response, FilterChain chain)


  import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public mFliter implements Fliter{
   private FliterConfig config;
   public void destroy(){
   }
   public void doFliter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException,IoException{
       chain.doFilter(req,res);  //当前过滤器处理结束,往下一个过滤器
       //多个过滤器会形成一个过滤器链,如果不想拦截下该请求的话,应该让Filter往下传,后面没有filter后就到达资源
    //想拦截的话可以什么都不干,也可以直接跳到另一个地方(用redirect,forwer之类的
   }
}
//Filter通常要做的事情:
//Filter最最最早执行。
//在资源到达后端之前就执行了。  也就是拦截器
//数据写在session里
//常见作用:1.认证 不拦截/login.html 2.日志 3.数据转换 4.数据压缩 5.加密 6

访问计时: 应该加在filter
getServletContext().log(“xxxxxxxxxxxx”);

乱码解决 (Filter不仅可以用来拦截不合法的请求,还可以用来编码转换)

request.setCharcterEncoding("UTF-8"); //可以用filter实现
String wd=new String(wd.getBytes("ISO-8859-1"),"UTF-8");



public class EncodingFilter(){
     public void doFilter(...){
         request.setCharacterEncoding("UTF-8");
         chain.doiFliter(req,res);
         //是ServletRquest,可以处理各种协议.
         HttpServletRequest request=()req;
         HttpServletRespond request=()res;
         HttpSession session=request.getSession();
         String name=()session.getAtrtribute("Username");
         if(username==null || username.length()==0){
             request.getRepondDispatcher("/login.html").forward(req,res);

         }
     }
 }
 //之后还要部署



    <init-param><param-name></> <param-value></></>
    [servletConfigure.getConfig().]getInitParameter();



        </Filter>
<Filter-mapping>
    <url-pattern> /aa/*</url-pattern>  //可以过滤任何匹配的资源
    <servlet-name></>   //指定就过滤某个servlet
    <dispatcher>REQUEST</> //默认,容器内所产生的请求不拦截  想拦截可以 FORWARD INCLUDE [ERROR]() 都拦截写多个dispatcher
</Filter-mapping>


//登入服务里,登入完后,把值Set在session的attribute里
request.getSession().inavaild()//把session里面的东西都释放掉

在请求到达资源之前,先到过滤器
filter后面一般处理response.        
用forward过滤器一般不起作用

//Error 触发错误状态时会走过滤器
//include 通过<jsp:include page="xxx.jsp" />,嵌入进来的页面,每嵌入的一个页面,都会走一次指定的过滤器。    (servlet通过include指令过来的请求)
//Forward 当前页面是通过请求转发转发过来的场景,会走指定的过滤器

@WebFilter(dispatcherType={...,...})

JSP

由于JSP需要编译,第一次会比较慢
其本质是一个servlet,jsp会被编译成servlet,通过调用service()函数,得到结果。浏览器是无法直接识别jsp的。
jsp不需要部署

<%= new java.util.Date() &>
    <%@include file="footer.jsp" %>  //指令include 指令include会把代码直接插入进来用,最终只编译生成一个servlet
    <jsp:include page="footer2.jsp" %> //动作include,会编译生成2个servlet,把另一个servlet生成的结果拿过来用
    //还可以传参
    //因为指令<%@include 会导致两个jsp合并成为同一个java文件,所以就不存在传参的问题,在发出hello.jsp 里定义的变量,直接可以在footer.jsp中访问。
    //而动作其实是对footer.jsp进行了一次独立的访问,那么就有传参的需要。
    <jsp:include page="footer2.jsp">
        <jsp:param  name="year" value="2017" />  //参数是扔到request的parameters里的
    </jsp:include>
    在footer.jsp中
    <%=request.getParameter("year")%> //获取参数

<%— 隐式注释:定义类、方法、全局变量、常量 —%>

<%java代码%> 脚本片段:其中可包含局部变量、java语句

我们可以在<%%>中定义局部变量或者调用方法,但不能定义方法。在jsp页面可以有多个脚本片段,但是多个脚本片段之间要保证结构完整。

<%@ 指令

详见下面的内容

<%! 声明

其中写的内容将来会直接翻译在Servlet类中,因为我们可以在类中定义方法和属性以及全局变量,所以我们可以在<%!%>中声明方法、属性、全局变量。

<%= %>表达式 用于将已经声明的变量或者表达式输出到网页上面。

JSP要掌握包括三个指令,六个动作,九个内置对象还有四大域对象等
jsp会自动引入

    import java.lang.*;
    import javax.servlet.*;
    import javax.servlet.http.*;
    import javax.servlet.jsp.*;  

Page指令

作用 : 用于定义JSP页面的各种属性,告诉tomcat服务器如何翻译JSP文件。

  • import:引入包
    <%@ page import=”java.util.List”%>
  • session:是否自动创建session
    <%@ page session=”true” %> <%—Default—%>
  • contentType:等同于response.setContentType(“text/html;charset=utf-8”);
    <%@ page contentType=”text/html;charset=utf-8”%>
  • pageEncoding:告诉JSP引擎要翻译的文件使用的编码。
  • isELIgnored: 是否支持EL表达式。 默认是false
  • isErrorPage: 是否创建throwable对象。默认是false;
  • errorPage: 如果页面中有错误,则跳转到指定的资源。
  • isThreadSafe 默认true,线程安全下servlet可以并行访问

Include指令

静态包含:把其它资源包含到当前页面中,代码格式:

include file
1
2
3
4

动态包含:

```<jsp:include page="/include/header.jsp"></jsp:include>

taglib指令

在JSP页面中导入JSTL标签库。替换jsp中的java代码片段。

taglib uri
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72

Q:如何自定义标签?

**SimpleTag & SimpleTagSupport**
SimpleTag是一个接口,SimpleTagSupport实现了它
>SimpleTag里的函数
>+ **doTag()** //是实现这个标签 void
>+ **getParent()** //return JspTag ,返回父标签
>**setParent()**

**SimpleTagSupport**
>+ **doTag()**
>+ **getJspBody()** //JspFragment
>....


创建自定义标签:
+ **step1:**
创建一个类实现SimpleTag接口,也可以直接继承SimpleTagSupport

+ **step2:** 重写doTag()

package com.akb.tag;
import java.io.*;
import javax.servlet.jsp.JspException; //要记
import javax.servlet.jsp.tagext.SimpleTagSupport;
public class HelloTag extends SimpleTagSupport {
@Override
public void doTag() throws JspException, IOException {
System.out.println("hello");
}
}


+ **step 3:**
创建tld文件
![Alt text](./1576922331031.png)



<?xml version="1.0" encoding="UTF-8" ?>

<taglib xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd"
version="2.1">
<!-- 描述 -->
<description>QJL 1.2 core library</description>
<!-- 显示名称 -->
<display-name>QJL core</display-name>
<!-- 版本信息 -->
<tlib-version>1.2</tlib-version>
<!-- 建议使用的标签的前缀 -->
<short-name>akb</short-name> //重要,是标签的前缀
<!-- 标签的uri -->
<uri>http://java.sun.com/akb/core</uri>

<tag>
<!-- 标签名称 -->
<name>hello</name> //重要,标签名
<!-- 标签处理类的全限定性类名 -->
<tag-class>com.akb.tag.HelloTag</tag-class> //重要,要指定实现了SimpleTag的类
<!-- 标签的内容:为空就表示是一个单标签,标签里面没有内容 -->
<body-content>empty</body-content>
</tag>
</taglib>




**short-name 用来定义当前定义的一系列标签的前缀,在页面上引入的时候可以这样写:**
```<%@ taglib uri="http://java.sun.com/akb/core" prefix="akb" %>

uri 其实是个虚的路径映射,并不是访问互联网上的某个网址(lz 一开始就以为引入 jstl 就要访问网页…),在 tld 文件中的 uri 标签上写什么,在页面上引入的时候就在 uri 属性上写什么,容器就会自动去 tld 文件中找该 uri 对应的标签了。

之后只需要在jsp中

<akb:hello />

就能输出hello world

编译后的代码

com.lyu.tag.HelloTag _jspx_th_qjl_005fhello_005f0 = new com.lyu.tag.HelloTag();
  _jsp_getInstanceManager().newInstance(_jspx_th_qjl_005fhello_005f0);
  try {
    _jspx_th_qjl_005fhello_005f0.setJspContext(_jspx_page_context);
// 调用 doTag 方法
_jspx_th_qjl_005fhello_005f0.doTag();
  } finally {
    _jsp_getInstanceManager().destroyInstance(_jspx_th_qjl_005fhello_005f0);
  }

可以看到是 servlet 容器(tomcat)创建了标签处理类的对象,并且调用了标签处理类的 doTag 方法。

如何自定义一个单标签<akb:path />,调用该标签的时候把当前web应用的上下文路径在页面输出?
思路:我要要获取ServletContext,而PageContext有getServletContext()
我们可以首先用SimpleTagSupport 提供的JspContext getJspContext()方法, 而PageContext是JspContext的直接子类
把他强转换为PageContext

@Override
public void doTag() throws IOException {
// 1.获取当前web应用的上下文路径
PageContext ctx = (PageContext) this.getJspContext();
String path = ctx.getServletContext().getContextPath();
// 2.向浏览器输出路径
ctx.getOut().write(path);
}

PageContext非常重要 (四大域之一)
可以通过其获取ServletContext,还可以获取Session,还有forward()和include(),还可以getServletConfig(),getRequest(),getResponse() (…因为jsp本质就个servlet)
其在 javax.servlet.jsp.*;

有没有想过这个 JspContext 是什么时候注入到 SimpleTagSupport 里面的呢?

com.lyu.tag.PathTag _jspx_th_qjl_005fpath_005f0 = new com.lyu.tag.PathTag();
  _jsp_getInstanceManager().newInstance(_jspx_th_qjl_005fpath_005f0);
  try {
// 看这里,是容器调用了 setJspContext 方法来注入的一个 pageContext,注意是 PageContext,所以我们才可以进行强制类型转换
_jspx_th_qjl_005fpath_005f0.setJspContext(_jspx_page_context);
_jspx_th_qjl_005fpath_005f0.doTag();
} finally {
 _jsp_getInstanceManager().destroyInstance(_jspx_th_qjl_005fpath_005f0);
 }
return false;

Q:如何实现一个<akb:set var="" value="" scope="" />标签来向四大域中设置值?

Web的四大域对象:

  • ServletContext: 代表整个web应用的ServletContext对象,当服务器关闭,或web应用被移除时,ServletContext对象跟着被销毁。
  • ServletRequest:在service方法调用前由服务器创建,传入service()方法,整个请求结束,ServletRequest生命周期结束。
    作用于整个请求链。(请求转发也存在,请求转发属于一次请求。)
  • Session
    浏览器可以为每一个用户的浏览器创建一个其独享的session对象,由于session为用户浏览器独享,所以用户在访问服务器的web资源时,可以把各自的数据放在各自的session中,当用户再去访问服务器中的其它web资源时,其它web资源再从用户各自的session中取出数据为用户服务。
  • PageContext
    当对JSP的请求开始时创建,当响应结束时销毁。作用于整个JSP页面,是四大域中最小的一个。

getAttribute()只能获取自己域中保存的属性,而findAttribute()则会按照pageContext->request->session->servletContext的顺序查找有无对应的属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
		public class SetTag extends SimpleTagSupport {

private String var = "";
private String value = "";
// 因为这个属性在 tld 文件中设置为不是必须的所以要设置一个默认值,否则方法中调用的时候会报空指针
private String scope = "";

public void setVar(String var) {
this.var = var;
}

public void setValue(String value) {
this.value = value;
}

public void setScope(String scope) {
this.scope = scope;
}

@Override
public void doTag() throws JspException, IOException {
// 1.拿到PageContext
PageContext pageContext = (PageContext) this.getJspContext();

// 2.获取标签中设置的属性值
// get方法中已经获得

// 3.判断scope是哪个域,把属性值保存到对应的作用域里面
switch (scope) {
case "request": //ServletRequest域
pageContext.setAttribute(var, value, PageContext.REQUEST_SCOPE); //注意第三个参数
break;
case "session": //Session域
pageContext.setAttribute(var, value, PageContext.SESSION_SCOPE); //注意第三个参数
break;
case "application": //也就是ServletContext域
pageContext.setAttribute(var, value, PageContext.APPLICATION_SCOPE); //注意第三个参数
break;
default:
pageContext.setAttribute(var, value);
break;
}
}
}

tld文件有点变化,要加上属性声明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
		<tag>
<!-- 标签的名称 -->
<name>set</name>
<!-- 继承了SimpleTagSupport的标签处理类 -->
<tag-class>com.lyu.tag.SetTag</tag-class>
<!-- 标签的内容,empty就表示是一个单标签 -->
<body-content>empty</body-content>
<!-- 属性1 -->
<attribute>
<!-- 属性名 -->
<name>var</name>
<!-- 该属性是否必须 -->
<required>true</required>
<!-- 该属性是否可以接受运行时表达式的值(EL表达式) -->
<rtexprvalue>false</rtexprvalue>
</attribute>
<!-- 属性2 -->
<attribute>
<!-- 属性名 -->
<name>value</name>
<!-- 该属性是否必须 -->
<required>true</required>
<!-- 该属性是否可以接受运行时表达式的值(EL表达式) -->
<rtexprvalue>true</rtexprvalue>
</attribute>
<!-- 属性3 -->
<attribute>
<!-- 属性名 -->
<name>scope</name>
<!-- 该属性是否必须 -->
<required>false</required>
<!-- 该属性是否可以接受运行时表达式的值(EL表达式) -->
<rtexprvalue>false</rtexprvalue>
</attribute>
</tag>
}

如果不用SimpleTagSupport类,我们也可以自己写类继承TagSupport
然后重写doEngTag()和doStartTag()函数
//doTag() throws JspException,IOException
//doStartTag() throws Jspxception
是适配器模式设计模式

在jsp2.0还可以使用更简单的方式
WEB-INF/tags下创建tag文件 (time.tag)

<%@ tag import="java.util.*" import="java.text.*" %>
<%
  DateFormat df = DateFormat.getDateInstance(DateFormat.LONG);
  Date d = new Date(System.currentTimeMillis());
  out.println(df.format(d));
%>

引用

<%@ taglib prefix="util" tagdir="/WEB-INF/tags" %>
<util:time/>

有参数的标签

<%@ attribute name="count" type="java.lang.Integer" required="true" %>
<%@ attribute name="value" type="java.lang.String" required="true" %>
<%!....%>

补充PageContext相关

getSession(),    getServletContext() , getServletConfig()
    getRequest(),     getRequest()     forward(String relativeUrlPath)
        include(String relativeUrlPath)     include(String relativeUrlPath,boolean flush)

关于flush:

默认为false,在include另一个jsp文件时,默认情况下,服务器会等待该文件被读到底端,然后才输出到客户端,并且销毁该次访问的request和response。而当把flush属性赋为真值 时,在缓存累积了一定数据时,服务器会先提供一部分数据给浏览器,并等待后续内容 。

JSP动作指令

  • jsp:forward
    page
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    既可以转发到静态的 HTML 页面,也可以转发到动态的 JSP 页面,或者转发到容器中的 Servlet。
    ```xml
    <jsp:forward page="{relativeURL | <%=expression%>}" >
    {<jsp:param ... />} //可以用getParameter获取
    </jsp:forward>
    比如
    <jsp:forward page="form.jsp">
    <jsp:param name="age" value="29" />
    </jsp:forward>
    <input type="hidden" name="age" value="<%=request.getParameter("age")%>">

使用 forward 指令转发请求时,用户请求的地址不会改变,客户端的请求参数也不会丢失。
从表面上看, 指令似乎将用户请求转发到了一个新页面,但实际上只是采用了新页面来对用户请求生成响应 — 请求依然是一次请求,所以请求参数、请求属性都不会丢失。

  • jsp:include
    <jsp:include page="{relativeURL | <%=expression%>}" flush="true">
    <jsp:param name="parameterName" value="parameterValue" />
    </jsp:include>

flush 属性用于指定输出缓存是否转移到被导入文件中。如果指定为 true,则包含在被导入文件中;如果指定为 false,则包含在原文件中。

  • useBean、setProperty、getProperty 指令

    这三个指令都是与 JavaBean 相关的指令,其中 useBean 指令用于在 JSP 页面中初始化一个 Java 实例;setProperty 指令用于为 JavaBean 实例的属性设置值;getProperty 指令用于输出 JavaBean 实例的属性。

    <jsp:useBean id="name" class="classname" scope="page | request | session | application" />
    <jsp:setProperty name="BeanName" property="propertyName" value="value" />    
    <jsp:getProperty id="BeanName" property="propertyName" />    
    <!--userBean也可以指定type而不用class,一般来说没什么区别。但是当bean变null时,用type的情况会抛出异常,因为如果用class如果一个bean不存在会自动new一个-->

其中,id 属性是 JavaBean 的实例名,class 属性确定 JavaBean 的实现类。scope 属性用于指定 JavaBean 实例的作用范围,该范围有以下 4 个值:
page:该 JavaBean 实例仅在该页面有效。
request:该 JavaBean 实例在本次请求有效。
session:该 JavaBean 实例在本次 session 内有效。
application:该 JavaBean 实例在本应用内一直有效。

name 属性确定需要设定 JavaBean 的实例名;property 属性确定需要设置的属性名;value 属性则确定需要设置的属性值。

//尽量少写java代码
<jsp:getProperty name="a" property="age"/>
<jsp:setProperty name="a" property="age" value="20"/>   //jsp就是java程序
<jsp:setProperty name="a" property="age" param="age"/> // =student.setAge(Ingter.Parser( request.getParameter("age"))
page:bean只在当前页面存在.有的话a指向这个对象. request的话到request去找。getAttribute,
session:在session对象中getAttribute有的话能返回 application:只有一个,都能用

其中的param属性 相当于value=”<%=request.getParameter(“num”)%>”

  • jsp:param


    param 指令用于设置参数值,这个指令本身不能单独使用,因为单独的 param 指令没有任何实际意义。
    param 指令可以与以下三个指令结合使用。
    jsp:include
    jsp:forward
    jsp:plugin

当与 include 指令结合使用时,param 指令用于将参数值传入被导入的页面;当与 forward 指令结合使用时,param 指令用于将参数值传入被转向的页面

JSP 9个 内置对象

request,response,session,application(javax.servlet.ServletContext),pageContext //也就是4大域对象+一个response
out (javax.servlet.jsp.JspWriter),config (ServletConfig),page (JspPage),exception (JspException)

###Web Listener

优先加载Listenrer

ApplicationInitlizer

public class ApllicationInitlizer implements ServletContextLisenter{
    //最早最早执行的函数. 创建完context马上执行
    public void contextinitialized(ServletContextEvent sce){
        //创建连接池的代码就应该写在这里
        //sce就一个函数  getServletContext()
    }

    public void contextDestroyed(ServletContextEvent sce){
        //最后一个
        一切事件都是来源于Event
    }
}
  • HttpSessionBindingListener 调用setAttribute时触发

web.xml中的监听器设置

<listener>
    <listener-class>nc.xyzq.listener.WebServicePublishListener</listener-class>
</listener> 
// 该元素用来注册一个监听器类。可以收到事件什么时候发生以及用什么作为响应的通知。事件监听程序在建立、修改和删除会话或servlet环境时得到通知。常与context-param联合使用。

也可以用标注@WebListener 就不用上面的xml定义监听器了
listen-class 指定了监听类,上例中该类实现ServletContextListener (也可以实现其他的监听器) 包含初始化方法contextInitialized(ServletContextEvent event) 销毁方法contextDestoryed(ServletContextEvent event);

Alt text

HttpSessionBindingEvent(session,name,value) //session属性变化时
HttpSessionEvent(HttpSession source)
ServletRequestEvent(ServletContext sc, ServletRequest request)
ServletContextEvent(ServletContext source)
HttpSessionAttributeListener: attributeAdded, attributeRemoved, attributeRemoved都可以
参数为(HttpSessionBindingEvent event) 和binding不同,binding是监听属性绑定,解绑事件

监听三个作用域的创建和销毁的监听器
都只有两个函数 一个是 xxxxDestroyed(xxxxEvent ) 另一个是 xxxxInitialized(xxxxEvent )

监听三个作用域属性变更的监听器:
有3个函数 attributeAdded(xxxEvent) attributeAdded(xxxEvent) attributeAdded(xxx) 就是监听属性的增删改

PageBean

在开发中有时需要将数据库中的数据显示到页面中,数据中的数据又比较多,使用一页是显示不了的。此时需要在数据库中分页查询,然后分页显示在页面中。如果使用一个通用的分页工具类,会比较方便。只需在创建该类时传入每页显示的行数,总行数(数据库中的数据总行数),以及当前页码。就可以求出总页数,并且对传入的页码数进行判断。

可以自定义一个PageBean

public class PageBean<T> 

Alt text

JDBC连接池的配置

可以在应用的web.xml也可以在Tomcat的conf/context.xml,也可以在Tomcat的conf/server.xml(GlobalNamingResources标签)中配置,也可以在应用的context.xml中配置。

<Context>
<Resource name="jdbc/mysqlds" 
    auth="Container" //验证方式为容器
    type="javax.sql.DataSource"  
    username="root" 
    password="root" 
    maxIdle="30" 
    maxWait="10000" 
    maxActive="100"
    driverClassName="com.mysql.jdbc.Driver" 
    url="jdbc:mysql://127.0.0.1:3306/db_blog"
    logAbandoned="true" />
</Context>

命名服务

import javax.naming.*;
Binding类:
Alt text
只有get,set

目录服务

目录服务的命名服务的扩展,允许命名服务的入口具有特性。现在再回到打印机的例子中,该打印机有可能具有一定的特性,比如颜色/能够打印双面等,所有这些属性保存在目录服务中.

Java 命名与目录接口(Java Naming and Directory Interface),在J2EE规范中是重要的规范之一

jvax.naming.directory:对命名包的扩充,提供了访问目录服务的类和接口。例如,它为属性增加了新的类,提供了表示目录上下文的DirContext接口,定义了检查和更新目录对象的属性的方法。 Javax.naming.event:提供了对访问命名和目录服务时的事件通知的支持。例如,定义了NamingEvent类,这个类用来表示命名/目录服务产生的事件,定义了侦听NamingEvents的NamingListener接口。 Javax.naming.ldap:这个包提供了对LDAP 版本3扩充的操作和控制的支持,通用包javax.naming.directory没有包含这些操作和控制。 Javax.naming.spi:这个包提供了一个方法,通过javax.naming和有关包动态增加对访问命名和目录服务的支持。这个包是为有兴趣创建服务提供者的开发者提供的。

MVC与struts

Controller: 负责将用户的动作映射到模型的执行
controller没有移植性
降低软件的粒度就能提高复用性

ActionServlet

ActionServlet类是Struts框架的内置核心控制器组件,它继承了javax.servlet.http.HttpServlet类。Struts的启动通常从

加载ActionServlet开始。Web容器会在首次启动或Struts应用的第一个请求到达时加载ActionServlet。一般情况下都
配置web容器比如tomcat启动的时候加载ActionServlet类,使用1</load-on-startup>标签配置启动
加载。

一定要记
ActionServlet的功能为

  • 读取Struts-config.xml
  • 把请求分发到相应的action
  • 从请求中获取数据,填充FromBean(如果需要)
    <servlet>
        <servlet-name>actionServlet</servlet-name>
        <servlet-class>org.apache.struts.action.ActionServlet</servlet-class> <!--一定要记-->
        <load-on-startup>1<load-on-startup>
        <init-param>
            <param-name>config</param-name>
            <param-value>/WEB-INF/struts-config.xml</param-value>  <!--指定配置文件-->
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>actionServlet</servlet-name>
        <url-pattern>*.do</url-pattern>
    </servlet-mapping>

struts配置文件

  • data-source

      <data-source>
                          <!-- 所用的JDBC驱动类,必须-->
                          <set-property property="driverClass" value="com.mysql.jdbc.Driver"/>
                          <!-- 所用的JDBC的URL,必须-->
                          <set-property property="url" value="jdbc:mysql://localhost/test"/>
                          <!-- 同时打开的最小连结数,缺省值为1,可选-->
                          <set-property property="minCount" value="1"/>
                          <!-- 同时打开的最大连结数,缺省值为2,可选-->
                          <set-property property="maxCount" value="5"/>
                          <!-- 连结到数据库的用户名,必须-->
                          <set-property property="user" value="root"/>
                          <!-- 连结到数据库的密码,必须-->
                          <set-property property="password" value="root"/>
       </data-source>
    
  • form-beans

    子元素form-beans用来配置绑定到Action的各个FormBean的实例。每个FormBean实例用form-bans的子元素form-bean来定义。form-bean又分普通的FormBan和动态FormBean。

 1. classname:一般用得少,指定和form-bean无素对应的配置类,默认为org.apache.struts.config.FormBeanConfig,如果自定义,则必须扩展FormBeanConfig类。可有可无。
  2. name:ActionForm Bean的惟一标识。必须。
  3. type:ActionForm的完整类名。必须。

    <form-beans>
      <form-bean name="Loign" type="com.ha.login"></form-bean>
    </form-beans>

对应的FormBean类一般是继承ActionForm类,例如下面的例子定义了一个UserForm,它具有userName和password两个属性。该类的代码如下:

package com.amigo.struts.form.user;
import org.apache.struts.action.ActionForm;
public class UserForm extends ActionForm {
    //...
}

动态form-bean

动态form-bean不需要定义对应的javabean类,其元素都在struts-config.xml中定义。其type为:org.apache.struts.validator.DynaValidatorForm。下面的动态FormBean定义了userName和password属性,配置如下:

<form-bean name="UserForm" type="org.apache.struts.validator.DynaValidatorForm">
         <form-property name="userName" type="java.lang.String"/>
         <form-property name="password" type="java.lang.String"/>
</form-bean>
  • global-forwards

    1. className:和forward元素对应的配置类,默认为:org.apache.struts.action.ActionForward。可有可无。
        2. contextRelative:此项为true时,表时path属性以"/"开头,相对于当前上下文的URL,默认为false.可有可无。 
        3. name:转发路径的逻辑名.必填。
        4. path:转发或重定向的URL,当contextRelative=false时,URL路径相对于当前应用(application),当为ture时,表示URL路径相对于当前上下文(context)。
        5. redirect:当此项为ture时,表示执行重定向操作。当此项为false时表示转向操作。默认为false。
      <global-forwards>
        <forward  name="forms1"  path="/a.do"/>
        <forward  name="forms2"  path="/nb.jsp"/>
      <global-forwards>
    

    global-forwards用于配置全局转发,struts首先会在\元素中找对应的,若找不到,则到全局转发配置中找

  • action-mapping

    该元素用于将Action元素定义到ActionServlet类中,它含有0到多个\元素,其格式如下:

      <action-mappings>
      <action path="Action请求的相对路径"
      type="该Action的对应类的全路径"
      name="该Action绑定的FormBean"
      <forward name="指定处理相应请求所对应的地址" path="相对路径"/>
      </action>
      </action-mappings>
    

name:和Action关联的Action FormBean的名字,必须在FromBean定义过
其他属性:
scope:指定ActionForm Bean的作用域(session和request),缺省为session。(可选);
input:当Bean发生错误时返回的路径(可选);
classname:指定一个调用这个Action类的ActionMapping类的全名。缺省用org.apache.struts.action.ActionMapping(可选);include:如果没有forward的时候,它起forward的作用(可选);validate:若为true,则会调用ActionForm的validate()方法,否则不调用,缺省为true(可选)。

  • message-resources

    该元素用来定义资源文件,格式如下:

      <message-resources parameter="给定资源文件的全名"
      classname="定义处理消息资源的类名的全名"
    
      factory="定义MessageResourcesFactory类的全名"
    
      key="定义绑定在这个资源包中的ServletContext的属性主键"
    
      null=" 如果为true,则找不到消息key时,则返回null "/>
    

理解Form-bean与Action

    <form-beans>
       <form-bean name="loginForm" type="com.text.LoginForm"/>
    </form-beans>

用来设置Model的指向,这个例子意思是页面里的loginForm需要调用com.text.LoginForm 这个bean

    <action   path="/login"
           type="com.test.LoginAction"
           name="loginForm"
          scope="request"
          input="/Login.jsp">
  <forward name="logsuccess" path="/LoginSuccess.jsp"/>
 </action>

用来设置Control的指向

ActionForward:

配置中的forward标签可以看成是一个ActionForward对象
ActionForward forward1=new ActionForward();
forward1.setName(“success”);
forward1.setPath(“/success.jsp”);
ActionForward forward2=new ActionForward();
forward2.setName(“error”);
forward2.setPath(“/error.jsp”);

map.put(“success”,forward1);
map.put(“error”,forward2);

要找时: ActionForward forward=mapping.findForward(“result”)

ActionMapping对应的是action标签

public class TestAction extends Action{
    @Override
    public ActionForward(ActionMapping mapping,ActionForm form,HttpServletRequest request,
    HttpServletResponse response) throws Exception{
        return mapping.findForward("success");
}
} 

DispatchAction

DispatchAction是Action的子类,它重写了Action的execute方法
在Struts中允许多个请求对应一个Action,例如对Book对象的增删改查操作,可以交给一个Action处理,Action根据请求地址中附加的参数信息,再派发到相应的方法
DispatchAction是struts包含的另一个能大量节省开发时间的Action类。与其他Action类仅提供单个execute()方法实现单个业务不同,DispatchAction允许你在单个Action类中编写多个与业务相关的方法。这样可以减少Action类的数量,并且把相关的业务方法集合在一起使得维护起来更容易。

  1. 继承DispatchAction

     import javax.servlet.http.HttpServletRequest;
     import javax.servlet.http.HttpServletResponse;
    
     import org.apache.struts.action.ActionForm;
     import org.apache.struts.action.ActionForward;
     import org.apache.struts.action.ActionMapping;
     import org.apache.struts.actions.DispatchAction;
    
     public class BookAction extends DispatchAction {
    
         public ActionForward add(ActionMapping arg0, ActionForm arg1, HttpServletRequest arg2, HttpServletResponse arg3)
             throws Exception {
         System.out.println("add");
             return null;
     }
     public ActionForward update(ActionMapping arg0, ActionForm arg1, HttpServletRequest arg2, HttpServletResponse arg3)
         throws Exception {
         System.out.println("update");
         return null;
     }
     public ActionForward find(ActionMapping arg0, ActionForm arg1, HttpServletRequest arg2, HttpServletResponse arg3)
             throws Exception {
         System.out.println("find");
         return null;
     }
     public ActionForward delete(ActionMapping arg0, ActionForm arg1, HttpServletRequest arg2, HttpServletResponse arg3)
             throws Exception {
         System.out.println("delete");
         return null;
     }
    
     }
    

2.配置struts-config文件的action并指定parameter

<action path="/bookAction" type="com.kexin.web.action.BookAction"
        parameter="method">
    </action>

使用:

<html:link action="/bookAction?method=add">增加</html:link>
<html:link action="/bookAction?method=delete">删除</html:link>
<html:link action="/bookAction?method=update">更改</html:link>
<html:link action="/bookAction?method=find">查找</html:link>

MappingDispatchAction

MappingDispatchAction是DispatchAction的子类,它提供的功能和DispatchAction功能类似,它也允许多个请求对应一个Action,但它的实现方式更加灵活。

与DispatchAction不同,其配置文件是有几个方法,就写几个Action,path不同

<action path="/addBook" type="com.kexin.web.action.BookAction2"
        parameter="add"></action>
    <action path="/deleteBook" type="com.kexin.web.action.BookAction2"
        parameter="delete"></action>

在配置上 DispatchAction路径所能对应的只能达到类级别,还需要通过request对象传入的一个parameter的值来确定方法,
MappingDispatchAction将不同的路径(action 标签中 path属性来指定)对应到不同的方法级别精度已经达到方法级别具体的方法只需要读配置文件里面的parameter值就可以了

前端控制器(ActionServlet)如何控制后端控制器:
Alt text
Alt text
Alt text

strut2使用:

1.编写xml配置Action

<aciton name="hello" class="com.akb.action.HelloAction" method="sayHello">
    <!--配置结果视图-->
    <result name="success">/success.jsp</result>
</action>
//action指向控制器。控制器调用模型的执行, Action最后会返回到一个视图

//建立Action类

//不需要继承任何东西
    public String HelloAction{
        public String sayHello(){
            System.out.println("hello");
            return "success";
        }
    }

//struts1
//摘自org.apache.struts.action.Action类
 public ActionForward execute(ActionMapping mapping, ActionForm form,ServletRequest request,ServletResponse response) throws Exception 
 {
    try
    {
        return execute(mapping, form,(HttpServletRequest) (Object) request,(HttpServletResponse) (Object) response);
    } catch (ClassCastException e) {
        return null;
    }
}

public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request,HttpServletResponse response) throws Exception 
{
    return null;
}


public class LoginHandlerAction extends Action {

public ActionForward execute(ActionMapping mapping, ActionForm form,
        HttpServletRequest request, HttpServletResponse response) {

    LoginHandlerForm loginHandlerForm = (LoginHandlerForm) form;        
    //从Form中取得表单数据
    String userName = loginHandlerForm.getUserName();
    String userPwd = loginHandlerForm.getUserPwd();

    //生成一个Session对象
    HttpSession session = request.getSession(true);
    session.removeAttribute("userName");
    session.setAttribute("userName", userName);

    //生成一个ArrayList 
    ArrayList arr = new ArrayList();
    arr.add(userName);
    arr.add(userPwd);

    String forward;

    //调用模型组件
    LoginHandler login = new LoginHandler();
    boolean flag = login.checkLogin(arr);
    if(flag)
        forward = "success";
    else
        forward = "fail";

    //转向
    return mapping.findForward(forward);

}
}        

struts2中action是多例的,即一个session产生一个action。struts 2的Action是多实例的并发单例,也就是每次请求产生一个Action的对象
struts 2的Action中包含数据,例如你在页面填写的数据就会包含在Action的成员变量里面。如果Action是单实例的话,这些数据在多线程的环境下就会相互影响,例如造成别人填写的数据被你看到了。所以Struts2的Action是多例模式的。
多例会对每一个请求产生一个session来处理

html格式

  • 物理格式 应该有起始标记,结束标记
  • 逻辑格式 定义了HTML的逻辑结构,受制于具体应用。

dtd文件声明了根元素里面可以写哪些子元素
加上xsl:xxxx xml就受控了 .xsd文件

国际化的问题

properties文件

s4.properties
s4_zh_CN.properties
s4_ja_JP.properties
….

java.util.ResourceBoundle() 构造函数:资源文件的名字

说的简单点,这个类的作用就是读取资源属性文件(properties),然后根据.properties文件的名称信息(本地化信息),匹配当前系统的国别语言信息(也可以程序指定),然后获取相应的properties文件的内容。

Alt text
Alt text

struts1 和struts2的区别要考

一个表单多种提交方式比较困难。
spring的路径支持更好,可以多种匹配方式

s2不灵活,不支持单实例。

Spring

struts 所有后端控制器都要继承Action

spring配置步骤
导入前端控制器
创建servlet:
initparameter
contextConfigLocation
urlpattern *.do

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<servlet>
    <servlet-name>dispatcherSevlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-calss>
    <init-param>
        <param-name>contextConfigLocation</param-name>  <!--配置文件路径-->
        <param-value>classpath:spring-config.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>dispatcherServlet</servlet-name>
    <url-pattern>/</url-pattern>
<servlet-mapping>

  <!--读取静态文件-->
<servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>*.js</url-pattern>
    <url-pattern>*.css</url-pattern>
    <url-pattern>*.woff</url-pattern>
    <url-pattern>*.woff2</url-pattern>
    <url-pattern>*.ttf</url-pattern>
    <url-pattern>*.png</url-pattern>
    <url-pattern>*.jpg</url-pattern>
    <url-pattern>*.ogg</url-pattern>
    <url-pattern>*.mp4</url-pattern>
</servlet-mapping>


<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter
</filter-class>
<init-param>
    <param-name>encoding</param-name>
    <param-value>UTF-8</param-value>
</init-param>
<init-param>
    <param-name>forceEncoding</param-name>
    <param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

上下文监听器代码:(前端控制器)

public class ContextLoaderListener extends ContextLoader implements ServletContextListener {



 public ContextLoaderListener() {

 }



 public ContextLoaderListener(WebApplicationContext context) {

           super(context);

 }



 @Override
 ///要考
 public void contextInitialized(ServletContextEvent event) {
           initWebApplicationContext(event.getServletContext());
 }



 @Override

 public void contextDestroyed(ServletContextEvent event) {

           closeWebApplicationContext(event.getServletContext());

           ContextCleanupListener.cleanupAttributes(event.getServletContext());

 }

}

!! ContextParameter

getinitparameter()获取servlet的参数。在dopost doget都可以. getServletConfig().getinitparameter() 都可以 HttpServlet实现了2个接口:Servlet和ServletConfig

动后加载ApplicationContext

有2个容器(子容器)
一个servlet可以有多个mapping

/*不好,会把jsp引擎过滤掉

Alt text

@RequestBody
如public void add(@RequestBody Account account) 会自动将传入的参数转换成对象
@ResponseBody
public @ResponseBody Account get() 会把结果转换成JSON

@RestController
Alt text

@Controller

如果需要返回的是index字符串(JSON、XML等数据类型),则需要在每个方法体里面添加@ResponseBody注解(简单理解为@ResponseBody注解就是标记方法体不调用视图解析器)

!Controller的返回值可以是

  • void
  • View
  • String
  • map
  • model
  • ModelAndView
  • ModelMap

controller依赖于view view依赖于model

物理和逻辑视图分离.

retruern “xxx” //返回的是逻辑名称
配置文件里要把逻辑名称映射到物理文件. 为了降低controller对视图的依赖

在SpringMVC后台控制层获取参数的方式主要有两种,一种是request.getParameter(“name”),另外一种是用注解@RequestParam直接获取。

@RequestMapping("testRequestParam")    
   public String filesUpload(@RequestParam String inputStr, HttpServletRequest request) {    
    System.out.println(inputStr);  

    int inputInt = Integer.valueOf(request.getParameter("inputInt"));  
    System.out.println(inputInt);  

    // ......省略  
    return "index";  
   }    
   //前端 
   <form action="/gadget/testRequestParam" method="post">    
     参数inputStr:<input type="text" name="inputStr">    
     参数intputInt:<input type="text" name="inputInt">    
    </form>  

注解类

@Component、@Repository、@Service、@Controller实质上属于同一类注解,用法相同,功能相同,区别在于标识组件的类型。@Component可以代替@Repository、@Service

装配bean

@Autowired
@Resource

a:提供方 @Autowired是Spring的注解,@Resource是javax.annotation注解,而是来自于JSR-250,J2EE提供, 需要JDK1.6及以上。
b :注入方式 @Autowired只按照Type 注入;@Resource默认按Name自动注入,也提供按照Type 注入;
c:属性
@Autowired注解可用于为类的属性、构造器、方法进行注值。默认情况下,其依赖的对象必须存在(bean可用),如果需要改变这种默认方式,可以设置其required属性为false。
d:@Autowired注解默认按照类型装配

Spring MVC注解

  • @Controller :表明该类会作为与前端作交互的控制层组件,通过服务接口定义的提供访问应用程序的一种行为,解释用户的输入,将其转换成一个模型然后将试图呈献给用户
  • !!@RequestMapping
  • !!@RequestParam

    将请求的参数绑定到方法中的参数上,有required参数,默认情况下,required=true,也就是改参数必须要传。如果改参数可以传可不传,可以配置required=false。

       @PostMapping(value = "/updateStudent")
        public String updateStudentByStudentId(
        @RequestParam(value = "studentId") Integer studentId,
        @RequestParam(value = "name") String name) 
    
  • !!@PathVariable
    该注解用于方法修饰方法参数,会将修饰的方法参数变为可供使用的uri变量(可用于动态绑定)。

      @RequestMapping("/testPathVariable/{id}")
      public String testPathVariable(@PathVariable("id") Integer id)
      {
          System.out.println("testPathVariable:"+id);
          return SUCCESS;
      }
    
  • @RequestBody
    如public void add(@RequestBody Account account) 会自动将传入的参数转换成对象

  • @ResponseBody
    public @ResponseBody Account get() 会把结果转换成JSON

BeanFactory

Alt text

视图解析

 <bean id="viewResolver" class="...视图解析器类">
 <!--会新建一个 viewClass 属性指定的视图类型予以返回,即返回一个 url 为“ /WEB-INF/test.jsp ”的 InternalResourceView 对象-->
     <property name="viewClass" value="org.springframework.web.servlet.view.InternalResourceView">
     <property name="prefix" value="/WEB-INF/jsp/">  <!--在视图逻辑名前面加上prefix,后面加上suffix-->
     <property name="suffix" value=".jsp">
 </bean>

UrlBasedViewResolver
InternalResourceViewResolver
XmlViewResolver
ResourceBundleViewResolver

在 SpringMVC 中可以同时定义多个 ViewResolver 视图解析器,然后它们会组成一个 ViewResolver 链。当 Controller 处理器方法返回一个逻辑视图名称后, ViewResolver 链将根据其中 ViewResolver 的优先级来进行处理。所有的 ViewResolver 都实现了 Ordered 接口,在 Spring 中实现了这个接口的类都是可以排序的。在 ViewResolver 中是通过 order 属性来指定顺序的,默认都是最大值。所以我们可以通过指定 ViewResolver 的 order 属性来实现 ViewResolver 的优先级, order 属性是 Integer 类型, order 越小,对应的 ViewResolver 将有越高的解析视图的权利

<bean class="org.springframework.web.servlet.view.XmlViewResolver">
   <property name="location" value="/WEB-INF/views.xml"/>
   <property name="order" value="1"/>
</bean>

<bean
   class="org.springframework.web.servlet.view.UrlBasedViewResolver">
   <property name="prefix" value="/WEB-INF/" />
   <property name="suffix" value=".jsp" />
   <property name="viewClass" value="org.springframework.web.servlet.view.InternalResourceView"/>
</bean>

ViewResolver接口是在DispatcherServlet中进行调用的,当DispatcherServlet调用完 Controller后,会得到一个ModelAndView对象,然后DispatcherServlet会调用render方法进行视图渲染。
Alt text
第一步:前端控制器dispatcher接受请求

Client---url--->Dispatcher

第二步:前端控制器去发起handler映射查找请求

Dispatcher---HttpServletRequest---> HandlerMapping

第三步:处理器映射器查找hanlder并返回HandlerExetuionChain

 Dispatcher <---HandlerExeutionChain---HandlerMapping

第四步:前端控制器发起请求处理器适配器请求执行

Dispatcher—-Handler—-> HandlerAdapter

第五步:处理器适配器去调用handler执行

HandlerAdapter—-HttpServletRequest> Handler(Controller)

第六步:处理器处理后返回ModelAndView给HandlerAdapter

HandlerAdapter <—-ModelAndView—-Handler(Controller)

第七步:处理器适配器将ModelAndView返回给前端控制器

Dispatcher <—-ModelAndView—-HandlerAdapter

第八步:前端控制器请求视图解析器解析ModelAndView

Dispatcher—-ModelAndView—-> ViewReslover

第九步:视图解析器解析视图后返回视图View给前端控制器

Dispatcher <—-View—-ViewReslover

第十步:前端控制器请求视图要求渲染视图

Dispatcher—->View—->render

第十一步:前端控制器返回响应

Response <—-Dispatcher

ViewResolver接口就一个函数

    @Nullable
    public void
resolverViewName(String name,Local local)

View里面定义了两个抽象函数

  • render(@Nullable Map model,HttpServletRequest,HttpServletResoponse) //渲染,把模型中的数据渲染到response中 ?可以extend
  • String getContentType()

render(@Nullable Map model,HttpServletRequest,HttpServletResoponse) //渲染,把模型中的数据渲染到response中 ?可以extend

protected View resolveViewName(String viewName, Map<String, Object> model, Locale locale,  
        HttpServletRequest request) throws Exception {  

    for (ViewResolver viewResolver : this.viewResolvers) {  
        View view = viewResolver.resolveViewName(viewName, locale);  
        if (view != null) {  
            return view;  
        }  
    }  
    return null;  
}  

view controller

#重定向
<mvc:view-controller path="/" view-name="redirect:/admin/index"/> <!--即如果当前路径是/ 则重定向到/admin/index-->
<mvc:view-controller path="/" view-name=admin/index"/> <!--如果当前路径是/ 则交给相应的视图解析器直接解析为视图-->
比如视图解析器配置如下
    <bean id="defaultViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver" p:order="2">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="contentType" value="text/html"/>
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
则得到的视图: /WEB-INF/jsp/admin/index.jsp

Aware接口

是为了让组件知道容器的存在.知道了applicationContainer的存在可以知道一切组件的存在。但是应该少用。因为会加大与容器的耦合

Alt text

Aware函数就一个setXXXX();

  • BeanNameAware:
    void setBeanName(String name)
  • BeanFactoryAware
    void setBeanFactory(BeanFactory beanFactory) BeanFactory有getBean()函数
  • ApplicationContextAware
    void setApplicationContext(ApplicationContext applicationContext) //知道了ApplicationContext,简直可以为所欲为

使用例子:

public class User implements BeanNameAware{

private String id;

private String name;

private String address;
@Override
public void setBeanName(String beanName) {
    //ID保存BeanName的值
    id=beanName;
}
}


 <bean id="zhangsan"  class="com.github.jettyrun.springinterface.demo.aware.beannameaware.User">
    <property name="name" value="zhangsan"></property>
    <property name="address" value="火星"></property>
</bean>

数据访问,存储

JDO (java data object) 完成数据持久化 ,代码增强. 性能好,麻烦

Spring jdbc

1.配置数据源datasource
2.初始化jdbcTemplate
3.使用jdbcTemplate对数据库做增删查改

  • 配置数据源

      #代码方式(不推荐,既然可以new 出来的实例,我们应该交给spring去管理。)
       DriverManagerDataSource driverManagerDataSource = new DriverManagerDataSource();
      driverManagerDataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
      driverManagerDataSource.setUrl("jdbc:mysql://localhost:3306/suveng?serverTimezone=Asia/Shanghai&characterEncoding=utf8");
      driverManagerDataSource.setUsername("root");
      driverManagerDataSource.setPassword("root");
    
       JdbcTemplate jdbcTemplate = new JdbcTemplate(driverManagerDataSource);
    
      #推荐,Bean配置方式
       <!--配置DataSource-->
      <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
      <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
          <property name="url" value="jdbc:mysql://localhost:3306/suveng?serverTimezone=Asia/Shanghai &amp;characterEncoding=utf8"/>
      <property name="username" value="root"/>
      <property name="password" value="root"/>
      </bean>
      <!--配置jdbcTemplate-->
      <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
          <property name="dataSource" ref="dataSource"/>
      </bean>
    

    之后在代码中直接注入

       @Resource (或@Autowired)
       JdbcTemplate jdbcTemplate;
    

使用JdbcTemplate:

jdbcTemplate.update("update user set name = ? where id = ?", "春花", 1);  //改
jdbcTemplate.update("insert into user(name,age) values( ?, ?)", "花花", 1000); //整
jdbcTemplate.update("delete from user where id = ?", 1); //删

String sql="select id,name,deptid from user where id=?";
RowMapper<User> rowMapper=new BeanPropertyRowMapper<User>(User.class);
User user= jdbcTemplate.queryForObject(sql, rowMapper,52);


String sql="select id,name,deptid from user";

RowMapper<User> rowMapper=new BeanPropertyRowMapper<User>(User.class);
List<User> users= jdbcTemplate.query(sql, rowMapper);

mybatist

Alt text
Alt text

Transational

Spring框架支持编程式事务管理,也支持声明式事务管理。

1) Spring框架事务管理的实现

org.springframework.transaction.PlatformTransactionManager接口
commit()
rollback()
org.springframework.transaction.TransactionDefinition接口
String getName()
int getIsolationLevel()
int getPropagationBehavior()
int getTimeout()
boolean isReadOnly()
org.springframework.transaction.TransactionStatus接口
boolean isNewTransaction();
boolean hasSavepoint();
void setRollbackOnly();
boolean isRollbackOnly();
void flush();
boolean isCompleted();
PlatformTransactionManager接口的实现类:
org.springframework.jdbc.datasource.DataSourceTransactionManager(JDBC)
org.springframework.transaction.jta.JtaTransactionManager(JTA)
(过时了)org.springframework.orm.hibernate.HibernateTransactionManager(Hibernate)
(Spring 3.0.0)org.springframework.orm.hibernate3.HibernateTransactionManager(Hibernate)
(Spring 3.1.0)org.springframework.orm.hibernate4.HibernateTransactionManager(Hibernate)
(Spring 4.2.0)org.springframework.orm.hibernate5.HibernateTransactionManager(Hibernate)

propagation默认 requre (事务的传播属性)
Alt text

JTS (java transation service) 事务服务

不支持分布式事务 (需要第三方类库 automaker)

编写注解

//MyAnno.java
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.METHOD,ElementType.TYPE})  // 表示该注解可以用以标注方法和类
@Retention(RetentionPolicy.RUNTIME)   // 表示该注解是运行时级别(运行时可见) (只有运行时级别,才能通过反射获取到)
public @interface MyAnno {  // 用@interface定义注解

//注解的属性 (后面要有小括号)
String name();

int age() default 28;  // 可以为属性指定默认值

//String value();
//String[] value();

}


public static void main(String[] args) throws NoSuchMethodException, SecurityException {

    //获得字节码对象
    Class clazz = MyAnnoTest.class;

    //获得所有的方法
    Method[] methods = clazz.getMethods();
    if(methods!=null){
        //获得使用了@MyAnno注解的方法
        for(Method method:methods){
            //判断该方法是否使用了@MyAnno注解
            boolean annotationPresent = method.isAnnotationPresent(MyAnno.class);
            if(annotationPresent){  //如果该方法使用了MyAnno注解
                method.invoke(clazz.newInstance(), null);
            }
        }
    }

    Method method = clazz.getMethod("show", String.class);
    //获得show方法上的@MyAnno注解
    MyAnno annotation = method.getAnnotation(MyAnno.class); // 只有运行时级别的注解,才能通过反射获取到
    //获得@MyAnno上的属性值
    System.out.println(annotation.name()); //zhangsan
    System.out.println(annotation.age());  //28

    // .....

}

JMS (java message service) 消息服务

使用JMS不仅能发送(send)/发布(publish)消息,也能获得(receive)/订阅(subscribe)的消息

总结

相关重要的接口

Servlet接口

因为其是组件。所以有init(ServletConfig)函数destroy()函数。为了获取servlet的配置还有getServletConfig()
主要就是service(ServletRequest,ServletResponse)函数

ServletConfig

  • 可以通过其来获取ServletContext

    可以获取ServletContext的方式很多。1. 直接在HttpServlet类里getServletContext() 2.从 ServletRequest getServletContext() 3.request.getSession().getServletContext() 也就是从Session获取 4.从ServletConfig获取
    5.从ServletContextEvent获取 6.从PageContext获取 (PageContext也是很NB的类,其相当于一个Servlet,servletconfig,session,request,response都可以get到)

  • getInitParameter(String name)
    很重要的函数,用来获取初始化参数的。 初始化参数定义在web.xml里或者注解里
    xml形式:

     <servlet>
         <servlet-name>A</servlet-name>
         <servlet-class>....</servlet-class>
         <init-param>
             <param-name>p1</param-name>
             <param-value>123</param-value>
         </init-param>
         <init-param>
             <param-name>p2</param-name> 
             <param-value>v2</param-value>
         </init-param>
     <servlet>
    

注解形式

@WebServlet(name="A",urlPatterns={"/hello"},initParms={@WebInitParam(name="p1",value="123"),@WebInitParam(name="p2",value="v2")})

  • getServletName():就是获取Servlet的名字。。感觉不太用得上

HttpServlet

实现了Servlet接口.默认已经实现了init,destroy,getServletConfig,service.
可以直接doGet(ServletRequest,ServeletResponse),doPost(ServletRequest,ServletResponse)

doHead是一个已经实现的方法,它将执行doGet但是仅仅向客户端返回doGet应该向客户端返回的头部的内容; doOptions方法自动的返回servlet所直接支持的HTTP方法信息; doTrace方法返回TRACE请求中的所有头部信息。 对于那些仅仅支持HTTP/1.0的容器而言,只有doGet, doHead 和 doPost方法被使用

还有getLastModified
Alt text

ServletRequest 接口

Web四大域之一(session,application(或者servletcontext),pagecontext,request),域都有 removeAttribute setAttribute

  • 可以get的:
    servletName
    servletport
    remotehost
    remoteport
    protocol
    scheme 和protocol差不多。不过不带版本号。 比如http/1.1 只返回http
    RealPath()

Parameter
ParameterNames
ParameterValues
!!getRequestDispatcher(String path)
!!ServletContext
getServletPort
getLocale 为实现国际化可以使用。可以获取用户的语言\地区

HttpServletRequest

实现了上面的ServletRequest接口。所以上面的函数都有
为了适应HTTP协议还具有
Cookie[] getCookies()
getHeader(String) //因为是发来的请求,所以只有get,没有set. response里面get/set都有
getMethod() 请求类型是GET还是POST或者PUT

getRequestURL()
getRequestURI()
getServletPath()
getContextPath()
的区别
Alt text
Alt text
Alt text
可以看到contextpath是设置好的context地址
uri是包含context地址的资源定位地址 因为我们contextpath设成/c后,要使用/c/hello才能访问到相应资源
serverpath就是servlet mapping到的地址。
url是完整的请求地址

HttpSession 接口

web四大域之一(session,application(或者servletcontext),pagecontext,request),域都有 removeAttribute setAttribute
也可以getServletContext()

还可以用getId()获取sessionId
invalidate() //干掉一个session

构造函数
:public Cookie(String name,String value)
还可以设置生存时间
setMaxAge(int expiry) //秒

setDomin(String domin)

可以跨域共享cookies 跨域共享cookie的方法:
A机所在的域:home.langchao.com,A有应用cas
B机所在的域:jszx.com,B有应用webapp_b
1)在cas下面设置cookie的时候,增加cookie.setDomain(“.jszx.com”);,这样在webapp_b下面就可以取到cookie。

setPath(String path)

此处的参数,是相对于应用服务器存放应用的文件夹的根目录而言的(比如tomcat下面的webapp),为了让所有web应该都可以共享cookies,可以cookie.setPath(“/“);

ServletResponse接口 & HttpServletResponse类

ServletResponse接口:
PrintWrite getWrite()
setContentType(String type)
setLocale(Locale loc)
HttpServletResponse类:

addCookie(Cookie cookie);//添加cookie
//关于回应头的函数
addDateHeader(String name, long date); //添加日期头信息
addHeader(String name, String value) //添加头信息
containsHeader(String name)
getHeader(String name)

sendRedirect(String location) //跳转

突然发现重复的东西太多了,所以下面只补充一些笔记里没有的了

Annotations 注解

源注解
1. @Target(value=TYPE) //指定注解的作用范围

 /** 可用于类,接口,枚举 */
TYPE,

/** Field declaration (includes enum constants) */
FIELD,

/** 用于方法 */
METHOD,

/** 用于参数 */
PARAMETER,

/** 用于构造函数 */
CONSTRUCTOR,

/** 用于局部变量 */
LOCAL_VARIABLE,

/** 用于注解 */
ANNOTATION_TYPE,

/** 用于包的声明*/
PACKAGE

2. @Retention(RetentionPolicy.RUNTIME)
Alt text

3. @Documented
Documented注解表明这个注释是由 javadoc记录的,在默认情况下也有类似的记录工具。 如果一个类型声明被注释了文档化,它的注释成为公共API的一部分

javax.servlet.annotation

@WebServlet

直接用注解定义servlet
先看看该注解的定义

@Target(value=TYPE)  //说明作用于类
 @Retention(value=RUNTIME)
 @Documented
public @interface WebServlet

使用方法
@WebServlet(name='', urlPatterns={"",""...} , loadOnStartup=1 , initParams={@WebInitParam(name="", value=""),...}, asyncSupported=true, description="")

参数同在xml里声明servlet一样
loadOnStartup是启动优先级,数字越小优先级越高

@WebListener
@Target(value=TYPE)
 @Retention(value=RUNTIME)
 @Documented
public @interface WebListener

很简单的注解。就是标识监听器类。。。不需要提供任何参数(也没参数可提供,除了一个description)
实例: 下面就创建了一个ServletContext的监听。(具体监听器类型已在前面的笔记提到)

@WebListener
public class MyServletContextListener implements ServletContextListener {
     @Override
    public void contextDestroyed(ServletContextEvent sce) {
        System.out.println("===========================MyServletContextListener销毁");
    }

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("===========================MyServletContextListener初始化");
        System.out.println(sce.getServletContext().getServerInfo());
    }

}
@WebInitParam

一个Servlet的初始化参数注解。用在@WebServlet里面, 除了description只有 name,value属性

@WebFilter

因为filter本质也是servlet..所以和WebServlet用法差不多
@WebFilter(filterName='', urlPatterns={"",""...} , initParams={@WebInitParam(name="", value=""),...}, asyncSupported=true, description="", servletNames={"",""})

不同点

  1. name变成了filterName
  2. 没有了loadOnstartup属性
  3. 增加了servletNames属性。因为其不仅可以作用在urls上,还可以作用在指定servlet上
@ServletSecurity

servlet的安全机制
该注解有两个属性
HttpMethodConstraint[] httpMethodConstraints

HttpConstraint value

相应的,有注解

  • @HttpMethodConstraint(value=”http协议方法名” ,rolesAllowed={“”,””…}, transportGuarantee=)

      //CODE EXAMPLE 4-3 对所有HTTP方法,所有访问都被拒绝
      @ServletSecurity(@HttpConstraint(EmptyRoleSemantic.DENY))
      //CODE EXAMPLE 4-4 对所有HTTP方法,要求角色R1成员的权限约束
      @ServletSecurity(@HttpConstraint(rolesAllowed="R1"))
          //CODE EXAMPLE 4-5 对所有除了GET和POST的HTTP方法,没有约束;对GET和POST方法,要求Role R1成员的权限约束,加密传输要求。
              @ServletSecurity((httpMethodConstraints = { @HttpMethodConstraint(value="GET", rolesAllowed="R1"), @HttpMethodConstraint(value="POST", rolesAllowed="R1", transportGuarantee=TransportGuarantee.CONFIDENTIAL)})
      //CODE EXAMPLE 4-7 对于除了TRACE的所有HTTP方法,要求Role R1中成员的权限约束;对于TRACE, 所有访问都拒绝。
          @ServletSecurity(value = @HttpConstraint(rolesAllowed = "R1"), httpMethodConstraints = @HttpMethodConstraint(value="TRACE", emptyRoleSemantic = EmptyRoleSemantic.DENY))
    

@HandlesTypes(class[])

根据API介绍,该注解定义了能够被ServlerContextInitializer处理的类

@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {


@Override
public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
        throws ServletException {

    .......篇幅有限,去除无用代码.......

    for (WebApplicationInitializer initializer : initializers) {  // 通知启动WebApplicationInitializer 的实现

        initializer.onStartup(servletContext);
    }
}
}

Tomcat的Host容器在添加子容器时,会通过解析.xml并通过classloader加载 @HandlesTypes注解的类
读取@HandlesTypes注解value值。并放入ServletContainerInitializers 对应的Set集合中
在ApplicationContext 内部启动时会通知 ServletContainerInitializers 的onStart方法()。这个onStart方法的第一个参数就是@HandlesTypes注解的value 值指定的Class集合
在Spring 应用中,对ServletContainerInitializers的实现就是SpringServletContainerInitializer,注解指定的类就是WebApplicationInitializer.

@MultipartConfig:

Servlet3.0提供了对文件上传的支持,通过@MultipartConfig标注和HttpServletRequest的两个新方法getPart()和getParts(),开发者能够很容易实现文件的上传操作。

使用方法。直接@MultiparConfig标注一个Servlet类(说明该Servlet处理的是multipart/form-data类型的请求)
表单代码

        <form action="/HelloWorld/UpLoad" method="post" enctype="multipart/form-data">
            <input type="file" name="file">
            <input type="submit" name="upload" value="上传">
        </form>

在doPost中request.getPart(“file”); //Servlet3.0中新引入的方法,用来处理multipart/form-data类型编码的表单
然后part.write(“路径”)即可完成上传

@MultipartConfig(localtion="c:\aaa",fileSizeThreshold=0,maxFileSize=102400,     maxRequestSize=102400)
/*fileSizeThreshold默认0,多少字节后存到文件系统上,mfs:最大文件大小 mrs:最大请求的multipart form大小*/
对应xml
<multipart-config>
    <location>c:\aaa</location>
</ultipart-config>

EL表达式

所有EL都是以${为起始、以}为结尾的

Alt text
如果你要用EL输出一个常量的话,字符串要加双引号,不然的话EL会默认把你认为的常量当做一个变量来处理,

Spring Controller

再次上这张图
Alt text

在SpringMVC 中,控制器Controller 负责处理由DispatcherServlet 分发的请求,它把用户请求的数据经过业务处理层处理之后封装成一个Model ,然后再把该Model 返回给对应的View 进行展示。在SpringMVC 中提供了一个非常简便的定义Controller 的方法,你无需继承特定的类或实现特定的接口,只需使用@Controller 标记一个类是Controller
然后使用@RequestMapping 和@RequestParam 等一些注解用以定义URL 请求和Controller 方法之间的映射,这样的Controller 就能被外界访问到。此外Controller 不会直接依赖于HttpServletRequest 和HttpServletResponse 等HttpServlet 对象

@Controller  
public class MyController {  
  @RequestMapping ( "/showView" )  
    public ModelAndView showView() {  
       ModelAndView modelAndView = new ModelAndView();  
       modelAndView.setViewName( "viewName" );  
       modelAndView.addObject( " 需要放到 model 中的属性名称 " , " 对应的属性值,它是一个对象 " );  
       return modelAndView;  
}        
}   

@RequestParam
@PathVariable 要看,可以看上面spring部分的笔记

Spring 注解补充

@Configuration注解

必需使用扫描. 也可以直接@ComponentScan(value = “包名”)

类里面用Bean标记的方法,就会作为这个Spring容器中的Bean

为了简化从properties里取配置,可以使用@Value, 可以properties文件中的配置值。

引入配置文件
<context:property-placeholder location="classpath:test.properties" />

@Value(“${p1}”)
public String p1;

@Async

基于@Async标注的方法,称之为异步方法,这个注解用于标注某个方法或某个类里面的所有方法都是需要异步处理的。被注解的方法被调用的时候,会在新线程中执行,而调用它的方法会在原来的线程中执行。

@Singleton
让一个类是单例类

@Bean

@Scope(“prototype”或”singleton”)
.singleton bean仅有一个实例
prototype bean 每次对该特定的bean请求时创建一个新的bean实例.

spring补充

bean可以有多个名字,例如:

<bean name="a,b,c" class="a.b.c.Hello"/>
<bean class="a.b.c.Hello"/>   id, name都没有指定,那么name值默认为和类的全名一致。
尽量用id来标识bean,因为有唯一性要求

ApplicationContext

使用FileSystemXmlApplicationContext和使用ClassPathXmlApplicationContext的区别在于: FileSystemXmlApplicationContext在指定的文件系统路径下查找xml文件; 而ClassPathXmlApplicationContext是在所有的类路径(包含JAR文件) 下查找xml文件。
ClassPathXmlApplicationContext 默认会去 classPath 路径下找。classPath 路径指的就是编译后的 classes 目录。

ApplicationContext context = new FileSystemXmlApplicationContext(“C:/bean.xml”);//因此如果xml文件放在WEB-INF文件夹下,需要用这个,不然会找不到文件
ApplicationContext context = new ClassPathXmlApplicationContext(“bean.xml”);
a.b.c.Hello h3 = (a.b.c.Hello) ctx.getBean("a.b.c.Hello");

BeanFactory

从名字可以看出来是Bean的工厂类
有getBean ContainBean 等方法

关于对象的创建,有一些设计模式,例如,工厂方法,对应的spring配置为

<bean id="clientService" class="examples.ClientService"
factory-method="createInstance"/>
对应的Java代码
public class ClientService {
private static ClientService clientService = new ClientService();
private ClientService() {}

public static ClientService createInstance() {
    return clientService;
}
}

这就声明了ClientService为一个工厂bean,利用createInstance()来创建实例

也可以不指定类型,完全有一个工厂bean来负责创建
<!-- the factory bean, which contains a method called createInstance() -->
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
</bean>
<!-- 如果id, name都没有指定,那么name值默认为和类的全名一致。-->
<bean id="clientService"
    factory-bean="serviceLocator"
    factory-method="createClientServiceInstance"/>


public class DefaultServiceLocator {

    private static ClientService clientService = new ClientServiceImpl();
    private static AccountService accountService = new AccountServiceImpl();

    public ClientService createClientServiceInstance() {
        return clientService;
    }

    public AccountService createAccountServiceInstance() {
        return accountService;
    }
}

//创建bean

<bean id="exampleBean" class="examples.ExampleBean">
    <constructor-arg type="int" value="7500000"/>     //调用构造函数提供的参数  
    <constructor-arg type="java.lang.String" value="42"/>
</bean>

下面是通过Setter方法注入依赖。
<bean id="exampleBean" class="examples.ExampleBean">
<!-- setter injection using the nested ref element -->
<property name="beanOne">
    <ref bean="anotherExampleBean"/>
</property>

<!-- setter injection using the neater ref attribute -->
<property name="beanTwo" ref="yetAnotherBean"/>
<property name="integerProperty" value="1"/>
</bean>

<beans xmlns="http://www.springframework.org/schema/beans"  
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
   xsi:schemaLocation="http://www.springframework.org/schema/beans  
       http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">  

<bean id="id" class="com.loster.li.Id">  
    <property name="id" value="123"></property>  
    <property name="name" value="xiaoli"></property>  
</bean>  
</beans>  

需要有set函数

也可以不用type,直接写name="变量名",自动匹配类型 ![Alt text](./1577359741824.png) 当然,一个工厂bean可以定义不同的方法来创建不同类型的bean,配置代码和Java代码如下:
<bean id="clientService"
    factory-bean="serviceLocator"
    factory-method="createClientServiceInstance"/>

<bean id="accountService"
    factory-bean="serviceLocator"
    factory-method="createAccountServiceInstance"/>


public class DefaultServiceLocator {

    private static ClientService clientService = new ClientServiceImpl();
    private static AccountService accountService = new AccountServiceImpl();

    public ClientService createClientServiceInstance() {
        return clientService;
    }

    public AccountService createAccountServiceInstance() {
        return accountService;
    }
}

前面笔记也提到,对于连接池,我们也应该用bean来建立,没必要上java代码

下面是通过Spring来给应用程序配置一个连接池的代码
<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/mydb"/>
    <property name="username" value="root"/>
    <property name="password" value="asdfasdf"/>
</bean>

得到ApplicationContext我们就可以获取所有的bean
获取ApplicationContext的方式有

  1. ApplicationContextAware接口 setApplicationContext()
  2. WebApplicationContextUtils抽象类

    public static WebApplicationContext getWebApplicationContext(ServletContext sc)
    <!--其原理十分简单,在spring容器初始化的方法org.springframework.web.context.ContextLoader.initWebApplicationContext(ServletContext)中-->将WebApplicationContext的实例放入ServletContext 中了。
    

很少用

bean的scope

<bean id="accountService" class="com.something.DefaultAccountService" scope="singleton"/>

scope的值可以是singleton, prototype, request, session, application, 以及page。

  • singleton 单例
  • prototype 每次get会新建一个
  • request 为每个请求新建一个
  • session 为每个session新建一个
  • application 每个应用一个
  • websocket 每个socket一个
通过注解创建bean

      在xml beans里面加
      <context:annotation-config/>

    @Configuration
    public class MyConfiguration {

    @Bean
    public StringStore stringStore() {
        return new StringStore();
    }

    @Bean
    public IntegerStore integerStore() {
        return new IntegerStore();
    }
}

这些bean都需要在@Configuration注解下进行创建

Spring的@Bean注解用于告诉方法,产生一个Bean对象,然后这个Bean对象交给Spring管理。产生这个Bean对象的方法Spring只会调用一次,随后这个Spring将会将这个Bean对象放在自己的IOC容器中。

依赖注入: @Autowired

  1. @Autowired
    @Autowired是Spring 提供的,需导入
    Package:org.springframework.beans.factory.annotation.Autowired;
    只按照byType 注入。
  2. @Resource
    @Resource默认按 byName 自动注入,是J2EE提供的,
    @Autowired(required=false)@Qualifier("loginService") 
    private LoginService loginService;

(1).@Autowired 与@Resource都可以用来装配bean. 都可以写在字段上,或写在setter方法上;
(2).@Autowired 默认按类型装配,默认情况下必须要求依赖对象必须存在,如果要允许null值,可以设
置它的required属性为false,如:@Autowired(required=false) .

如果我们想使用名称装配可以结合 @Qualifier注解进行使用;

Autowired用在属性上:

 @Autowired
   private SpellChecker spellChecker;

就免去setter了。会自动寻找bean来注入进去

用在方法上也差不多(也可以注解在Setter方法上,构造函数注入依赖)

之后
@ComponentScan(basePackages = "org.example")
或者在xml beans里 <context:component-scan base-package="org.example"/>

组件类可以通过这些注解来声明:@Component, @Repository, @Service, @Controller

比较一下Java注解来声明bean,和使用xml来声明bean

@Configuration
public class AppConfig {

    @Bean("myService")
   public MyService myService() {
        return new MyServiceImpl();
    }
    }
<beans>
    <bean id="myService" class="com.acme.services.MyServiceImpl"/>
</beans>

其他资源也是可以注入的
资源文件的处理:

一切资源都使用接口Resource来声明,非常抽象的一个接口,如同BeanFactory, ApplicationContext
该接口定义在org.springframework.core.io包中,当然也属于spring core(核心包),
如同其他注入的属性类型,也可以注入Resource,例如给某个bean对象注入Resource属性

<property name="template" value="classpath:some/resource/path/myTemplate.txt">
<property name="template" value="file:///some/resource/path/myTemplate.txt"/>

Spring对配置文件的加载
<context:property-placeholder location="classpath:jdbc.properties " />

property-placeholderlocation元素只能出现一次,出现多个的话,只加载一个,其余不加载。
可以这样使用:

<context:property-placeholderlocation="classpath:*.properties" />
或者
<bean id="propertyConfigurer"class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
   <property name="locations">
     <list>
        <value>conf/sqlmap/jdbc.properties</value>
        <value>conf/config/app.properties</value>
    </list>
   </property>
</bean>


#app.properties
abc=123

@Value(“${abc}”)
private String abc;

于是上面的连接池建立可以直接使用配置文件注入方式

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations" value="classpath:com/something/jdbc.properties"/>
</bean>

<bean id="dataSource" destroy-method="close"
        class="org.apache.commons.dbcp.BasicDataSource">
    <property name="driverClassName" value="${jdbc.driverClassName}"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
</bean>

上面是spring的基础,下面是spring MVC内容

Spring Controller

Struts 1的后端控制器侵入性太强,要求必须继承Action或者Action类的某个派生类。
从请求(HttpServletRequest)中获取参数也使用了类型(ActionForm),所以使用起来
需要记住很多类,需要封装。
Strtus2 的后端控制器尽管比较灵活,但是也需要些繁琐的配置文件。后端控制器到
路径的映射也是很麻烦。
无论Struts哪个版本,对视图技术(view)的支持都不够强。

spring 前端控制器是org.springframework.web.servlet.DispatcherServlet

要导入的话

<web-app>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/app-context.xml</param-value>
    </context-param>

    <!-- 这个子元素必须有,Servlet容器新增加的特性,支持多模块开发 -->
    <absolute-ordering /> 
    <servlet>
        <servlet-name>app</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet
        </servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value></param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>app</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>
  • 1.添加ContextLoaderListener
  • 2.添加context参数 写上配置文件的路径
  • 3.配置DispatcherServlet 这个前端控制器DispatcherServlet也可以通过init-param加载ApplicationContext的配置文件。

tomcat安装文件夹下的子文件夹conf下有一个全局的配置文件web.xml,
它提供了几个Servlet组件,也配置了/和.jsp路径。
请记住:*如果应用的web.xml文件的配置路径和tomcat的web.xml中的路径一致,那么应用程序优先,如果没有配置,那么会继承tomcat的web.xml中的配置。

建议配置成拦截所有资源,但是不包括jsp文件。用户发送过来的任何请求,都视作用户动作,
用户动作当然要由控制器来进行处理!!!
!!!所以前端控制器的url-pattern应该是/,而不是/*

ContextLoaderListener和DispatcherServlet都会创建ApplicationContext,前面创建的是后面创建的AppicationContext的parent,这个可以调用ApplicationContext的getParent函数获取。

DispatcherServlet和MVC有关,所以,MVC的组件可以配置在DispatcherServlet对应的配置文件中。
而ContextLoaderListener尽量是业务逻辑对应的组件。

因此两个applicationcontext会引起一些问题。最好只定义一个
Alt text

好了,导入了前端控制器,前端控制器收到客户端发送过来的url,它如何处理呢?

可以把静态资源和后端控制器逻辑分开。例如,在配置文件中声明:

在beans标签里映射静态资源

    <mvc:resources mapping="/js/**" location="/WEB-INF/js/" />
    <mvc:resources mapping="/css/**" location="/WEB-INF/css/" />

上面映射中的**表示支持子文件夹

后端控制器的编写:
先在配置文件里 开启MVC注解

<mvc:annotation-driven/>

在配置文件中启动扫描组件,注意是包的名字,别写其他的,也别写适配符*

<context:component-scan    base-package="com.abc.project.controller" />

那么路径到处理函数的映射?

@Controller
public class MyController {
    @RequestMapping("/calc/add")
    public String add(@RequestParam(value="v1") String v1, @RequestParam(value="v2") String v2) {

    }

    @RequestMapping("/calc/subtract")
    public String subtract(@RequestParam(value="v1") String v1, @RequestParam(value="v2") String v2) {

    }
}
或者
@Controller
@RequestMapping("/calc")
public class MyController {
    @RequestMapping("/add")
    public String add(String v1, String v2) {

    }

    @RequestMapping("/subtract")
    public String subtract(String v1, String v2) {

    }
}

可以指定请求方法
@RequestMapping(value=”/add”, method = RequestMethod.GET)
或者直接使用不同的注解
@GetMapping, @PostMapping, @PutMapping, @DeleteMapping, …

后端控制器的函数名称没有任何要求,不过参数当然不能随意的写。
后端控制器支持的常见参数类型列表
HttpServletRequest, HttpServletResponse,javax.servlet.http.HttpSession
java.util.Locale
java.util.Map, org.springframework.ui.Model, org.springframework.ui.ModelMap
(也就是request,response,session,locale,map,model,modelmap)
@PathVariable
@RequestParam
@RequestHeader
@CookieValue
@RequestBody
@RequestPart
@ModelAttribute, @SessionAttributes, @SessionAttribute, @RequestAttribute

@RequestBody可以直接把输入的json干成对象

public String add(@RequestBody Account account)

Spring对路径的支持非常灵活,支持带参数的路径,或者说模板路径
@RequestMapping(“/student/{studentid}/modify”);

public String modify(@PathVariable String studentid) {

}
//或者下面的声明
@RequestMapping("/student/modify/{studentid}") 
public String modify(@PathVariable("studentid") String studentid) {

}

controller处理函数的返回数据 (前面笔记也提到)

1.ModelAndView
2.Model
3.ModelMap
4.Map
5.View 直接返回视图。
6.String 字符串描述的视图的逻辑名字,通过视图解析器解析为物理视图地址。
7.void
8.@ResponseBody Object

4.返回json数据。

  public @ResponseBody Map<String, Object> add(@RequestParam String id) {
     Map<String,Object> map = new HashMap<String,Object>();
     map.put(.........);
     ......;
     return map;
  }

//转JSON可以更简单

@RestController
   public class MyController {
      @RequestMapping(value="/add", produces = "application/json; charset=utf-8")
      public Map /* 或者其他格式的数据,只要能转JSON就可以*/ add(@RequestBody Student s) {

      }
      @RequestMapping("/retrieve")
      public Student retrieve(String id) {

      }
   }

Model, ModelAndView, View 在后面说

redirect和forward有何区别?!!!

response.sendRedirect(“”)
request.getDispatcher().forward()

forward是服务器内部重定向,使用的是同一个request 客户端浏览器的网址是不会发生变化的。
本质上说:forward转发是服务器上的行为,而redirect是客户端行为
Alt text

View

视图解析,视图类的选择,可以使用配置来完成。
应用程序只管定义模型,更新模型的内容。

如果选择jsp视图技术
那么配置如下:

<bean id="viewResolver" 
        class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
    <property name="prefix" value="/WEB-INF/jsp/" />
    <property name="suffix" value=".jsp" />
</bean>

如果返回字符串“welcome”,那么spring会选择视图
/WEB-INF/jsp/welcome.jsp

org.springframework.web.servlet.ViewResolver

定义了根据视图名称和区域语言来决定一个视图的抽象函数
@Nullable
public View resolveViewName(String viewName,Locale locale) throws Exception

JSP视图对应的视图类为org.springframework.web.servlet.view.JstlView,BeanNameAware接口,ApplicationContextAware接口, ServletContextAware接口.实现了View接口

在org.springframework.web.servlet.View接口中声明的两个抽象函数:

    @Nullable
public default String getContentType();
public void render(@Nullable Map<String,?> model,
        HttpServletRequest request,
        HttpServletResponse response) throws Exception

spring提供根据mime类型来确定输出视图,对应的视图解析器类是ContentNegotiatingViewResolver

Alt text
Alt text

或者控制器的处理函数返回ModelAndView。
@RequestMapping("welcome");
public ModelAndView welcome() {
    ModelAndView m = new ModelAndView();
    ModelMap map = m.getModelMap();
    map.addAttribute("username", "zs");

    m.setViewName("index");
    return m;
}

Alt text

spring提供
org.springframework.jdbc.core.JdbcTemplate类
另外,它也提供了O/R Mapping功能。
对象到关系数据库的映射。完全可以取代Hibernate,MyBatis等技术。

public List<Map<String,Object>> queryForList(String sql,
                                         @Nullable
                                         Object... args)
                                  throws DataAccessException



List<Student> queryForList("select * from student", new BeanPropertyRowMappper<Student>(Student.class))
    还有一个映射类也实现了RowMapper接口

ColumnMapRowMapper类,
public class ColumnMapRowMapper implements RowMapper>
映射函数当然是
Map mapRow(ResultSet rs, int rowNum);

可以自己定义RowMapper,将特定表中的数据映射为实体对象。

业务逻辑封装在Service层, Service层访问DAO层。

Service组件的声明也是采用注解这种形式

@Service
public class MyService {
    @Autowired
    private SomeDao dao;

    public void setDao(SomeDao dao) { this.dao = dao; }
    public SomeDao getDao() { return dao; }

    public void businessLogic() {
    //dao operation....
    }

}

业务逻辑就处理业务数据,service函数内一般不完成数据格式转换。
@Service注解通常和@Transactional注解一起使用。

Alt text

如果想使用spring提供的事务控制,那么需要在配置文件中,声明事务管理器对象
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
 <property name="dataSource" ref="dataSource" />
</bean>

spring并不支持分布式事务

JSP

Alt text
Alt text
Alt text
(上面的笔记也讲过)

Alt text
…具体还是看前面的笔记内容吧。。 4个指令,9个动作,还有4个域对象什么的

EL表达式

Alt text
Alt text
Alt text

EL内置的11个对象:

  • pageScope
  • requestScope
  • sessionScope
  • applicationScope
  • param 是一个map,key是参数,value是多个参数值
  • paramValues 是一个map,key是参数,value是多个参数值,request.getParameterValues(“xxx”)
  • header: 相当于请求头,对应request.getHeader(String)
  • headerValues
  • init-param 获取context-param中的参数
  • cookie 中key是cookie的name,value是cookie对象 ${cookie.JSESSIONID.value }就是获取sessionId
  • pageContext
    相当于使用该对象调用getxxx()方法,例如pageContext.getRequest()可以写为${pageContext.request)

表达式中的变量不给定范围时,则默认在page范围查找,然后依次在request、session、application范围查找。也就是从小到大的范围找**

EL 提供“.“和“[ ]“两种运算符来存取数据。
当要存取的属性名称中包含一些特殊字符,如 . 或 - 等并非字母或数字的符号,就一定要使用“[ ]“。例如:
${ user. My-Name}应当改为${user["My-Name"]}
如果要动态取值时,就可以用“[ ]“来做,而“.“无法做到动态取值。例如:
${sessionScope.user[data]}中data 是一个变量

<%@ page isELIgnored=”true” %> 表示是否禁用EL语言

< %=request. getParameter(“username”)% > 等价于 ${ param. username }
<%=request.getAttribute(“userlist”) %> 等价于$ { requestScope.userlist }
<%=user.getAddr( ) %> 等价于 ${user.addr}

补充struts2

Alt text

struts2的前端控制器改成了filter而不是servlet

<filter>
    <filter-name>struts2</filter-name>
    <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
    <init-param>
        <param-name>config</param-name>
        <param-value>struts-default.xml,struts-plugin.xml,../struts2.xml</param-value>
    </init-param>
</filter>
    <filter-mapping>
        <filter-name>struts2</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

Action也和strut1不同。strut1只有一个execute函数而且需要4个参数(request,response,form,mapping)
strut2可以自定义函数

//编写Action充当控制器
public class LoginAction extends ActionSupport {
    private String username;
    private String password;
    public String getUsername(){
        return this.username;
    }
    public void setUsername(String s){
        this.username = s;
    }
    public String getPassword(){
        return this.password;
    }
    public void setPassword(String s){
        this.password = s;
    }

    public String execute() throws Exception{

        if(getUsername().equals("walker")&&getPassword().equals("yam")){
            return "sucess";
        }
        return “error”;  //返回的是逻辑名
    }
}

对应action 配置
 <action name="login" class="mypackage.LoginAction">
        <result name="error">/error.jsp</result>
        <result name="success">/index.jsp</result>
  </action>

高端用法:

<package name="mooc" extends="struts-default" namespace="/">
     <action name="product_*" class="action.ProductAction"  method="{1}"/>
  </package>

{1}代表第一个*

获取request的方法:

ActionContext.getContext().get("request");
ServletActionContext.getRequest();
ServletRequestAware接口
    public void setServletRequest(HttpServletRequest request){
        this.request = request;

    }
-------------End of this passage-------------