1、分层结构
目的:实现应用程序的模块化和解耦,便于团队协作和维护。
1. 控制层(Controller):控制层主要负责处理用户请求和响应
2. 服务层(Service):服务层主要负责业务逻辑的处理。
3. 数据访问层(DAO):数据访问层主要负责与数据库进行交互。
2、使用技术
2.1、 控制层(Controller):
Servlet 是 Java Web 应用程序的基础,它提供了一种用于处理 HTTP 请求的标准 Java 接口,每个 Servlet 类处理特定的请求。在早期的 Web 开发中,Servlet 是一种主流的解决方案。
2.1.1、 Servlet的工作模式
客户端发送请求至服务器
服务器运行并调用Servlet,Servlet根据客户端请求生成响应内容并将其传给服务器,响应内容动态生成,通常取决于客户端的请求
服务器将响应返回客户端
出自CSDN博主「atCarl」的原创文章,原文链接:https://blog.csdn.net/2201_75955594/article/details/130232573
2.1.2、 Servlet的生命周期
Servlet的生命周期是由容器管理的,Servlet容器(例如Tomcat)会根据下面的规则来调用这三个方法:
初始化方法init( ),只会执行一次(启动Tomcat的时候默认是不执行的,在访问的时候才会执行)当Servlet第一次被请求时,Servlet容器会实例化这个Servlet,然后就会调用这个方法来初始化Servlet,但是这个方法在后续请求中不会在被Servlet容器调用,我们可以利用init()方法来执行相应的初始化工作。
服务方法service( ),每当请求Servlet时,Servlet容器就会调用这个方法。第一次请求时,Servlet容器会先调用init( )方法初始化一个Servlet对象出来,然后会调用它的service( )方法进行工作,但在后续的请求中,Servlet容器只会调用service方法了。
service()方法根据请求的类型(Get、Post等)调用相应的doGet()、doPost()等方法
要么重写doGet、doPost ,要么重写 service,必须二选一,而且必须进行重写。
销毁方法destory(),当要销毁Servlet时,Servlet容器就会调用这个方法,卸载应用程序或者关闭Servlet容器时,就会发生这种情况,一般在这个方法中会写一些清除代码。
参考CSDN博主「atCarl」的原创文章,原文链接:https://blog.csdn.net/2201_75955594/article/details/130232573
2.1.3、 Servlet的开发步骤
1.导入 jar 包【servlet】
<!--tomcat依赖 内含tomcat-servlet-api-->
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-api</artifactId>
<version>8.5.41</version>
<scope>provided</scope>
</dependency>
或者
<!--servlet-api-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
2.创建一个 Servlet类
3.在web容器中注册 servlet【配置Servlet】
一般有两种方式:web.xml / 注解(Servlet 3.0 开发人员可以使用注解的方式配置Servlet)
4.写一个 jsp 页面(此处忽略)
5.处理逻辑并响应请求
参考知乎作者[JasonWX]的原创文章,原文链接:https://zhuanlan.zhihu.com/p/591105720?utm_id=0
2.1.4、 Servlet之Filter(过滤器)
Filter(过滤器):过滤器可以动态地拦截请求和响应,以变换或使用包含在请求或响应中的信息,也就是说可以监视,修改或以某种方式处理客户端与服务端下在交流的数据。
(1)在客户端的请求访问后端资源之前,拦截这些请求。
例如静态资源或登录、注册、验证码等资源放行,其他资源拦截
(2)在服务器的响应发送回客户端之前,处理这些响应。
乱码的统一处理、过滤非法字符、对非法的请求进行拦截(权限校验)
过滤器的生命周期:
1)服务器启动时实例化过滤器
2)调用init()方法初始化参数
3)利用FilterConfig ->getInitParams(name)方法获取参数值
4)客户端请求数据时doFilter()执行过滤器
5)服务器关闭时容器销毁过滤器实例前调用destory()方法
过滤器的开发步骤
1)创建一个类实现javax.servlet.Filter
接口
2)重写接口中所有的方法,其中有doFilter方法,用来执行过滤的任务。有请求和响应两个参数
3)注册过滤器在 web.xml中进行配置 或者 通过注解的方式:@WebFilter("过滤的地址")
2.1.5、 Servlet之Listener(监听器)
Listener(监听器):
用于监听web应用中某些对象、信息的创建、销毁、增加,修改,删除等动作的发生,然后作出相应的响应处理。当范围对象的状态发生变化的时候,服务器自动调用监听器对象中的方法。常用于统计在线人数和在线用户,系统加载时进行信息初始化,统计网站的访问量等等。
特点:
1)监听方法中的逻辑代码由程序员根据需要编写
2)监听方法由tomcat根据监听结果来调用执行
相关概念:
事件源:被监听的对象(三个域对象 request、session、servletContext)
监听器:监听事件源对象事件源对象的状态的变化都会触发监听器
注册监听器:将监听器与事件源进行绑定
响应行为:监听器监听到事件源的状态变化时所涉及的功能代码(程序员编写代码)
监听器的类别:
在JavaWEB中,监听器分为三大类:
对象类型 | 作用范围 | 生命周期 |
---|---|---|
ServletContext (应用域、上下文域) | 所有用户的所有请求 | 服务器关闭时结束 |
HttpSession(会话域) | session销毁之前 | 默认30分钟 |
HttpServletRequest(请求域) | 一个用户的一次请求 | 每次请求就结束 |
八大种:
1)监听域对象需要在web.xml中配置
对象类型 | 对应的监听器 | 作用 |
---|---|---|
ServletContext | ServletContextListener | 监听ServletContext的生命周期 |
HttpSession | HttpSessionListener | 监听Session的生命周期 |
HttpServletRequest | ServletRequestListener | 监听Request的生命周期 |
2)监听属性需要在web.xml中配置
对象类型 | 对应的监听器 | 作用 |
---|---|---|
ServletContext | ServletContextAttributeListener | 监听ServletContext属性的变化 |
HttpSession | HttpSessionAttributeListener | 监听Session属性的变化 |
HttpServletRequest | ServletRequestAttributeListener | 监听Request属性的变化 |
3)监听session对象状态不需要在web.xml中配置
对象类型 | 对应的监听器 | 作用 |
---|---|---|
HttpSession | HttpSessionBindingListener(绑定,解除绑定) | 监听session的属性绑定与移除 |
HttpSession | HttpSessionActivationListener(钝化和活化) | 监听session中的属性钝化与活化 |
(一)钝化:当服务器正常关闭时,还存活着的session(在设置时间内没有销毁) 会随着服务器的关闭被以文件(“SESSIONS.ser”)的形式存储在tomcat 的work 目录下,这个过程叫做Session 的钝化。
(二)活化:当服务器再次正常开启时,服务器会找到之前的“SESSIONS.ser” 文件,从中恢复之前保存起来的Session 对象,这个过程叫做Session的活化。
监听器的开发步骤
1)实现对应的接口
2)重写接口中的方法
3)在web.xml中注册该listener(根据监听器类型判断需不需要)
参考CSDN博主「猿小许」的原创文章,原文链接:https://blog.csdn.net/weixin_44205087/article/details/117923765。
2.1.6、 请求转发和请求重定向
转发与重定向实现的功能是相同的,用于从一个页面跳转到另一个页面。但是从内部细节上有很大的不同。
请求转发(forward):发生在服务端程序内部,由服务器进行的页面跳转,因此也叫服务器内部转发;当服务器端收到一个客户端的请求之后,会先将请求转发给目标地址,再将目标地址返回的结果转发给客户端, 而客户端对于这一切毫无感知的。
转发的特点:
1)转发是服务器内部的行为,因此在转发时,客户端请求的地址不会发生变化,即使服务器内部已经跳转了好几次了,但是客户端访问的地址依旧不变;
2)整个转发过程中,客户端从始至终只发送了一次请求;
3)由于在整个转发过程中,客户端只发送了一次请求,因此请求域的数据不会失效;
4)转发是服务器内部的行为,因此我们不需要加上项目名;例如:request.getRequestDispatcher("/demo01")
转发示例:客户端访问资源/demo01
,服务器发现客户端想要的资源不在/demo01
于是自己将请求转发到/demo02
,这个过程客户端无感知;客户端只请求了一次;
请求重定向:服务器端接收到客户端的请求之后,会给客户端返回了一个临时响应头,这个临时响应头中记录了客户端需要再次发送请求(重定向)的 URL 地址,客户端根据服务器反馈的信息再次请求服务器,这就是请求重定向。
重定向的特点:
1)由于客户端重定向是客户端再次请求,因此客户端的地址栏的地址已经发生了变化;
2)客户端请求了两次服务器
3)在整个重定向过程中,客户端发送了两次请求,请求域中的数据丢失;
4)重定向是客户端行为(客户端再次请求),我们需要加上项目名;例如:sendRedirect("request.getContextPath()+/demo01")
如果需要保留请求域中的数据,使用转发,否则使用重定向。
重定向示例:客户端首先访问资源/demo01
,服务器发现想要的资源不在demo01这里,于是告诉(sendRedirect)客户端:"你要的资源不在我这里,你去访问/demo02
"吧!于是客户端再次访问资源/demo02
;在重定向中,客户端已经请求了两次服务器。
2.2、服务层(Service):
开发人员主动管理对象。
2.3、 数据访问层(DAO):
Java Database Connectivity,简称JDBC;是Java语言中用来规范客户端程序如何来访问数据库的应用程
序接口,提供了诸如查询和更新数据库中数据的方法。
2.3.1、 JDBC的优点:
- JDBC API是一组接口,没有具体的实现。实现类由各数据库厂商去实现。只需要调用接口中
的方法即可。 - JDBC代码只需要少量的修改就可以访问另一种数据库。
- 所有数据库框架,底层最终都会转成JDBC的代码来实现。
2.3.2、 JDBC的操作步骤
2.3.2.1、 加载驱动
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
2.3.2.2、 获得连接
//数据库连接信息,一般配置在.properties文件中
String url = "jdbc:mysql://localhost:3306/project_db?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC";
String username= "root";
String password= "abc1234.";
//获取连接
Connection con = DriverManager.getConnection(url, username, password);
2.3.2.3、 执行指令
PreparedStatement ps = con.prepareStatement("select * from studernt");
2.3.2.4、 获取查询结果集(查询才有,增删无此步骤)
List<Student> list=new ArrayList<Student>();
rs=ps.executeQuery();
while (rs.next()) {
Student stu=new Student();
stu.setId(rs.getInt(1));
stu.setName(rs.getString(2));
stu.setAge(rs.getInt(3));
stu.setBirthday(rs.getDate(4));
list.add(stu);
}
2.3.2.5、 关闭资源
if(con!=null&&!con.isClosed()) {
con.close();
}
if(ps!=null) {
ps.close();
}
参考CSDN博主「LJT_1314520」的原创文章,原文链接:https://blog.csdn.net/LJT_1314520/article/details/125241649
2.3.3、 SQL注入问题
Statement是先在sql语句中传值再编译,存在sql注入问题
。
PreparedStatement是先对sql语句进行预编译,然后再传入变量值,预编译中的待传参数部分用?占位符代替。解决了sql注入
2.3.4、 连接池
原因:每次访问数据库都必须先创建连接,执行完毕以后关闭连接对象。连接对象需要不停的创建,不停的关
闭。
问题:
1)数据库创建连接通常需要消耗相对较多的资源,创建时间也较长,而每次操作都要重新获取新的
连接对象,执行一次操作就把连接关闭,这样数据库连接对象的使用率低。
2)假设网站一天10万访问量,数据库服务器就需要创建10万次连接,极大的浪费数据库的资源,并
且极易造成数据库服务器内存溢出。
连接对象 | 操作特点 |
---|---|
创建时 | 程序启动的时候,由应用程序在内存中创建好一定数量的连接对象,放在内存中,这个内存区域就称为连接池,也叫数据源DataSource 。 |
使用时 | 访问数据库的时候,直接从连接池中得到一个连接对象就可以了。 |
关闭时 | 不是真的关闭连接,而是将连接对象再放回到连接池中,给下一个用户使用。 |
作用:
1)提高获取连接对象的速度;
2)提高连接对象的使用率,每个连接对象可以重复使用。