JSP
1、JSP执行流程
jsp→java(servlet文件)→class
第一次访问:服务端将jsp翻译成java,再将java编译成class文件
第二次访问:直接访问class文件(如果服务端代码修改了,将会再访问时重新翻译、编译)
2、使用IDEA开发JSP项目
(1)在IDEA中创建JSP项目
(2)统一字符集编码
设置jsp文件的编码(jsp文件中的pageEncoding属性):jsp → java
设置浏览器读取jsp文件的编码(jsp文件中的content属性)
(3)IDEA中Tomcat乱码问题
(4)热部署设置
Run → Edit Configurations,进行如下的配置。
(5)jar包问题
①jar包本身只在运行时有效
处理办法:将jar包复制在Web-Content/lib目录中。
②jar包在开发和运行时有效
处理办法:将jar包复制在Web-Content/lib目录中,并且再复制一份到src目录,并右击再点Add as Library。
3、JSP页面元素
(1)脚本Scriptlet
①<% 局部变量、java语句 %>
②<%! 全局变量、定义方法 %>
③<%=输出表达式 %>
(2)指令
指令 | 作用 |
---|---|
<%@ page ... %> | 定义网页依赖属性,比如脚本语言、error页面、缓存需求等等 |
<%@ include ... %> | 包含其他文件 |
<%@ taglib ... %> | 引入标签库的定义 |
page指令的属性:
①language:jsp页面使用的脚本语言
②import:导入类
③pageEncoding:jsp文件自身编码
④contentType:浏览器解析jsp的编码
(3)注释
①html注释 <!-- --> 可以通过浏览器查看源代码查看
②java注释 // /.../
③jsp注释 <%-- --%>
4、JSP内置对象
内置对象指自带的,不需要new也能使用的对象。
①request(请求对象) ②response(响应对象)
③session(会话对象) ④application(全局对象)
⑤out(输出对象) ⑥pageContext(JSP页面容器)
⑦config(配置对象) ⑧page(当前JSP页面对象)
⑨exception(异常对象)
5、内置对象:request
(1)request
请求对象,存储“客户端向服务端发送的请求信息”。
(2)request对象的常见方法
(3)请求方法分为get和post,区别如下所示:
①get方式在地址栏显示请求信息(但是地址栏能够容纳的信息有限,4-5KB;如果请求数据存在大文件,会导致地址栏无法容纳全部的数据而出错);post不会显示
②文件上传操作,必须是post
(4)统一请求的编码
get请求出现乱码,解决方法如下:
①统一每一个变量的编码
1 String name = request.getParameter("uname");
2 name = new String(name.getBytes("iso-8859-1"),"utf-8");
②修改server.xml,一次性的更改tomcat默认get提交方式的编码
1 //增加URIEncodeing属性,指定编码方式
2 <Connector port="8080" protocol="HTTP/1.1"
3 connectionTimeout="20000"
4 redirectPort="8443" URIEncodeing="UTF-8" />
post请求出现乱码,解决方法如下:
①在获取数据的页面增加下面代码
1 request.setCharacterEncoding("utf-8");//只对post方式有效
6、内置对象:response
响应对象。
(1)response对象的常见方法
方法 | 作用 |
---|---|
void addCookie(Cookie cookie) | 添加指定的cookie至响应中 |
void setCharacterEncoding(String charset) | 指定响应的编码集(MIME字符集),例如UTF-8 |
void setContentType(String type) | 设置响应的内容的类型,如果响应还未被提交的话 |
void sendRedirect(String location) | 使用指定的URL向客户端发送一个临时的间接响应 |
void addHeader(String name, String value) | 添加指定名称的响应头和值 |
(2)页面跳转方式
1 //重定向:页面跳转,但数据会丢失;地址栏会发生改变
2 // 请求2次,它会将目标页面返回给客户端,客户端再向服务端请求
3 response.sendRedirect("success.jsp");
4 //请求转发:页面跳转,数据不会丢失;地址栏没有改变,仍然保留转发时的页面
5 // 请求1次,服务端内部直接跳转到目标页面
6 request.getRequestDispatcher("success.jsp").forward(request,response);
7、Cookie
Cookie不是内置对象,是否服务端生成的,再发送给客户端保存。
Cookie可以提高访问服务端的效率,但是安全性较差。
Cookie对象是由javax.servlet.http.Cookie类产生。
(1)Cookie对象的常见方法
方法 | 作用 |
---|---|
public String getName() | 返回 cookie 的名称,名称创建后将不能被修改 |
public String getValue() | 获取cookie的值 |
public void setMaxAge(int expiry) | 设置 cookie 有效期,以秒为单位,默认有效期为当前session的存活时间 |
(2)交互
服务端准备Cookie,并跳转到客户端(重定向和请求转发都可以)。
1 response.addCookie(Cookie cookie);
2 response.sendRedirect("success.jsp");
客户端获取Cookie,不能直接获取一个单独对象,只能一次性将全部的cookie都拿到。
1 request.getCookies();
8、内置对象:session
session指会话,表示从开始到结束。
(1)session机制
①session存储在服务端
②session是在用一个用户(客户)请求时共享
③实现机制:
客户端第一次访问服务端时,服务端会生成一个session对象(用于保存该客户的信息),并且每个session对象都会有一个唯一的sessionId(用于区分其他session)。服务端会自动产生一个Cookie,并且该Cookie的name=JSESSIONID,value=服务端sessionId的值。然后服务端会在响应客户端的同时,将该Cookie发送给客户端,至此客户端就有了一个Cookie(JSESSIONID)。因此,客户端的Cookie就可以和服务端的session一一对应(JSESSIONID — sessionId)。
客户端第二/n次请求服务端时:服务端会先用客户端Cookie中的JSESSIONID,去服务端的session中匹配sessionId。如果匹配成功(Cookie JESSIONID 和 session sessionId),说明此用户不是第一次访问,无需再次登录。
(2)session的常见方法
方法 | 作用 |
---|---|
public String getId() | 返回session对象的ID |
public boolean isNew() | 返回是否为一个新的客户端,或者客户端是否拒绝加入session |
public void invalidate() | 将session无效化,解绑任何与该session绑定的对象(退出登录、注销) |
public void setAttribute(String name, Object value) | 使用指定的名称和值来产生一个对象并绑定到session中 |
public Object getAttribute(String name) | 返回session对象中与指定名称绑定的对象,如果不存在则返回null |
public void setMaxInactiveInterval(int interval) | 设置最大有效非活动时间 |
public int getMaxInactiveInterval() | 获取最大有效非活动时间 |
(3)Cookie和session的区别
①保存的位置:session在服务端,Cookie在客户端
②安全性:session较安全,Cookie较不安全
③保存的内容:session保存Object,Cookie保存String
(4)Session对象的四种状态
①对象a绑定到session中:session.setAttribute("a",xxx)
②对象a从session中解绑:session.removeAttribute("a")
③钝化:内存 → 硬盘,将session从内存中取出保存到硬盘
④活化:硬盘 → 内存,将session从硬盘中取出保存到内存
(5)监听Session对象的状态
监听绑定和解绑:HttpSessionBingListener
监听钝化和活化:HttpSessionActivationListener
绑定和解绑:需要添加的对象实现该HttpSessionBingListener接口,例如session.setAttribute("a",a),a对象应实现该接口。
这两个监听器不需要配置web.xml。
(6)如何钝化、活化
配置tomcat安装目录/conf/context.xml。钝化和活化的本质就是序列化、反序列化,因此需要实现Serializable接口。
1 <!--通过配置实现钝化、活化 -->
2 <!--maxIdleSwap:最大空闲时间,如果超过该时间将会被钝化
3 FileStore:通过该类具体实现钝化操作-->
4 <Manager className="org.apache.catalina.session.PersistentManager" maxIdleSwap="1">
5 <Store className="org.apache.catalina.session.FileStore" directory="mysession"/>
6 </Manager>
9、内置对象:application
application是全局对象。当Web服务器启动时,Web服务器会自动创建一个application对象。application对象一旦创建,它将一直存在,直到Web服务器关闭。在整个应用程序的运行过程中只有一个application对象,也即所有访问该网站的客户都共享一个application对象。
(1)application的常见方法
方法 | 作用 |
---|---|
String getContextPath() | 获取虚拟路径 |
String getRealPath(String name) | 获取绝对路径(name为虚拟路径) |
10、四种范围对象
范围从小到大:pageContext、request、session、application
(1)对象共有的方法
①Object getAttribute(String name);根据属性名获取属性值
②void setAttribute(String name,Object obj);设置属性值(新增、修改)
例如setAttribute(“a”,"b");
如果a对象之前不存在,则创建一个a对象;如果a对象之前已经存在,则将a的值改为b。
③void removeAttribute(String name);根据属性名,删除对象
(2)有效范围
①pageContext—当前页面有效(页面跳转后无效)
②request—同一次请求有效;其他请求无效(请求转发有效;重定向无效)
③session—同一次会话有效(无论怎么跳转,都有效;关闭/切换浏览器后无效;从 登录→退出 之间全部有效)
④application—全局变量;整个项目运行期间都有效(切换浏览器也有效);关闭服务、其他项目 无效
(3)建议
以上4个对象范围依次增大,尽量使用最小的范围。因为对象的范围越大,造成的性能损耗越多。
11、JDBC原理
JDBC可以为多种关系型数据库DBMS提供统一的访问方式,目的是用java操纵数据库。
(1)JDBC概念模型
①JDBC API:提供各种操作访问接口,包括Connection、Statement、PreparedStatement、ResultSet
②JDBC DriverManager:管理不同的数据库驱动
③各种数据库驱动:相应的数据库厂商提供的(第三方公司提供),连接\直接操作数据库
(2)JDBC API
JDBC API的主要功能是与数据库建立连接、发送SQL语句和返回处理结果。
具体是通过以下类/接口实现:
①DriverManager:管理JDBC驱动
②Connection:连接(通过DriverManager产生)
③Statement(PreparedStatement):增删改查(通过Connection产生)
④CallableStatement:调用数据中的存储过程/存储函数(通过Connection产生)
⑤Result:返回的结果集(上面的Statement等产生)
(3)JDBC访问数据库的具体步骤
①导入驱动,加载具体的驱动类
②与数据库建立连接
③发送sql,数据库执行
④处理结果集
1 String url = "jdbc:mysql://localhost:3306/stu";
2 String username = "root";
3 String password = "";
4 Connection conn = null;
5 Statement st = null;
6 ResultSet rs = null;
7
8
9 //1.加载驱动(开发推荐的方式)
10 Class.forName("com.mysql.jdbc.Driver");
11 //2.获取与数据库的链接
12 conn = DriverManager.getConnection(url, username, password);
13 //3.获取用于向数据库发送sql语句的statement
14 st = conn.createStatement();
15 //4.向数据库发sql
16 String sql = "select * from student";
17 rs = st.executeQuery(sql);
18 //5.处理结果集
19 while(rs.next()){
20 String id = rs.getString("id");
21 String name = rs.getString("name");
22 int age = rs.getInt("age");
23 System.out.println(id + "," + name + "," + "age");
24 }
25 //6.关闭,关闭顺序与打开顺序相反
26 rs.close();
27 st.close();
28 conn.close();
(4)数据库驱动类
数据库 | 具体驱动类 | 连接字符串 |
---|---|---|
oracle | oracle.jdbc.OracleDriver | jdbc:oracle:thin:@localhost:1521:ORCL |
mysql | com.mysql.jdbc.Driver | jdbc:mysql://localhost:3306/数据库实例名 |
SlqSever | com.microsoft.sqlserver.jdbc.SQLServerDriver | jdbc:microsoft:sqlserver:localhost:1433;databasename=数据库实例名 |
(5)Connection产生操作数据库的对象
①产生Statement对象:createStatement()
②产生PreparedStatement对象:prepareStatement()
③产生CallableStatement对象:prepareCall()
(6)Statement操作数据库
①增删改:executeUpdate()
②查询:executeQuery()
(7)PreparedStatement操作数据库
①增删改:executeUpdate()
②查询:executeQuery()
③赋值操作:setXxx()
(8)ResultSet保存结果集
①next():光标下移,判断是否有下一条数据,返回true/false
②previous():光标上移,返回true/false
③getXxx(字段名|位置):获取具体的字段值
(9)Statement和PreparedStatement区别
Statement是PreparedStatement的父类,功能上基本相同,但推荐使用PreparedStatement。原因如下:
①编码更简单(避免了字符串拼接)
②安全(可以有效防止sql注入)
1 select count(*) from users where uname='任意' or 1=1 -- ' and pwd='任意';
2 //该语句一直为真,因为or始终未true,而--后面为注释
3 //在输入用户名时直接输入 任意' or 1=1 --
③提高性能(PreparedStatement会预编译,只需编译一次,而可以多次执行;Statement每次执行前都要编译)
1 //Statement
2 String name = 'Yu';
3 Statement st = = conn.createStatement();
4 String sql = "select * from users where uname ='" + name + "'";
5 st.executeQuery(sql);
6
7 //PreparedStatement
8 String sql2 = "select * from users where uname=?";
9 PreperedStatement st2 = conn.preparedStatement(sql2);//预编译
10 st2.setString(1, name);
11 st2.executeQuery();
(10)JDBC调用存储过程和存储函数
connection.prepareCall(参数:存储过程或存储函数名)
①存储过程(无返回值return,用out参数替代)
参数格式:{ call 存储过程名(参数列表) }
1 //SQL创建存储过程
2 create or replace procedure addTwoNum(num1 in number,num2 in number,result out number)
3 as
4 begin
5 result:num1 + num2;
6 end;/
7 //JDBC调用存储过程
8 CallableStatement cstmt = connection.prepareCall("{ call addTwoNum(?,?,?)}");
9 cstmt.setInt(1,3);
10 cstmt.setInt(2,3);
11 cstmt.registerOutParameter(3,Types.INTEGER);//设置输出参数的类型
12 cstmt.execute();//num1 + num2
13 int result = cstmt.getInt(3);//获取计算结果
②存储函数(有返回值return)
参数格式:{ ? = call 存储函数名(参数列表) }
1 //SQL创建存储函数
2 create or replace function addTwoNumfunction(num1 in number,num2 in number)
3 return number
4 as
5 result number;
6 begin
7 result:=num1 + num2;
8 return result;
9 end;/
10 //JDBC调用存储函数
11 CallableStatement cstmt = connection.prepareCall("{ ? = call addTwoNumfunction(?,?)}");
12 cstmt.setInt(2,3);
13 cstmt.setInt(3,3);
14 cstmt.registerOutParameter(1,Types.INTEGER);//设置输出参数的类型
15 cstmt.execute();//num1 + num2
16 int result = cstmt.getInt(1);//获取计算结果
12、JDBC处理CLOB及BLOB类型数据
CLOB:大文本数据(小说→数据),Oracle是CLOB,在mysql是Text
BLOB:二进制
1 //存储CLOB类型的数据
2 String sql = "insert into novels values(?,?)";
3 PreperedStatement st = conn.preparedStatement(sql);
4 st.setInt(1,1);
5 File file = new File("D:\\novel.txt");
6 InputStream in = new FileInputStream(file);
7 Reader reader = new InputStreamReader(in,"GBK")//转换流可以设置编码
8 st.setCharacterStream(2,reader,file.length());
9 st.executeUpdate();
10 //读取CLOB类型的数据
11 String sql = "select NOVEL from novels where id=?";
12 PreperedStatement st = conn.preparedStatement(sql);
13 st.setInt(1,1,);
14 ResultSet rs = st.executeQuery();
15 if(rs.next()){
16 Reader reader = rs.getCharacterStream("NOVEL");
17 Writer writer = new FileWriter("src/novel.txt");
18 char[] chs = new char[100];
19 int len = -1;
20 while((len = reader.read(chs))!=-1){
21 writer.write(chs,0,len);
22 }
23 writer.close();
24 reader.close();
25 }
26
27 //存储BLOB类型的数据
28 String sql = "insert into music values(?,?)";
29 PreperedStatement st = conn.preparedStatement(sql);
30 st.setInt(1,1);
31 File file = new File("D:\\music.mp3");
32 InputStream in = new FileInputStream(file);
33 st.setBinaryStream(2,in,file.length());
34 st.executeUpdate();
35 //读取BLOB类型的数据
36 String sql = "select msc from music where id=?";
37 PreperedStatement st = conn.preparedStatement(sql);
38 st.setInt(1,1,);
39 ResultSet rs = st.executeQuery();
40 if(rs.next()){
41 InputStream in = rs.getBinaryStream("msc");
42 OutputStream out = new FileOutputStream("src/mus.mp3");
43 byte[] chs = new byte[100];
44 int len = -1;
45 while((len = in.read(chs))!=-1){
46 out.write(chs,0,len);
47 }
48 out.close();
49 in.close();
50 }
13、JSP访问数据库
在JSP的<% %>中写JDBC语句。
(1)JavaBean定义
一般会将JSP的<% %>中的操作数据的代码写到一个java文件中,这个类称为JavaBean。
定义:满足以下两点,就可以称为JavaBean
①public修饰的类,public无参构造
②所有属性都是private,并且提供set/get(如果boolean,则get可替换is)
(2)JavaBean的作用
①减轻JSP的复杂度
②提高代码的复用率
(3)JavaBean分类
①封装业务逻辑的JavaBean(LoginDao.java)
用于操作数据库,对应一个封装数据的JavaBean
②封装数据的JavaBean(实体类,Student.java)
对应数据库中的一张表
注意:有时候会出现java.lang.ClassNotFoundException: com.mysql.jdbc.Driver错误,此时要么你没有将jar包导入项目,要么就是你的jar包没有放入tomcat的lib目录而导致无法识别。
14、MVC设计模式
MVC设计模式一般指MVC框架。经典MVC模式中,M是指业务模型,V是指用户界面,C则是控制器,使用MVC的目的是将M和V的实现代码分离,从而使同一个程序可以使用不同的表现形式。
①Model:模型,代表一个功能。用JavaBean实现。
②View:视图,用于展示和与用户交互。用html、css、js、jsp、jquery等前端技术实现。
③Controller:控制器,用于接受请求,将请求跳转到模型进行处理;模型处理完毕后,再将结果返回给请求处。可以用jsp实现,但是一般建议使用servlet实现。
15、Servlet
Servlet(Server Applet)是Java Servlet的简称,称为小服务程序或服务连接器。必须符合一定的规范:
①必须继承javax.servlet.http.HttpServlet
②重写其中的doGet()(接受并处理所有get提交方式的请求)或doPost()(接受并处理所有post提交方式的请求)方法
(1)Servlet创建和配置
(2)Servlet部署与流程
请求 → 被相应的<url-pattern>拦截 → 根据<servlet-mapping>中的<servlet-name> 去匹配<servlet>中的<servlet-name> → 寻找到<servlet-class> →将请求交给该<servlet-class>执行。
1 <servlet>
2 <servlet-name>WelcomeServlet</servlet-name>
3 <servlet-class>com.my.servlets.WelcomeServlet</servlet-class>
4 <load-on-startup>1</load-on-startup>
5 </servlet>
6
7 <servlet-mapping>
8 <servlet-name>WelcomeServlet</servlet-name>
9 <url-pattern>/servlets/WelcomeServlet</url-pattern>
10 </servlet-mapping>
11
12 <url-pattern>存放请求发起时的位置
13 <servlet-class>Servlet存放的位置
14 <load-on-startup>当有多个Sevlet需要启动时,如果是1,则该Servlet先启动
(3)Servlet3.0及以上版本
Servlet3.0及以上版本不需要在web.xml中配置,但需要在Servlet类的定义处之上编写注解@WebServlet(“url-pattern的值”)。
匹配流程:请求地址与@WebServlet中的值进行匹配。如果匹配成功,则说明请求的就是该注解所对应的类。
(4)项目根目录问题
项目根目录:Web(在Eclipse中是WebContent)、src
例如:
Web中有一个index.jsp文件,src中有一个WelcomeServlet.java文件。
①如果index.jsp中请求<a href="WelcomeServlet"/>,寻找范围:在Web和src目录中寻找WelcomeServlet文件
②如果index.jsp中请求<a href="servlets/WelcomeServlet"/>,寻找范围:先在Web和src目录中寻找servlets目录,再在该目录寻找WelcomeServlet文件
(5)Servlet生命周期
①加载
②初始化:init(),该方法会在Servlet被加载并实例化以后执行
③服务:service() → doGet()、doPost()
④销毁:destroy(),Servlet被系统回收时执行
init():第一次访问Servlet时被执行(只执行一次)
service() :调用几次,则执行几次
(6)Servlet参数值
1 //1、Servlet2.0方式
2 <servlet>
3 <servlet-name>WelcomeServlet</servlet-name>
4 <servlet-class>com.my.servlets.WelcomeServlet</servlet-class>
5 <init-param>
6 <param-name>WelcomeServletParamName</param-name>
7 <param-value>WelcomeServletParamValue</param-value>
8 </init-param>
9 </servlet>
10 //2、Servlet3.0方式
11 @WebServlet(value = "/servlets/WelcomeServlet2",loadOnStartup = 2,initParams = {@WebInitParam(name="WelcomeServlet2Name",value="WelcomeServlet2Value")})
12 //3、设置全局参数
13 <context-param>
14 <param-name>globalParamName</param-name>
15 <param-value>globalParamValue</param-value>
16 </context-param>
17 //4、在Servlet方法中获取参数
18 String value = super.getInitParameter("WelcomeServletParamName");
19 System.out.println("当前Servlet的参数:" + value);
20 ServletContext context = super.getServletContext();
21 String globalValue = context.getInitParameter("globalParamName");
22 System.out.println("全局Servlet的参数:" + globalValue);
(7)Servlet中获取对象
1 //1、out
2 PrintWriter out = response.getWriter();
3 //2、session
4 HttpSession session = request.getSession();
5 //3、application
6 ServletContext context = request.getServletContext();
16、三层架构
目标是为了解耦合、提高代码复用。
(1)三层组成
①表示层(USL,User Show Layer:视图层)
—前台:对应于MVC中的View,用于与用户交互、界面的显示
JSP、JS、HTML、CSS、Jquery等web前端技术
—后台:对应于MVC中的Controller,用于控制跳转、调用业务逻辑层
Servlet(SpringMVC Struts2)
②业务逻辑层(BLL,Business Logic Layer:Service层)
—接受表示层的请求调用
—组装数据访问层,逻辑性的操作(增:查+增;删:查+删;改:查+改;查)
③数据访问层(DAL,Data Access Layer:Dao层)
—直接访问数据库的操作,原子性的操作(增删改查)
17、三层优化
(1)加入接口
建议面向接口开发:先接口,再实现类(service、dao加入接口)
接口与实现类的命名规范:
①接口:interface 起名:I实体类Service IUserService、IUserDao
②实现类:implements 起名:实体类ServiceImpl UserServiceImp、UserDapImpl
③接口包名:Xxx.service Xxx.dao
④实现类包名:Xxx.service.Impl Xxx.dao.Impl
(2)DBUtil
通用的数据库帮助类,可以简化Dao层的代码量。
18、分页
要实现分页,必须知道某一页的数据从哪里开始到哪里结束。
Oracle和SQLServer是从1开始计数的。假设每页可显示10条数据,可知第n页的数据,从第(n-1)*10+1条开始到第n * 10条结束。
第n页 开始 结束
1 1 10
2 11 20
3 21 30
...
n (n-1)*10+1 n*10
Mysql是从0开始计数的。假设每页可显示10条数据,可知第n页的数据,从第 n10条开始到第 (n+1)10-1 条结束。
第n页 开始 结束 0 0 9 1 10 19 3 20 29 ... n n*10 (n+1)*10-1
(1)Mysql实现分页的sql
select * from users limit 页数*页面大小,页面大小
--例如第0页
select * from users limit 0,10;
(2)Oracle实现分页的sql
--1、在字段存在rownum属性(ROWNUM是一个序列,是oracle数据库从数据文件或缓冲区中读取数据的顺序)可以根据该属性进行筛选
select * from
(
select rownum r,t.* from
(select u.* from users u order by sno asc) t
)
where r>=(n-1)*10+1 and r<=n*10;
优化:
select * from
(
select rownum r,t.* from
(select u.* from users u order by sno asc) t
where rownum <= n*10
)
where r>=(n-1)*10+1;
(3)SQLServer实现分页的sql
--1、需要使用到row_number() over(字段),表示将表中某一字段作为rownum使用
select * from
(
select row_number() over(sno order by sno asc) as r,t.* from users where r <= n*10
)
where r>=(n-1)*10+1;
--2、top(如果id不连续,不保证每页的量相同)
select top 页面大小 * from users where id not in
(select top (页数-1)*页面大小 id from users)
--3、offset fetch next only
select * from users order by id
offset (页数-1)*页面大小+1 fetch next 页面大小 rows only;
19、分页实现
需要5个属性:
①数据总数
②页面大小(每页显示的数据条数)
③总页数
④当前页(页码)
⑤当前页的对象集合(实体类集合):每页所显示的所有数据
20、文件上传
需要引入2个jar包,commons-fileupload.jar和commons-io.jar。
(1)前端JSP页面
1 <form action="UploadServlet" method="post" enctype="multipart/form-data">
2 学号:<input name="sno" /><br/>
3 姓名:<input name="sname" /><br/>
4 上传照片:<input type="file" name="spicture"/><br/>
5 <input type="submit" value="注册" />
6 </form>
7 //enctype="multipart/form-data"是必需的,否则后台无法解析文件
(2)Servlet
1 request.setCharacterEncoding("utf-8");
2 response.setCharacterEncoding("utf-8");
3 response.setContentType("text/html;charset=UTF-8");
4 //上传
5 try{
6 boolean isMultipart = ServletFileUpload.isMultipartContent(request);
7 if(isMultipart){//判断前台的from是否有mutipart属性
8 FileItemFactory factory = new DiskFileItemFactory();
9 ServletFileUpload upload = new ServletFileUpload(factory);
10 //通过parseRequest解析form中的所有请求字段,并保存到items集合中
11 List<FileItem> items = upload.parseRequest(request);
12 //遍历items中的数据
13 Iterator<FileItem> iter = items.iterator();
14 while(iter.hasNext()){
15 FileItem item = iter.next();
16 String itemName = item.getFieldName();
17 int sno = -1;
18 String sname = null;
19 //判断前台字段是普通form表单字段(sno、sname),还是文件字段
20 if(item.isFormField()){
21 if(itemName.equals("sno")){
22 sno = Integer.parseInt(item.getString("UTF-8"));
23 }else if(itemName.equals("sname")){
24 sname = item.getString("UTF-8");
25 }
26 }else{
27 //文件上传
28 //getFieldName()获取普通表单字段name值
29 //getName()获取文件名
30 String fileName = item.getName();
31 //获取服务器路径
32 String path=request.getSession().getServletContext().getRealPath("upload");
33 File file = new File(path,fileName);
34 item.write(file);
35 System.out.println(fileName + "上传成功!");
36 return;
37 }
38 }
39 }
40 } catch (FileUploadException e) {
41 e.printStackTrace();
42 } catch (Exception e) {
43 e.printStackTrace();
44 }
(3)注意的问题
上传的目录upload:
①如果修改代码,则在tomcat重新启动时会被删除
原因:当修改代码后,tomcat会重新编译一份class并且重新部署(重新创建各种目录)
②如果不修改代码,则不会删除
原因:没有修改代码,class仍然是之前的class
(4)限制大小和类型
1 //限制大小
2 //限制大小需要在解析request之前就进行
3 //设置上传时用到的临时文件的大小DiskFileItemFactory
4 factory.setSizeThreshold(10240);//设置临时缓冲文件大小为10KB
5 factory.setRepository(new File("d:\\uploadtemp"));//设置临时目录,存放临时文件
6 //控制上传单个文件的大小20KB
7 upload.setSizeMax(20480);
8
9 //限制类型
10 //限制文件类型
11 String ext = fileName.substring(fileName.indexOf("."));
12 if(!(ext.equals("png") || ext.equals("gif") || ext.equals("jpg"))){
13 System.out.println("文件格式不正确。");
14 return;//终止
15 }
21、下载
下载不需要任何jar包。
下载的执行过程: ①请求(地址 a form),请求Servlet
②Servlet通过文件的地址,将文件转为输入流,读到Servlet中
③通过输出流将刚才已经转为输入流的文件输出给用户
(1)Servlet
1 request.setCharacterEncoding("utf-8");
2 String fileNmae = request.getParameter("fileName");
3 //下载文件需要设置消息头
4 response.addHeader("content-Type","application/octet-stream");
5 response.addHeader("content-Disposition","attachement;filename=" + fileNmae);
6 //Servlet通过文件的地址将文件转为输入流读到Servlet中
7 InputStream in = getServletContext().getResourceAsStream("/rs/" + fileNmae);
8 //通过输出流将刚才已经转为输入流的文件输出给用户
9 ServletOutputStream out = response.getOutputStream();
10 byte[] bs = new byte[10];
11 int len = -1;
12 while((len=in.read(bs))!=-1){
13 out.write(bs,0,len);
14 }
15 out.close();
16 in.close();
(2)各文件的content-Type
文件类型 | Content-Type |
---|---|
二进制文件(任何类型的文件) | application/octet-stream |
Word | application/msword |
Excel | application/vnd.ms-excel |
PPT | application/vnd.ms-powerpoint |
图片 | image/gif,image/bmp,image/jpeg |
文本文件 | text/plain |
html网页 | text/html |
(3)各浏览器下载乱码问题
①Microsoft Edge
response.addHeader("content-Disposition","attachment;filename=" + URLEncoder.encode(fileNmae,"UTF-8"));
②Firefox
response.addHeader("content-Disposition","attachment;filename==?UTF-8?B?" + new String(Base64.encodeBase64(fileNmae.getBytes("UTF_8"))) + "?=");
22、EL
EL(Expression Language) 是为了使JSP写起来更加简单。可以替代JSP页面中的Java代码。
(1)EL示例
1、点操作符
${requestScope.user.name}
${域对象.域对象中的属性.属性.属性.级联属性}
2、中括号操作符([""]、[''])
${requestScope.user["name"]}
(2)点操作符和中括号操作符区别
基本上上功能相同。点操作符使用方便。
中括号操作符功能强大,可以包含特殊字符(. 、 -)。也可以使用变量,例如[name],name是一个变量。可以访问数组,例如${requestScope.hobbies[0]}、${requestScope.hobbies[1]}。
(3)获取map属性
1 //Servlet
2 Map<String,String> map = new HashMap<String,String>();
3 map.put("cn","中国");
4 requests.setAttribute("map",map);
5
6 //JSP页面
7 ${requestScope.map.cn}
8 ${requestScope.map["cn"]}
(4)EL关系运算符和逻辑运算符
(5)Empty运算符
判断一个值是否为null或不存在,是则返回为true。
1 ${empty requestScope["id"]} //id值存在,为false
2 ${empty requestScope["pwd"]} //pwd不存在或为null,为true
(6)EL的隐式对象(不需要new就能使用,自带的对象)
①作用域访问对象(EL域对象)
pageScope、requestScope、sessionScope、applicationScope
如果不指定域对象,则默认会根据从小到大的顺序依次取值
例如:${sessionKey}
②参数访问对象:获取表单的数据、超链接中传的值、地址栏中的值
${param} → request.getParamter()
${paramValues} → request.getParamterValues()
例如:${param.uname} 、${paramValues.hobbies[0]}
③JSP隐式对象:pageContext(可以获取JSP其他常见隐式对象)
例如:获取request对象${pageContext.request}
23、JSTL
JSTL(Java server pages standarded tag library,即JSP标准标签库)是由JCP(Java community Proces)所制定的标准规范。开发人员可以利用这些标签取代JSP页面上的Java代码,从而提高程序的可读性,降低程序的维护难度。
需要引入2个jar包,jstl.jar和standard.jar。在JSP页面使用前还需要引入taglib。
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
(1)核心标签库
①通用标签库
②条件标签库
③迭代标签库
(2)通用标签库
①< c:set >
//1、在某个域之中(4个范围对象),给某个变量赋值
<%
request.setAttribute("name","zhangsan");
%>
<c:set var="name" value="zhangsan" scope="request" />
//2、在某个域之中(4个范围对象),给某个对象的属性赋值
//给普通对象赋值
servlet中:request.setAttribute("user",user);
<c:set target="${requestScope.user}" property="name" value="zs"/>
//给map对象赋值
servlet中:request.setAttribute("countries",map);
<c:set target="${requestScope.countries}" property="cn" value="中国"/>
//注意:<c:set>可以给不存在的变量赋值,但不能给不存在的对象赋值
②< c:out >
//显示
${requestScope.name}
<c:out value="${requestScope.name}"/>
//name不存在,显示zs
<c:out value="${requestScope.name}" default="zs"/>
//将value中的值原样显示,否则显示a标签
<c:out value='<a href="https://www.baidu.com">百度</a>' escapeXml="true"/>
③< c:remove >
//移除
<c:remove var="a" scope="request"/>
(3)条件标签库
①选择
//1、单重选择,输出结果为true
<c:if test="${10>2}" var="result" scope="request">
${requestScope.result}
</c:if>
//2、多重选择,输出结果为权限为学生
<c:set var="role" value="学生" scope="request"/>
<c:choose>
<c:when test="${requestScope.role == '老师'}">
权限为老师
</c:when>
<c:when test="${requestScope.role == '学生'}">
权限为学生
</c:when>
<c:otherwise>无权限</c:otherwise>
</c:choose>
(4)迭代标签库
①循环
//1、for循环,输出结果为0;1;2;3;4;5;
<c:forEach begin="0" end="5" step="1" varStatus="status">
${status.index};
</c:forEach>
//2、foreach循环
<c:forEach var="name" items="${requestScope.names}">
${name}
</c:forEach>
//names是数组,相当于for(String name: names)
24、过滤器
Servlet 过滤器可以动态地拦截请求和响应,以变换或使用包含在请求或响应中的信息。
(1)编写Filter类
创建一个类并实现Filter接口,doFilter()方法完成实际的过滤操作。
1 public class MyFilter implements Filter {
2
3 @Override
4 public void init(FilterConfig filterConfig) throws ServletException {}
5
6 @Override
7 public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
8 System.out.println("拦截请求...");
9 filterChain.doFilter(servletRequest,servletResponse);//放行
10 System.out.println("拦截响应...");
11 }
12
13 @Override
14 public void destroy() {}
15 }
(2)配置Filter
<filter>
<filter-name>MyServlet</filter-name>
<filter-class>com.my.filters.MyFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>MyServlet</filter-name>
<url-pattern>/filters/MyServlet</url-pattern>
</filter-mapping>
//url-pattern放服务端Servlet,当访问MyServlet时会启动过滤器
//<url-pattern>设置为/*表示拦截所有请求
/*可以在一个<filter-mapping>元素中加入任意数目的<dispatcher>
REQUEST---拦截HTTP请求,get和post
FORWARD---只拦截通过请求转发方式的请求
INCLUDE---只拦截通过request.getRequestDispatcher("").include()、通过<jsp:include page="..." />此种方式发出的请求
ERROR---只拦截<error-page>发出的请求
*/
(3)过滤器链
对于一个请求可以有多个过滤器进行拦截,拦截的顺序是过滤器在web.xml中的<filter-mapping>的声明顺序决定,先声明的先拦截。
25、监听器
(1)监听对象
①request
②session
③application
(2)监听对象的创建和销毁
每个监听器各自提供2个方法:监听创建和监听销毁的方法。
①request—ServletRequestListener
②session—HttpSessionListener
③application—ServletContextListener
(3)监听对象的属性变更
每个监听器提供3个方法:attributeAdded()、attributeRemoved()、attributeReplaced()
①request—ServletRequestAttributeListener
②session—HttpSessionAttributeListener
③application—ServletContextAttributeListener
(4)创建监听器
创建一个类并实现相应的接口。
(5)配置
<listener>
<listener-class>com.my.listeners.ContextSessionRequestListener</listener-class>
</listener>
26、Ajax
Ajax= Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)。Ajax实现异步更新(与服务器交换数据并只更新需要更新的那部分网页)。
(1)Js实现Ajax
//1、创建一个XMLHttpRequest对象
xhr = new XMLHttpRequest();
//2、设置回调函数
xhr.onreadystatechange = callBack;
//3、调用open方法
//open(“get|post”,服务器地址,true)与服务器建立连接
//get方式传递值:xhr.open("get","MobileServlet?mobile=" + mobile,true);
xhr.open("post","MobileServlet",true);
//4、设置post头信息
//get:不需要设置此方法;post:需要设置此方法
//请求元素中不包含文件时上传:
xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded");
//请求元素中包含文件时上传:
xhr.setRequestHeader("Content-type","multipart/form-data");
//5、发送请求及相关信息
//get:send(null);post:send(参数值)
xhr.send("mobile=" + mobile);
XMLHttpRequest对象的属性:
①readyState:请求状态,只有4代表请求完毕
②status:响应状态,只有200代表响应正常
③onreadystatechange:设置回调函数
④responseText:响应格式为String
⑤responseXML:响应格式为XML
(2)Jquery实现Ajax
①方式一
function register1(){
var $mobile = $("#mobile").val();
$.ajax({
url:"MobileServlet", //服务器地址
type:"post", //请求方式
data:"mobile=" + $mobile, //数据
success:function(result,testStatus){//成功时调用
if(result == "true"){
alert("此号码已存在!");
}else{
alert("此号码不存在!");
}
},
error:function(xhr,errorMessage,e){//失败时调用
alert("系统异常!");
}
})
}
②方式二
$.get(
服务器地址,
请求数据,
function(result){
},
返回值类型(“xml”,“json”,“text”)
)
$.post(
服务器地址,
请求数据,
function(result){
},
返回值类型(“xml”,“json”,“text”)
)
function register2(){
var $mobile = $("#mobile").val();
$.get(
"MobileServlet",
"mobile="+$mobile,
function(result){
if(result == "true"){
alert("此号码已存在!");
}else{
alert("此号码不存在!");
}
},
"text")
}
(3)Jquery方式的Ajax-load
将服务端的返回值直接加载到$(xxx)所选择的元素中。
$(xxx).load(
服务端地址,
请求数据,
function(result){ //该方法可有可无
}
)
function register3(){
var $mobile = $("#mobile").val();
$("#tip").load(
"MobileServlet",
"mobile="+$mobile
)
}
(4)Jquery方式的Ajax-getJSON
通过该方法,请求数据类型是JSON,返回类型也是JSON。
$.getJSON(
服务器地址,
JSON格式的请求数据,
function(result){ //result类型是JSON格式
}
)
function register4(){
var $mobile = $("#mobile").val();
$.getJSON(
"MobileServlet",
{"mobile":$mobile},
function(result){
if(result.msg == "true"){
alert("此号码已存在!");
}else{
alert("此号码不存在!");
}
}
)
}
(5)Ajax处理JSON对象
在JSP中要使用JSON对象,需要导入下面的jar包:
//1、Servlet
PrintWriter out = response.getWriter();
User user = new User("user","123");
JSONObject json = new JSONObject();
json.put("user",user);
out.print(json);
out.close();
//2、JSP
function testJSON(){
$.getJSON(
"JSONServlet",
{"name":"user2"},
function(result){
//js需要通过eval()函数,将返回值转为一个js能够识别的json对象
//JSON对象中只有一个
var jsonUser = eval(result.user);
alert(jsonUser.name + "---" + jsonUser.pwd);
}
)
}
function testJSON2(){
$.getJSON(
"JSONServlet",
{"name":"user2"},
function(result){
//JSON对象中有多个
var jsonUsers = eval(result);
$.each(jsonUsers,function(i,element){
alert(this.name + "---" + this.pwd);
});
}
)
}
27、JNDI
JNDI(Java Naming and Directory Interface,Java命名和目录接口)是SUN公司提供的一种标准的Java命名系统接口。将某一个资源(对象),以配置文件(tomcat/conf/context.xml)的形式写入。
//在context.xml文件中配置
<Environment name="jndiName" value="jndiValue" type="java.lang.String" />
//JSP中使用
Context ctx = new InitialContext();
String str = (String) ctx.lookup("java:comp/env/jndiName");
out.print(str);
28、连接池
连接池是创建和管理一个连接的缓冲池的技术,这些连接准备好被任何需要它们的线程使用。常见的连接池:Tomcat-dbcp、dbcp、c3p0、druid。可以用数据源(javax.sql.DataSource)管理连接池。
传统连接数据库的方式是应用程序直接创建一个指向数据库的连接。而引入连接池后,应用程序需要先获取数据源,再通过数据源在连接池中拿到连接。
(1)Tomcat-dbcp
①配置context.xml
<Resource
<!-- 指定Resource的JNDI名字 -->
name="users"
<!-- 指定Resource的管理者:Container(由容器来创建Resource) 和Application(由Web应用来创建和管理Resource)-->
auth="Container"
<!-- 指定Resource的类型 -->
type="javax.sql.DataSource"
<!-- 处于活动状态的数据库连接的最大数量;为0表示不受限制 -->
maxActive="400"
<!-- 处于空闲状态的数据库连接的最大数量;为0表示不受限制 -->
maxIdle="20"
<!-- 连接处于空闲状态的最大等待时间(单位是毫秒;为-1表示无限制等待) -->
maxWait="5000"
<!-- 用户名和密码 -->
username="root" password="12345678"
<!-- 指定连接数据库的驱动程序的类名 -->
driverClassName="com.mysql.jdbc.Driver"
<!-- 指定连接数据库的URL -->
url="jdbc:mysql://localhost:3306/jsppro" />
</Context>
②配置web.xml
<resource-ref>
<res-ref-name>users</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
③使用数据源,更改连接对象Connection的获取方式
public static void getConnection() throws ClassNotFoundException, SQLException {
Class.forName("com.mysql.jdbc.Driver");
//将访问数据库的连接指向数据源
try{
Context ctx = new InitialContext();
DataSource ds = (DataSource)ctx.lookup("java:comp/env/users");
connection = ds.getConnection();
}catch (NamingException e){
e.printStackTrace();
}
}
(2)dbcp
引入commons-dbcp-1.4.jar包和commons-pool2-2.9.0.jar包。
①BasicDataSource方式(硬编码)
1 //获取dbcp方式的ds对象
2 public static DataSource getDataSourceWithDBCP(){
3 BasicDataSource dbcp = new BasicDataSource();
4 dbcp.setDriverClassName("com.mysql.jdbc.Driver");//设置连接数据库的驱动名
5 dbcp.setUrl("jdbc:mysql://localhost:3306/jsppro");//设置连接数据库的URL
6 dbcp.setUsername("root");//设置数据库的用户名
7 dbcp.setPassword("12345678");//设置数据库的密码
8 dbcp.setInitialSize(20);//设置初始化时,连接池中的连接数量
9 dbcp.setMaxActive(10);//设置连接池中,处于活动状态的数据库连接的最大数量
10 dbcp.setMaxIdle(10);//设置连接池中,处于空闲状态的数据库连接的最大数量
11 return dbcp;
12 }
②BasicDataSourceFactory方式(配置文件)
i.创建dbcpconfig.properties文件,添加以下内容
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jsppro
username=root
password=12345678
initialSize=20
ii.获取DataSource
1 public static DataSource getDataSourceWithDBCPByProperties() throws Exception{
2 DataSource dbcp = null;
3 Properties props = new Properties();
4 InputStream input = new DBCPDemo().getClass().getClassLoader().getResourceAsStream("dbcpconfig.properties");
5 props.load(input);
6 dbcp = BasicDataSourceFactory.createDataSource(props);
7 return dbcp;
8 }
(3)c3p0
提供了硬编码和配置文件两种方式,通过ComboPooledDataSource的构造方法参数区分:如果无参,硬编码;有参,配置方式。
①引入c3p0-0.9.5.2.jar和mchange-commons-java-0.2.15.jar,c3p0-oracle-thin-extras-0.9.1.2.jar(oracle需要)
②硬编码方式获取DataSource
1 public static DataSource getDataSourceWithC3P0(){
2 ComboPooledDataSource c3p0 = new ComboPooledDataSource();
3 try {
4 c3p0.setDriverClass("com.mysql.jdbc.Driver");
5 } catch (PropertyVetoException e) {
6 e.printStackTrace();
7 }
8 c3p0.setJdbcUrl("jdbc:mysql://localhost:3306/jsppro");
9 c3p0.setUser("root");
10 c3p0.setPassword("12345678");
11 return c3p0;
12 }
③配置文件方式获取DataSource
i.创建c3p0-config.xml,添加以下内容
<?xml version="1.0" encoding="UTF-8" ?>
<c3p0-config>
<!--默认的配置-->
<default-config>
<!--如果要研究某个xml中可以设置哪些属性,找相关类的属性或setXxx()-->
<property name="initialPoolSize">10</property>
<property name="maxIdleTime">30</property>
<property name="maxPoolSize">100</property>
<property name="minPoolSize">10</property>
<property name="maxStatements">200</property>
</default-config>
<!--这里没有默认配置中的属性,因此使用默认配置中的那些没有的属性-->
<named-config name="users">
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/jsppro</property>
<property name="user">root</property>
<property name="password">12345678</property>
</named-config>
</c3p0-config>
ii.获取DataSource
1 public static DataSource getDataSourceWithC3P0ByXML(){
2 ComboPooledDataSource c3p0 = new ComboPooledDataSource("users");
3 return c3p0;
4 }
29、ApacheDbutils
Commons DbUtils 是Apache组织提供的一个对JDBC进行简单封装的开源工具类库,使用它能勾简化JDBC应用程序的开发。
(1)下载commons-dbutils-1.7.jar
(2)重点类
①DbUtils:做一些辅助操作,如打开或关闭连接
②QueryRunner:增删改查,update()和query()
③ResultHandler:接口。有很多实现类,一个实现类对应于一种不同的查询结果类型
(3)ResultHandler实现类
①testArrayHandler:返回结果集中的第一行数据,并用Object[]接收
②ArrayListHandler:返回结果集中的多行数据,List<Object[]>接收
③BeanHandler:返回结果集中的第一行数据,用对象接收
④BeanListHandler:返回结果集中的多行数据,用对象接收
⑤BeanMapHandler:用Map<Integer,Student>接收
⑥MapHandler:返回结果集中的第一行数据,并用Map<String,Object>接收
⑦MapListHandler:返回结果集中的多行数据,并用List<Map<String,Object>>接收,{{id=2,name=ww},{id=3,name=zl}}
⑧KeyedHandler:返回结果集中的多行数据,并用Map<String,Map<String,Object>>接收,{ww={id=2, name=ww}, zl={id=3, name=zl}}
⑨ColumnListHandler:把结果集中的某一列保存到List中,[ww, zl]
⑩ScalarHandler:返回一个单值结果
(4)查询
1 public static void testArrayHandler() throws SQLException {
2 QueryRunner runner = new QueryRunner(DataSourceUtils.getDataSourceWithC3P0ByXML());
3 String sql = "select * from student where id > ?";
4 Object[] student = runner.query(sql,new ArrayHandler(),1);//ArrayHandler在结果中只取一行
5 System.out.println(student[0] + "," + student[1] + "," + student[2]);
6 }
(5)增加
1 public static void add() throws SQLException {
2 QueryRunner runner = new QueryRunner(DataSourceUtils.getDataSourceWithC3P0ByXML());
3 String sql = "insert into student(id,name,age) values(?,?,?)";
4 int count = runner.update(sql,new Object[]{4,"ls",20});
5 System.out.println(count);
6 }
(6)更新
1 public static void update() throws SQLException {
2 QueryRunner runner = new QueryRunner(DataSourceUtils.getDataSourceWithC3P0ByXML());
3 String sql = "update student set name=?,age=? where id=?";
4 int count = runner.update(sql,new Object[]{"xl",22,4});
5 System.out.println(count);
6 }
(7)删除
1 public static void delete() throws SQLException {
2 QueryRunner runner = new QueryRunner(DataSourceUtils.getDataSourceWithC3P0ByXML());
3 String sql = "delete from student where id=?";
4 int count = runner.update(sql,4);
5 System.out.println(count);
6 }
30、ThreadLocal
ThreadLocal叫做线程本地变量,意思是ThreadLocal中填充的变量属于当前线程,该变量对其他线程而言是隔离的。可以为每个线程创建一个副本,每个线程可以访问自己内部的副本。
(1)数据库操作与ThreadLocal
①通过ThreadLocal,既保证数据的安全性,也保证了性能
②对于数据库来说,一个连接对应于一个事务,一个事务可以包含多个DML操作。Dao中包含了原子型的增删改查。一般来说,一个业务(Service)中的多个dao操作应该包含在一个事务中。因此借助ThreadLocal本身特性,可以在第一个dao操作时创建一个真正的connection对象,然后在其他几次dao操作时,会自动将该connection复制多个(connection只创建一个,因此该connection中的所有操作必然对应于同一个事务;ThreadLocal将connection在使用层面复制了多个,因此可以完成多个dao操作)。
(2)重点方法
①set():给ThreadLocal中存放一个变量
②get():从ThreadLocal中获取变量(副本)
③remove():删除副本
(3)事务流程
①开启事务(将自动提交改为手动提交)
②进行各种DML操作
③正常,将DML操作提交(全部成功);失败,将DML操作全部回滚(全部失败)
src/com/my/transaction · sumAll/JSP学习 - 码云 - 开源中国 (gitee.com)
31、元数据
元数据是描述数据的数据,主要是描述数据属性(property)的信息。例如一张表student(id,name),元数据就是id和name。
(1)类型
①数据库元数据:DatabaseMetaData
②参数元数据:ParameterMetaData
③结果集元数据:ResultSetMetaData
32、自定义标签
(1)涉及到的类
(2)实现步骤
①编写标签处理类
传统方式(JSP1.1):实现javax.servlet.jsp.tagext.Tag接口
简单方式(JSP2.0):实现javax.servlet.jsp.tagext.SimpleTag接口
②编写标签描述符(tld文件)
③导入并使用
导入:myTag.tld导入WEB INF或子目录下
使用:引入具体要使用的tld文件<% @taglib uri="..." prefix="..." %>
i.空标签(没有标签体的标签)
< d:foreach >< /d:foreach > 、< d:foreach/ >
ii.带标签体
< d:foreach > xxx < /d:foreach >
iii.带属性
< d:foreach 属性名=“属性值” > xxx < /d:foreach >
(3)Tag接口
没有循环时使用Tag接口。
①doStartTag():标签处理类的核心方法(标签体的执行逻辑)
该方法有以下2个返回值:0/1
int SKIP BODY = 0—标签体不会被执行
int EVALBODY_ INCLUDE = 1—标签体会被执行
②doEndTag():标签执行完毕之后的方法.例如可以让标签在执行完毕后,再执行一次
int SKIP PAGE =5—后面的JSP页面内容不被执行 int EVAL_PAGE = 6—后面的JSP页面内容继续执行
Tag接口中的所有方法执行顺序:
当JSP容器(Tomcat、jetty)在将.jsp翻译成.servlet(.java)的时候﹐如果遇到JSP中有标签,就会依次执行setPageContext() → setParent() → doStartTag() → doEndTag() → release()。
(4)javax.servlet.jsp.tagext.IterationTag接口
IterationTag是Tag的子接口。如果有循环,就使用IterationTag。
①doAfterBody ():当标签体执行完毕之后的操作,通过返回值决定:
int EVAL_BODY_AGAIN =2—重复执行 int SKIP_ BODY=0—不再执行
(5)javax.servlet.jsp.tagext.BodyTag接口
BoyTag接口可以在标签体被显示之前,进行一些其他的“额外”操作。
int EVAL_BODY_BUFFERED = 2,是doStartTag()的第三个返回值。代表一个缓冲区(BodyContent)。
如果返回值是EVAL_BODY_BUFFERED,则服务器会自动将标签体需要显示的内容放入缓冲区中(BodyContent)。因此,如果要更改最终显示结果,只需要从缓冲区获取原来的数据进行修改即可。
(6)javax.servlet.jsp.tagext.SimpleTag接口
将传统方式的doStartTag()、doEndTag()、doafterBody()等方法简化成了一个通用的doTag()方法。
简单方式没有缓冲区,若需要修改显示内容,可以通过流实现。
javax.servlet.jsp.tagext.JspFragment类:代表一块JSP元素(该块不包含scrlptlet,因此简单方式的tld文件中<body-content>不能是JSP)
JspFragmen中有一个invoke(Writer varl)方法,入参是“流”,即如果要修改显示内容,只需要修改此流
invoke(Writer var1):每调用一次invoke()方法,会执行一次标签体。
SimpleTagSupport的 getJspBody()可以获取JspFragment对象。
获取JSP内置对象:getJSPContext() → JSPContext → 转成子类PageContext → PageContext就是所有JSP内置对象的入口,即可以获取一切JSP内置对象。
(7)执行的流程
33、集群
(1)集群的好处
①负载均衡—分摊到多个操作单元上进行执行
②失败迁移—当一个服务器出现错误时,客户端可以去访问另一个服务器来完成操作
(2)服务端集群
①水平集群—将服务器安装在各个不同的计算机上(失败迁移)
②垂直集群—将多个服务器安装在同一个计算机上(负载均衡)
(3)服务器特点
①apache:特点是处理静态资源(html、js、图片)
②tomcat:特点是可以处理动态资源
(4)搭建集群
①下载apache服务器工具
②apache配置
修改conf/http.conf
配置成windows服务,在命令行窗口执行以下命令
“D:\cluster\Apache24\bin\httpd.exe” -k install -n apache24
③准备tomcat
i.将tomcat复制两份,并修改端口号(server.xml)。端口规划如下:
服务器 | server端口号 | http协议端口号 | ajp协议端口号 |
---|---|---|---|
tomcat-a | 1005 | 1080 | 1009 |
tomcat-b | 2005 | 2080 | 2009 |
ii.配置引擎Engine(server.xml)
tomcat-a:
<Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcat-a">
tomcat-b:
<Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcat-b">
iii.打开集群开关(server.xml)
打开以下注释。
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>
④下载mod_jk.so文件,用来结合apache和tomcat
下载 tomcat-connectors-1.2.40-windows-x86_64-httpd-2.4.x.zip 。
⑤配置mod_jk.so
i.存放位置:Apache24\modules
ii.创建Apache24\conf\workers.properties
worker.list=controller,tomcata,tomcatb
#tomcata
worker.tomcata.port=1009
worker.tomcata.host=localhost
worker.tomcata.type=ajp13
#负载均衡的权重
worker.tomcata.lbfactor=1
#tomcatb
worker.tomcatb.port=2009
worker.tomcatb.host=localhost
worker.tomcatb.type=ajp13
worker.tomcatb.lbfactor=2
#controller
worker.controller.type=lb
worker.controller.balanced_workers=tomcata,tomcatb
#true表示session策略为sticky,false表示为session广播
worker.controller.sticky_session=false
iii.配置Apache24\conf\mod_jk.conf(用于加载mod_jk.so和workers.properties)
#加载mod_jk.so
LoadModule jk_module modules/mod_jk.so
#加载workers.properties
JkWorkersFile conf/workers.properties
#表示拦截一切
JkMount /* controller
iiii.httpd.conf在apache启动时自动加载,因此需要将mod_jk.conf引入到该文件,在文件末尾追加一下语句
include conf/mod_jk.conf
(5)注意
①要把系统变量CATALINA_HOME删除,因为如果配置了该变量,在点击startup.bat来启动tomcat时,会自动启动该变量指向的tomcat,导致本地计算机只能启动一台tomcat。
(6)分布式session策略
①sticky:将每一个用户的请求分给特定的服务器,后期的请求不会分给其他服务器,只分给第一次给的服务器
②session广播:自动同步session
弊端:如果服务器太多,可能造成广播风暴(一个服务器的session,需要同步到其他所有的服务器)