JavaWeb开发(二)
完结篇?
MVC 架构模式
MVC(Model View Controller)是一种软件架构模式,将软件系统分为模型、视图和控制器三个部分,对应业务逻辑、数据和显示界面。


DAO(Data Access Object)层又称“持久层”或“数据访问层”,作用是封装所有对数据库的访问逻辑、将数据库操作(增删改查)与业务逻辑彻底分离提供一组接口,让上层(Service 层)只需调用接口方法,无需关心底层如何与数据库交互。DAO层一般需要定义接口和实现类。
POJO是 Plain Old Java Object 的缩写,意思是“普通的 Java 对象”。它通常指不依赖任何框架或特殊类继承/注解的、只包含属性、构造方法、Getter/Setter、toString() 等基本方法的实体类。
POJO的实体类的基本要求:
- 类名和表格名称应该对应
- 类的字段名和表格的列名应该对应
- 每个字段必须是私有的
- 每个属性都应该具备 getter setter
- 必须具备无参构造器
- 应该事先序列化接口(缓存、分布式项目数据传递,可能会将对象序列化)
- 应该重写类的
hashcode
和equals
方法 toString
是否重写都可
lombok
一个小工具,可以通过注解的形式把 getter setter hashcode等模板方法直接补全。详见lombok官方指南。
数据类相关:
注解 | 功能 |
---|---|
@Getter |
自动为所有字段生成 getXxx() 方法 |
@Setter |
自动为所有字段生成 setXxx() 方法 |
@ToString |
自动生成 toString() 方法 |
@EqualsAndHashCode |
自动生成 equals() 和 hashCode() 方法 |
@Data |
综合注解,包含
@Getter 、@Setter 、@ToString 、@EqualsAndHashCode
和 @RequiredArgsConstructor (对于 final
字段) |
构造器相关:
注解 | 功能 |
---|---|
@NoArgsConstructor |
生成无参构造函数 |
@AllArgsConstructor |
生成包含所有字段的构造函数 |
@RequiredArgsConstructor |
生成包含所有 final 字段或带 @NonNull
字段的构造函数 |
构建器模式:
注解 | 功能 |
---|---|
@Builder |
提供链式构建对象方式 |
@Singular |
与 @Builder
配合,用于集合字段构建时避免重复添加逻辑 |
其他实用注解:
注解 | 功能 |
---|---|
@Slf4j |
自动为类引入 private static final org.slf4j.Logger log
日志对象(也有 @Log4j , @Log 等变种) |
@SneakyThrows |
自动捕获并抛出 checked 异常(不用写 try-catch) |
@NonNull |
在方法参数或字段上使用,自动生成空值检查 |
@Cleanup |
自动调用 close() ,常用于资源释放(如
InputStream ) |
会话管理
概述
“会话管理”(Session Management)是指:在客户端与服务器之间的交互过程中,服务器如何识别并保持用户身份和状态信息。
为什么需要会话管理?因为 HTTP 协议本身是无状态的协议,对于发送的请求和响应都不会进行保存,没有记忆功能。为了实现用户登录状态的记录、购物车、填写表单等具有记忆的功能,需要一种能够记忆用户状态的机制——会话管理。
Cookie
使用
cookie 是一种客户端会话技术, cookie 由服务端产生,它是服务器存放在浏览器的一小份数据,浏览器以后每次访问该服务器的时候都会将这小份数据携带到服务器去。
- 服务端创建cookie,将cookie放入响应对象中,Tomcat容器将cookie转化为set-cookie响应头,响应给客户端
- 客户端在收到服务端发来的cookie的响应头后,在以后每次请求该服务的资源时,会以cookie请求头的形式携带之前收到的Cookie
- cookie是一种键值对格式的数据,从tomcat8.5开始可以保存中文,但是不推荐
- 由于cookie是存储于客户端的数据,比较容易暴露,一般不存储一些敏感或者影响安全的数据

创建cookie:
1 |
|
获取cookie:
1 |
|
时效性
默认情况下Cookie的有效期是一次会话范围内,关闭浏览器后Cookie就会消失,可以通过cookie的setMaxAge()方法让Cookie持久化保存到浏览器上
cookie.setMaxAge(int expiry)
参数单位是秒,表示cookie的持久化时间,如果设置参数为0,表示将浏览器中保存的该cookie删除。
- servletA设置一个Cookie为持久化cookie
1 |
|
限制提交路径
访问互联网资源时不能每次都需要把所有Cookie带上。访问不同的资源时,可以携带不同的cookie,我们可以通过cookie的setPath(String path) 对cookie的路径进行设置
1 |
|
Session
HttpSession是一种保留更多信息在服务端的一种技术,服务器会为每一个客户端开辟一块内存空间,即session对象. 客户端在发送请求时,都可以使用自己的session. 这样服务端就可以通过session来记录某个客户端的状态了。
和cookie的区别:
- cookie是存储在客户端(浏览器)的小块文本数据,又服务器发送并保存在浏览器中,每次请求时浏览器都会自动把它们发送给服务器,用于找回服务器中保存的Session数据。
- session是存储在服务器的数据,记录了用户的会话状态,比如登录信息、购物车内容等。
- session的安全性更高,容量没有明确的限制;cookie的安全性低,容量一般不超过4kb。
Session 和 Cookie 的关系:
- Session 依赖 Cookie 来实现识别用户身份。
- 通常,服务器在创建一个 Session 时,会生成一个唯一的 Session ID。
- 这个 Session ID 会通过 Set-Cookie 响应头被发送到客户端,客户端会把这个 ID 保存在 Cookie 中。
- 后续客户端的请求会自动携带这个 Session ID 的 Cookie,服务器就可以用这个 ID 找回之前保存的 Session 数据,从而识别用户状态。
Session的原理图:

示例:假如有一个表单name属性为username
,现在使用ServeletA将表单提交的用户名存入Session:
1 |
|
使用getSession()
方法可以获取当前会话的实例,获取的逻辑如下:

这时返回给客户端的响应中包含一个键为JSESSIONID
的cookie。
然后在另一个ServletB中就可以取出用户名:
1 |
|
调用servletB时可以看到请求中也携带了一个JSESSIONID的cookie。
Session的时效性:
默认的session最大闲置时间(两次使用同一个session中的间隔时间) 在tomcat/conf/web.xml配置为30分钟。
也可以通过HttpSession的API 对最大闲置时间进行手动设定
1 |
|
也可以直接让session失效
1 |
|
域对象
域对象是用于存储和传递数据的对象。
比较重要的域:请求域,会话域,应用域。
- 请求域对象:HttpServletRequest,传递数据的范围是一次请求之内和多次转发。
- 会话域对象:HttpSession,传递数据的范围是一次会话之内,同一个浏览器发起的请求可以共享该对象,但是换一个浏览器就不可以了。
- 应用域:ServletContext,传递数据的范围是本应用之内。




过滤器
过滤器的工作位置:
过滤器的使用
日志记录
骨架:
1 |
|
传入参数中的FilterChain
是一个接口,内部定义了一个方法:
1 |
|
注意这个doFilter
和LoggingFilter
类重写的doFilter
方法不是一个方法,这里没有递归。这里的doFilter
方法的作用是:
在多个 Filter 之间传递请求和响应对象,或者最终将请求传递给目标 Servlet。
放行代码
1 |
|
为什么调用doFilter
就是放行了?举个例子,假设我们有这样一条过滤器链:
1 |
|
如果在FilterB
中调用了chain.doFilter(request, response);
,那么请求才会继续往下传,传到下一个过滤器。反之,如果不调用该方法,那么请求就会卡在过滤器B中,达到了拦截的效果。
通过设计doFilter
方法,我们可以在过滤器中处理请求,经过仔细判断之后再决定要不要往下传。调用doFilter
,则相当于通过过滤器,传给下一个过滤器或Servlet;不调用则不再往下传递,视为拦截。
还有一个问题:多个过滤器链式调用的时候,它们的先后顺序是怎么确定的?答案是web.xml
中的<filter-mapping>
的前后顺序决定执行顺序。
如果采用 Servlet 3.0+ 的标准注解形式,那么执行顺序取决于类名在字典中的顺序。在使用 Spring 的情况下才可以通过注解明确指定执行顺序。

配置过滤器
方法一:通过web.xml
配置
初始化过滤器;
1 |
|
配置过滤器
1 |
|
servlet-name
: 根据servlet的name
属性过滤(这个属性的默认值为""
,需要手动配置)url-pattern
: 根据请求的资源路径来过滤资源。具体规则:精确路径匹配/servlet, 路径匹配/*, 扩展名匹配*.action,默认匹配 / 优先级依次靠后
。如果使用/*
,说明所有的请求和响应都会经过这个过滤器。
两者不能混用,否则会导致同一个过滤器被错误地重复执行。
简易日志的实现代码如下:
1 |
|
过滤器的生命周期
过滤器作为web项目的组件之一,和Servlet的生命周期类似,略有不同,没有servlet的load-on-startup的配置,默认就是系统启动立刻构造
阶段 | 对应方法 | 执行时机 | 执行次数 |
---|---|---|---|
创建对象 | 构造器 | web应用启动时 | 1 |
初始化方法 | void init(FilterConfig filterConfig) |
构造完毕 | 1 |
过滤请求 | void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) |
每次请求 | 多次 |
销毁 | default void destroy() |
web应用关闭时 | 1次 |
监听器
使用频率不如过滤器。
监听器:专门用于对域对象对象身上发生的事件或状态改变进行监听和相应处理的对象。
监听对象:域对象发生的事件,例如:域对象的创建、销毁,数据的修改、删除等,当这些事件发生时监听器执行相应的代码。
application listener
配置方式:
1 |
|
或者使用注解:@WebListener
如果想要监听应用域的初始化和销毁事件,需要实现ServletContextListener
接口:
1 |
|
在应用启动和销毁时各执行一次.
如果监听 ServletContext
的参数的变化,需要实现ServletContextAttributeListener
,一共三个抽象方法:
1 |
|
会话域和请求域
和应用域完全类似,只不过调用的方法的ServletContext
替换成Session
和Request
。
Session
还有两个特殊的监听器:
HttpSessionBindingListener
监听当前监听器对象在Session域中的增加与移除HttpSessionBindingEvent
对象代表属性变化事件HttpSessionActivationListener
监听钝化和活化,将内存中的session序列化变成磁盘中的文件称之为钝化,反序列化变成内存中的对象称之为活化。