当前位置: 首页 > news >正文

手写Spring-MVC之前后置处理器与异常处理、数据库框架

Day48

手写Spring-MVC之前后置处理器与异常处理

前后置处理器

概念:从服务器获取的JSON数据可能是加密后的,因此服务端获取的时候需要进行解密(前置处理器)。

而从服务器传出的JSON数据可能需要加密,因此需要在处理返回值的时候进行加密(后置处理器)。

思路:

首先搭建前后置处理器的框架

和是否处理JSON格式的数据类似,需要根据注解判断controller层中的方法是否需要对JSON格式的数据进行解密或者返回数据进行加密,因此要添加两个注解@BeforeAdviser和@AfterAdviser

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface BeforeAdviser {
}@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AfterAdviser {
}

controller层的方法需要添加相应的注解标识

/*** 使用postman发送请求* url:http://localhost:8080/user/test12.action* json:{"username":"zs","password":"123123"}* 传递JSON参数和返回JSON*/@RequestMapping("/test12.action")@ResponseBody@AfterAdviserpublic User test12(@RequestBody @BeforeAdviser User user){System.out.println("user对象:"+ user);return user;

同时需要在参数描述类中和方法描述类中添加是否有相应注解的属性:

/*** 参数描述类
*/
@NoArgsConstructor
@AllArgsConstructor
@Data
public class ParameterDefinition {private String name;//参数名private Class<?> clazz;//参数类型private int index;//参数下标private Type[] actualTypeArguments;//参数泛型的数组private boolean requestBodyHasOrNot;//参数上是否有@RequestBody注解private boolean beforeAdviserHasOrNot;//参数上是否有@BeforeAdviser注解}/*** 方法描述类
*/
@NoArgsConstructor
@AllArgsConstructor
@Data
public class MethodDefinition {private String requestMappingPath;//子级URiprivate String name;//方法名private Method method;//方法对象private Class<?> returnClazz;//返回值类型private List<ParameterDefinition> parameterDefinitions;//参数描述类对象的集合private boolean responseBodyHasOrNot;//方法上是否有@ResponseBody注解private boolean afterAdviserHasOrNot;//方法上是否有@AfterAdviser注解
}

添加后监听器中封装部分也要进行相应修改:

//获取参数上是否有@BeforeAdviser注解
boolean beforeAdviserHasOrNot = false;
BeforeAdviser beforeAdviser = parameters[i].getAnnotation(BeforeAdviser.class);
if(beforeAdviser!=null){beforeAdviserHasOrNot = true;
}ParameterDefinition parameterDefinition = new ParameterDefinition(parameterName, parameterType, index,actualTypeArguments,requestBodyHasOrNot,beforeAdviserHasOrNot);//封装参数描述类对象
parameterList.add(parameterDefinition);
//获取方法上是否有@AfterAdviser注解
boolean afterAdviserHasOrNot = false;
AfterAdviser afterAdviser = method.getAnnotation(AfterAdviser.class);
if(afterAdviser!=null){afterAdviserHasOrNot = true;
}
MethodDefinition methodDefinition = new MethodDefinition(sonUri, methodName, method, returnType, parameterList,responseBodyHasOrNot,afterAdviserHasOrNot);//封装方法描述类对象

至此,监听器就能把信息记录下来,调度的DispatcherServlet进行工作的时候就可以获取到相应的注解信息。

前置处理器的使用是在获取参数类型的时候会判断是否有@BeforeAdviser注解,如果有则代表需要进行解密操作:

//解密
if(parameterDefinition.isBeforeAdviserHasOrNot()){//在这里进行具体的解密操作吗?
}

在处理返回值的时候会判断方法是否有@AfterAdviser注解,如果有则代表需要进行加密操作:

//加密
if(methodDefinition.isAfterAdviserHasOrNot()){//在这里进行加密操作吗?
}

进行具体的解密和加密操作:

前后置处理器的框架搭建好之后,会发现一个问题,如果在DispatcherServlet中进行具体的解密加密的话,那么在用户使用该框架的时候,就只能使用框架所规定的解密加密,这显然不具有灵活性。不同的用户解密、加密逻辑不同,所以这里的思路是在框架中只写抽象类,在DispatcherServlet中利用多态创建抽象类的继承类对象,调用继承类对象中的解密加密方法。而在web模块中用户可以自己重写一个解密加密抽象方法,这样调用的就是用户自定义的逻辑方法了。

抽象方法:

public abstract class HanderAdviserResolver {public abstract String beforeRequestBody(String reqData);public abstract String afterResponseBody(String respData);public String before(String reqData){return beforeRequestBody(reqData);}public String after(String respData){return afterResponseBody(respData);}
}

注意:这里的抽象方法是交给用户重写的,而自己的成员方法则直接调用抽象方法,通过这种方式实现在DispatcherServlet中调用用户重写方法的逻辑。

用户自定义前后置处理器:

public class BeforeAndAfterAdviser extends HanderAdviserResolver {@Overridepublic String beforeRequestBody(String reqData) {System.out.println("解密:"+reqData);return reqData;}@Overridepublic String afterResponseBody(String respData) {System.out.println("加密:"+respData);return respData;}
}

又一个问题来了,框架中怎样拿到用户自定义的类对象呢?思路和监听器拿到controller层类对象相似,通过配置文件拿到注解类,注解类通过一个注解注明用户自定义的前后置处理器路径。这样的注解叫做使能注解,它的功能就是使得DispatcherServlet能够拿到用户自定义类。然后在DispatcherServlet中重写init()方法,在方法中获取配置文件信息,进而拿到注解类,通过注解类的注解信息拿到自定义前后置处理器类对象,调用其重写的解密加密处理方法。

使能注解:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface EnableAdviser {String adviserPackage();
}

注解类:

@EnableAdviser(adviserPackage= "com.qf.shop.web.adviser.BeforeAndAfterAdviser")

DispatcherServlet中:

private HanderAdviserResolver adviserResolver;public HanderAdviserResolver getAdviserResolver(String config){try {Class<?> clazz = Class.forName(config);EnableAdviser enableAdviserAnnotation = clazz.getAnnotation(EnableAdviser.class);String adviserPackage = enableAdviserAnnotation.adviserPackage();if(adviserPackage!=null){Class<?> adviserClass = Class.forName(adviserPackage);adviserResolver = (HanderAdviserResolver) adviserClass.newInstance();}return adviserResolver;} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {throw new RuntimeException(e);}}@Overridepublic void init() throws ServletException {ServletContext servletContext = this.getServletContext();String config = servletContext.getInitParameter("config");adviserResolver = getAdviserResolver(config);}
//加密
if(methodDefinition.isAfterAdviserHasOrNot()){jsonString = adviserResolver.after(jsonString);
}
//解密
if(parameterDefinition.isBeforeAdviserHasOrNot()){jsonStr = adviserResolver.before(jsonStr);
}

异常

概念:DispatcherServlet中对于全局的异常需要进行处理,而具体如何处理也是由业务决定的,换言之是用户进行定义而非框架中写死。但是框架中又需要调用处理异常的方法,如何处理?-和前后置处理器一样,通过多态,servlet调用的是用户继承框架中抽象类的类重写的方法。

抽象类:

public abstract class HanderGlobalException {public abstract void handlerException(Exception err, HttpServletRequest request, HttpServletResponse response);public void hander(Exception err, HttpServletRequest request, HttpServletResponse response){handlerException(err,request,response);}
}

用户继承类:

public class GlobalException extends HanderGlobalException {@Overridepublic void handlerException(Exception err, HttpServletRequest request, HttpServletResponse response) {System.out.println("处理全局异常......");try {request.getRequestDispatcher("/err.jsp").forward(request,response);} catch (ServletException | IOException e) {throw new RuntimeException(e);}}
}

那么框架如何拿到用户自己写的处理异常类和方法呢?思路和前后置处理器一样,DispactherServlet在重写的init()方法中通过配置文件拿到配置类,配置类通过框架中写的使能注解将用户自定义的异常处理类路径告诉servlet。

使能注解:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface EnableException {String exceptionPackage();
}

配置类:

/*** 当前项目的配置类
*/
@Configuration("com.qf.shop.web.controller")
@EnableAdviser(adviserPackage= "com.qf.shop.web.adviser.BeforeAndAfterAdviser")
@EnableException(exceptionPackage = "com.qf.shop.web.globalException.GlobalException")
public class AppConfig {
}

DispatcherServlet:

private HanderGlobalException globalException;public HanderGlobalException getGlobalException(String config){try {Class<?> clazz = Class.forName(config);EnableException enableExceptionAnnotation = clazz.getAnnotation(EnableException.class);String exceptionPackage = enableExceptionAnnotation.exceptionPackage();if(exceptionPackage!=null){Class<?> exceptionClass = Class.forName(exceptionPackage);globalException = (HanderGlobalException) exceptionClass.newInstance();}return globalException;} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {throw new RuntimeException(e);}
}@Overridepublic void init() throws ServletException {ServletContext servletContext = this.getServletContext();String config = servletContext.getInitParameter("config");adviserResolver = getAdviserResolver(config);globalException = getGlobalException(config);}
try{//调用Controller层里的某个方法Object returnVal = method.invoke(t, args);if(returnVal!=null){//处理返回值handlerReturnVal(methodDefinition,returnVal,request,response,model);}
}catch (Exception e){globalException.hander(e,request,response);//处理全局异常
}

数据库模块

功能:该模块不是Spring-MVC中的一部分,是用来和数据库交互的框架。

思路:分包逐步实现JDBC。

base包:根据获得的结果集实现封装功能的接口,包含结果集处理器接口和行处理器接口

/**
* 结果集处理器的接口
*实现类:BeanHandler(获取单个对象)、BeanListHandler(获取对象集合)
*/
public interface ResultSetHandler<T> {public T handler(ResultSet resultSet)throws SQLException,IllegalAccessException,InstantiationException, InvocationTargetException;
}
/**
* 行处理器的接口
* @param <T>
*/
public interface RowProcessor<T> {public T toArray(ResultSet resultSet) throws SQLException;
}

handler包:实现接口:

/**
* 结果集处理接口的实现类
* 操作结果集并封装实体类
* @param <T>
*/
public class BeanHandler<T> implements ResultSetHandler<T> {private Class beanClass;public BeanHandler(Class beanClass) {this.beanClass = beanClass;}@Overridepublic T handler(ResultSet resultSet) throws SQLException, IllegalAccessException, InstantiationException, InvocationTargetException {ResultSetMetaData metaData = resultSet.getMetaData();int columnCount = metaData.getColumnCount();if(resultSet.next()){T t = (T) beanClass.newInstance();for (int i = 0; i < columnCount; i++) {String columnName = metaData.getColumnName(i + 1);Object columnValue = resultSet.getObject(columnName);BeanUtils.copyProperty(t,columnName,columnValue);}return t;}return null;}
}
/**
* 结果集处理接口的实现类
* 处理结果集并封装为集合
* @param <T>
*/
public class BeanListHandler<T> implements ResultSetHandler<List<T>> {private Class beanListClass;public BeanListHandler(Class beanListClass) {this.beanListClass = beanListClass;}@Overridepublic List<T> handler(ResultSet resultSet) throws SQLException, IllegalAccessException, InstantiationException, InvocationTargetException {List<T> list = new ArrayList<>();ResultSetMetaData metaData = resultSet.getMetaData();int columnCount = metaData.getColumnCount();while(resultSet.next()){T t = (T) beanListClass.newInstance();for (int i = 0; i < columnCount; i++) {String columnName = metaData.getColumnName(i + 1);T columnValue = (T) resultSet.getObject(columnName);BeanUtils.copyProperty(t,columnName,columnValue);list.add(t);}}return list;}
}
/**
* 结果集处理接口的实现类
* 操作结果集并封装数组对象
*
* @param <T>
*/
public class ArrayHandler<T> implements ResultSetHandler<T> {//行处理器private RowProcessor<T> rowProcessor;public ArrayHandler(RowProcessor<T> rowProcessor) {this.rowProcessor = rowProcessor;}@Overridepublic T handler(ResultSet resultSet) throws SQLException, IllegalAccessException, InstantiationException, InvocationTargetException {return  rowProcessor.toArray(resultSet);}
}

其中,行处理的逻辑是拿到结果集后交给自定义的行处理接口实现类方法处理,这样做的目的是统一代码的格式(见后续web项目中的使用)。

processor包(处理行):

/**
* 行处理器的实现类,将结果集中的数据获取并封装成数组
* @param <T>
*/
public class BaskRowProcessor<T> implements RowProcessor<T[]> {private Class arrayTClass;public BaskRowProcessor(Class arrayTClass) {this.arrayTClass = arrayTClass;}@Overridepublic T[] toArray(ResultSet resultSet) throws SQLException {//创建数据容器List<T> list = new ArrayList<>();//遍历结果集while(resultSet.next()){T t = (T) resultSet.getObject(1);list.add(t);}if(list.size()==0||resultSet==null){throw new RuntimeException("参数异常无法获取泛型数组");}else {//创建数组T[] ts = (T[]) Array.newInstance(arrayTClass, list.size());//添加数据for (int i = 0; i < list.size(); i++) {ts[i] = list.get(i);}return ts;}}
}

通过结果集返回封装好的对象、列表、数组的功能已实现,接下类实现操作数据库返回结果集的功能:

core包:

public class QueryRunner {private DruidDataSource dataSource;private ThreadLocal<Connection> local = new ThreadLocal<>();public QueryRunner(DruidDataSource dataSource) {this.dataSource = dataSource;}//获取连接private Connection getConnection() throws SQLException {Connection connection = local.get();if(connection==null){connection = dataSource.getConnection();local.set(connection);}return connection;}//配置参数private PreparedStatement getPreparedStatement(Connection connection,String sql,Object... args) throws SQLException {PreparedStatement statement = connection.prepareStatement(sql);for (int i = 0; i < args.length; i++) {statement.setObject(i+1,args[i]);}return statement;}//更新操作public int update(String sql,Object... args) throws SQLException {Connection connection = getConnection();PreparedStatement statement = getPreparedStatement(connection, sql, args);int i = statement.executeUpdate();return i;}//查询操作public <T> T query(ResultSetHandler<T> handler,String sql,Object... args) throws SQLException, InvocationTargetException, IllegalAccessException, InstantiationException {Connection connection = getConnection();PreparedStatement statement = getPreparedStatement(connection, sql, args);ResultSet resultSet = statement.executeQuery();T t = handler.handler(resultSet);return t;}}

至此,框架搭建完毕,接下来以用户创建的web项目举例:

数据库工具类:

public class JDBCUtils {//德鲁伊连接池引用private static DruidDataSource dataSource;static{//创建德鲁伊连接池dataSource = new DruidDataSource();//获取配置信息Properties properties = new Properties();try {properties.load(JDBCUtils.class.getClassLoader().getResourceAsStream("DBConfig.properties"));String username = properties.getProperty("username");String password = properties.getProperty("password");String url = properties.getProperty("url");String driverName = properties.getProperty("driverName");dataSource.setUrl(url);dataSource.setUsername(username);dataSource.setPassword(password);dataSource.setDriverClassName(driverName);} catch (IOException e) {throw new RuntimeException(e);}}public static QueryRunner getQueryRunner(){return new QueryRunner(dataSource);}
}

controller层中对数据库进行操作:

//---操作数据库---------------------------------------------------------------------------------------
@RequestMapping("/test13.action")
public void test13() throws SQLException {//操作更新语句JDBCUtils.getQueryRunner().update("update user set password = ? where username = ?","123456","zs");
}
@RequestMapping("/test14.action")
public void test14() throws SQLException, InvocationTargetException, IllegalAccessException, InstantiationException {//查询对象User zs = JDBCUtils.getQueryRunner().query(new BeanHandler<>(User.class), "select * from user where username = ?", "zs");System.out.println(zs);
}
@RequestMapping("/test15.action")
public void test15() throws SQLException, InvocationTargetException, IllegalAccessException, InstantiationException {//查询对象列表List<User> users = JDBCUtils.getQueryRunner().query(new BeanListHandler<>(User.class), "select * from user");for (User user : users) {System.out.println("列表中的对象:"+user);}
}
@RequestMapping("/test16.action")
public void test16() throws SQLException, InvocationTargetException, IllegalAccessException, InstantiationException {//查询所有的usernameString[] usernames = JDBCUtils.getQueryRunner().query(new ArrayHandler<>(new BaskRowProcessor<>(String.class)),"select username from user");System.out.println(usernames);
}

遇到的问题:

1.无法找到数据库连接的配置文件DBCfig.properties

解决方案:将配置文件放到src/main/resources文件夹中去,这样 资源文件才会被复制到target/classes 目录下。

相关文章:

手写Spring-MVC之前后置处理器与异常处理、数据库框架

Day48 手写Spring-MVC之前后置处理器与异常处理 前后置处理器 概念&#xff1a;从服务器获取的JSON数据可能是加密后的&#xff0c;因此服务端获取的时候需要进行解密&#xff08;前置处理器&#xff09;。 而从服务器传出的JSON数据可能需要加密&#xff0c;因此需要在处理返…...

学习笔记(linux高级编程)11

进程间通信 》信号通信 应用&#xff1a;异步通信。 中断&#xff0c;&#xff0c; 1~64&#xff1b;32应用编程。 如何响应&#xff1a; Term Default action is to terminate the process. Ign Default action is to ignore the signal. wait Core Default action is …...

vite+vue3+nginx配置统一公共前缀

方案1&#xff1a;重定向 server {listen 80;server_name localhost;location / {root /usr/share/nginx/html;index index.html;}location /music/ {proxy_pass http://127.0.0.1:80/;} }方案2&#xff1a;vitenginx双重配置 在方案1中&#xff0c;我们虽然能够实现 通过 …...

android 国内下载Gradle源

在中国使用 Gradle 时&#xff0c;可以配置使用一些国内的镜像源&#xff0c;以提高下载速度和稳定性。以下是几个常用的 Gradle 镜像源地址&#xff1a; 配置 gradle-wrapper.properties 文件: 阿里云: distributionUrlhttps\://services.gradle.org/distributions/gradle-7.…...

mysql8一键安装脚本(linux) 拿走即用

创建一个shell文件,将下面的代码放里面去,然后放到linux服务器上运行就可以了 #!/bin/bash#---------------------* # * # 2021-10-08 * # install mysql-8 * # * #---------------------*route=/usr #包存放路径 mys…...

C# 开发Winform DataGridView的增删改查实战

在C# WinForms应用程序中&#xff0c;DataGridView控件是一个非常强大的工具&#xff0c;用于显示和编辑表格数据。下面我将详细介绍如何在WinForm应用程序中使用DataGridView实现基本的数据库操作&#xff1a;增加、删除、修改和查询&#xff08;CRUD&#xff09;。 第一步&a…...

CentOS 7镜像列表服务下线,还想继续使用该怎么办?

目录 问题和解决方法 mirrorlist.centos.org 作用 vault.centos.org 作用 CentOS 7的生命周期已经在2024年6月30日终止&#xff08;End of Life&#xff0c;EOL&#xff09;&#xff0c;官方将不再对该版本进行问题修复、功能更新以及其他形式的维护支持。这意味着使用 Cent…...

代码随想录训练营第二十八天 122买卖股票的最佳时间II 55跳跃游戏 45跳跃游戏II 1005K次取反后最大化的数组和

第一题&#xff1a; 原题链接&#xff1a;122. 买卖股票的最佳时机 II - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a; 这题十分简单&#xff0c;就是把相邻天数的金额相减&#xff0c;如果发现大于0就加到res中&#xff0c;返回res即可 代码如下&#xff1a; …...

在node环境使用MySQL

什么是Sequelize? Sequelize是一个基于Promise的NodeJS ORM模块 什么是ORM? ORM(Object-Relational-Mapping)是对象关系映射 对象关系映射可以把JS中的类和对象&#xff0c;和数据库中的表和数据进行关系映射。映射之后我们就可以直接通过类和对象来操作数据表和数据了, 就…...

spdlog一个非常好用的C++日志库(四): 源码分析之logger类

目录 1.简介 2.类图关系 3.logger数据成员 4.logger函数成员 4.1.构造与析构 4.1.1.构造函数 4.1.2.拷贝构造、移动构造 4.2.交换操作 4.3.log()记录日志消息 4.3.1.格式串 4.3.2.普通字符串 4.3.3.日志级别 4.3.4.宽字符支持 4.4.sink_it_&#xff1a;将log消息…...

逻辑这回事(七)---- 器件基础

Xilinx FPGA创建了先进的硅模块(ASMBL)架构,以实现FPGA具有针对不同应用程序领域优化的各种功能组合的平台。通过这一创新,Xilinx提供了更多的设备选择,使客户能够为其特定设计选择具有正确的功能和功能组合的FPGA。ASMBL体系结构通过以下方式突破了传统的设计障碍:消除几…...

中俄汽车产业链合作前景广阔,东方经济论坛助力双边合作与创新

随着中国汽车零部件企业的竞争力和创新能力不断增强&#xff0c;中国汽车及零部件行业在俄罗斯的市场份额和品牌影响力显著提升&#xff0c;中俄两国在汽车产业链上的合作展现出巨大的潜力和广阔的前景。2024年5月&#xff0c;俄罗斯乘用车新车销量达到12.8万辆&#xff0c;同比…...

第六篇:精通Docker Compose:打造高效的多容器应用环境

精通Docker Compose&#xff1a;打造高效的多容器应用环境 1. 引言 1.1 目的与重要性 在现代软件开发中&#xff0c;随着应用程序的复杂性不断增加&#xff0c;传统的单一容器部署方式已无法满足需求。Docker Compose作为一种强大的工具&#xff0c;专门用于定义和运行多容器…...

C++视觉开发 一.OpenCV环境配置

一.OpenCV安装环境配置 1.OpenCV安装 &#xff08;1&#xff09;下载 官方下载链接&#xff1a;http://opencv.org/releases 这边选择需要的版本&#xff0c;我是在windows下的4.9.0。&#xff08;科学上网下载很快&#xff0c;否则可能会有点慢&#xff09; (2)安装 双击下…...

大数据面试题之Kafka(3)

目录 Kafka支持什么语义&#xff0c;怎么实现ExactlyOnce? Kafka的消费者和消费者组有什么区别?为什么需要消费者组? Kafka producer的写入数据过程? Kafka producer的ack设署 Kafka的ack机制&#xff0c;解决了什么问题? Kafka读取消息是推还是拉的模式?有什…...

视频监控平台web客户端的免密查看视频页:在PC浏览器上如何调试手机上的前端网页(PC上的手机浏览器的开发者工具)

目录 一、手机上做前端页面开发调试 1、背景 2、视频监控平台AS-V1000的视频分享页 3、调试手机前端页面代码的条件 二、手机端的准备工作 1、手机准备 2、手机的开发者模式 3、PC和手机的连接 &#xff08;1&#xff09;进入调试模式 &#xff08;2&#xff09;选择…...

力扣2488.统计中位数为 K 的子数组

力扣2488.统计中位数为 K 的子数组 等价转换 子数组为奇数 &#xff1a; 左小 右小 左大 右大 左小 – 左大 右大 – 右小 子数组为偶数 &#xff1a; 左小 右小 左大 右大 – 1 左小 – 左大 右大 – 右小 - 1提示中说明k为两数中左边那个 先从k的下标pos开始往左逆序…...

Zabbix对接Elasticsearch(ES)数据库(未成功)

0.需求分析 不管zabbix的后端数据库是oracle还是mysql&#xff0c;当zabbix监控的量级达到了一定程度后&#xff0c;那么对数据库的性能是一个非常严峻的挑战。特别是对历史数据的查询&#xff0c;将会变得非常非常的慢&#xff0c;别告诉我可以建索引优化&#xff0c;当量级达…...

【unity实战】使用Unity实现动作游戏的攻击 连击 轻重攻击和打击感

最终效果 文章目录 最终效果前言素材下载&#xff1a;玩家移动跳跃控制攻击动画配置轻攻击重攻击 攻击时禁止移动和攻击移动补偿敌人击退和播放受击动画受击特效攻击停顿和屏幕震动局部顿帧&#xff08;补充&#xff09;参考源码完结 前言 注意本文为自己的学习记录笔记&#…...

ELK 企业实战7

ELKkafkafilebeat企业内部日志分析系统 1、组件介绍 1、Elasticsearch&#xff1a; 是一个基于Lucene的搜索服务器。提供搜集、分析、存储数据三大功能。它提供了一个分布式多用户能力的全文搜索引擎&#xff0c;基于RESTful web接口。Elasticsearch是用Java开发的&#xff…...

vscode(仍待补充)

写于2025 6.9 主包将加入vscode这个更权威的圈子 vscode的基本使用 侧边栏 vscode还能连接ssh&#xff1f; debug时使用的launch文件 1.task.json {"tasks": [{"type": "cppbuild","label": "C/C: gcc.exe 生成活动文件"…...

2024年赣州旅游投资集团社会招聘笔试真

2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...

Opencv中的addweighted函数

一.addweighted函数作用 addweighted&#xff08;&#xff09;是OpenCV库中用于图像处理的函数&#xff0c;主要功能是将两个输入图像&#xff08;尺寸和类型相同&#xff09;按照指定的权重进行加权叠加&#xff08;图像融合&#xff09;&#xff0c;并添加一个标量值&#x…...

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility 1. 实验室环境1.1 实验室环境1.2 小测试 2. The Endor System2.1 部署应用2.2 检查现有策略 3. Cilium 策略实体3.1 创建 allow-all 网络策略3.2 在 Hubble CLI 中验证网络策略源3.3 …...

SpringBoot+uniapp 的 Champion 俱乐部微信小程序设计与实现,论文初版实现

摘要 本论文旨在设计并实现基于 SpringBoot 和 uniapp 的 Champion 俱乐部微信小程序&#xff0c;以满足俱乐部线上活动推广、会员管理、社交互动等需求。通过 SpringBoot 搭建后端服务&#xff0c;提供稳定高效的数据处理与业务逻辑支持&#xff1b;利用 uniapp 实现跨平台前…...

土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等

&#x1f50d; 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术&#xff0c;可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势&#xff0c;还能有效评价重大生态工程…...

实现弹窗随键盘上移居中

实现弹窗随键盘上移的核心思路 在Android中&#xff0c;可以通过监听键盘的显示和隐藏事件&#xff0c;动态调整弹窗的位置。关键点在于获取键盘高度&#xff0c;并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...

AspectJ 在 Android 中的完整使用指南

一、环境配置&#xff08;Gradle 7.0 适配&#xff09; 1. 项目级 build.gradle // 注意&#xff1a;沪江插件已停更&#xff0c;推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...

LeetCode - 199. 二叉树的右视图

题目 199. 二叉树的右视图 - 力扣&#xff08;LeetCode&#xff09; 思路 右视图是指从树的右侧看&#xff0c;对于每一层&#xff0c;只能看到该层最右边的节点。实现思路是&#xff1a; 使用深度优先搜索(DFS)按照"根-右-左"的顺序遍历树记录每个节点的深度对于…...

Mysql中select查询语句的执行过程

目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析&#xff08;Parser&#xff09; 2.4、执行sql 1. 预处理&#xff08;Preprocessor&#xff09; 2. 查询优化器&#xff08;Optimizer&#xff09; 3. 执行器…...