经典权限五张表案例分析

文章目录

  • 模块分析
    • 模块
    • 分析
  • 描述五张表的关系
  • 重要知识讲解
    • 抽取成一个BaseServlet
    • SpringIOC思想(底层)
      • 实现代码
      • IOC概述
    • SPI机制(为学习框架做思想和技术铺垫)
      • SPI引入
        • 1. 标准/规范
        • 2. 具体的实现
        • 3. 调用
      • SPI介绍
      • SPI练习JDBC4.0免注册驱动原理
      • Servlet实现方式三 ServletContainerInitializer

模块分析

模块

项目分为三个模块

  1. 用户模块:完成增删改查
  2. 角色模块:增删改查
  3. 权限模块:增删改查

分析

用户和角色的关系
一个用户有多个角色,张三可以是SVIP,也可以是绿钻
一个角色可以对应多个用户,SVIP可以是张三,也可以是李四
结果:用户和角色属于多对多的关系,根据表设计原则,多对多关系创建中间表,在中间表起码要有另外俩张主表用户和角色的主键作为外键进行关联
角色和权限的关系
一个角色有多个权限,SVIP可以点赞20次,可以使qq名变红
一个权限可以对应多个角色,使qq名变红可以是SVIP,也可以是VIP
结果:角色和权限属于多对多的关系,根据表设计原则,多对多关系创建中间表,在中间表起码有另外俩张主表权限和角色的主键作为外键进行关联
:用户 角色 权限具有经典的五张表

描述五张表的关系

image.png

重要知识讲解

抽取成一个BaseServlet

问题:对用户进行增删改查,那么单对用户就要写4个Servlet,这样创建的类太多比较麻烦
解决措施:我们想的是对于用户只创建一个Servlet,路径的话变成/user/*,这样关于用户的所有操作都会放到这个Servlet类中,在里面定义方法来分别操作用户,方法名和路径名保持相同,代码看着简单明了,很简洁
问题:但是这样做我们就只能使用在用户模块,不能使用其他模块,造成代码冗余,还得必须使用if判断到底执行哪个方法
解决措施:前面已经学过可以获取前端的请求数据,那么就可以获取到请求路径,然后用反射来执行方法,然后将其抽取到一个类中,让其他模块创建的Servlet类继承这个类,其他模块的Servlet类中只写操作方法即可
关键:this关键字
代码如下

public class BaseServlet extends HttpServlet {protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {doGet(request, response);}protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//获取请求路径String uri = request.getRequestURI();int i = uri.lastIndexOf("/");String methodName = uri.substring(i + 1);//判断方法名//名字固定,不利于整体书写
//        if ("findAll".equals(methodName)){
//            findAll(request,response);
//        } else if ("update".equals(methodName)) {
//            update(request,response);
//        } else if ("add".equals(methodName)) {
//            add(request,response);
//        }else if ("delete".equals(methodName)){
//            delete(request,response);
//        }//用反射技术,使用固定代码执行所有方法//方法名和路径相同,利用反射获取方法名Class aClass = this.getClass();try {Method method = aClass.getMethod(methodName,HttpServletRequest.class,HttpServletResponse.class);method.invoke(this,request,response);} catch (Exception e) {throw new RuntimeException(e);}}
}
@WebServlet("/user/*")
public class UserServlet extends BaseServlet {private void delete(HttpServletRequest request, HttpServletResponse response) {System.out.println("删除用户");}public void add(HttpServletRequest request, HttpServletResponse response) {System.out.println("增加用户");}public void update(HttpServletRequest request, HttpServletResponse response) {System.out.println("更新用户");}public void findAll(HttpServletRequest request, HttpServletResponse response) {}
}

SpringIOC思想(底层)

实现代码

问题:我们以前创建对象都是通过new创建对象,例如:创建业务层对象:
UserServiceImple userServiceImpl = new UserServiceImpl();
这种方式创建对象的弊端是:严重耦合。如果业务层实现类UserServiceImpl被删除了,web会报错。或者在实际开发中向对业务层进行扩展,一般是定义一个新的业务层类UserServiceImpl2,然后将之前的UserServiceImpl替换,就造成web层无法运行,那么太过于耦合
解决措施:降低耦合。就是不在web层中使用new方式创建对象
使用:反射+面向接口编程+读取配置文件+工程设计模式等方式来取代new创建对象

  1. 定义接口

在业务层下定义接口,然后定义一个impl包,将UserServiceImpl类放到impl包下,这样就可以将上面创建业务层对象改成:
UserService userService = new UserServiceImpl();
这样的话就是实现左边解耦了,如果扩展实现类只需要修改等号右边

  1. 书写properties文件

在resources下定义一个beans.properties文件,然后按照key=value格式书写,key:userService,value:写UserServiceImpl的全路径名

  1. 使用反射+读取配置文件创建对象

使用ResourceBundle抽象类去读取properties配置文件,利用反射创建对象

  1. 定义一个工具类,使用工厂设计模式来创建对象

定义一个Map集合,key存properties文件内容=左边的值,value存用反射创建的对象
代码如下
工厂类

package com.itheima.case2.utils;import java.util.HashMap;
import java.util.ResourceBundle;/*TODO:当前工厂类的作用就是创建对象的回顾:一个类对象:1)单例 单个对象2)多例 多个对象我们这里实现产生的对象是单例 userService=com.itheima.case2.service.impl.UserServiceImpl1.创建一个Map集合: new HashMap<String,Object>();2.Map集合的key:例如配置文件等号左边的标识userService roleService3.Map集合的value:就是创建的对象UserServiceImpl 类的对象  RoleServiceImpl类的对象4.实现步骤:1)创建map集合存储创建的对象2)定义静态方法创建具体类的对象3)在静态方法中判断map集合的key是否有值,如果没有值,说明第一创建对象4)直接使用反射+读取配置文件方式创建对象5)将创建的对象作为map集合的value和key存入到map中6)返回给调用者创建的对象*/
public class BeansFactory {// 1)创建map集合存储创建的对象/*key            valueuserService    UserServiceImpl0x001roleService    RoleServiceImpl0x002*/private static HashMap<String,Object> map = new HashMap<>();// 2)定义静态方法创建具体类的对象//多线程安全问题:t1 t2public static synchronized  <T> T getInstance(String key) throws Exception{//String key=userService roleService// 3)在静态方法中判断map集合的key是否有值,如果没有值,说明第一创建对象Object obj = map.get(key);// 4)直接使用反射+读取配置文件方式创建对象if(obj == null){//说明当前map集合中没有传递的key对应的值//4.1使用反射+读取配置文件创建对象ResourceBundle bundle = ResourceBundle.getBundle("beans");//参数beans表示要关联的配置文件的名字,不能书写后缀名//4.2根据配置文件的key获取值 t1//userService=com.itheima.case2.service.impl.UserServiceImplString classNameStr = bundle.getString(key);//"com.itheima.case2.service.impl.UserServiceImpl"//4.3使用反射创建对象Class<?> clazz = Class.forName(classNameStr);obj = clazz.newInstance();//调用UserServiceImpl类的无参构造方法// 5)将创建的对象作为map集合的value和key存入到map中map.put(key,obj);//t1线程创建的对象0x001 t2线程创建的对象0x002}// 6)返回给调用者创建的对象return (T)obj;}}

配置文件

userService=com.itheima.case2.service.impl.UserServiceImpl

web层UserServlet

UserService userService = BeanFactory.getInstance("userService");

IOC概述

Inversion of Control:控制反转
以前我们要获取对象,我们自己new主动获取,现在有了工厂模式,我们需要获取对象,是工厂创建,我们被动接收工厂创建的对象,这就是控制反转,说白了就是ioc采用工厂模式创建对象达到解耦合
其实SpringIOC底层是Map集合,我们经常会说SpringIOC容器即Map集合。

SPI机制(为学习框架做思想和技术铺垫)

SPI引入

1. 标准/规范
  1. 工程 spi_interface
  2. 只有一个接口car
2. 具体的实现
  1. 工程 honda_car 和 tesla_car
  2. 工程依赖了spi_interface
    pom.xml
  3. 有一个实现类,实现了标准
    HondaCar implements Car
    TeslaCar implements Car
  4. 还有一个配置文件
    1). 在类路径classpath下
    resources/META-INF/services
    2). 文件名: 接口的全限定名
    com.itheima.Car
    3). 文件内容: 实现类的全限定名
    com.itheima.impl.HondaCar
3. 调用
  1. 工程 spi_test
  2. 工程依赖了 honda_car 和 tesla_car
  3. 测试类 SpiTest

测试类代码

 # ServiceLoader<Car> cars = ServiceLoader.load(Car.class);加载接口实现1. 加载当前工程依赖的所有工程 classpath:META-INF/services目录下跟当前接口参数同名的文件(classpath:META-INF.services/com.itheima.Car文件)2. 当前案例依赖了两个工程,那么这两个工程的配置文件都会被读取到honda_car===META-INF.services/com.itheima.Car文件中的com.itheima.impl.HondaCartesla_car===META-INF.services/com.itheima.Car文件中的com.itheima.impl.TeslaCar注意:配置文件名必须是实现的接口全路径,配置文件中书写实现类的全路径3. 通过反射创建接口文件中配置的实例Class clazz= Class.forName("com.itheima.impl.TeslaCar");Car car =  clazz.newInstance();

ServiceLoader类介绍

  1. ServiceLoader功能和ClassLoader功能类似,能装载类文件,但是使用时是有区别的
  2. ServiceLoader装载的是一系列有某种共同特征的实现类(类实现同一个接口,在实现类的工程中的resources目录下存在META-INF/services目录下接口同名的文件),即这些类实现接口或者抽象类。而ClassLoader是可以加载任何类的类加载器;
  3. ServiceLoader加载时需要特殊的配置:
    1. 在类路径:classpath:META-INF/services/接口全路径文件
    2. 在文件中配置实现类全路径com.itheima.impl.HondaCar
  4. ServiceLoader还实现了Iterable接口,可以进行迭代
  5. 原理:在ServiceLoader.load的时候,根据传入的接口Class对象,遍历META-INF/services目录下的以该接口命名的文件中的所有类,将创建实现类的对象返回。

SPI介绍

全称Service Provider Interface,是Java提供的一套用来被第三方实现或者扩展的API,它可以用来启用框架扩展和替换组件
Java的SPI机制就是将一些类信息写在约定的文件中,然后由特定的类加载器ServiceLoader加载解析文件获取资源
Java SPI 基于"接口编程+策略模式+配置文件(约定)"组合实现的动态加载机制
运用场景

场景说明
数据库驱动数据库驱动加载接口实现类的加载 JDBC加载不同类型数据库的驱动
日志门面SLF4J接口实现类加载SLF4J加载不同提供商的日志实现类
SpringSpring中大量使用了SPI,比如:对servlet3.0规范对ServletContainerInitializer的实现、自动类型转换Type Conversion SPI(Converter SPI、Formatter SPI)等
DubboDubbo中也大量使用SPI的方式实现框架的扩展, 不过它对Java提供的原生SPI做了封装,允许用户扩展实现Filter接口
SpringBootSpringBoot基于SPI思想实现自动装配

SPI练习JDBC4.0免注册驱动原理

public class JdbcDemo {public static void main(String[] args) throws Exception {//1.加载驱动
//        Class.forName("com.mysql.jdbc.Driver");//2.获取连接Connection connection = DriverManager.getConnection("jdbc:mysql:///dbvue", "root", "1234");System.out.println(connection);}
}
//DriverManager类源码:
public class DriverManager {//静态代码块static {loadInitialDrivers();println("JDBC DriverManager initialized");}private static void loadInitialDrivers() {.....AccessController.doPrivileged(new PrivilegedAction<Void>() {public Void run() {//spi/*1.java.sql.Driver 是一个接口2.java.sql.Driver接口实现类,com.mysql.jdbc.Driver使用SerciveLoader类加载器调用方法获取java.sql.Driver接口的实现类对象放到LoadedDrivers中*/ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(java.sql.Driver.class);Iterator<Driver> driversIterator = loadedDrivers.iterator();try{while(driversIterator.hasNext()) {driversIterator.next();}} catch(Throwable t) {// Do nothing}return null;}});......}
}

Servlet实现方式三 ServletContainerInitializer

前俩种方式是注解和xml
第三种就是spi方式,后面学习的框架底层就是采用这种方式
ServletContainerInitializer 是 Servlet 3.0 新增的一个接口,主要用于在容器启动阶段通过编程风格注册Filter, Servlet以及Listener,以取代通过web.xml配置注册。这样就利于开发内聚的web应用框架.
运行原理

  1. ServletContainerInitializer接口的实现类通过java SPI声明自己是ServletContainerInitializer 的提供者.
  2. web容器启动阶段依据java spi获取到所有ServletContainerInitializer的实现类,然后执行其onStartup方法.
  3. 在onStartup中通过编码方式将组件servlet加载到ServletContext

小结
ServletContainerInitializer 在web容器启动时为提供给第三方组件机会做一些初始化的工作,例如注册servlet或者filter等,servlet规范中通过ServletContainerInitializer实现此功能。每个框架要使用ServletContainerInitializer就必须在对应的jar包的META-INF/services 目录创建一个名为javax.servlet.ServletContainerInitializer的文件,文件内容指定具体的ServletContainerInitializer实现类,那么,当web容器启动时就会运行这个初始化器做一些组件内的初始化工作
image.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/10083.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

idea使用前的全局配置,一次配置,多次使用

前提&#xff1a;每次导入一个新的项目&#xff0c;就需要重新设置编码、maven、jdk、git版本等信息。实际每个项目所用到的配置信息是一致的&#xff0c;除非换一家公司&#xff0c;不然不会改动到这些内容。 idea版本&#xff1a;2024.1.1 1.1、全局Maven配置 IDEA启动页面…

微火一文盘点:为何全域运营系统会成为创业新风口?

当前&#xff0c;微火全域运营已经成为创业的新风口&#xff0c;想要做微火全域运营服务商的创业者数量日益增多。据目前了解到的最新消息&#xff0c;微火全域运营系统的市场占有率已经超过了48%&#xff0c;并且还在持续不断地上涨中。 所谓微火全域运营系统&#xff0c;就是…

InLine Chat功能优化对标Github Copilot,CodeGeeX带来更高效、更直观的编程体验!

VSCode中的CodeGeeX 插件上线InLine Chat功能后&#xff0c;收到不少用户的反馈&#xff0c;大家对行内交互编程这一功能非常感兴趣。近期我们针对这个功能再次进行了深度优化&#xff0c;今天详细介绍已经在VSCode插件v2.8.0版本上线的 CodeGeeX InLine Chat功能&#xff0c;以…

蓝桥杯成绩已出

蓝桥杯的成绩早就已经出来了&#xff0c;虽然没有十分惊艳 &#xff0c;但是对于最终的结果我是心满意足的&#xff0c;感谢各位的陪伴&#xff0c;关于蓝桥杯的刷题笔记我已经坚持更新了49篇&#xff0c;但是现在即将会告别一段落&#xff0c;人生即将进入下一个规划。我们一起…

Spring框架学习笔记(一):Spring基本介绍(包含IOC容器底层结构)

1 官方资料 1.1 官网 https://spring.io/ 1.2 进入 Spring5 下拉 projects, 进入 Spring Framework 进入 Spring5 的 github 1.3 在maven项目中导入依赖 <dependencies><!--加入spring开发的基本包--><dependency><groupId>org.springframework<…

基于STC12C5A60S2系列1T 8051单片机实现一主单片机与一从单片机进行双向串口通信功能

基于STC12C5A60S2系列1T 8051单片机实现一主单片机与一从单片机进行双向串口通信功能 STC12C5A60S2系列1T 8051单片机管脚图STC12C5A60S2系列1T 8051单片机串口通信介绍STC12C5A60S2系列1T 8051单片机串口通信的结构基于STC12C5A60S2系列1T 8051单片机串口通信的特殊功能寄存器…

QT---day5,通信

1、思维导图 2、TCp 服务器 #ifndef MYWIDGET_H #define MYWIDGET_H #include <QWidget> #include <QTcpServer> #include <QList> #include <QTcpSocket> #include <QMessageBox> #include <QDebug> #include <QTcpServer> QT_B…

镭速实现利用Libarchive实现高效、智能的文件传输和管理

在前一篇报道中&#xff0c;我们阐述了Libarchive这一开源库的强大功能&#xff0c;它专门用于处理归档文件。通过整合Libarchive&#xff0c;镭速在包括Windows和Linux在内的多个操作系统上提供了在线解压缩服务&#xff0c;为企业构建了一个既强大又安全的文件传输系统&#…

【代码分享】使用HTML5的Canvas绘制编码说明图片

最急在工作中遇到一个需求&#xff0c;根据给定的编码生成编码说明&#xff0c;像下面这样的效果。 不同含义的编码用横杠分割&#xff0c;然后每个编码下面用箭头指明具体的含义。下面是我使用canvas实现的代码。具体的编码宽度大家可以根据实际情况进行调整&#xff0c;目前…

Ranger 面试题及答案整理,最新面试题

Ranger 的安全模型是如何设计的&#xff1f; Ranger的安全模型设计主要基于访问控制和安全策略的管理&#xff0c;它通过以下几个关键组件实现&#xff1a; 1、策略管理&#xff1a; Ranger 提供了一个中央管理平台&#xff0c;用于定义、更新和管理安全策略。这些策略根据资…

基于RT-Thread的HC-SR04超声波驱动

前言 本次驱动用的是这款超声波&#xff0c;超声波的驱动大同小异&#xff0c;均可参考 一、引脚定义 引脚功能VCC接直流5V电TRIG接外部电路的TRIG端&#xff0c;向此引脚输入10us以上的高电平可触发超声波测距ECHO接外部电路的ECHO端&#xff0c;测距结束时该引脚会输出一…

Python框架Django入门教程

Django 是一个使用 Python 编程语言开发的、免费且开源的 Web 应用框架。它遵循 "DRY&#xff08;Dont Repeat Yourself&#xff09;" 原则&#xff0c;旨在简化创建功能丰富的、高效率的 Web 网站。Django 提供了模型-视图-控制器&#xff08;MVC&#xff09;架构的…

时尚圈的节制美学 — 奥柔拉 AVRALA的独特设计理念

在这个多元化的时代&#xff0c;女性正在经历一场前所未有的角色变革。她们不再仅仅满足于传统的社会角色&#xff0c;而是勇敢地追求个人职业发展和自我实现。在这样的背景下&#xff0c;服饰不仅仅是外在的装饰&#xff0c;更是内心故事的讲述者、个性自我的表达者、身份归属…

NX/UG二次开发—3D几何—多边形内部最大圆

多边形内部最大圆&#xff0c;为什么不能说最大内切圆&#xff1f;如果正方形或正凸多边形&#xff0c;最大内部圆是与边相切的&#xff0c;但对于不规则多边形&#xff0c;很多情况是正好经过一些凹点。 本次介绍在NX中计算封闭边界内部最大圆&#xff1a; 1、首先按顺序排序…

Spring、SpringMVC

一、Spring框架中的单例Bean是线程安全的吗&#xff1f; 【默认单例的情况下】Spring Bean并没有可变的状态&#xff08;如Service类和DAO类&#xff09;&#xff0c;即只能查不能改&#xff0c;所以没有并发问题&#xff0c;所以某种程度上来说Spring的单例Bean是线程安全的。…

【ZZULI数据结构实验】压缩与解码的钥匙:赫夫曼编码应用

&#x1f4c3;博客主页&#xff1a; 小镇敲码人 &#x1f49a;代码仓库&#xff0c;欢迎访问 &#x1f680; 欢迎关注&#xff1a;&#x1f44d;点赞 &#x1f442;&#x1f3fd;留言 &#x1f60d;收藏 &#x1f30f; 任尔江湖满血骨&#xff0c;我自踏雪寻梅香。 万千浮云遮碧…

前端工程化之包管理器

这里写目录标题 什么是包包管理工具常用的包管理工具npmnpm 基本使用初始化搜索工具包下载安装包导入 npm 包基本流程生产依赖与开发依赖全局安装安装全部依赖安装指定版本的包删除依赖包 cnpm全局安装nrm 的使用 yarn全局安装yarn 常用命令 pnpm 【推荐使用】全局安装 什么是包…

我国吻合器市场规模不断扩大 国产化率有所增长

我国吻合器市场规模不断扩大 国产化率有所增长 吻合器是替代手工切除或缝合的一种医疗器械&#xff0c;其工作原理与订书机十分相似&#xff0c;可利用钛钉对组织进行离断或吻合。经过多年发展&#xff0c;吻合器种类逐渐增多&#xff0c;根据手术方式不同&#xff0c;吻合器大…

【JavaEE 初阶(三)】多线程代码案例

❣博主主页: 33的博客❣ ▶️文章专栏分类:JavaEE◀️ &#x1f69a;我的代码仓库: 33的代码仓库&#x1f69a; &#x1faf5;&#x1faf5;&#x1faf5;关注我带你了解更多线程知识 目录 1.前言2.单例模式2.1饿汉方式2.2饿汉方式 3.阻塞队列3.1概念3.2实现 4.定时器4.1概念4.…