Java后端进阶之路: JavaWeb(三)https://developer.aliyun.com/article/1469549
10.三层架构
什么是mvc: model view Controller 模型 视图,控制器
10.1早些年的架构
用户直接访问控制层,控制层就可以直接操作数据库
servlet -->CRUD-->数据库 弊端:程序十分臃肿,不利于维护 servlet的代码中,处理请求,响应,视图跳转,处理jdbc,处理业务代码,处理逻辑代码 架构:没有什么是加一层解决不了的 JDBC 程序员-->jdbc-->数据库
10.2、MVC三层架构
model
- 业务处理:业务逻辑(service)
- 数据持久层(CRUD)(DAO)
view
- 展示数据
- 提供链接发起Servlet请求(a,form.img..)
Controller(Serlet)
- 接收用户请求:(req:请求参数,session信息.....)
- 交给业务层处理对应代码
- 控制视图的跳转
登陆-->接受用户的登录请求--->处理用户请求(获取用户登陆的参数,username,password)---->交给业务层处理登陆业务,判断用户名密码是否正确:事务--->DAO查询用户名和密码是否正确--->数据库
11、Filter
Filter:过滤器,用来过滤网站的数据;
- 处理中文乱码
- 登陆验证
1.导包
不要搞错包
2实现Filter接口
package com.hyc.filter; import javax.servlet.*; import java.io.IOException; public class ChatcterEncodingFilter implements Filter { //初始化:web服务器启动的就以及初始化了,随时等待过去对象出现 public void init(FilterConfig filterConfig) throws ServletException { System.out.println("ChatcterEncodingFilter已经初始化"); } // 在过滤中的所有代码,在过滤特定请求的时候都会执行 // 必须要让过滤器找到同行 // chain.doFilter(request,response); public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { request.setCharacterEncoding("utf-8"); response.setCharacterEncoding("utf-8"); response.setContentType("text/html;charset=utf-8"); System.out.println("执行前"); chain.doFilter(request,response);//让请求继续往前走,如果不写的程序到这里停止 System.out.println("执行后"); } //销毁 public void destroy() { System.out.println("ChatcterEncodingFilter销毁"); } }
3在web.xml中配置Filter
<filter> <filter-name> ChatcterEncodingFilter</filter-name> <filter-class>com.hyc.filter.ChatcterEncodingFilter</filter-class> </filter> <filter-mapping> <filter-name>ChatcterEncodingFilter</filter-name> <url-pattern>/servlet/*</url-pattern> </filter-mapping>
12.监听器
实现一个监听器的接口;(有无数种)
- 编写一个监听器
package com.hyc.listner; import javax.servlet.ServletContext; import javax.servlet.http.HttpSessionEvent; import javax.servlet.http.HttpSessionListener; public class OnlineListner implements HttpSessionListener { //创建session监听,看你的一举一动 //一旦创建session就会触发一个事件 public void sessionCreated(HttpSessionEvent se) { System.out.println(se.getSession().getId()); ServletContext ctx = se.getSession().getServletContext(); Integer oc =(Integer) ctx.getAttribute("OnlineCount"); if (oc==null){ oc = new Integer(1); }else { int count = oc.intValue(); oc = new Integer(count+1); } ctx.setAttribute("OnlineCount",oc); } public void sessionDestroyed(HttpSessionEvent se) { ServletContext ctx = se.getSession().getServletContext(); Integer oc =(Integer) ctx.getAttribute("OnlineCount"); if (oc==null){ oc = new Integer(0); }else { int count = oc.intValue(); oc = new Integer(count-1); } ctx.setAttribute("OnlineCount",oc); } /* * 销毁session * 手动销毁 * 自动销毁 * */ }
- 在web.xml中注册监听器
<!-- 注册监听器--> <listener> <listener-class>com.hyc.listner.OnlineListner</listener-class> </listener>
3,看情况准备
13、过滤器和监听器的常见应用
监听器:GUI编程中经常使用;
package com.hyc.listner; import java.awt.*; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; public class Testpanel { public static void main(String[] args) { Frame frame = new Frame("中秋快乐");//一个窗体 Panel pa1 = new Panel(null);//面板 frame.setLayout(null); frame.setBounds(300,300,500,500); frame.setBackground(new Color(0,0,255)); pa1.setBounds(50,50,300,300); pa1.setBackground(new Color(255,0,0)); frame.add(pa1); frame.setVisible(true); //监听事件:关闭时间 frame.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { System.out.println("关闭"); System.exit(0); } } );
用户登陆之后才能进入首页,用户注销之后就不能进入主页了!
登记权限问题:什么权限进入什么页面
- 用户登陆后,向session中放入用户的数据
- 进入主页的时候判断用户是否已经登陆
HttpServletRequest request1 = (HttpServletRequest) request; HttpServletResponse response1 = (HttpServletResponse) response; Object user_session = request1.getSession().getAttribute(Coestext.USER_SESSION); if (user_session==null){ response1.sendRedirect("/jf/error.jsp"); } chain.doFilter(request,response);
14、jdbc(复习)
什么是JDBC: java连接数据库
新建数据库
添加数据库依赖
<dependencies> <dependency> <!-- mysql的驱动--> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> </dependencies>
idea连接数据库
jdbc固定六部曲,(固定步骤)
- 加载驱动
- 连接数据库,代表数据库
- 向数据库发送sql的接口,做增删改查(CRUD)
- 编写sql(根据业务,编写不同sql)
- 执行SQL
- 关闭连接
String url = "jdbc:mysql://localhost:3306/peojdbc?userUnicode=true&charcterEncoding=utf-8"; String username = "root"; String password = "root"; //1加载驱动 Class.forName("java.sql.Driver"); //2连接数据库,代表数据库 Connection connection = DriverManager.getConnection(url, username, password); Statement statement = connection.createStatement(); //3编写sql String sql = "select *\n" + "from users ;"; //4返回结果集 ResultSet rs = statement.executeQuery(sql); while (rs.next()){ System.out.println("id"+rs.getInt("id")); System.out.println("name"+rs.getString("name")); } //5先开后关 statement.close(); connection.close();
PreparedStatement安全接口的复习
package com.hyc.test; import java.sql.*; public class TestJdbc { public static void main(String[] args) throws ClassNotFoundException, SQLException { String url = "jdbc:mysql://localhost:3306/peojdbc?userUnicode=true&charcterEncoding=utf-8"; String username = "root"; String password = "root"; //1加载驱动 Class.forName("java.sql.Driver"); //2连接数据库,代表数据库 Connection connection = DriverManager.getConnection(url, username, password); String sql = "insert into users (id, name, password, email, birthday) values (?,?,?,?,?);"; PreparedStatement pst = connection.prepareStatement(sql); //3编写sql String sql1 = "select *\n" + "from users ;"; pst.setInt(1,4); pst.setString(2,"hjj"); pst.setString(3,"123456"); pst.setString(4,"一二三@qq.com"); pst.setDate(5,new Date(new java.util.Date().getTime())); //4返回结果集 int i = pst.executeUpdate(); if (i>=0){ System.out.println("添加成功"); } ResultSet rs = pst.executeQuery(sql1); while (rs.next()){ System.out.println("id"+rs.getInt("id")); System.out.println("name"+rs.getString("name")); } //5先开后关 pst.close(); connection.close(); } }
事务
要么都成功,要么都失败
ACID原则:保证数据安全
开始事务 事务提交 comit() 事务回滚 rollback() 关闭事务 经典例子 a:1000 b:1000 a(900)---》100---》b(1100)
junit单元测试
依赖
<dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter</artifactId> <version>RELEASE</version> <scope>compile</scope> </dependency>
测试成功是这样的
package com.hyc.test; import org.junit.jupiter.api.Test; public class testjunit { @Test() public void test(){ System.out.println("hello"); } }
测试失败是这样的
接下来用junit来测试数据库中的事务
package com.hyc.test; import org.junit.jupiter.api.Test; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class testjunit { @Test() public void test() { String url = "jdbc:mysql://localhost:3306/peojdbc?userUnicode=true&charcterEncoding=utf-8"; String username = "root"; String password = "root"; Connection connection = null; //1加载驱动 try { Class.forName("java.sql.Driver"); //2连接数据库,代表数据库 connection = DriverManager.getConnection(url, username, password); //3.通知数据库开启事务 connection.setAutoCommit(false); String sql = "update accout\n" + "set moeny = moeny-100\n" + "where name =\"a\";"; connection.prepareStatement(sql).executeUpdate(); // int i = 1/0; String sql2 = "update accout\n" + "set moeny = moeny+100\n" + "where name =\"b\";"; connection.prepareStatement(sql2).executeUpdate(); connection.commit();//以上sql全执行成功了就提交事务 System.out.println("success"); } catch (Exception e) { try { connection.rollback(); } catch (SQLException throwables) { throwables.printStackTrace(); }finally { try { connection.close(); } catch (SQLException throwables) { throwables.printStackTrace(); } } } } }
smbms项目
数据库:
项目如何搭建:
考虑使用不使用maven?依赖?jar包
项目搭建准备工作
- 搭建maven项目
- 配置tomcat
- 测试项目是否能够跑起来
- 导入项目会遇到的依赖jar包
- jsp.Servlet.mysql,jsp
- 创建项目结构
- 编写实体类
- orm: 表映射
- 编写基础公共类
- 数据库配置文件
driver=com.mysql.jdbc.Driver url =jdbc:mysql://localhost:3306?useUnicode=true&charcterEncoding=utf-8 username = root password = root
- 编写数据库公共类
package com.hyc.dao; import java.io.InputStream; import java.sql.*; import java.util.Properties; //操作数据库的公共类 public class BaseDao { private static String driver; private static String url; private static String username; private static String password; //静态代码块,类加载的时候就初始化了 static { Properties properties = new Properties(); InputStream is = BaseDao.class.getClassLoader().getResourceAsStream("db.Properties"); try { properties.load(is); }catch (Exception e){ e.printStackTrace(); } driver = properties.getProperty("driver"); url = properties.getProperty("url"); password = properties.getProperty(" password"); username = properties.getProperty("username"); } public static Connection getconnection(){ Connection conn = null; try { Class.forName(driver); conn = DriverManager.getConnection(url,username,password); } catch (Exception e) { e.printStackTrace(); } return conn; } //查询公共类 //预编译的sql我们直接执行就好了 public static ResultSet execute(Connection conn,String sql,Object[] params,PreparedStatement pst,ResultSet rs){ try { pst = conn.prepareStatement(sql); for (int i = 0; i < params.length ; i++) { //pst.setObject占位符是从1开始的,但是我们的数组是从0开始的 pst.setObject(i+1,params[i]); } rs = pst.executeQuery(); } catch (SQLException throwables) { throwables.printStackTrace(); } return rs; } //增删改公共方法 public static int execute(Connection conn,String sql,Object[] params,PreparedStatement pst){ int UpdateRows = 0; try { pst = conn.prepareStatement(sql); for (int i = 0; i < params.length ; i++) { //pst.setObject占位符是从1开始的,但是我们的数组是从0开始的 pst.setObject(i+1,params[i]); } UpdateRows = pst.executeUpdate(); } catch (SQLException throwables) { throwables.printStackTrace(); } return UpdateRows; } public static boolean closeResoure(Connection conn,PreparedStatement pst ,ResultSet rs) { boolean flag = true; if (conn!=null){ try { conn.close(); //GC回收 conn=null; } catch (SQLException throwables) { throwables.printStackTrace(); flag = false; } } if (pst!=null){ try { pst.close(); //GC回收 pst=null; } catch (SQLException throwables) { throwables.printStackTrace(); flag = false; } } if (rs!=null){ try { rs.close(); //GC回收 rs=null; } catch (SQLException throwables) { throwables.printStackTrace(); flag = false; } } return flag; } }
- 编写过滤器
- 导入静态资源
登陆功能实现
- 编写前端也眯娜
- 设置欢迎页面
<welcome-file-list> <welcome-file>login.jsp</welcome-file> </welcome-file-lit>
- 编写dao层得到用户登陆的接口
public user getLoginUser(Connection conn, String usercode);
- 编写dao得到用户登陆的接口实现
package com.hyc.dao.user; import java.sql.*; import com.hyc.dao.BaseDao; import com.hyc.pojo.user; import java.sql.Connection; public class UserDaoImpl implements UserDao { @Override public user getLoginUser(Connection conn, String usercode) { PreparedStatement pst = null; ResultSet rs = null; user user = null; if (conn != null) { String sql = "select * from smbms_user where userCode=?"; Object[] params = {usercode}; try { rs = BaseDao.execute(conn,sql,params,pst,rs); if (rs.next()){ user = new user(); user.setId(rs.getInt("id")); user.setUserCode(rs.getString("userCode")); user.setUserName(rs.getString("userName")); user.setUserPassword(rs.getString("userPassword")); user.setGender(rs.getInt("gender")); user.setBirthday(rs.getDate("birthday")); user.setPhone(rs.getString("phone")); user.setAddress(rs.getString("address")); user.setUserRole(rs.getInt("userRole")); user.setCreatedBy(rs.getInt("createdBy")); user.setCreationDate(rs.getTimestamp("creationDate")); user.setModifyBy(rs.getInt("modifyBy")); user.setModifyDate(rs.getTimestamp("modifyDate")); } BaseDao.closeResoure(null,pst,rs); }catch (Exception e){ } } return user; } }
- 编写业务层
public interface UserService { public user login(String userCode,String password); }
- 业务层实现类
public user login(String userCode, String password) { Connection conn = null; user user = null; try{ conn = BaseDao.getconnection(); user = userDao.getLoginUser(conn,userCode); }catch (Exception e){ e.printStackTrace(); }finally { BaseDao.closeResoure(conn,null,null); } return user; }
- servlet编写
package com.hyc.servlet.user; import com.hyc.pojo.user; import com.hyc.service.user.UserService; import com.hyc.service.user.UserServiceImpl; import com.hyc.util.Constaets; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; public class LoginSerlvet extends HttpServlet { //Servlet:控制层调用业务层代码 @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("进入servlet"); //获取用户名和密码 String uc = req.getParameter("userCode"); String upwd = req.getParameter("userPassword"); UserServiceImpl userService = new UserServiceImpl(); user user = userService.login(uc,upwd); if (user!=null){ System.out.println("查有此人"); req.getSession().setAttribute(Constaets.USER_SEESION,user); //跳转到内部主页 resp.sendRedirect("/smbms/jsp/frame.jsp"); }else { //查无此人转发会登陆页面 req.setAttribute("error","用户名或者密码不正确"); req.getRequestDispatcher("login.jsp").forward(req,resp); } } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } }
- 注册servlet
<!-- servlet--> <servlet> <servlet-name>login</servlet-name> <servlet-class>com.hyc.servlet.user.LoginSerlvet</servlet-class> </servlet> <servlet-mapping> <servlet-name>login</servlet-name> <url-pattern>/login</url-pattern> </servlet-mapping>
9.优化登陆选项,拦截登陆优化
编写过滤器
测试,登陆,注销,权限都要ok
密码修改
- 导入素材
- 写项目,从底层往上面写
- 编写userdao
//修改密码 @Override public int updateuser(Connection conn, int id, String password) { String sql = "update smbms_user\n" + "set userPassword=?\n" + "where id=?;"; int execute = 0; if (conn!=null){ PreparedStatement pst = null; Object parms[] = {password,id}; execute =BaseDao.execute(conn, sql, parms, pst); BaseDao.closeResoure(null,pst,null); } return execute; }
- 在服务层接口新增修改密码方法
public boolean updateuser( int id, String password);
- 编写service层别忘记注册
public boolean updateuser(int id, int password) { boolean flag = false; Connection conn = null; try { conn= BaseDao.getconnection(); //修改密码 if(userDao.updateuser(conn,password,id)>0){ System.out.println("修改成功"); flag = true; } }catch (Exception e){ } return flag; }
- 实现复用需要提取方法
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String method = req.getParameter("method"); if (method.equals("savepwd")&&method!=null){ this.updatepwd(req,resp); } }
- 测试功能
优化密码注册密码修改使用ajax
- 阿里巴巴的fastjson;
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.76</version> </dependency>
2.后台代码修改
//验证旧密码,session中有用户的密码 public void pwdmodify(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException{ Object o = req.getSession().getAttribute(Constaets.USER_SEESION); String oldpassword = req.getParameter("oldpassword"); //万能的map:结果集 HashMap<String, String>resultMap = new HashMap<String, String>(); if (o==null){//session失效了,过期了 resultMap.put("result","sessionerror"); }else if(oldpassword!=null){ resultMap.put("result","error"); }else { String userPassword = ((user)o).getUserPassword(); if (oldpassword.equals(userPassword)){ resultMap.put("result","true"); }else { resultMap.put("result","false"); } } try{ resp.setContentType("application/json"); PrintWriter writer = resp.getWriter(); //JSONArray 阿里巴巴的json工具类,转换格式 writer.write(JSONArray.toJSONString(resultMap)); writer.close(); }catch (Exception e){ System.out.println(e.getMessage()); } }
实现底层用户管理分页
- 导入分页的工具类
- 用户列表页面导入
- 引入页面
1.获取用户数量
- userdao
public int getUserCount(Connection conn ,String username,int userrole);
- userDaoimpl
//根据用户名或者角色名查询用户总数[核心sql]项目内最难得sql @Override public int getUserCount(Connection conn, String username, int userrole) { PreparedStatement pst = null; ResultSet rs = null; int count = 0; if (conn!=null){ StringBuffer sql = new StringBuffer(); sql.append("select count(1) as count from smbms_user u,smbms_role r where u.userRole = r.id"); ArrayList<Object> list = new ArrayList<>();//存放参数 if (!StringUtils.isNullOrEmpty(username)){ sql.append(" and u.userName like ?"); list.add("%"+username+"%");//list默认下标0 } if (userrole>0){ sql.append(" and u.userRole like ?"); list.add(userrole);//list默认下标0 } //怎么把list转换为数组 Object[] params = list.toArray(); System.out.println("userdaoimpl"+sql.toString());//输出最后完整的sql语句; rs = BaseDao.execute(conn,sql.toString(),params,pst,rs); try { if (rs.next()){ count = rs.getInt("count");//从结果集获取数量 } } catch (SQLException throwables) { throwables.printStackTrace(); }finally { BaseDao.closeResoure(conn,pst,rs); } } return count; }
- UserService
public int getUserCount(String username,int userRole);
- UserServiceimpl
@Override public int getUserCount(String username, int userRole) { Connection conn = null; int count = 0; conn = BaseDao.getconnection(); count = userDao.getUserCount(conn,username,userRole); BaseDao.closeResoure(conn,null,null); return count; }
- 第二条线,分页查询用户
Userdao 确认sql:
select u.*,r.roleName as userRoleName from smbms_user u ,smbms_role r where u.userRole = r.id and u.userName like ? and u.userRole = ? order by u.createdBy DESC limit ?,?;
- 编写分页查询用户实现类
public List<user> getUserList(Connection conn, String userName, int userRole, int CurrentPagNo, int pageSize) { PreparedStatement pst = null; ResultSet rs = null; List<user> userList = new ArrayList<>(); List<Object> List = new ArrayList<>(); if (conn!=null){ StringBuffer sql = new StringBuffer(); sql.append("SELECT u.*,r.roleName AS userRoleName FROM smbms_user u ,smbms_role r WHERE u.userRole = r.id"); if (!StringUtils.isNullOrEmpty(userName)){ sql.append(" and u.userName like ?"); List.add("%"+userName+"%"); } if (userRole>0){ sql.append(" and u.userRole = ?"); List.add(userRole); } sql.append(" order by u.createdBy DESC limit ?,?"); CurrentPagNo = (CurrentPagNo-1)*pageSize; List.add(CurrentPagNo); List.add(pageSize); Object[] params = List.toArray(); System.out.println("sql-->"+sql.toString()); rs = BaseDao.execute(conn,sql.toString(),params,pst,rs); try { if (rs.next()){ user _user = new user(); _user.setId(rs.getInt("id")); _user.setId(rs.getInt("id")); _user.setUserCode(rs.getString("userCode")); _user.setUserName(rs.getString("userName")); _user.setGender(rs.getInt("gender")); _user.setBirthday(rs.getDate("birthday")); _user.setPhone(rs.getString("phone")); _user.setUserRole(rs.getInt("userRole")); _user.setUserRoleName(rs.getString("userRoleName")); userList.add(_user); } } catch (SQLException throwables) { throwables.printStackTrace(); } return userList; }
- 编写服务层
public List<user> getUserList(String queryUserName,int queryUserRole,int currentPagNo,int PageSize);
@Override public List<user> getUserList( String queryUserName,int queryUserRole, int currentPagNo, int PageSize) { Connection conn = null; List<user> userList = null; conn = BaseDao.getconnection(); userList = userDao.getUserList(conn,queryUserName,queryUserRole,currentPagNo,PageSize); return userList; }
2 .显示用户serlvet
- 获取用户的前端数据(查询)
- 判断请求是否需要执行,看参数的值来判断
- 为了实现分页,需要计算出当前页面和总页面的参数
- 用户列表展示
- 返回前端
15.文件上传原理
文件上传原理就是操作io流
一共就那麽几个步骤
1.判断文件是不是文件表单
2.创建缓冲区以防文件过大
处理上传文件,一般需要通过流来获取,我们可以使用req的流,原生态的文件上传流获取,十分麻烦
但是我们都建议使用apache的文件上传组件来实现,common-fileupload,他需要以来与commons-io组件
通过这个工厂设置一个缓冲区,当上传文件大于缓冲区的时候,将他放到临时文件中
3.处理上传文件把前端请求解析成一个fileitems集合对象
可以使用UUID(唯一识别通用的通用码),保证文件名唯一
随意生成一个唯一的识别通用码
网络传输中的东西,都需要序列化
pojo 实体类 如果想要通过网络在多个电脑上运行,传输===>需要把对象都序列化
没有方法的接口叫做标记接口 jvm--> Java栈, 本地方法栈 :native--> c++
jni = java Native Interface
4.确认存放文件存放路径
5文件传输和返回前端成功与否的信息
idea插件