> 文章列表 > Java阶段二Day07

Java阶段二Day07

Java阶段二Day07

Java阶段二Day07

文章目录

  • Java阶段二Day07
    • V17
      • UserController
      • DispatcherServlet
      • Controller
      • RequestMapping
    • V18
      • DispatcherServlet
      • HandleMapping
    • V19
      • BirdBootApplication
    • 线程
      • 线程的执行过程
      • 线程池API
    • 数据库
      • 数据库的基本概念
      • 数据库管理系统中常见的概念
    • SQL分类
    • DDL语言-数据定义语言
      • 对数据库的操作
      • 对数据表的操作

V17

利用反射机制重构DispatcherServlet,使得将来添加新的业务时DispatcherServlet不必再添加分支判断(不进行修改)

实现:

  1. 新建包com.webserver.annotation
  2. annotation包下添加两个注解
    • @Controller:用于标注哪些类是处理业务的Controller
    • @RequestMapping:用于标注处理某个业务请求的业务方法
  3. com.webserver.controller包下的所有Controller类添加注解@Controller并将里面用于处理某个业务的方法标注@RequestMapping并指定该方法处理的请求
  4. DispatcherServlet在处理请求时,先扫描controller包下的所有Controller类,并找到处理该请求的业务方法,使用反射调用

UserController

package com.birdboot.controller;import com.birdboot.annotations.Controller;
import com.birdboot.annotations.RequestMapping;
import com.birdboot.entity.User;
import com.birdboot.http.HttpServletRequest;
import com.birdboot.http.HttpServletResponse;import java.io.*;/*** 处理与用户相关的业务*/
@Controller
public class UserController {private static File userDir;//表示存放所有用户信息的目录:usersstatic {userDir = new File("./users");if(!userDir.exists()){userDir.mkdirs();}}@RequestMapping("/regUser")public void reg(HttpServletRequest request, HttpServletResponse response){System.out.println("开始处理用户注册");String username = request.getParameter("username");String password = request.getParameter("password");String nickname = request.getParameter("nickname");String ageStr = request.getParameter("age");System.out.println(username+","+password+","+nickname+","+ageStr);//必要的验证if(username==null||username.isEmpty()||password==null||password.isEmpty()||nickname==null||nickname.isEmpty()||ageStr==null||ageStr.isEmpty()||!ageStr.matches("[0-9]+")){response.sendRedirect("/reg_info_error.html");return;}int age = Integer.parseInt(ageStr);//将年龄转换为int值//2User user = new User(username,password,nickname,age);/*File的构造器File(File parent,String child)创建一个File对象表示child这个子项,而它是在parent这个File对象所表示的目录中*/File file = new File(userDir,username+".obj");if(file.exists()){//注册前发现以该用户名命名的obj文件已经存在,说明是重复用户response.sendRedirect("/have_user.html");return;}try (FileOutputStream fos = new FileOutputStream(file);ObjectOutputStream oos = new ObjectOutputStream(fos);){oos.writeObject(user);//保存用户信息完毕//3给用户回馈注册成功页面//要求浏览器重新访问下述地址对应的页面response.sendRedirect("/reg_success.html");} catch (IOException e) {e.printStackTrace();}}@RequestMapping("/loginUser")public void login(HttpServletRequest request,HttpServletResponse response){System.out.println("开始处理登录!!!!");//1获取登录信息String username = request.getParameter("username");String password = request.getParameter("password");//必要验证if(username==null||username.isEmpty()||password==null||password.isEmpty()){response.sendRedirect("/login_info_error.html");return;}//根据该登录用户的名字去定位users下该用户的注册信息File file = new File(userDir,username+".obj");//判断该文件是否存在,不存在则说明该用户没有注册过if(file.exists()){//将该用户曾经的注册信息读取出来用于比较密码try (FileInputStream fis = new FileInputStream(file);ObjectInputStream ois = new ObjectInputStream(fis);){User user = (User)ois.readObject();//读取注册信息//比较本次登录的密码是否与注册时该用户输入的密码一致if(user.getPassword().equals(password)){//密码一致则登录成功response.sendRedirect("/login_success.html");return;}} catch (IOException | ClassNotFoundException e) {e.printStackTrace();}}//如果程序可以执行到这里,则说明要么是用户名没输入对,要么是密码没有输入对.都属于登录失败response.sendRedirect("/login_fail.html");}
}

DispatcherServlet

package com.birdboot.core;import com.birdboot.annotations.Controller;
import com.birdboot.annotations.RequestMapping;
import com.birdboot.controller.UserController;
import com.birdboot.http.HttpServletRequest;
import com.birdboot.http.HttpServletResponse;import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URISyntaxException;/*** V8新增内容:* 该类是SpringMVC框架与Tomcat整合时的一个关键类* Tomcat处理业务原生的都是调用继承了HttpServlet的类来完成,此时需要进行很多配置* 以及使用时要作很多重复性劳动。* SpringMVC框架提供的该类也是继承了HttpServlet的,使用它来接收处理请求的工作。*/
public class DispatcherServlet {private static File baseDir;//类加载路径private static File staticDir;//类加载路径下的static目录static {try {//定位当前项目的类加载路径baseDir = new File( DispatcherServlet.class.getClassLoader().getResource(".").toURI());//定位类加载路径下的static目录staticDir = new File(baseDir, "static");} catch (URISyntaxException e) {e.printStackTrace();}}/*** service方法实际上是当我们继承了HttpServlet后必须重写的方法* 该方法要求接收两个参数:请求对象与响应对象。* Tomcat在处理请求时就是调用某个Servlet的service方法并将请求与响应对象传入* 来让其完成处理工作的。*/public void service(HttpServletRequest request, HttpServletResponse response) {//获取请求的抽象路径//不能在使用uri判断请求了,因为uri可能含参数,内容不固定。String path = request.getRequestURI();//path:"/regUser"System.out.println(path);//判断该请求是否为请求一个业务/*V17新增内容:当我们得到本次请求路径path的值后,我们首先要查看是否为请求业务:1:扫描controller包下的所有类2:查看哪些被注解@Controller标注的过的类(只有被该注解标注的类才认可为业务处理类)3:遍历这些类,并获取他们的所有方法,并查看哪些时业务方法只有被注解@RequestMapping标注的方法才是业务方法4:遍历业务方法时比对该方法上@RequestMapping中传递的参数值是否与本次请求路径path值一致?如果一致则说明本次请求就应当由该方法进行处理因此利用反射机制调用该方法进行处理。提示:反射调用后要记得return,避免执行后续处理静态资源的操作5:如果扫描了所有的Controller中所有的业务方法,均未找到与本次请求匹配的路径则说明本次请求并非处理业务,那么执行下面请求静态资源的操作*///定位controller目录   使用File访问文件系统时,路径中目录的层级分割是用"/"try {File dir = new File(baseDir,"com/birdboot/controller");//获取该目录下的所有.class文件File[] subs = dir.listFiles(f->f.getName().endsWith(".class"));for(File sub : subs){//遍历每一个.class文件String fileName = sub.getName();//获取文件名String className = fileName.substring(0,fileName.indexOf("."));//根据文件名获取类名//包名的层级分割使用"."Class cls = Class.forName("com.birdboot.controller."+className);//判断该类有没有注解@Controllerif(cls.isAnnotationPresent(Controller.class)){//扫描该类的所有方法Method[] methods = cls.getDeclaredMethods();//遍历每一个方法for(Method method : methods){//判断该方法是否有注解@RequestMappingif(method.isAnnotationPresent(RequestMapping.class)){//获取注解RequestMapping rm = method.getAnnotation(RequestMapping.class);//通过注解对象获取参数String value = rm.value();//对比本次请求是否与该方法注解参数一致?if(path.equals(value)){//一致则说明本次请求应当交由该方法处理//实例化该ControllerObject c = cls.newInstance();//调用该方法处理本次请求method.invoke(c,request,response);return;//处理后解除本次处理请求(避免执行下面处理静态资源的部分)}}}}}} catch (ClassNotFoundException e) {e.printStackTrace();} catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();}File file = new File(staticDir, path);if (file.isFile()) {//由于响应对象中状态代码和描述默认值为200,OK因此正确情况下不用再设置response.setContentFile(file);//设置响应头response.addHeader("Server", "BirdServer");} else {response.setStatusCode(404);response.setStatusReason("NotFound");file = new File(staticDir, "404.html");response.setContentFile(file);response.addHeader("Server", "BirdServer");}}
}

Controller

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Controller {
}

RequestMapping

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestMapping {String value();
}

V18

重构代码
上一个版本那种DispatcherServlet每次处理请求时都要扫描controller包,这样性能低下,因此改为仅扫描一次

实现:

  1. com.webserver.core包下新建类HandlerMapping
  2. 定义静态属性Map mapping key:请求路径;value:Method记录处理该请求的方法对象
  3. 在静态块中初始化,完成原DispatcherServlet扫描controller包的工作并初始化mapping
  4. 提供静态方法:getMethod()可根据请求路径获取处理该请求的方法
  5. DispatcherServlet中处理业务时只需要根据请求获取对应的处理方法利用反射机制调用即可

DispatcherServlet

package com.birdboot.core;import com.birdboot.annotations.Controller;
import com.birdboot.annotations.RequestMapping;
import com.birdboot.controller.UserController;
import com.birdboot.http.HttpServletRequest;
import com.birdboot.http.HttpServletResponse;import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URISyntaxException;/*** V8新增内容:* 该类是SpringMVC框架与Tomcat整合时的一个关键类* Tomcat处理业务原生的都是调用继承了HttpServlet的类来完成,此时需要进行很多配置* 以及使用时要作很多重复性劳动。* SpringMVC框架提供的该类也是继承了HttpServlet的,使用它来接收处理请求的工作。*/
public class DispatcherServlet {private static File baseDir;//类加载路径private static File staticDir;//类加载路径下的static目录static {try {//定位当前项目的类加载路径baseDir = new File(DispatcherServlet.class.getClassLoader().getResource(".").toURI());//定位类加载路径下的static目录staticDir = new File(baseDir, "static");} catch (URISyntaxException e) {e.printStackTrace();}}/*** service方法实际上是当我们继承了HttpServlet后必须重写的方法* 该方法要求接收两个参数:请求对象与响应对象。* Tomcat在处理请求时就是调用某个Servlet的service方法并将请求与响应对象传入* 来让其完成处理工作的。*/public void service(HttpServletRequest request, HttpServletResponse response) {//获取请求的抽象路径//不能在使用uri判断请求了,因为uri可能含参数,内容不固定。String path = request.getRequestURI();//path:"/regUser"System.out.println(path);//判断该请求是否为请求一个业务Method method = HandlerMapping.getMethod(path);if(method!=null){//本次为请求业务//通过方法获取其所属类的类对象(某个Controller)try {Class cls = method.getDeclaringClass();Object obj =cls.newInstance();method.invoke(obj,request,response);} catch (Exception e) {//如果处理业务过程中报错,这是典型的因为服务端处理请求出现错误导致处理失败(500错误)response.setStatusCode(500);response.setStatusReason("Internal Server Error");}}else {File file = new File(staticDir, path);if (file.isFile()) {//由于响应对象中状态代码和描述默认值为200,OK因此正确情况下不用再设置response.setContentFile(file);//设置响应头response.addHeader("Server", "BirdServer");} else {response.setStatusCode(404);response.setStatusReason("NotFound");file = new File(staticDir, "404.html");response.setContentFile(file);response.addHeader("Server", "BirdServer");}}}
}

HandleMapping

package com.birdboot.core;import com.birdboot.annotations.Controller;
import com.birdboot.annotations.RequestMapping;import java.io.File;
import java.lang.reflect.Method;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Map;/*** SpringMVC框架使用这个类来维护所有Controller*/
public class HandlerMapping {private static File baseDir;//类加载路径/*请求与对应的处理方法的映射关系key:请求路径(与对应方法上@RequestMapping注解参数一致)value:处理该请求的方法*/private static Map<String, Method> mapping = new HashMap<>();static{try {//定位当前项目的类加载路径baseDir = new File(HandlerMapping.class.getClassLoader().getResource(".").toURI());initMapping();} catch (URISyntaxException | ClassNotFoundException e) {e.printStackTrace();}}/*** 初始化mapping*/private static void initMapping() throws ClassNotFoundException {File dir = new File(baseDir,"com/birdboot/controller");//获取该目录下的所有.class文件File[] subs = dir.listFiles(f->f.getName().endsWith(".class"));for(File sub : subs){//遍历每一个.class文件String fileName = sub.getName();//获取文件名String className = fileName.substring(0,fileName.indexOf("."));//根据文件名获取类名//包名的层级分割使用"."Class cls = Class.forName("com.birdboot.controller."+className);//判断该类有没有注解@Controllerif(cls.isAnnotationPresent(Controller.class)){//扫描该类的所有方法Method[] methods = cls.getDeclaredMethods();//遍历每一个方法for(Method method : methods){//判断该方法是否有注解@RequestMappingif(method.isAnnotationPresent(RequestMapping.class)){//获取注解RequestMapping rm = method.getAnnotation(RequestMapping.class);//通过注解对象获取参数String value = rm.value();//将该方法与其处理的请求路径以key,value形式存入mappingmapping.put(value,method);}}}}}/*** 根据请求路径获取某个Controller下的对应处理方法* @param path* @return*/public static Method getMethod(String path){return mapping.get(path);}public static void main(String[] args) {Method method = getMethod("/regUser");System.out.println(method);}
}

V19

使用线程池管理线程
在主启动类BirdBootApplication中定义线程池,
并在接收客户端链接后将ClientHandler交给线程池处理

BirdBootApplication

package com.birdboot.core;import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;/*** 主启动类*/
public class BirdBootApplication {private ServerSocket serverSocket;private ExecutorService threadPool;public BirdBootApplication(){try {System.out.println("正在启动服务端...");serverSocket = new ServerSocket(8088);threadPool = Executors.newFixedThreadPool(50);System.out.println("服务端启动完毕!");} catch (IOException e) {e.printStackTrace();}}public void start(){try {while(true) {System.out.println("等待客户端链接...");Socket socket = serverSocket.accept();System.out.println("一个客户端链接了!");//启动一个线程处理该客户端交互ClientHandler handler = new ClientHandler(socket);threadPool.execute(handler);}} catch (IOException e) {e.printStackTrace();}}public static void main(String[] args) {BirdBootApplication application = new BirdBootApplication();application.start();}
}

线程池

  • 复用线程
  • 线程的数量控制

线程的执行过程

  1. 线程任务:Runnable
  2. 创建线程:new Thread()
  3. 启动:start()
  4. 并发执行中:Running
  5. 任务执行完毕:Dead
  6. 线程销毁:gc

线程池API

  • Executors:创建线程池的工厂类,用于创建线程池对象
  • ExecutorService:线程池接口,规定线程池相关功能方法

创建线程池ExecutorService pool = Executors.newFixedThreadPool(容量)

创建并指派任务

  1. 创建任务:实现Runnable接口重写run()方法
  2. 指派任务:调用线程池方法 pool.execute(任务)

线程池的关闭

  1. shutdown():不再接收新的任务,将现有任务执行完毕结束线程池中所以线程
  2. shutdownNow():不再接收新任务,中断线程池所有线程

数据库

数据库的基本概念

  • 数据库DataBase:保存一组数据的仓库就是数据库

  • 数据库管理系统(DBMS):一套可以独立运行的软件,用于维护磁盘上的数据,维护性好,性能好,可扩展性强

    常见的DBMS:

    • MySQL
    • MariaDB
    • Oracle
    • DB2
    • SQLServer

数据库管理系统中常见的概念

  • 库:是表的集合,一个库中可以存放若干张表。通常库是服务于项目的。

  • 表:是数据的集合,具有一组相同属性的数据存放在同一张表中。

    • 行:存放数据
    • 列:存放字段
  • 数据库的角色:数据库是一个独立运行的软件,并且是以服务端的形式体现的,我们要操作数据库则需要以客户端的角度与服务端建立连接并进行相关的操作

  • 数据库的交互:连接数据库后,向其发送SQL语句(Structured Query Language 结构化查询语言),数据库会理解该SQL语句的含义并进行对应操作

    SQL语句的标准:SQL92标准,所有DBMS都支持SQL92标准,该标准就是操作数据库的“普通话”,但不是所有的数据库操作都在SQL92标准中,不在该标准中的操作,不同的数据库提供的语法可能完全不同,此时这类SQL称为“方言”

  • 常用的客户端

    • 命令行
    • 图形化界面
    • JDBC
    • 集成开发环境(IDE)

Java阶段二Day07

SQL分类

  • DDL:数据定义语言,是操作数据库对象的语言。数据库对象(库,表,视图,索引,序列)
  • DML:数据操作语言,是操作表中数据的语言。对表中数据操作的语言(增INSERT,删DELETE,改UPDATE)
  • DQL:数据查询语言,是查询表中数据的语言。SELECT语句(学习的重点和难点)
  • DCL:数据控制语言,管理数据库的语言(权限分配等,DBA关心的)
  • TCL:事务控制语言,控制事务操作

DDL语言-数据定义语言

对数据库的操作

  • 新建一个数据库:

    CREATE DATABASE 数据库名 [charset = 字符集]
    # 指定字符集
    CREATE DATABASE mydb1 CHARSET = UTF8;
    CREATE DATABASE mydb2 CHARSET = GBK;
    
  • 查看已创建数据库:

    SHOW DATABASES;
    
  • 查看创建数据库时的信息:

    SHOW CREATE DATABASE 数据库名
    
  • 删除数据库:

    DROP DATABASE 数据库名
    
  • 切换数据库:

    USE 数据库名
    

对数据表的操作

  • 创建数据库表

    CREATE TABLE 表名(字段名1 类型[(长度)][DEFALUT 默认值][约束],字段名2 类型[(长度)][DEFALUT 默认值][约束],
    ) [charset = 字符集]
    
  • 查看表结构:

    DESC 表名
    
  • 查看表创建时的信息:

    SHOW CREATE TABLE 表名
    
  • 查看当前数据库中创建的所有表:

    SHOW TABLES
    
  • 修改表名:

    RENAME TABLE 原表名 TO 新表名
    
  • 删除表:

    DROP TABLE 表名
    
  • 修改表结构,在表末尾追加新字段:

    ALTER TABLE 表名 ADD 字段名 类型[(长度) 默认值 约束]
    
  • 修改表结构,在表最开始添加新字段:

    ALTER TABLE 表名 ADD 字段名 类型 [(长度) 默认值 约束] FIRST
    
  • 修改表结构,在表中间插入新字段:

    ALTER TABLE 表名 ADD 字段名 类型[(长度) 默认值 约束] AFTER 表中现有字段名
    
  • 修改表结构,在表中删除字段:

    ALTER TABLE 表名 DROP 字段名
    
  • 修改表结构,在表中修改现有字段:

    ALTER TABLE 表名 CHANGE 原字段名 新字段名 类型 [(长度) 默认值 约束]
    

修改表结构的注意事项

  • 修改表结构最好在表中没有数据的情况下进行
  • 当表中含有数据时,
    • 尽量不修改表中某字段类型,否则可能因为现有数据不满足新修改的类型导致修改失败
    • 尽量不缩短字段长度
    • 若为字段新添加约束,该字段现有的数据不能违反该约束