经典权限五张表案例分析

news2024/11/24 7:32:22

文章目录

  • 模块分析
    • 模块
    • 分析
  • 描述五张表的关系
  • 重要知识讲解
    • 抽取成一个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.UserServiceImpl
        1.创建一个Map集合: new HashMap<String,Object>();
        2.Map集合的key:例如配置文件等号左边的标识userService roleService
        3.Map集合的value:就是创建的对象UserServiceImpl 类的对象  RoleServiceImpl类的对象
        4.实现步骤:
            1)创建map集合存储创建的对象
            2)定义静态方法创建具体类的对象
            3)在静态方法中判断map集合的key是否有值,如果没有值,说明第一创建对象
            4)直接使用反射+读取配置文件方式创建对象
            5)将创建的对象作为map集合的value和key存入到map中
            6)返回给调用者创建的对象
 */
public class BeansFactory {

    // 1)创建map集合存储创建的对象
    /*
        key            value
        userService    UserServiceImpl0x001
        roleService    RoleServiceImpl0x002
     */
    private static HashMap<String,Object> map = new HashMap<>();
    // 2)定义静态方法创建具体类的对象
    //多线程安全问题:t1 t2
    public 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.UserServiceImpl
            String 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.HondaCar
            	tesla_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.coloradmin.cn/o/1672248.html

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

相关文章

卷积神经网络边缘识别

为什卷积神经网络能够识别图片呢&#xff1f;是基于图片相似度比较&#xff0c;两张图片的点击越大说明两张图片越像&#xff0c;比如我们那狗胡子的图片去比较&#xff0c;如果相似度很高&#xff0c;就是认为这个动物更像狗。点积越大&#xff0c;图片越相似&#xff0c;这个…

量化交易包含些什么?

我们讲过许多关于量化交易的内容&#xff0c;但是量化交易具体可以做些什么&#xff1f;很多朋友都还不清楚&#xff0c;我们详细来探讨下&#xff01; 第一&#xff1a;什么是量化交易&#xff1f; 量化交易是一种利用先进的数学模型和计算机技术&#xff0c;从大量的历史数…

老黄终于不穿皮衣了,分享一个AI换装AI试衣软件!

用AI实现在线试衣&#xff0c;或者在线换装&#xff0c;这不是一个新概念&#xff0c;肯定有人这么想过&#xff0c;但并不是所有人能都能轻松做到啊&#xff01; 今天就来分享一个人人都可以实现的方法&#xff0c;而且是那种傻瓜式的不用付钱的那种&#xff0c;甚至可以把软件…

ONVIF系列一:ONVIF介绍

感谢博主OceanStar的学习笔记&#xff0c;ONVIF系列二和系列三中安装操作过程及代码实现参考了这位博主的博客。 ONVIF系列&#xff1a; ONVIF系列一&#xff1a;ONVIF介绍 ONVIF系列二&#xff1a;Ubuntu安装gSOAP、生成ONVIF代码框架 ONVIF系列三&#xff1a;ONVIF客户端实现…

3款常用的可视化工具Matplotlib、Seaborn和Pandas

大家好&#xff0c;Seaborn 是基于 Matplotlib 的扩展库&#xff0c;Pandas 的可视化功能同样也依赖于 Matplotlib。尽管二者都使用相同的底层图形库&#xff0c;但绘制图表的方法却各有千秋。本文将介绍各种柱状图的绘制&#xff0c;比较 Matplotlib、Pandas 和 Seaborn 在数据…

ONVIF系列三:ONVIF客户端实现

ONVIF系列&#xff1a; ONVIF系列一&#xff1a;ONVIF介绍 ONVIF系列二&#xff1a;Ubuntu安装gSOAP、生成ONVIF代码框架 ONVIF系列三&#xff1a;ONVIF客户端实现 在系列二中完成了在Ubuntu上安装gSOAP并生成ONVIF代码框架&#xff0c;接下来我们利用生成的框架实现ONVIF客户端…

探究NVMe SSD HMB应用场景与影响-<续>

如果需要采用HMB功能&#xff0c;需要SSD支持NVME协议且NVMe 1.2及以上版本。NVME协议中对HMB对应有2个关键参数&#xff1a; HMB建议值&#xff08;HMPRE&#xff09;&#xff1a;设定实际分配给HMB使用的主机内存容量&#xff0c;为设备提供最优性能的内存分配量。 HMB最小值…

冥想的时候怎么专注自己

冥想的时候怎么专注自己&#xff1f;我国传统的打坐养生功法&#xff0c;实际最早可追溯到五千年前的黄帝时代。   每天投资两个半小时的打坐&#xff0c;有上千年之久的功效。因为当你们打坐进入永恒时&#xff0c;时间停止了。这不只是两个半小时&#xff0c;而是百千万亿年…

Github上5个实用的ChatGPT仓库

ChatGPT是一款基于聊天场景的大模型AI&#xff0c;最近火出圈。 Chat表示聊天&#xff0c;GPT表示大模型算法&#xff0c;它通过生成式的人机对话功能&#xff0c;让使用者第一次有了AI机器人‘懂我‘的感觉&#xff0c;而不是Siri、小爱那种傻瓜式的语音服务。 ChatGPT不仅仅…

现货黄金白银行情走高带来的投资机会分析

当现货黄金和白银行情呈现出走高的态势时&#xff0c;这常常被投资者解读为一个潜在的投资机会。本文旨在分析在黄金白银价格上涨时的投资机会&#xff0c;并指出应对策略。 一、走高行情背后的机会 行情的上升&#xff0c;往往代表了市场在某种程度上的认可&#xff0c;无论这…

知乎知+广告推广该如何做?怎么收费?

知乎作为一个汇聚高质量用户群体的知识分享平台&#xff0c;成为了众多品牌和产品推广的优选之地。特别是知乎的“知”广告推广服务&#xff0c;以其精准定向、内容原生的特点&#xff0c;深受广告主青睐。 一、知乎知广告推广基础 1. 什么是知乎知&#xff1f; 知是知乎官方…

企业网站慎用免费SSL证书!OV证书才是首选

市面上有很多免费证书提供&#xff0c;免费的SSL证书更适用于个人博客、学生学习、测试等应用场景。如果您的网站是企业网站一定慎用免费的SSL证书&#xff0c;而是选择企业级的OV证书。 一&#xff0e;免费SSL证书的风险 1安全性&#xff1a;免费SSL证书通常只提供基本的加密…

全新Transformer模型:全球与局部双重突破!

DeepVisionary 每日深度学习前沿科技推送&顶会论文分享&#xff0c;与你一起了解前沿深度学习信息&#xff01; 引言&#xff1a;探索视觉变换器在对象重识别中的全局与局部特征 在对象重识别&#xff08;Re-ID&#xff09;的研究领域中&#xff0c;如何有效地从不同时间…

pytest教程-45-钩子函数-pytest_report_testitemFinished

领取资料&#xff0c;咨询答疑&#xff0c;请➕wei: June__Go 上一小节我们学习了pytest_report_collectionfinish钩子函数的使用方法&#xff0c;本小节我们讲解一下pytest_report_testitemFinished钩子函数的使用方法。 pytest_report_testitemFinished 钩子函数在每个测试…

7天精通Web APIs——正则阶段案例(理论+实战)(第六天)

正则表达式的定义和使用 定义&#xff1a;是一种匹配模式&#xff0c;用于匹配字符串中字符组合 作用&#xff1a;表单验证&#xff08;匹配&#xff09;、过滤敏感词&#xff08;替换&#xff09;、字符串中提取我们想要的部分&#xff08;提取&#xff09; 使用分为两步&…

MySQL-InnoDB数据存储结构

1、存储结构-页 索引结构提供了高效的索引方式&#xff0c;索引信息以及数据记录都保存在数据文件或索引文件中&#xff08;本质存储在页结构中&#xff09; 1.1、磁盘与内存交互的基本单位&#xff1a;页 在InnoDB中将数据划分为若干页&#xff0c;页的默认大小为&#xff…

SQLZOO:The JOIN operation

数据表&#xff1a;game-gaol-eteam game idmdatestadiumteam1team210018 June 2012National Stadium, WarsawPOLGRE10028 June 2012Stadion Miejski (Wroclaw)RUSCZE100312 June 2012Stadion Miejski (Wroclaw)GRECZE100412 June 2012National Stadium, WarsawPOLRUS... goal …

@游戏行业er!MongoDB广州线下沙龙邀您报名!

随着游戏和应用程序的发展&#xff0c;数据变得越来越重要。在为您的下一个游戏选择数据库时&#xff0c;数据库管理者常常会面对灵活性、可扩展性、可靠性、运营效率等问题或挑战。 MongoDB在游戏开发领域有着广泛的应用&#xff0c;灵活数据模型可以存储和处理各种类型的数据…

C++--String类

系列文章目录 文章目录 目录 系列文章目录 文章目录 前言 一、为什么要学习string 1.c语言的字符串 2.OJ上的使用 二、string类的接口介绍 1.string简介 2.string构造成员函数 3.operator函数 4.string容器size和length 5.重载operator[]和引用返回的意义 5.1 oper…

嫦娥六号揭秘真相:阿波罗登月是真是假?一文终结所有疑问!

近期&#xff0c;嫦娥六号的成功发射如同璀璨的星辰&#xff0c;再次将人们的视线聚焦于浩瀚的宇宙&#xff0c;与此同时&#xff0c;网络上关于美国阿波罗登月是否造假的争议也如潮水般涌现。一些声音宣称&#xff0c;嫦娥六号的发射为揭示美国阿波罗登月任务的真实性提供了关…