MVC三层架构
三层架构也就是:实体层,视图层,控制层
实体层Model:包括业务service层,crud操作数据库的dao层,实体类pojo/entity,以及jdbc跟数据库进行连接。
视图层view:包括jsp,html,vue等界面,展示数据模型,供用户操作。
控制层controller:接收用户请求,响应用户请求,视图跳转(转发和重定向)。
Filter(过滤器)
过滤器的应用:对请求或响应信息进行过滤的的通用方法
1.解决汉字乱码问题
2.登陆验证
使用过滤器的步骤:
1.导入jar包
<dependencies>
<!--servlet依赖-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<!--jsp依赖-->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
</dependency>
<!--jstl表达式依赖-->
<dependency>
<groupId>javax.servlet.jsp.jstl</groupId>
<artifactId>jstl-api</artifactId>
<version>1.2</version>
</dependency>
<!--standard标签库依赖-->
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
<!--数据库连接-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
2.实现javax.servlet的Filter,重写初始化,执行过滤内容,销毁的方法
初始化方法:服务器启动时,执行初始化方法,即过滤器启动
执行过滤内容:filterChain.doFilter是过滤器的关键,如果没有这句话,执行代码会停留在Filter内,不会进入servlet内。
销毁:服务器关闭后,执行Filter销毁的方法。
public class ChracterEncodingFilter implements Filter {
//ChracterEncodingFilter初始化,tomcat服务器启动,Filter就初始化了
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("character初始化");
}
//filterChain过滤器链
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
servletRequest.setCharacterEncoding("utf-8");
servletResponse.setCharacterEncoding("utf-8");
servletResponse.setContentType("text/html;charset=utf-8");
//filterChain.doFilter是为了将servletRequest,servletResponse传递下去,没有这个,执行代码会停留在过滤器内,无法进入servlet
System.out.println("character传递前");
filterChain.doFilter(servletRequest,servletResponse);
System.out.println("character传递后");
}
//CharacterEncodingFilter销毁,tomcat服务器关闭,过滤器才会销毁
public void destroy() {
System.out.println("character销毁");
}
}
3.配置web.xml
如果servlet的的前缀和Filter过滤器的的前缀相同,则该地址下的servlet在执行前,会先执行Filter内容。
<filter>
<filter-name>adminFilter</filter-name>
<filter-class>com.wp.filter.AdminFilter</filter-class>
</filter>
<filter>
<filter-name>chracterEncodingFilter</filter-name>
<filter-class>com.wp.filter.ChracterEncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>adminFilter</filter-name>
<url-pattern>/servlet/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>chracterEncodingFilter</filter-name>
<url-pattern>/servlet/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>showServlet</servlet-name>
<servlet-class>com.wp.servlet.ShowServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>servletDemo01</servlet-name>
<servlet-class>com.wp.servlet.ServletDemo01</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>showServlet</servlet-name>
<url-pattern>/servlet/show</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>servletDemo01</servlet-name>
<url-pattern>/servlet/demo01</url-pattern>
</servlet-mapping>
filter实现权限拦截:存在登陆页面和登陆成功的界面,还有一个登陆失败错误界面。达到的效果,登陆成功,进入主界面;点击主界面的注销,进入登陆界面,在未成功登陆的情况下,直接进入主界面,无法进入,会进入错误界面。
添加登陆界面,错误界面,系统主界面
登陆界面:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="/servlet/login" method="post">
<input type="text" name="username">
<input type="submit">
</form>
</body>
</html>
登陆成功主界面:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%--<%
Object user_session = request.getSession().getAttribute("USER_SESSION");
if(user_session==null){
response.sendRedirect("/login.jsp");
}
%>--%>
<h1>主页</h1>
<h3>登陆成功</h3>
<a href="/servlet/loginout">注销</a>
<a href="user.jsp" name="">user权限界面</a>
<a href="user.jsp">user权限界面</a>
</body>
</html>
登陆失败错误界面:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>登陆错误</h1>
<a href="login.jsp">返回登陆页面</a>
</body>
</html>
登陆servlet代码:
String username = req.getParameter("username");
if (username.equals("admin")){
resp.sendRedirect("/sys/main.jsp");
req.getSession().setAttribute(Constant.USER_SESSION,req.getSession().getId());
}
else{
resp.sendRedirect("/error.jsp");
}
注销servlet代码:
req.getSession().removeAttribute(Constant.USER_SESSION);
resp.sendRedirect("/login.jsp");
过滤器代码:
System.out.println("进入admin.doFilter");
HttpServletRequest servletRequest1 = (HttpServletRequest) servletRequest;
HttpServletResponse servletResponse1 = (HttpServletResponse) servletResponse;
if(servletRequest1.getSession().getAttribute(Constant.USER_SESSION)==null){
servletResponse1.sendRedirect("/login.jsp");
}
/*String name = (String) servletRequest.getAttribute("name");
if (name.equals("wp")){
filterChain.doFilter(servletRequest,servletResponse);
}*/
filterChain.doFilter(servletRequest,servletResponse);
web.xml配置文件:
<filter>
<filter-name>adminFilter</filter-name>
<filter-class>com.wp.filter.AdminFilter</filter-class>
</filter>
<filter>
<filter-name>chracterEncodingFilter</filter-name>
<filter-class>com.wp.filter.ChracterEncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>adminFilter</filter-name>
<url-pattern>/sys/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>chracterEncodingFilter</filter-name>
<url-pattern>/servlet1/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>showServlet</servlet-name>
<servlet-class>com.wp.servlet.ShowServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>servletDemo01</servlet-name>
<servlet-class>com.wp.servlet.ServletDemo01</servlet-class>
</servlet>
<servlet>
<servlet-name>loginServlet</servlet-name>
<servlet-class>com.wp.servlet.LoginServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>loginoutServlet</servlet-name>
<servlet-class>com.wp.servlet.LoginoutServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>showServlet</servlet-name>
<url-pattern>/servlet/show</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>servletDemo01</servlet-name>
<url-pattern>/servlet/demo01</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>loginServlet</servlet-name>
<url-pattern>/servlet/login</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>loginoutServlet</servlet-name>
<url-pattern>/servlet/loginout</url-pattern>
</servlet-mapping>
<listener>
<listener-class>com.wp.listener.OnlineCountListener</listener-class>
</listener>
<session-config>
<session-timeout>1</session-timeout>
</session-config>
可以将用户user实体类设置一个等级level的属性,用户登陆时,将用户信息存储到session中,然后在过滤器中,获取session中的用户等级信息,根据等级判断,是否有权限,有权限,就放行,无权限就返回信息。
Listener(监听器)
监听器的应用:监听各种触发事件,比如session的创建,session的销毁。
实现session监听,在启动服务器后,浏览器进行访问时,创建session;session销毁有两种方式,一种是手动销毁,一种自动销毁。
session手动销毁:
httpSessionEvent.getSession().invalidate();//手动销毁session
session自动销毁:在web.xml中配置,30分钟后销毁
<session-config>
<session-timeout>30</session-timeout>
</session-config>
//统计网站在线人数,一个用户对应一个session
public class OnlineCountListener implements HttpSessionListener {
//创建session时触发此方法
public void sessionCreated(HttpSessionEvent httpSessionEvent) {
System.out.println("进入session监听器");
System.out.println(httpSessionEvent.getSession().getId());
ServletContext servletContext = httpSessionEvent.getSession().getServletContext();
Integer onlineCount = (Integer)(servletContext.getAttribute("onlineCount"));
if (onlineCount==null){
onlineCount=new Integer(1);
}
else{
/*int count=onlineCount.intValue();
onlineCount=new Integer(count+1);*/
onlineCount+=1;
}
servletContext.setAttribute("onlineCount",onlineCount);
}
//销毁session时触发此方法
public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
System.out.println("销毁session监听器");
ServletContext servletContext = httpSessionEvent.getSession().getServletContext();
Integer onlineCount = (Integer)(servletContext.getAttribute("onlineCount"));
if (onlineCount==null){
onlineCount=new Integer(0);
}
else{
/* int count=onlineCount.intValue();
onlineCount=new Integer(count-1);*/
onlineCount-=1;
}
servletContext.setAttribute("onlineCount",onlineCount);
}
}
注意:在启动服务器,浏览器访问后,可能会出现在线人数大于1,按道理应该只有一个在线,也就是一个session,这种情况是tomcat服务器的原因,启动浏览器时,tomcat去连接,就会创建session,有可能tomcat连接失败,这时tomcat会继续连接,知道连接成功,这样服务器自己创建了多个session,这时ReDeploy重新部署下程序,就可以解决这个问题。
监听器在GUI桌面中的应用:程序运行后,可以弹出一个桌面端程序
public class TestGUI {
public static void main(String[] args) {
Frame frame = new Frame("端午节快乐!");
Panel panel=new Panel(null);
frame.setLayout(null);
frame.setBounds(300,300,500,500);
frame.setBackground(new Color(0,0,255));
panel.setBounds(50,50,400,400);
panel.setBackground(new Color(0,255,0));
frame.add(panel);
frame.setVisible(true);
//给窗口增加监听事件,一次性监听窗口的多个事件
frame.addWindowListener(new WindowListener() {
public void windowOpened(WindowEvent windowEvent) {
System.out.println("窗口打开后");
}
public void windowClosing(WindowEvent windowEvent) {
System.out.println("窗口关闭进行时");
System.exit(0);//窗口正常关闭
System.exit(1);//窗口异常关闭
}
public void windowClosed(WindowEvent windowEvent) {
System.out.println("窗口关闭后");
}
public void windowIconified(WindowEvent windowEvent) {
}
public void windowDeiconified(WindowEvent windowEvent) {
}
public void windowActivated(WindowEvent windowEvent) {
System.out.println("窗口激活,也就是在最顶层");
}
public void windowDeactivated(WindowEvent windowEvent) {
System.out.println("窗口未激活,也就是不在最顶层");
}
});
//给窗口增加监听事件,选择性的给窗口增加监听事件
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.out.println("窗口关闭ing");
System.exit(0);
}
});
}
}
jdbc:java database connect
为了增加程序的迁移性,加一层统一驱动,然后使用各个数据库驱动。没有什么是加一层结构解决不了的。
第一步:导包
java.sql
javax.sql
mysql.connecter.java
最重要的是导mysql.connector.java包
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
使用池化技术dpcp.c3p0操作数据库优点,减少网络开销,提升数据库性能
使用dbcp池化技术,需导入包如下:
<!-- 数据库连接池commons-dbcp -->
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-pool/commons-pool -->
<dependency>
<groupId>commons-pool</groupId>
<artifactId>commons-pool</artifactId>
<version>1.6</version>
</dependency>
使用c3p0池化技术,需要导入的包如下:
<!-- 数据库连接池c3p0 -->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.mchange/mchange-commons-java -->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>mchange-commons-java</artifactId>
<version>0.2.19</version>
</dependency>
第二步:创建数据库表,配置dbcp的配置文件如下:
# 连接设置
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=utf8&useSSL=false
username=root
password=Mysql@3306
# 初始化连接数
initialSize=10
# 最大连接数
maxActive=50
# 最大空闲连接数
maxIdle=20
# 最小空闲连接数
minIdle=5
# 超时等待时间,以毫秒为单位
maxWait=6000
#JDBC驱动建立连接时附带的连接属性
connectionProperties=useUnicode=true;characterEncoding=UTF8
# 自动提交
defaultAutoCommit=true
#连接池创建的连接为只读状态,注意没有设置值的情况下,则不会调用“setReadOnly”方法
defaultReadOnly=
#连接池创建的连接的事务级别 transactionIsolation的值范围:NONE,READ_UNCOMMITTED,READ_COMMITTED,REPEATABLE_READ,SERIALIZABLE
defaultTransactionIsolation=READ_UNCOMMITTED
C3P0的配置文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<!-- c3p0的缺省(默认)配置,如果没有指定(ComboPooledDataSource的无参构造)则使用这个配置 -->
<default-config>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/shop?useUnicode=true&characterEncoding=utf8&useSSL=false</property>
<property name="user">root</property>
<property name="password">Mysql@3306</property>
<!-- 如果池中数据连接不够时一次增长多少个 -->
<property name="acquireIncrement">5</property>
<!-- 初始化数据库连接池时连接的数量 -->
<property name="initialPoolSize">10</property>
<!-- 数据库连接池中的最大的数据库连接数 -->
<property name="maxPoolSize">20</property>
<!-- 数据库连接池中的最小的数据库连接数 -->
<property name="minPoolSize">5</property>
</default-config>
<!-- 命名的配置,(ComboPooledDataSource的有参构造,且构造函数的参数为配置的配置的命名)可以通过方法调用实现 -->
<named-config name="Mysql">
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/shop?useUnicode=true&characterEncoding=utf8&useSSL=false</property>
<property name="user">root</property>
<property name="password">Mysql@3306</property>
<!-- 如果池中数据连接不够时一次增长多少个 -->
<property name="acquireIncrement">5</property>
<!-- 初始化数据库连接池时连接的数量 -->
<property name="initialPoolSize">10</property>
<!-- 数据库连接池中的最大的数据库连接数 -->
<property name="maxPoolSize">20</property>
<!-- 数据库连接池中的最小的数据库连接数 -->
<property name="minPoolSize">5</property>
</named-config>
</c3p0-config>
将配置文件放到资源文件目录下
第三步:引用dbcp包资源,编写数据库连接工具类
dbcp数据库连接工具类:
package com.wp.util;
import org.apache.commons.dbcp.BasicDataSourceFactory;
import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
/**
* @author wp
* @PackageName:com.wp.util
* @ClassName:JdbcdbcpUtil
* @Description:
* @date:2021/6/25 15:49
*/
public class JdbcdbcpUtil {
private static DataSource dt=null;
static {
try {
InputStream inputStream= JdbcdbcpUtil.class.getClassLoader().getResourceAsStream("dpcp.properties");
Properties properties=new Properties();
properties.load(inputStream);
dt= BasicDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
e.printStackTrace();
}
}
public static Connection getConnection() throws SQLException {
return dt.getConnection();
}
public static void release(Connection con, Statement st, ResultSet rs){
if(rs!=null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(st!=null){
try {
st.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(con!=null){
try {
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
c3p0数据库连接工具类:
package com.wp.util;
import javax.sql.DataSource;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
/**
* @author wp
* @PackageName:com.wp.util
* @ClassName:JdbcUtil
* @Description:
* @date:2021/6/16 13:23
*/
public class Jdbcc3p0Util {
private static DataSource dt=null;
static {
try {
dt=new ComboPooledDataSource();
} catch (Exception e) {
e.printStackTrace();
}
}
public static Connection getConnection() throws SQLException {
return dt.getConnection();
}
public static void release(Connection con, Statement st, ResultSet rs){
if(rs!=null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(st!=null){
try {
st.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(con!=null){
try {
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
第四步:测试操作数据库
dbcp:
public static void dpcbJdbcTest(){
Connection con=null;
PreparedStatement st=null;
try {
con= JdbcdbcpUtil.getConnection();
//String sql="UPDATE ACCOUNT SET money=money-50 WHERE NAME='A'";
String sql="UPDATE users SET password=123 WHERE NAME='曹操'";
st=con.prepareStatement(sql);
if(st.executeUpdate()>0){
System.out.println("成功");
}
} catch (SQLException e) {
e.printStackTrace();
}
finally {
JdbcdbcpUtil.release(con,st,null);
}
}
c3p0:
public static void c3p0JdbcTest(){
Connection con=null;
PreparedStatement st=null;
try {
con= Jdbcc3p0Util.getConnection();
String sql="UPDATE ACCOUNT SET money=money-50 WHERE NAME='A'";
//String sql="UPDATE users SET password=1 WHERE NAME='曹操'";
st=con.prepareStatement(sql);
if(st.executeUpdate()>0){
System.out.println("成功");
}
} catch (SQLException e) {
e.printStackTrace();
}
finally {
JdbcdbcpUtil.release(con,st,null);
}
}
注意:之前测试dbcp操作数据库时,通过调试,发现无法读取dbcp的配置文件,后来发现是因为配置文件路径问题,配置文件必须放在资源文件目录下。测试C3P0时,多次无法操作数据库,后来莫名其妙的又可以了。
数据库事务:要么都成功,要么都失败。
步骤:开启事务==>事务提交==>事务回滚==>关闭事务
在执行完数据库操作时,提交事务,在异常情况代码里,使用事务回滚,最后关闭事务。
典型例子:转账。
A向B转账100,正常逻辑A减少100,B增加100。为了避免意外情况下A账户减少100,但是B账户没有增加100,这时就要用到事务。
参考案例链接
事务案例代码:
public void test() {
Connection con= null;
try {
con = Jdbcc3p0Util.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
//开启事务
if (con != null) {
try {
con.setAutoCommit(false);
String sqlA="update account set money=money-50 where name='A'";
String sqlB="update account set money=money+50 where name='B'";
PreparedStatement stA=con.prepareStatement(sqlA);
PreparedStatement stB=con.prepareStatement(sqlB);
if(stA.executeUpdate()>0 && stB.executeUpdate()>0){
System.out.println(1/0);
con.commit();
}
else{
con.rollback();
}
} catch (SQLException e) {
try {
con.rollback();
} catch (SQLException ex) {
ex.printStackTrace();
}
e.printStackTrace();
}
}
System.out.println("A");
}
测试代码使用注解:目的是可以代替public static void main(string[] args)方法,直接可以测试,在pom文件里配置junit
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
@Test
public void test2(){
System.out.println("test2");
}