JAVAweb入门
#1. Servlet
1. 获取参数
s1.用户发请求,action=add;
s2.项目中web.xml中找到url-pattern=/add 第12行
s3.找第11行的servlet-name = AddServlet
s4.找servlet-mapping中ervlet-name一致的servlet,找到第7行
s5.找第8行的servlet-class -> servlets.AddServlet
1 |
|
s6.用户发送的是post请求(method=post),因此tomcat会执行AddServlet中的doPost方法
1 | public class AddServlet extends HttpServlet { |
注意!
- 出现这种问题,要关注读取配置文件的方式
1 | //法一: |
- 原因:使用ClassLoder读取配置文件,该方式只能读取类路径下的配置文件,所以一般需要将配置文件放在类路径目录下,即resources下。
- 而配置文件放在src下,则无法读取
- 问题的关键就在于配置文件的位置,这会影响读取的方法
2.设置编码
- post方式下,设置编码,防止中文乱码;get方式目前不需要设置编码
- 注意,设置编码这一句代码必须在所有的获取参数动作之前
1 | request.setCharacterEncoding("utf-8"); |
3. Servlet的继承关系
1. 继承关系
javax.servlet.Servlet接口
javax.servlet.GenericServlet抽象类
javax.servlet.http.HttpServlet抽象子类
2. 相关方法
javax.servlet.Servlet接口:
- void init(config) - 初始化方法
- void service(request, response) - 服务方法
- void destroy() - 销毁方法
javax.servlet.GenericServlet抽象类:
- void service(request, response) - 仍然是抽象的
javax.servlet.http.HttpServlet 抽象子类:
- void service(request, response) - 不是抽象的
1.String method = req.getMethod(); 获取请求的方式:是post还是get还是其他什么
2.各种if判断,根据请求方式不同,调用不同的do方法
3.在HttpServlet这个抽象类中,do方法都差不多:
service()方法
当有请求过来时,service方法会自动响应(其实是tomcat容器调用的)
在HttpServlet中我们会去分析请求的方式:到底是get、post还是其他等等
然后再决定调用的是哪个do开头的方法,这些方法都需要我们子类去实现对应的方法,否则会报405错误
因此,我们在新建Servlet时,我们就会考虑请求方法,并重写相应的do方法,否则默认调用get方法并报405错误
4. Servlet的生命周期
生命周期:对应Servlet中的三个方法:init(), service(), destroy()
默认情况下:
第一次接收请求时,这个Servlet会进行实例化(调用构造方法)、初始化(init())、然后服务(service())
从第二次请求开始,每一次都是服务
当容器关闭时,其中所有的Servlet实例会被销毁,调用销毁方法(destroy)
Servlet实例tomcat只会创建一个,所有的请求都是这个实例去响应。
只有第一次接收请求,才会实例化、初始化,然后开始服务。
优点:提高系统的启动速度;缺点:第一次请求时,耗时较长
结论:如果需要提高系统的启动速度,这样就可以了;如果需要提高响应速度,我们应该设置Servlet的初始化时机。
Servlet的初始化时机
默认是第一次接收请求时,实例化、初始化,
我们可以通过在配置文件中设置
<load-on-startup>
来设置Servlet启动的先后顺序,数字越小,启动越靠前,最小值0
Servlet在容器中是:单例的,线程不安全的
单例:所有的请求都是同一个实例去响应
线程不安全:一个线程需要根据这个实例中的某个成员变量值去做逻辑判断。但是中间某个时机,另一个线程改变了这个成员变量的值,导致第一个线程的执行路径发生了变化
我们已经知道了Servlet是线程不安全的,给我们的启发是:尽量不要在Servlet中定义成员变量,如果不得不定义成员变量,那么不要去:①修改成员变量的值②根据成员变量的值进行逻辑判断
初始化
Servlet中的初始化方法有两个:init(), init(ServletConfig config)
其中带参数的方法代码如下:
public void init(ServletConfig config) throws ServletException{
this.config = congfig;
init();
}
另一个无参的方法如下:
public void init() throws ServletException{
}
如果我们想在Servlet初始化时做一些准备工作,我们可以重写init()方法
我们可以通过如下步骤去获取初始化设置的数据
-获取config对象:ServletConfig config = getServletConfig();
-获取初始化参数值:config.getInitParameter(key);
在web.xml文件中配置Servlet
xml1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>Demo01Servlet</servlet-name>
<servlet-class>servlet.Demo01Servlet</servlet-class>
<init-param>
<param-name>hello</param-name>
<param-value>world</param-value>
</init-param>
<init-param>
<param-name>uname</param-name>
<param-value>jim</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>Demo01Servlet</servlet-name>
<url-pattern>/demo01</url-pattern>
</servlet-mapping>
</web-app>也可以通过注解的方式进行配置:
java1
2
3
4
5学习Servlet中的ServletContext和
<context-param>
1.获取ServletContext,有很多方法
在初始化方法中,ServletContext servletContext = getServletContext();
在服务方法中也可以通过req对象获取,也可以通过session获取
2.获取初始化值:
servletContext.getInitParameter();
5. Http协议
- HTTP: Hyper Text Transfer Protocol 超文本传输协议。HTTP最大的作用就是确定了请求和响应数据的格式。
- HTTP无状态:服务器无法判断这两次请求是同一个客户端发过来的,还是不同的客户端发过来的
- HTTP请求响应包含两个部分:请求和响应
1. 请求
- 请求包含三个部分:1. 请求行;2. 请求消息头; 3. 请求主体
请求行包含三个信息:1. 请求的方式;2. 请求的URL; 3. 请求的协议(一般都是HTTP1. 1)
请求消息头中包含了很多客户端需要告诉服务器的信息,比如浏览器型号、版本、能接收的内容的类型、发给服务器内容的类型、长度等等
请求体,三种情况:
get方式,没有请求体,但是有一个queryString
post方式,有请求体,form data
json格式,有请求体,request payload
2. 响应
- 响应包含三个部分:1. 响应行; 2. 响应消息头; 3. 响应体
- 响应行包含三个信息:1. 协议; 2. 响应状态码(200); 响应状态(ok)
响应状态码:
200:正常响应
404:找不到资源
405:请求方式不支持
500:服务器内部错误
- 响应消息头:包含了服务器的信息;服务器发送给浏览器的信息(内容的媒体类型、编码、内容长度等)
- 响应体:响应的实际内容(比如请求add.html页面时,响应的内容就是<form…)
6. 会话
HTTP无状态:服务器无法判断这两次请求是同一个客户端发过来的,还是不同的客户端发过来的
需要通过会话跟踪技术来解决无状态的问题
会话跟踪技术:
客户端第一次发请求给服务器,服务器获取session,获取不到,则创建新的,然 后通过响应头响应给客户端;
下次客户端给服务器发请求时,会把sessionID通过请求头带给服务器,那么服务器就能获取到了,服务器判断两次是同一个客户端。
常用的API:
request.getSession() -> 获取当前的会话,没有则创建一个新的会话
request.getSession(true) -> 效果和不带参数相同
request.getSession(false) -> 获取当前会话,没有则返回null,不会创建新的
session.getId() -> 获取sessionId
session.isNew() -> 判读当前session是否是新的
session.getMaxInactiveInterval() -> session的非激活间隔时长, 默认1800秒
session.setMaxInactiveInterval() -> 设置session的非激活间隔时长
session.invalidate() -> 强制性让会话立即失效
…
session保存作用域
session.setAttribute(“uname”, “lina”); -> 向当前session保存作用域保存一个数据”lina”,对应的key为”uname”;
session.getAttribute(“uname”); -> 从当前session保存作用域获取指定的key”uname’对应的value值
void removeAttribute(k);
session保存作用域是和具体的某一个session对应的
7. 服务器内部转发以及客户端重定向
服务器内部转发:request.getRequestDispatchera(“…”).forward(request, response);
一次请求响应的过程,对于客户端而言,服务器内部经过了多少次转发,客户端是不知道的
地址栏没有变化
客户端重定向:response.sendRedirect(“…”);
两次请求响应的过程,客户端肯定知道URL有变化
地址栏有变化
8. 保存作用域
- 原始情况下,保存作用域有四个:page(页面级别,现在几乎不用), request(一次请求响应范围), session(一次会话范围), application(整个应用程序范围)
1 | req.setAttribute("uname", "adomais");//request保存作用域 |
9. 路径问题
../:返回上一级目录
<base href="http://localhost:8080/pro/" />
的作用是:当前页面上的所有的路径都以这个为基础<link href="css/shopping.css">
th:href=”@{}” 表示基于当前根目录
<link th:href="@{/css/shopping.css}">
10. 什么是业务层
MVC: Model 模型,View 视图, Controller 控制器
- 视图层:用于做数据展示以及和用户交互的一个界面
- 控制层:能够接受客户端的请求,具体的业务功能还是需要模型来完成
- 模型层:模型分为很多种:有比较简单的pojo/vo(value object),有业务模型组件,有数据访问层组件
- pojo/vo:值对象
- DAO:数据访问对象
- BO:业务对象
区分业务对象和数据访问对象
1.DAO中的方法都是单精度方法或者称为细粒度方法。一个方法只考虑一个操作
2.BO中的方法属于业务方法,实际的业务是比较复杂的,因此业务方法的粒度是比较粗的
注册这个功能属于业务功能,也就是说注册这个方法属于业务方法
那么这个业务方法中包含了多个DAO方法。也就是说注册这个业务功能需要通过多个DAO方法的组合调用,从而完成注册功能开发。
11. IOC
耦合/依赖
依赖指的是某某某离不开某某某
在软件系统中,层与层是存在依赖的。我们也称之为耦合
我们系统架构或者是设计的一个原则是:高内聚低耦合
层内部的组成应该是高度聚合的,而层与层之间的关系应该是低耦合的,最理想的情况0耦合
IOC - 控制反转 / DI - 依赖注入
控制反转:
之前在Servlet中,我们创建service对象,FruitService fruitService = new FruitServiceImpl();
这种写法如果出现在Service中的某个方法内部,那么这个fruitService的作用域应该就是这个方法级别;如果这种写法出现在Service的类中,那么这个fruitService的作用域就是这个Service实例级别
之后我们在applicationContext.xml中定义了这个fruitService。然后通过解析XML,产生fruitService实例,存放在beanMap中,这个beanMap在一个BeanFactory中。
因此我们转移了之前的service实例、dao实例等等他们的生命周期。控制权从程序员转移到BeanFactory。这个现象我们称之为控制反转
依赖注入:
之前我们在控制层出现代码:FruitService fruitService = new FruitServiceImpl();
那么,控制层和service层存在耦合。
之后我们将代码修改成FruitService fruitService = null;
然后在配置文件中配置:
<bean id="fruit" class="FruitController">
<property name="fruitService" ref="fruitService"/>
</bean>
这就是依赖注入。
12. 过滤器Filter
Filter也属于Servlet规范
Filter开发步骤:新建类实现Filter接口,然后实现其中的三个方法:init/doFilter/destroy
配置Filter,可以使用注解@WebFilter,也可以使用xml文件
Filter配置时,也可以配置通配符。例如@WebFilter(“*.do”)表示拦截所有以.do结尾的请求
过滤器链:
- 如果采取注解的方式进行配置,那么过滤器的拦截顺序是按照全类名的先后顺序排序的
- 如果采取的是xml的方式进行配置,那么按照配置的先后顺序进行排序
13. 监听器
- ServletContextListener - 监听ServletContext对象的创建和销毁
- HttpSessionListener - 监听HttpSession对象的创建和销毁
- ServletRequestListener - 监听ServletRequest对象的创建和销毁
- ServletContextAttributeListener - 监听ServletContext的保存作用域的改动(add, remove, replace)
- HttpSessionAttributeListener - 监听HttpSession的保存作用域的改动(add, remove, replace)
- ServletRequestAttributeListener - 监听ServletRequest的保存作用域的改动(add, remove, replace)
- HttpSessionBindingListener - 监听某个对象在Session域中的创建和移除
- HttpSessionActivationListener - 监听某个对象在Session域中的序列化和反序列化时
13. 事务管理
service操作应该是一个整体。事务管理不能以DAO层的单精度方法为单位,而应该以业务层的方法为单位
ThreadLocal
-get(), set(obj)
-ThreadLocal称为本地线程,我们可以通过set方法在当前线程上存储数据,通过get方法在当前线程上获取数据
-set方法源码分析:
java1
2
3
4
5
6
7
8
9
10public void set(T value){
Thread t = Thread.currentThread();//获取当前的线程
ThreadLocal map = getMap(t);//每一个线程都维护一个容器(ThreadLocalMap)
if(map != null)
map.set(this, value);
//this代表当前的ThreadLocal,因为我们的组件中需要传输的对象可能有多个,map中可以存放多个ThreadLocal
//一个线程对应一个ThreadLocalMap,一个ThreadLocalMap对应多个TreadLocal,一个ThreadLocal对应一个需要同步的值
else
creatMap(t, value);
}-get方法源码分析:
java1
2
3
4
5
6
7
8
9
10
11Thread t = Thread.currentThread();//获取当前的线程
ThreadLocalMap map = getMap(t);//每一个线程都维护一个容器(ThreadLocalMap)
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
T result = (T)e.value;
return result;
}
}
return setInitialValue();
#2. Thymeleaf - 视图模板技术
- Servlet从3.0版本开始支持注解方式的注册,不需要写xml文件了
- 添加thymeleaf的jar包
- 新建一个Servlet类VieBaseServlet
1 | package myssm.myspringMVC; |
在web.xml文件中添加配置
配置前缀 view - prefix
配置后缀 view - suffix
1 |
|
- 让我们的Servlet继承ViewBaseServlet
- 根据逻辑视图名称 得到 物理视图名称
1 | package fruit.servlets; |
使用thymeleaf的标签
th:if
th:unless
th:each
th:text