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

Day47

手写Spring-MVC之DispatcherServlet

DispatcherServlet的思路:

前端传来URI,在TypeContainer容器类中通过uri得到对应的类描述类对象(注意:在监听器封装类描述类对象的时候,是针对于每一个URI进行封装的,也就是说每一个方法(uri)都有一个类描述类),通过类描述类对象获得类对象和方法描述类对象,再通过方法描述类对象获得方法对象和参数描述类对象。对参数描述类对象进行处理获得参数数组,然后通过method.invoke()执行方法,并返回值,最后处理返回值。

public class DispatherServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doPost(request, response);}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {request.setCharacterEncoding("UTF-8");response.setContentType("text/html;charSet=UTF-8");String uri = request.getRequestURI();HashMap<String, BeanDefinition> maps = TypeContainer.getMaps();BeanDefinition beanDefinition = maps.get(uri);if(beanDefinition==null){//System.out.println("没找到该uri下的类描述类");throw new FrameWorkException(ResponseCode.REQUEST_PATH_EXCEPTION.getCode(),ResponseCode.REQUEST_PATH_EXCEPTION.getMessage());}Object t = beanDefinition.getT();MethodDefinition methodDefinition = beanDefinition.getMethodDefinition();Method method = methodDefinition.getMethod();method.setAccessible(true);Model model = new Model();List<ParameterDefinition> parameterDefinitions = methodDefinition.getParameterDefinitions();Object[] args = handlerParameterArgs(parameterDefinitions, request, response,model);try{//调用Controller层里的某个方法Object returnVal = method.invoke(t, args);if(returnVal!=null){//处理返回值handlerReturnVal(methodDefinition,returnVal,request,response,model);}}catch (Exception e){System.out.println("出现异常了,处理全局异常");}}public void handlerReturnVal(MethodDefinition methodDefinition,Object returnVal,HttpServletRequest request,HttpServletResponse response,Model model) throws ServletException, IOException {if(methodDefinition.isResponseBodyHasOrNot()){//返回数据为JSON格式String jsonString = JSON.toJSONString(returnVal);sendResponse(response,jsonString);}else if(returnVal.getClass()==String.class){//如果返回值是字符串,先添加请求数据,再跳转(转发)handlerRequestVal(request,model.getMaps());jumpPage(returnVal,request,response);} else if (returnVal.getClass() == ModelAndView.class) {//如果返回值是ModelAndView,添加请求后跳转(转发)ModelAndView modelAndView = (ModelAndView) returnVal;handlerRequestVal(request,modelAndView.getMaps());jumpPage(modelAndView.getViewName(),request,response);}}public void sendResponse(HttpServletResponse response,String jsonString) throws IOException {response.getWriter().write(jsonString);}public void handlerRequestVal(HttpServletRequest request,Map<String,Object> maps){Set<Map.Entry<String, Object>> entries = maps.entrySet();for (Map.Entry<String, Object> entry : entries) {String key = entry.getKey();Object value = entry.getValue();request.setAttribute(key,value);}}public void jumpPage(Object returnVal,HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {String str = (String) returnVal;request.getRequestDispatcher(str).forward(request,response);}public Object[] handlerParameterArgs(List<ParameterDefinition> parameterDefinitions,HttpServletRequest request,HttpServletResponse response,Model model){if(parameterDefinitions==null){return null;}Object[] args = new Object[parameterDefinitions.size()];for (ParameterDefinition parameterDefinition : parameterDefinitions) {Class<?> clazz = parameterDefinition.getClazz();//获取参数的class对象String name = parameterDefinition.getName();//获取参数的名字int index = parameterDefinition.getIndex();//获取参数的下标if(judgeTypeIsJavaOrNot(clazz)){//判断是否是Java常用数据类型args = handlerJavaType(request, name, clazz, args, index);} else if (clazz==HttpServletRequest.class) {//判断是否是请求类型args[index] = request;} else if (clazz==HttpServletResponse.class) {//判断是否是响应类型args[index] = response;} else if (clazz == String[].class) {//判断是否是数组类型String[] arr = request.getParameterValues(name);args[index] = arr;} else if (clazz == List.class) {//判断是否是List集合类型handlerListType(request,parameterDefinition,args,index);} else if (clazz== Model.class) {//判断是否是Model类型args[index] = model;}else{//判断是否是自定义类型handlerOtherType(parameterDefinition,request,args,index);}}return args;}public void handlerOtherType(ParameterDefinition parameterDefinition,HttpServletRequest request,Object[] args,int index){try {if(parameterDefinition.isRequestBodyHasOrNot()){//如果返回的是JSON格式数据BufferedReader br = request.getReader();StringBuffer sb = new StringBuffer();char[] cs = new char[1024];int len;while((len= br.read(cs))!=-1){sb.append(cs,0,len);}String jsonStr = sb.toString();Object obj = JSON.parseObject(jsonStr,parameterDefinition.getClazz());args[index] = obj;}else{Object obj = parameterDefinition.getClazz().newInstance();Map<String, String[]> parameterMap = request.getParameterMap();BeanUtils.populate(obj,parameterMap);args[index] = obj;}} catch (InstantiationException | IllegalAccessException | InvocationTargetException | IOException e) {throw new RuntimeException(e);}}public void handlerListType(HttpServletRequest request,ParameterDefinition parameterDefinition,Object[] args,int index){Map<String, String[]> parameterMap = request.getParameterMap();//key --- Value//user[0].username --- new String[]{"pengyuyan"};//user[0].password --- new String[]{"123123"};//user[1].username --- new String[]{"wuyanzu"};//user[1].password --- new String[]{"123456"};//获取泛型类型Type pt = parameterDefinition.getActualTypeArguments()[0];String className = pt.getTypeName();Class<?> aClass = null;try {aClass = Class.forName(className);} catch (ClassNotFoundException e) {throw new RuntimeException(e);}ArrayList<Object> list = new ArrayList<>();Set<Map.Entry<String, String[]>> entries = parameterMap.entrySet();for (Map.Entry<String, String[]> entry : entries) {String key = entry.getKey();//user[0].usernameString[] value = entry.getValue();//new String[]{"guodan"}int i = Integer.parseInt(key.substring(key.indexOf("[") + 1, key.indexOf("]")));//集合中的下标String fieldName = key.substring(key.indexOf(".") + 1);//集合中元素对象的属性名String fieldValue = value[0];//获取集合中元素对象里的属性值Object o =null;try{o = list.get(i);}catch (IndexOutOfBoundsException e){//该集合下标上没有元素try {o = aClass.newInstance();//创建对象list.add(o);//添加对象} catch (InstantiationException | IllegalAccessException ex) {throw new RuntimeException(ex);}}try {BeanUtils.setProperty(o,fieldName,fieldValue);//设置对象属性} catch (IllegalAccessException | InvocationTargetException ex) {throw new RuntimeException(ex);}args[index] = list;}}public Object[] handlerJavaType(HttpServletRequest request,String name,Class<?> clazz,Object[] args,int index){String parameter = request.getParameter(name);if(clazz==byte.class || clazz==Byte.class){args[index] =  Byte.parseByte(parameter);}else if(clazz==short.class || clazz==Short.class){args[index] = Short.parseShort(parameter);}else if(clazz==int.class || clazz==Integer.class){args[index] = Integer.parseInt(parameter);}else if(clazz==long.class || clazz==Long.class){args[index] = Long.parseLong(parameter);}else if(clazz==float.class || clazz==Float.class){args[index] = Float.parseFloat(parameter);}else if(clazz==double.class || clazz==Double.class){args[index] = Double.parseDouble(parameter);}else if(clazz==char.class || clazz==Character.class){args[index] = parameter.toCharArray()[0];}else if(clazz==boolean.class || clazz==Boolean.class){args[index] = Boolean.parseBoolean(parameter);}if(clazz==String.class){args[index] = parameter;}return args;}public boolean judgeTypeIsJavaOrNot(Class<?> clazz){if(clazz==byte.class || clazz==Byte.class || clazz==short.class || clazz==Short.class || clazz==int.class || clazz== Integer.class || clazz==long.class || clazz== Long.class || clazz==float.class || clazz== Float.class || clazz==double.class || clazz==Double.class || clazz==char.class || clazz== Character.class || clazz==boolean.class || clazz==Boolean.class || clazz==String.class){return true;}return false;}}

其中的细节和难点:

1.处理参数

处理参数的思路

获取参数的class对象、参数的名字、参数的下标。然后根据参数名利用请求从前端获取对应的参数对象并添加到参数数组args[]中,位置为对应下标的位置。

其中需要根据不同的参数类型分别进行处理

如果是Java常用数据类型(8大基本数据类型+string),则进行装箱处理(String 直接添加),然后添加到参数数组args[]中;

public Object[] handlerJavaType(HttpServletRequest request,String name,Class<?> clazz,Object[] args,int index){String parameter = request.getParameter(name);if(clazz==byte.class || clazz==Byte.class){args[index] =  Byte.parseByte(parameter);}else if(clazz==short.class || clazz==Short.class){args[index] = Short.parseShort(parameter);}else if(clazz==int.class || clazz==Integer.class){args[index] = Integer.parseInt(parameter);}else if(clazz==long.class || clazz==Long.class){args[index] = Long.parseLong(parameter);}else if(clazz==float.class || clazz==Float.class){args[index] = Float.parseFloat(parameter);}else if(clazz==double.class || clazz==Double.class){args[index] = Double.parseDouble(parameter);}else if(clazz==char.class || clazz==Character.class){args[index] = parameter.toCharArray()[0];}else if(clazz==boolean.class || clazz==Boolean.class){args[index] = Boolean.parseBoolean(parameter);}if(clazz==String.class){args[index] = parameter;}return args;}public boolean judgeTypeIsJavaOrNot(Class<?> clazz){if(clazz==byte.class || clazz==Byte.class || clazz==short.class || clazz==Short.class || clazz==int.class || clazz== Integer.class || clazz==long.class || clazz== Long.class || clazz==float.class || clazz== Float.class || clazz==double.class || clazz==Double.class || clazz==char.class || clazz== Character.class || clazz==boolean.class || clazz==Boolean.class || clazz==String.class){return true;}return false;}

如果是请求/响应类型的数据则直接添加到数组中;

如果是数组,则获取的是数组对象,并添加到args[]中;

String[] arr = request.getParameterValues(name);
args[index] = arr;

如果是List集合类型(前端设计页面的时候会规范返回的格式),则要先通过获取泛型(这里需要回到参数描述类中添加泛型类型这一属性)来获取集合里面元素的类型,然后获得传入的数据map,遍历每行数据拼接出元素的名字、下标和属性值。然后通过元素的类型利用反射创建类对象,创建List集合并添加对象,设置对象属性值(注意:这里有一个小技巧,利用try-catch先获取空集合里面的元素,在出现没有元素的异常后在catch中创建对象,并添加到list中。这样在同一个元素下标中的list位置就有了对象,就不会进入到异常处理中创建对象,而是在后续进行属性设置,直到下标改变再重新创建对象。),最后将List集合添加到args[]中;

参数类型描述类添加泛型属性(相应的需要修改监听器封装部分的代码):

@NoArgsConstructor
@AllArgsConstructor
@Data
public class ParameterDefinition {private String name;//参数名private Class<?> clazz;//参数类型private int index;//参数下标private Type[] actualTypeArguments;//参数泛型的数组}

监听器相关部分:

if(parameters.length!=0){for (int i = 0; i < parameters.length; i++) {//获取参数上泛型数组Type parameterizedType = parameters[i].getParameterizedType();Type[] actualTypeArguments = null;try{ParameterizedType pt = (ParameterizedType) parameterizedType;if(pt!=null){actualTypeArguments = pt.getActualTypeArguments();}}catch (Exception e){}String parameterName = parameters[i].getName();//获取参数名Class<?> parameterType = parameters[i].getType();//参数类型int index = i;//下标ParameterDefinition parameterDefinition = new ParameterDefinition(parameterName, parameterType, index,actualTypeArguments);//封装参数描述类对象parameterList.add(parameterDefinition);}
}

DispatcherServlet:

 public void handlerListType(HttpServletRequest request,ParameterDefinition parameterDefinition,Object[] args,int index){Map<String, String[]> parameterMap = request.getParameterMap();//key --- Value//user[0].username --- new String[]{"pengyuyan"};//user[0].password --- new String[]{"123123"};//user[1].username --- new String[]{"wuyanzu"};//user[1].password --- new String[]{"123456"};//获取泛型类型Type pt = parameterDefinition.getActualTypeArguments()[0];String className = pt.getTypeName();Class<?> aClass = null;try {aClass = Class.forName(className);} catch (ClassNotFoundException e) {throw new RuntimeException(e);}ArrayList<Object> list = new ArrayList<>();Set<Map.Entry<String, String[]>> entries = parameterMap.entrySet();for (Map.Entry<String, String[]> entry : entries) {String key = entry.getKey();//user[0].usernameString[] value = entry.getValue();//new String[]{"guodan"}int i = Integer.parseInt(key.substring(key.indexOf("[") + 1, key.indexOf("]")));//集合中的下标String fieldName = key.substring(key.indexOf(".") + 1);//集合中元素对象的属性名String fieldValue = value[0];//获取集合中元素对象里的属性值Object o =null;try{o = list.get(i);}catch (IndexOutOfBoundsException e){//该集合下标上没有元素try {o = aClass.newInstance();//创建对象list.add(o);//添加对象} catch (InstantiationException | IllegalAccessException ex) {throw new RuntimeException(ex);}try {BeanUtils.setProperty(o,fieldName,fieldValue);//设置对象属性} catch (IllegalAccessException | InvocationTargetException ex) {throw new RuntimeException(ex);}args[index] = list;}}}

如果是Model类型(model需要创建,属性只有一个map,用来存放属性的名和值,同时有一个setAttribute()方法,即添加键值。model是用来跳转页面的,将前端的数据封装到自己的容器属性中,然后通过请求传递给前端。同时创建一个ModelAndView类,除了容器属性外还有一个字符串类型的路径,功能和model类似,但适用于需要动态指定跳转页面的复杂业务),则直接添加到args[]中;

public class Model {private Map<String,Object> maps= new ConcurrentHashMap<>();public void setAttributes(String key,Object value){maps.put(key,value);}public Map<String, Object> getMaps() {return maps;}
}public class ModelAndView {private Map<String,Object> maps = new HashMap<>();private String viewName;public void setAttributes(String key,Object value){maps.put(key,value);}public Map<String, Object> getMaps() {return maps;}public void setMaps(Map<String, Object> maps) {this.maps = maps;}public String getViewName() {return viewName;}public void setViewName(String viewName) {this.viewName = viewName;}
}

如果是自定义类型,则用反射创建对象,将数据利用BeanUtils工具类的populate方法将参数整合到类对象中,并添加到args[]中。

public void handlerOtherType(Class<?> clazz,HttpServletRequest request,Object[] args,int index){try {Object obj = clazz.newInstance();Map<String, String[]> parameterMap = request.getParameterMap();BeanUtils.populate(obj,parameterMap);args[index] = obj;} catch (InstantiationException e) {throw new RuntimeException(e);} catch (IllegalAccessException e) {throw new RuntimeException(e);} catch (InvocationTargetException e) {throw new RuntimeException(e);}}
2.处理返回值

思路

如果返回值不为空,即该方法有返回值,则要对返回值的类型进行判断,然后添加请求数据,再进行转发。

不同类型返回值的处理:

这里返回值有两种情况,本质是跳转逻辑的不同。一种是字符串类型的路径,controller层会将前端的数据封装到Model对象中,servlet就获取model的map属性,遍历里面的数据利用请求设置到页面,而返回的字符串路径直接用来跳转,一种是ModelAndView类型的对象,同样遍历map属性,将属性通过请求设置到页面,并利用类里面的路径属性进行跳转。

public void handlerReturnVal(Object returnVal,HttpServletRequest request,HttpServletResponse response,Model model) throws ServletException, IOException {if(returnVal.getClass()==String.class){//如果返回值是字符串,先添加请求数据,再跳转(转发)handlerRequestVal(request,model.getMaps());jumpPage(returnVal,request,response);} else if (returnVal.getClass() == ModelAndView.class) {//如果返回值是ModelAndView,添加请求后跳转(转发)ModelAndView modelAndView = (ModelAndView) returnVal;handlerRequestVal(request,modelAndView.getMaps());jumpPage(modelAndView.getViewName(),request,response);}}//遍历Model/ModelAndView类对象的容器属性,把属性数据添加到请求public void handlerRequestVal(HttpServletRequest request,Map<String,Object> maps){Set<Map.Entry<String, Object>> entries = maps.entrySet();for (Map.Entry<String, Object> entry : entries) {String key = entry.getKey();Object value = entry.getValue();request.setAttribute(key,value);}}//页面跳转public void jumpPage(Object returnVal,HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {String str = (String) returnVal;request.getRequestDispatcher(str).forward(request,response);}
3.处理JSON数据

如果接收的是JSON格式数据,那么如何识别数据以及怎样传递数据呢?

**思路:**参数注解用来识别判断客户端传递过来的数据是否是JSON数据,需要将JSON格式的字符串解析成参数类型的数据;方法注解表示服务器传输过去的数据为JSON数据,需要将返回值解析成JSON格式的字符串。

具体实现:添加RequestBody注解用在参数上,和ResponseBody注解用在方法上。

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestBody {
}@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ResponseBody {
}

DispatcherServlet中在创建参数数组的时候判断参数上是否有注解,所以要在参数描述类中添加用来判断是否参数有注解的布尔值。在方法描述类中添加布尔值判断方法上是否有注解。(相应的要修改监听器中封装类的那部分代码)

参数描述类和方法描述类修改:

@NoArgsConstructor
@AllArgsConstructor
@Data
public class ParameterDefinition {private String name;//参数名private Class<?> clazz;//参数类型private int index;//参数下标private Type[] actualTypeArguments;//参数泛型的数组private boolean requestBodyHasOrNot;//参数上是否有@RequestBody注解}@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注解
}

监听器相关部分:

if(parameters.length!=0){for (int i = 0; i < parameters.length; i++) {//获取参数上泛型数组Type parameterizedType = parameters[i].getParameterizedType();Type[] actualTypeArguments = null;try{ParameterizedType pt = (ParameterizedType) parameterizedType;if(pt!=null){actualTypeArguments = pt.getActualTypeArguments();}}catch (Exception e){}String parameterName = parameters[i].getName();//获取参数名Class<?> parameterType = parameters[i].getType();//参数类型int index = i;//下标//获取参数上是否有@RequestBody注解boolean requestBodyHasOrNot = false;RequestBody requestBody = parameters[i].getAnnotation(RequestBody.class);if(requestBody!=null){requestBodyHasOrNot = true;}ParameterDefinition parameterDefinition = new ParameterDefinition(parameterName, parameterType, index,actualTypeArguments,requestBodyHasOrNot);//封装参数描述类对象parameterList.add(parameterDefinition);}
}//获取方法上是否有@ResponseBody注解
boolean responseBodyHasOrNot = false;
ResponseBody responseBody = method.getAnnotation(ResponseBody.class);
if(responseBody!=null){responseBodyHasOrNot = true;
}
MethodDefinition methodDefinition = new MethodDefinition(sonUri, methodName, method, returnType, parameterList,responseBodyHasOrNot);//封装方法描述类对象

servlet中在自定义类型中要判断是否有对应的注解,如果有就解析json数据封装成对象然后添加到数组。

public void handlerOtherType(ParameterDefinition parameterDefinition,HttpServletRequest request,Object[] args,int index){try {if(parameterDefinition.isRequestBodyHasOrNot()){//如果返回的是JSON格式数据BufferedReader br = request.getReader();StringBuffer sb = new StringBuffer();char[] cs = new char[1024];int len;while((len= br.read(cs))!=-1){sb.append(cs,0,len);}String jsonStr = sb.toString();Object obj = JSON.parseObject(jsonStr,parameterDefinition.getClazz());args[index] = obj;}else{Object obj = parameterDefinition.getClazz().newInstance();Map<String, String[]> parameterMap = request.getParameterMap();BeanUtils.populate(obj,parameterMap);args[index] = obj;}} catch (InstantiationException | IllegalAccessException | InvocationTargetException | IOException e) {throw new RuntimeException(e);}
}

在方法处理中判断是否有对应的方法注解,如果有就将返回的JSON转换为字符串写回前端。

public void handlerReturnVal(MethodDefinition methodDefinition,Object returnVal,HttpServletRequest request,HttpServletResponse response,Model model) throws ServletException, IOException {if(methodDefinition.isResponseBodyHasOrNot()){//返回数据为JSON格式String jsonString = JSON.toJSONString(returnVal);sendResponse(response,jsonString);}else if(returnVal.getClass()==String.class){//如果返回值是字符串,先添加请求数据,再跳转(转发)handlerRequestVal(request,model.getMaps());jumpPage(returnVal,request,response);} else if (returnVal.getClass() == ModelAndView.class) {//如果返回值是ModelAndView,添加请求后跳转(转发)ModelAndView modelAndView = (ModelAndView) returnVal;handlerRequestVal(request,modelAndView.getMaps());jumpPage(modelAndView.getViewName(),request,response);}}public void sendResponse(HttpServletResponse response,String jsonString) throws IOException {response.getWriter().write(jsonString);}
注意:

1.@WebServlet不要加,在web项目中配置。因为是在项目中使用到,而不是全局。

postman

代替浏览器发送请求的工具

请求里面输入url,body选择raw->JSON,发送一个JSON格式的字符串

测试能否成功。

相关文章:

Day47

Day47 手写Spring-MVC之DispatcherServlet DispatcherServlet的思路&#xff1a; 前端传来URI&#xff0c;在TypeContainer容器类中通过uri得到对应的类描述类对象&#xff08;注意&#xff1a;在监听器封装类描述类对象的时候&#xff0c;是针对于每一个URI进行封装的&#x…...

【面试系列】后端开发工程师 高频面试题及详细解答

欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;欢迎订阅相关专栏&#xff1a; ⭐️ 全网最全IT互联网公司面试宝典&#xff1a;收集整理全网各大IT互联网公司技术、项目、HR面试真题. ⭐️ AIGC时代的创新与未来&#xff1a;详细讲解AIGC的概念、核心技术、…...

mac|浏览器链接不上服务器但可以登微信

千万千万千万不要没有关梯子直接关机&#xff0c;不然就会这样子呜呜呜 设置-网络&#xff0c;点击三个点--选择--位置--编辑位置&#xff08;默认是自动&#xff09; 新增一个&#xff0c;然后选中点击完成 这样就可以正常上网了...

Spring Cloud Alibaba之负载均衡组件Ribbon

一、什么是负载均衡&#xff1f; &#xff08;1&#xff09;概念&#xff1a; 在基于微服务架构开发的系统里&#xff0c;为了能够提升系统应对高并发的能力&#xff0c;开发人员通常会把具有相同业务功能的模块同时部署到多台的服务器中&#xff0c;并把访问业务功能的请求均…...

tkinter显示图片

tkinter显示图片 效果代码解析打开和显示图像 代码 效果 代码解析 打开和显示图像 def open_image():file_path filedialog.askopenfilename(title"选择图片", filetypes(("PNG文件", "*.png"), ("JPEG文件", "*.jpg;*.jpeg&q…...

000.二分查找算法题解目录

000.二分查找算法题解目录 69. x 的平方根&#xff08;简单&#xff09;34. 在排序数组中查找元素的第一个和最后一个位置&#xff08;中等&#xff09;...

数据资产赋能企业决策:通过精准的数据分析和洞察,构建高效的数据资产解决方案,为企业提供决策支持,助力企业实现精准营销、风险管理、产品创新等目标,提升企业竞争力

一、引言 在信息化和数字化飞速发展的今天&#xff0c;数据已成为企业最宝贵的资产之一。数据资产不仅包含了企业的基本信息&#xff0c;还蕴含了丰富的市场趋势、消费者行为和潜在商机。如何通过精准的数据分析和洞察&#xff0c;构建高效的数据资产解决方案&#xff0c;为企…...

【java开发环境】多版本jdk 自由切换window和linux

win10 一、准备 各种版本的jdk&#xff0c;按自己的需要下载。 我这里是需要jdk17和jdk8。 1、jdk17 下载&#xff1a;Java Downloads | Oracle&#xff0c;选择exe后缀文件 2、jdk8下 载&#xff1a;Java Downloads | Oracle&#xff0c;选择exe后缀文件 二、详细步骤 1、…...

MySQL实训项目——餐饮点餐系统

项目简介&#xff1a;餐饮点餐系统是一款为餐厅和顾客提供便捷点餐服务的在线平台。通过该系统&#xff0c;餐厅能够展示其菜单&#xff0c;顾客可以浏览菜品&#xff0c;并将其加入购物车或直接下单。系统还提供了订单管理功能&#xff0c;方便餐厅跟踪和处理顾客的订单。 1. …...

昇思MindSpore学习总结七——模型训练

1、模型训练 模型训练一般分为四个步骤&#xff1a; 构建数据集。定义神经网络模型。定义超参、损失函数及优化器。输入数据集进行训练与评估。 现在我们有了数据集和模型后&#xff0c;可以进行模型的训练与评估。 2、构建数据集 首先从数据集 Dataset加载代码&#xff0…...

AI时代创新潮涌,从探路到引路,萤石云引领千行百业创新

步入AI新时代&#xff0c;AI、云计算、大数据等技术迅速迭代&#xff0c;并日益融入经济社会发展各领域全过程&#xff0c;数字经济成为推动千行百业转型升级的重要驱动力量。 今年的政府工作报告提出&#xff0c;深入推进数字经济创新发展。积极推进数字产业化、产业数字化&a…...

计算机毕业设计Python深度学习美食推荐系统 美食可视化 美食数据分析大屏 美食爬虫 美团爬虫 机器学习 大数据毕业设计 Django Vue.js

Python美食推荐系统开题报告 一、项目背景与意义 随着互联网和移动技术的飞速发展&#xff0c;人们的生活方式发生了巨大变化&#xff0c;尤其是餐饮行业。在线美食平台如雨后春笋般涌现&#xff0c;为用户提供了丰富的美食选择。然而&#xff0c;如何在海量的餐饮信息中快速…...

【鸿蒙学习笔记】鸿蒙ArkTS学习笔记

应用开发导读&#xff1a;https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/application-dev-guide-V5 这里写目录标题 基础组件通用属性容器组件Button 迭代完备 【鸿蒙培训】第&#xff11;天・环境安装 【鸿蒙培训】第&#xff12;天・装饰器・组件和页面…...

广东行政职业学院数据智能订单班开班暨上进双创工作室签约仪式圆满结束

为响应教育领域数字化与智能化浪潮这一变革&#xff0c;给学生提供更好的教育资源和实践机会&#xff0c;6月27日&#xff0c;“泰迪广东行政职业学院数据智能订单班开班仪式暨上进双创工作室签约授牌”在广东行政职业学院举行。广东行政职业学院智慧政务学院&#xff08;电子信…...

python与matlab微分切片的区别

python python使用np中的linespace生成等间隔数值&#xff0c; import numpy as np numpy.linspace(start, stop, num50, endpointTrue, retstepFalse, dtypeNone, axis0)start&#xff1a;序列的起始值。stop&#xff1a;序列的结束值。如果 endpoint 为 True&#xff0c;该…...

MSPG3507——蓝牙接收数据显示在OLED,滴答定时器延时500MS

#include "ti_msp_dl_config.h" #include "OLED.h" #include "stdio.h"volatile unsigned int delay_times 0;//搭配滴答定时器实现的精确ms延时 void delay_ms(unsigned int ms) {delay_times ms;while( delay_times ! 0 ); } int a0; …...

Linux 安装 Redis 教程

优质博文&#xff1a;IT-BLOG-CN 一、准备工作 配置gcc&#xff1a;安装Redis前需要配置gcc&#xff1a; yum install gcc如果配置gcc出现依赖包问题&#xff0c;在安装时提示需要的依赖包版本和本地版本不一致&#xff0c;本地版本过高&#xff0c;出现如下问题&#xff1a…...

【高考志愿】建筑学

目录 一、专业介绍 1.1 专业定义 1.2 专业培养目标 1.3 核心课程 二、就业方向和前景 2.1 就业方向 2.2 专业前景 三、报考注意 四、行业趋势与未来展望 五、建筑学专业排名 一、专业介绍 1.1 专业定义 建筑学&#xff0c;这一充满艺术与科技魅力的学科&#xff0c;…...

Kubernetes的发展历程:从Google内部项目到云原生计算的基石

目录 一、起源与背景 1.1 Google的内部项目 1.2 Omega的出现 二、Kubernetes的诞生 2.1 开源的决策 2.2 初期发布 三、Kubernetes的发展历程 3.1 社区的成长 3.2 生态系统的壮大 3.3 重大版本和功能 3.4 多云和混合云的支持 四、Kubernetes的核心概念 4.1 Pod 4.…...

/proc/config.gz

前言 有时候&#xff0c;我们想知道一个运行着的内核都打开了哪些编译选项&#xff0c;当然&#xff0c;查看编译环境的 .config 文件是一个不错的选择&#xff0c;除此之外&#xff0c;还有没有别的办法呢&#xff1f;当然有&#xff0c;那就是 /proc/config.gz。 一睹风采 …...

论坛万能粘贴手(可将任意文件转为文本)

该软件可将任意文件转为文本。 还原为原文件的方法&#xff1a;将得到的文本粘贴到记事本&#xff0c;另存为UUE格式&#xff0c;再用压缩软件如winrar解压即可得到原文件。建议用于小软件。 下载地址&#xff1a;https://download.csdn.net/download/wgxds/89505015 使用演示…...

学习笔记——动态路由——OSPF(OSPF协议的工作原理)

八、OSPF协议的工作原理 1、原理概要 (1)相邻路由器之间周期性发送HELLO报文&#xff0c;以便建立和维护邻居关系 (2)建立邻居关系后&#xff0c;给邻居路由器发送数据库描述报文(DBD)&#xff0c;也就是将自己链路状态数据库中的所有链路状态项目的摘要信息发送给邻居路由器…...

Mybatis1(JDBC编程和ORM模型 MyBatis简介 实现增删改查 MyBatis生命周期)

目录 一、JDBC编程和ORM模型 1. JDBC回顾 2. JDBC的弊端 3. ORM模型 Mybatis和hibernate 区别: 4. mybatis 解决了jdbc 的问题 二、MyBatis简介 1. MyBatis快速开始 1.1 导入jar包 1.2 引入 mybatis-config.xml 配置文件 1.3 引入 Mapper 映射文件 1.3 测试 …...

论文阅读YOLO-World: Real-Time Open-Vocabulary Object Detection

核心&#xff1a; 开放词汇的实时的yolo检测器。重参数化的视觉语言聚合路径模块Re-parameterizable VisionLanguage Path Aggregation Network (RepVL-PAN)实时核心&#xff1a;轻量化的检测器离线词汇推理过程重参数化 方法 预训练方案&#xff1a;将实例注释重新定义为区域…...

SM2的签名值byte数组与ASN.1互转

ASN.1抽象语言标记(Abstract Syntax Notation One) ASN.1是一种 ISO/ITU-T 标准,描述了一种对数据进行表示、编码、传输和解码的数据格式,它提供了一整套正规的格式用于描述对象的结构。 一、该结构的应用场景 例如在做待签名的数字信封时,数字信封使用ASN.1封装,这个时…...

云计算与生成式AI的技术盛宴!亚马逊云科技深圳 Community Day 社区活动流程抢先知道!

小李哥最近要给大家分享7月7日在深圳的即将举办的亚马逊云科技生成式AI社区活动Community Day &#xff0c;干货很多内容非常硬核&#xff0c;不仅有技术分享学习前沿AI技术&#xff0c;大家在现场还可以动手实践沉浸式体验大模型&#xff0c;另外参与现场活动还可以领取诸多精…...

【鸿蒙学习笔记】基础组件Progress:进度条组件

官方文档&#xff1a;Progress 目录标题 作用最全属性迭代追加进度赋值风格样式 作用 进度条组件 最全属性迭代追加 Progress({ value: 20, total: 100, type: ProgressType.Linear }).color(Color.Green)// 颜色.width(200)// 大小.height(50)// 高度.value(50)// 进度可更…...

前程无忧滑块

声明(lianxi a15018601872) 本文章中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff0c;抓包内容、敏感网址、数据接口等均已做脱敏处理&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff01; 前言(lianxi …...

一站式uniapp优质源码项目模版交易平台的崛起与影响

一、引言 随着信息技术的飞速发展&#xff0c;软件源码已成为推动行业进步的重要力量。源码的获取、交易和流通&#xff0c;对于开发者、企业以及项目团队而言&#xff0c;具有极其重要的意义。为满足市场对高质量源码资源的迫切需求&#xff0c;一站式uniapp优质源码项目模版…...

Python中vars用法

在 Python 中&#xff0c;vars() 函数返回对象的 __dict__ 属性的字典。这个字典包含了对象的所有属性和它们的值。vars() 函数可以用于模块、类、实例&#xff0c;或者拥有 __dict__ 属性的任何其它对象。这里有几个使用 vars() 的例子&#xff1a; 一.模块 如果对一个模块使…...

【机器学习】基于Transformer的迁移学习:理论与实践

引言 在机器学习领域&#xff0c;迁移学习已成为提升模型训练效率和性能的重要策略&#xff0c;特别是在标注数据稀缺的场景下。Transformer模型自2017年由Google提出以来&#xff0c;在自然语言处理&#xff08;NLP&#xff09;领域取得了突破性进展&#xff0c;并逐渐扩展到…...

如何应对情绪和培养理性的书

以下是几本关于如何应对情绪和培养理性的书籍推荐&#xff1a; 《情绪智商》&#xff08;Emotional Intelligence&#xff09; - 丹尼尔戈尔曼&#xff08;Daniel Goleman&#xff09; 这本书探讨了情绪智商&#xff08;EQ&#xff09;的重要性以及如何通过提高EQ来改善个人和职…...

[数据集][目标检测]电缆钢丝绳线缆缺陷检测数据集VOC+YOLO格式1800张3类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;1800 标注数量(xml文件个数)&#xff1a;1800 标注数量(txt文件个数)&#xff1a;1800 标注…...

【Git 学习笔记】Ch1.1 Git 简介 + Ch1.2 Git 对象

还是绪个言吧 今天整理 GitHub 仓库&#xff0c;无意间翻到了几年前自学 Git 的笔记。要论知识的稳定性&#xff0c;Git 应该能挤进前三——只要仓库还在&#xff0c;理论上当时的所有开发细节都可以追溯出来。正好过段时间会用到 Git&#xff0c;现在整理出来就当温故知新了。…...

Python 中别再用 ‘+‘ 拼接字符串了!

目录 引言 为什么不推荐使用 "" 示例代码 更高效的替代方法 使用 join 方法 示例代码 使用格式化字符串&#xff08;f-strings&#xff09; 示例代码 引言 大家好&#xff0c;在 Python 编程中&#xff0c;我们常常需要对字符串进行拼接。你可能会自然地想到…...

六西格玛绿带培训的证书有什么用处?

近年来&#xff0c;六西格玛作为一套严谨而系统的质量管理方法&#xff0c;被广泛运用于各行各业。而六西格玛绿带培训证书&#xff0c;作为这一方法论中基础且重要的认证&#xff0c;对于个人和企业而言&#xff0c;都具有不可忽视的价值。本文将从多个角度深入探讨六西格玛绿…...

《妃梦千年》第二十章:风雨欲来

第二十章&#xff1a;风雨欲来 战斗的胜利让林清婉和皇上的关系更加亲密&#xff0c;但宫中的阴谋却并未因此而停止。一天&#xff0c;林清婉正在寝宫中思考未来的对策&#xff0c;忽然接到一个紧急消息。小翠匆匆跑来&#xff0c;神色紧张&#xff1a;“娘娘&#xff0c;太后…...

深入理解二分法

前言 二分法&#xff08;Binary Search&#xff09;是一种高效的查找算法&#xff0c;广泛应用于计算机科学和工程领域。它用于在有序数组中查找特定元素&#xff0c;其时间复杂度为 O(log n)&#xff0c;显著优于线性搜索的 O(n)。本文将深入介绍二分法的原理、实现及其应用场…...

【C命名规范】遵循良好的命名规范,提高代码的可读性、可维护性和可复用性

/******************************************************************** * brief param return author date version是代码书写的一种规范 * brief &#xff1a;简介&#xff0c;简单介绍函数作用 * param &#xff1a;介绍函数参数 * return&#xff1a;函数返回类型说明 * …...

Hbase面试题总结

一、介绍下HBase架构 --HMaster HBase集群的主节点&#xff0c;负责管理和协调整个集群的操作。它处理元数据和表的分区信息&#xff0c;控制RegionServer的负载均衡和故障恢复。--RegionServer HBase集群中的工作节点&#xff0c;负责存储和处理数据。每个RegionServer管理若…...

C语言部分复习笔记

1. 指针和数组 数组指针 和 指针数组 int* p1[10]; // 指针数组int (*p2)[10]; // 数组指针 因为 [] 的优先级比 * 高&#xff0c;p先和 [] 结合说明p是一个数组&#xff0c;p先和*结合说明p是一个指针 括号保证p先和*结合&#xff0c;说明p是一个指针变量&#xff0c;然后指…...

Rust学习笔记 (命令行命令) : 用override set 设置工具链

在cargo run某个项目时出现了如下错误&#xff1a;error: failed to run custom build command for ring v0.16.20&#xff08;无法运行“Ring v0.16.20”的自定义构建命令&#xff09;&#xff0c;在PowerShell命令行运行命令 rustup override set stable-msvc后成功运行。 o…...

cv::Mat类的矩阵内容输出的各种格式的例子

操作系统&#xff1a;ubuntu22.04OpenCV版本&#xff1a;OpenCV4.9IDE:Visual Studio Code编程语言&#xff1a;C11 功能描述 我们可以这样使用&#xff1a;cv::Mat M(…); cout << M;&#xff0c;直接将矩阵内容输出到控制台。 输出格式支持多种风格&#xff0c;包括O…...

Redis--注册中心集群 Cluster 集群-单服务器

与“多服务器集群”一致需要创建redis配置模板 参照以下链接 CSDN 创建redis容器 node01服务器上创建容器 docker run -d --name redis-6381 --net host --privilegedtrue \ -v /soft/redis-cluster/6381/conf/redis.conf:/etc/redis/redis.conf \ -v /soft/redis-cluster/6…...

CV01_相机成像原理与坐标系之间的转换

目录 0.引言&#xff1a;小孔成像->映射表达式 1. 相机自身的运动如何表征&#xff1f;->外参矩阵E 1.1 旋转 1.2 平移 2. 如何投影到“像平面”&#xff1f;->内参矩阵K 2.1 图像平面坐标转换为像素坐标系 3. 三维到二维的维度是如何丢失的&#xff1f;…...

Android Lint

文章目录 Android Lint概述工作流程Lint 问题问题种类警告严重性检查规则 用命令运行 LintAndroidStudio 使用 Lint忽略 Lint 警告gradle 配置 Lint查找无用资源文件 Android Lint 概述 Lint 是 Android 提供的 代码扫描分析工具&#xff0c;它可以帮助我们发现代码结构/质量…...

【算法刷题 | 动态规划14】6.28(最大子数组和、判断子序列、不同的子序列)

文章目录 35.最大子数组和35.1题目35.2解法&#xff1a;动规35.2.1动规思路35.2.2代码实现 36.判断子序列36.1题目36.2解法&#xff1a;动规36.2.1动规思路36.2.2代码实现 37.不同的子序列37.1题目37.2解法&#xff1a;动规37.2.1动规思路37.2.2代码实现 35.最大子数组和 35.1…...

vue3 vxe-grid列中绑定vxe-switch实现数据更新

1、先上一张图&#xff1a; <template #valueSlot"{ row }"><vxe-switch :value"getV(row.svalue)" change"changeSwitch(row)" /></template>function getV(value){return value 1;};function changeSwitch(row) {console.l…...

Hive SQL:实现炸列(列转行)以及逆操作(行转列)

目录 列转行行转列 列转行 函数&#xff1a; EXPLODE(ARRAY)&#xff1a;将ARRAY中的每一元素转换为每一行 EXPLODE(MAP)&#xff1a;将MAP中的每个键值对转换为两行&#xff0c;其中一行数据包含键&#xff0c;另一行数据包含值 数据样例&#xff1a; 1、将每天的课程&#…...

MD5算法详解

哈希函数 是一种将任意输入长度转变为固定输出长度的函数。 一些常见哈希函数有&#xff1a;MD5、SHA1、SHA256。 MD5算法 MD5算法是一种消息摘要算法&#xff0c;用于消息认证。 数据存储方式&#xff1a;小段存储。 数据填充 首先对我们明文数据进行处理&#xff0c;使其…...

高德离线地图OpenLayers实现添加标记点及标记点事件

要在使用高德离线地图数据的OpenLayers应用中添加标记点&#xff08;通常称为"特征"或"要素"&#xff09;以及为这些标记点添加事件&#xff0c;你需要首先确保你的离线地图数据是以OpenLayers能够理解的格式提供的&#xff08;如PNG瓦片用于底图&#xff…...

MacOS java多版本安装与管理

安装sdkman curl -s "https://get.sdkman.io" | bashsource "$HOME/.sdkman/bin/sdkman-init.sh"sdk version正常出现sdkman版本号就安装成功了 # 安装java # 安装java8 sdk install java 8.0.412.fx-zulu建议和上述一样安装 fx-zulu 的jdk&#xff0c…...

docker compose部署zabbix7.0官方yaml文件方法快速搭建

环境介绍&#xff1a; 系统&#xff1a;centos7 官方文档&#xff1a;https://www.zabbix.com/documentation/current/zh/manual/installation/containers docker镜像接口配置 vi /etc/docker/daemon.json{"registry-mirrors": ["https://docker.1panel.live…...

驾驭Pip源之变:在Windows系统中轻松更换源的指南

驾驭Pip源之变&#xff1a;在Windows系统中轻松更换源的指南 Python作为一门流行的编程语言&#xff0c;拥有一个庞大的第三方库生态系统&#xff0c;而Pip作为Python的包管理工具&#xff0c;承担着安装和管理这些库的重任。然而&#xff0c;默认的Pip源有时可能因为网络问题…...

Double 4 VR虚拟情景智能互动系统在小语种专业课堂上的应用

随着科技的进步&#xff0c;越来越多的教育机构开始尝试使用虚拟现实技术来提高教学效果。Double 4 VR虚拟情景智能互动系统就是这样一款能够为小语种专业课堂带来革新性体验的教学工具。 一、模拟真实环境&#xff0c;增强学习体验 系统通过高度仿真的虚拟环境&#xff0c;为学…...

高考后的抉择:专业优先还是学校优先?

随着2024年高考的帷幕落下&#xff0c;高考生们面临的一个重要抉择再度浮上心头&#xff1a;在分数受限的情况下&#xff0c;是选择一个心仪的专业&#xff0c;还是选择一个知名度更高的学校&#xff1f;这是一个困扰了众多考生和家长的长期难题。在这个关键的时刻&#xff0c;…...

实惠有面子!风行M7诠释出行新主张,商务车中的超值之选!

在快速发展的商业环境中,出行不再是简单的位移,更是展示企业形象、提升工作效率的重要环节。对于商务人士而言,一辆合适的商务车不仅能提供舒适的乘坐体验,更是身份与品味的象征。在众多商务车型中,风行M7凭借其实惠的价格、体面的外观、宽敞舒适的空间以及高效的动力系统等特点…...

安全与智能完美联合,问界新M5通过易车主动安全测试

随着汽车技术的不断完善,如今新能源汽车市场竞争更加激烈。各大车企也更重视汽车安全性层面的打造。在5月30日,由易车推出的《易起鏖战》节目正式上线,节目中也通过实测方式,针对问界新M5、特斯拉Model Y和理想L6这三款行业代表作进行了主动安全的测试。结果可想而知,问界…...

云原生架构内涵_3.主要架构模式

云原生架构有非常多的架构模式&#xff0c;这里列举一些对应用收益更大的主要架构模式&#xff0c;如服务化架构模式、Mesh化架构模式、Serverless模式、存储计算分离模式、分布式事务模式、可观测架构、事件驱动架构等。 1.服务化架构模式 服务化架构是云时代构建云原生应用的…...

对象解构与迭代器的猫腻?

前言 变量的解构赋值是前端开发中经常用到的一个技巧&#xff0c;比如&#xff1a; // 对象解构 const obj { a: 1, b: 2 }; const { a, b } obj; console.log(a, b)数组解构 const arr [1, 2, 3]; const [a, b] arr; console.log(a, b)工作中我们最经常用的就是类似上面…...

C语言知识大纲

一、基础 (一)变量定义和使用 (二)数据类型的字节数 (三)变量转换 (四)程序主要结构 (五)if和else判断 (六)switch判断 (七)while循环 (八)do while循环 (九)for循环 (十)基本输入输出 (十一)数组定义和使用 (十二)函数定义和使用 (十三)指针 (十四)多级指针 (十…...

06_知识点总结(JS高级)

一、进程与线程 1. 进程(process)&#xff1a;程序的一次执行, 它占有一片独有的内存空间 2. 线程(thread)&#xff1a; 是进程内的一个独立执行单元&#xff0c;CPU的基本调度单元, 是程序执行的一个完整流程 3. 进程与线程 * 应用程序必须运行在某个进程的某个线程上 * 一个…...