代理模式详解、RESTFul风格、Spring IOC

news2024/11/27 3:37:42

Day49

代理模式proxy

概念: 代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式,即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能.

代理模式分为静态代理和动态代理两种 。

静态代理

思路:对于同一个接口,代理类和被代理类都实现了这个接口,代理类中将被代理类对象持有为自己的属性,这样在使用方法的时候就可以在被代理类的方法前后加上增强,即自己的方法逻辑。

以Speaker接口、ChineseSpeaker、AmericaSpeaker、ChinsesSpeakerProxy、AmericaSpeakerProxy为例,其中ChinsesSpeakerProxy、AmericaSpeakerProxy分别是ChineseSpeaker、AmericaSpeaker的代理类:

Speaker:

public interface Speaker {
 void speak();
}

ChineseSpeaker:

public class ChineseSpeaker implements Speaker{
 public void speak() {
     System.out.println("中文演讲");
 }
}

ChinsesSpeakerProxy:

public class ChineseSpeakerProxy implements Speaker{
 private ChineseSpeaker speaker;

 public ChineseSpeakerProxy(ChineseSpeaker speaker) {
     this.speaker = speaker;
 }

 @Override
 public void speak() {
     System.out.println("增强处理");
     speaker.speak();
     System.out.println("增强处理");
 }
}

可以看出,这种写法每代理一个真实类就需要写一个代理类,对于AmericaSpeaker,同样要写一个AmericaSpeakerProxy。如果对于功能增强的内容完全相同,就可以使用一个对于Speaker接口通用的代理类CommonSpeakerProxy,利用多态完成代理。

public class CommonSpeakerProxy implements Speaker {
 private Speaker speaker;

 public CommonSpeakerProxy(Speaker speaker) {
     this.speaker = speaker;
 }

 @Override
 public void speak() {
     
     System.out.println("前置功能增强");
     speaker.speak();
     System.out.println("后置功能增强");
 }
}

除了这种写法外,还可以利用反射的思想来写,利用多态通过接口实现类拿到方法,通过有参传入的类对象,用method.invoke()方法完成代理。

假设这时再添加了Seller接口、ChineseSeller、AmericaSeller类及其代理类CommonSellerProxy

public class CommonSellerProxy implements Seller{
 private static Method method;
 private  Object seller;
 public CommonSellerProxy(Object seller){
     this.seller = seller;
 }
 static {
     try {
         method = Seller.class.getMethod("sell");
     } catch (NoSuchMethodException e) {
         e.printStackTrace();
     }
 }
 @Override
 public void sell() {
     try {
         System.out.println("前置功能增强");
         method.invoke(seller);
         System.out.println("后置功能增强");
     } catch (IllegalAccessException e) {
         throw new RuntimeException(e);
     } catch (InvocationTargetException e) {
         throw new RuntimeException(e);
     }
 }
}

可以看出,静态代理每实现一个真实类的代理就需要写一个代理类,如果代理的功能不同,就需要针对代理功能编写多个类,十分复杂。那么有没有一种方式使得在使用到代理的时候再去编写代理的逻辑功能,而不是每次都去多写一个类呢?这就是动态代理的思想。

动态代理

动态代理又根据代理对象进行划分:

为接口做代理:JDK动态代理

为类做代理:CGLIB动态代理

JDK动态代理

由于动态代理和静态代理差别较大,这里从静态代理开始进阶优化,直到达到动态代理的范畴。

**静态代理进阶:**思路:写一个接口,接口中定义了代理重写的方法,在代理类中以匿名内部类的方式创建一个类对象作为自己持有的属性,并用全参构造要求使用时创建这个匿名内部类(即重写代理方法内容),而代理类中就只需要调用接口的实现类的方法就行,不需要再写明方法逻辑。

这个接口是jdk自带的接口,在这里自己写一遍,以更好地明白逻辑:

MethodInvocationHandler接口:

public interface MethodInvocationHandler {
 Object handle(Object target, Method method,Object[] args) throws Exception;
}

注意:这个方法的本质是反射,利用method.invoke()方法进行调用真实类的方法,再加上代理类的方法,因此参数为method.invoke()的参数。

ChineseSpeakerProxy代理类:

public class ChineseSpeakerProxy implements Speaker{


 private Speaker speaker;
 private MethodInvocationHandler handler;
 private static Method method;
 static {
     try {
         method = Speaker.class.getMethod("speak");
     } catch (NoSuchMethodException e) {
         throw new RuntimeException(e);
     }
 }

 public ChineseSpeakerProxy(Speaker speaker, MethodInvocationHandler handler) {
     this.speaker = speaker;
     this.handler = handler;
 }

 //如果有多个需要代理的方法就都要进行重写
 @Override
 public void speak() {
     try {
         handler.handle(speaker,method,null);//由于真实类中的方法是无参的,所以这里的参数数组为空
     } catch (Exception e) {
         throw new RuntimeException(e);
     }
 }
}

使用:

public class Test01 {
 public static void main(String[] args) {
     ChineseSpeaker chineseSpeaker = new ChineseSpeaker();
     ChineseSpeakerProxy chineseSpeakerProxy = new ChineseSpeakerProxy(chineseSpeaker, new MethodInvocationHandler() {
         @Override
         public Object handle(Object target, Method method, Object[] args) throws Exception {
             System.out.println("功能增强");
             method.invoke(target,args);
             System.out.println("功能增强");
             return null;
         }
     });
     chineseSpeakerProxy.speak();
 }

至此,对于同一个代理类的多个功能,实现了让用户自己写增强方法的目的。但是对于一个真实类,如果要实现其代理,那还是要写一个代理类,如果真实类很多,那就需要写很多的代理类,代理繁多的问题依然存在。

如果这些代理类能够使用代码来生成,然后再编译,再加载至 JVM 中,那么再多的代理也就不是问题了。

动态代理

手动写一个能够自动创建代理类源码的类,然后手动完成编译、加载的过程。(这些功能jdk的接口都实现了,这里只手写一个自动创建代理类源码的类以便深刻理解)

因此,可以手动写一个能够自动创建代理类源码的类,然后手动完成编译、加载的过程。(这些功能jdk的接口都实现了,这里只手写一个自动创建代理类源码的类以便深刻理解)

package com.qf.proxy;

import com.qf.proxy.dynamic.MethodInvocationHandler;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

public class MyProxy {
 private static String generateProxyClass(Class<?> clazz){
     if(!clazz.isInterface()) throw new IllegalArgumentException(clazz.getName() + " 不是接口");
     StringBuilder builder = new StringBuilder();
     builder.append("package ").append(clazz.getPackage().getName()).append(";\n");
     builder.append("import ").append(Method.class.getName()).append(";\n");
     builder.append("import ").append(MethodInvocationHandler.class.getName()).append(";\n");
     builder.append("import ").append(MyProxy.class.getName()).append(";\n");
     builder.append("public class $proxy0 extends MyProxy implements ").append(clazz.getSimpleName()).append("{\n");
     StringBuilder staticBuilder = new StringBuilder();
     staticBuilder.append("static {\n");
     staticBuilder.append("try {\n");
     StringBuilder overrideMethodBuilder = new StringBuilder();
     Method[] methods = clazz.getMethods();
     for(int i=0; i<methods.length; i++){
         builder.append("private static Method m").append(i).append(";\n");
         staticBuilder.append("m").append(i).append("=Class.forName(\"").append(clazz.getName()).append("\").getMethod(\"")
                 .append(methods[i].getName()).append("\",");
         overrideMethodBuilder.append("\n@Override\n");
         overrideMethodBuilder.append("public ").append(methods[i].getReturnType().getSimpleName()).append(" ").append(methods[i].getName()).append("(");
         Parameter[] parameters = methods[i].getParameters();
         for(Parameter parameter : parameters){
             staticBuilder.append(parameter.getType().getSimpleName()).append(".class,");
             overrideMethodBuilder.append(parameter.getType().getSimpleName()).append(" ").append(parameter.getName()).append(",");
         }
         staticBuilder.deleteCharAt(staticBuilder.length()-1);
         staticBuilder.append(");\n");
         if(parameters.length > 0)
             overrideMethodBuilder.deleteCharAt(overrideMethodBuilder.length()-1);
         overrideMethodBuilder.append("){\n");
         Class returnType = methods[i].getReturnType();
         if(returnType != Void.class && returnType != void.class)
             overrideMethodBuilder.append("return (").append(methods[i].getReturnType().getSimpleName()).append(")");
         overrideMethodBuilder.append("handler.handle(m").append(i).append(",new Object[]{");
         for(Parameter parameter : parameters){
             overrideMethodBuilder.append(parameter.getName()).append(",");
         }
         if(parameters.length > 0)
             overrideMethodBuilder.deleteCharAt(overrideMethodBuilder.length()-1);
         overrideMethodBuilder.append("});\n}");
     }
     staticBuilder.append("} catch (NoSuchMethodException e) {\ne.printStackTrace();\n}catch (ClassNotFoundException e) {\ne.printStackTrace();\n}\n");
     staticBuilder.append("}\n");
     builder.append(staticBuilder);
     builder.append("protected $proxy0(MethodInvocationHandler handler) {\nsuper(handler);\n}\n");
     builder.append(overrideMethodBuilder);
     builder.append("\n}");
     System.out.println(builder);
     return builder.toString();
 }

//    public static void main(String[] args) {
//        generateProxyClass(Seller.class);
//    }
}


这个类完成了自动创建类源码的功能,其实现的思路就是将一个代理类中的特定类利用反射和object去替换,然后将整个类写成字符串放进StringBuilder中。

然后是编译代理类源文件、加载编译好的代理类(利用类加载器)、编写创建代理实例的方法。这些底层就不手写了。

接下来对比一下两种写法的区别(这里都实现jdk自带的InvocationHandler接口,接口内容和上面手写的MethodInvocationHandler一致,只是方法名字为invoke,我写的是handle):

手动创建ChineseSpeakerProxy(此时仍然为静态代理,因为代理类是在编译时明确定义的,并且代理类的代码是手动编写的。相对于动态代理,静态代理类在运行时不会自动生成,而是在编译时就已经存在。 ):

public class ChineseSpeakerProxy implements Speaker{


 private Speaker speaker;
 private InvocationHandler handler;
 private static Method method;
 static {
     try {
         method = Speaker.class.getMethod("speak");
     } catch (NoSuchMethodException e) {
         throw new RuntimeException(e);
     }
 }

 public ChineseSpeakerProxy(Speaker speaker, InvocationHandler handler) {
     this.speaker = speaker;
     this.handler = handler;
 }

 @Override
 public void speak() {
     try {
         handler.invoke(speaker,method,null);
     } catch (Throwable e) {
         throw new RuntimeException(e);
     }
 }
}


public class Test01 {
 public static void main(String[] args) {
     ChineseSpeaker chineseSpeaker = new ChineseSpeaker();
     ChineseSpeakerProxy chineseSpeakerProxy = new ChineseSpeakerProxy(chineseSpeaker, new InvocationHandler() {

         @Override
         public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
             method.invoke(proxy,args);
             System.out.println("方法增强");
             return null;
         }

     });
     chineseSpeakerProxy.speak();
 }


使用jdk自带的Proxy类静态方法创建(动态代理):

public class Test01 {
 public static void main(String[] args) {
     ChineseSpeaker chineseSpeaker = new ChineseSpeaker();
     Speaker ChineseSpeakerProxy = (Speaker) Proxy.newProxyInstance(ChineseSpeaker.class.getClassLoader(), ChineseSpeaker.class.getInterfaces(), new InvocationHandler() {
         @Override
         public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
             System.out.println("方法增强");
             method.invoke(chineseSpeaker,args);
             return null;
         }
     });


     ChineseSpeakerProxy.speak();
 }

注意:使用Proxy类实现动态代理的时候,method.invoke()的第一个参数不是形参给的,而是被代理的类对象! 因为使用 Proxy.newProxyInstance动态生成的代理类会把代理实例本身传递给 InvocationHandler.invoke方法的 proxy 参数。因此,如果在 invoke 方法中再次调用 method.invoke(proxy, args),就会导致递归调用形成死循环。

总结

静态代理

写法一:真实类和代理类实现同一个接口,代理类持有真实类对象作为属性,并在重写接口方法的时候调用其方法,再添加方法增强。

写法二:如果多个真实类需要进行的方法增强相同,则可以写一个通用的代理类,实现和写法一相同。

写法三:利用反射的思想,代理类持有真实类对象属性,并利用反射拿到方法,重写的时候用invoke(),并添加方法增强。

写法四(进阶):利用接口(这里自己写的是MethodInvocationHandler)定义重写的方法,在代理类中创建接口的实现类(匿名内部类),并通过全参构造让用户自己传入真实类对象和接口实现类(并重写代理方法),调用实现类的方法。

动态代理:自动创建代理类源码,然后完成编译、加载。 代理类在运行时根据目标对象和增强逻辑动态生成。 在使用的时候利用Proxy类的静态方法newProxyInstance()创建代理类,并 通过 InvocationHandler 接口的 invoke 方法在运行时拦截方法调用,执行增强逻辑。

RESTful风格

RESTful风格不是标准,但是在企业中经常使用RESTful风格来完成功能的开发。

REST = Representational State Transfer(表属性状态转移)

简单来说就是在编写Servlet的时候重写doGet,doPost,doPut,doDelete实现增查改删功能。

Spring IOC

Spring简介

Spring 是目前主流的 Java 开发框架,是 Java 世界最为成功的框架。其目的是用于简化企业级应用程序开发的难度和周期,任何 Java 应用都可以从 Spring 中受益。Spring 框架还是一个超级粘合平台,除了自己提供功能外,还提供粘合其他技术和框架的能力。

什么是框架? 框架是一个半成品,提供了基本的运行功能,但具体业务实现需要我们去编写。

Spring体系结构

在这里插入图片描述

IOC概念:

IOC全称为 Inverse Of Control,表示控制反转。指的是程序员使用硬编码创建的对象转为由Spring容器来创建,对于对象生命周期的控制交给Spring容器来管理。控制反转解决了具有依赖关系的组件之间的强耦合,使得项目形态更加稳健

依赖注入

DI全称为Dependency Injection,表示依赖注入。指的是在Spring创建对象的同时,为其属性赋值

设值注入:

创建一个类:

@Data
public class Student {

 private String name;

 private String sex;

 private int age;

 private Date birthday;
}

在xml配置文件中利用设值注入创建对象:

常见数据类型:

<!--application.xml-->
<bean name="stu" class="com.qf.spring.ioc.model.Student">
 <property name="name" value="张三" />
 <property name="age" value="20" />
 <property name="sex" value="" />
 <!--这里需要注意:日期类型的默认格式yyyy/MM/dd-->
 <property name="birthday" value="2021/10/10" />
</bean>

使用:

@Test
public void studentTest(){
 //应用上下文使用的是类路径下XML文档作为当前应用上下文
 ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
 //从上下文中根据bean的名称或者ID获取bean对象
 Student stu = context.getBean("stu", Student.class);
 System.out.println(stu);
}

注意:这里的name虽然是user,但是并不是指的user这个类,而是指向set方法!(理解为去掉set后的名字)设值注入本质就是通过set方法为属性注入值。设值注入必须保证存在无参构造,否则将报错。

注入数组类型:

spring 提供了 array 标签来进行数组类型的属性值的注入。

@Data
public class Clazz {

 private int id;

 private String name;

 private Student[] students;
}
<bean name="clazz" class="com.qf.spring.ioc.model.Clazz">
 <property name="id" value="1" />
 <property name="name" value="张三" />
 <property name="students">
     <array>
         <!--引用数据类型 可以使用bean标签创建bean对象注入值-->
         <!--<bean class=""></bean>-->
         <!--引用数据类型 可以使用ref标签引用bean对象注入值-->
         <ref bean="s" />
         <ref bean="stu" />
         <!--常用数据类型 可以使用value标签直接注入值-->
         <!-- <value></value>-->
     </array>
 </property>
</bean>

注入集合类型:

List集合

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {

 private String name;

 private String sex;

 private int age;

 private Date birthday;

 private List<Double> scores;
}
<bean name="stu" class="com.qf.spring.ioc.model.Student">
 <property name="name" value="张三" />
 <property name="age" value="20" />
 <property name="sex" value="" />
 <!--这里需要注意:日期类型的默认格式yyyy/MM/dd-->
 <property name="birthday" value="2021/10/10" />
 <property name="scores">
     <list>
         <value>80.0</value>
         <value>90.0</value>
         <value>81.0</value>
         <value>82.0</value>
     </list>
 </property>
</bean>

<bean name="s" class="com.qf.spring.ioc.model.Student">
 <!--这里按照顺序为属性注入值-->
 <constructor-arg index="0" value="李四" />
 <constructor-arg index="1" value="" />
 <constructor-arg index="2" value="22" />
 <constructor-arg index="3" value="2020/05/05" />
 <constructor-arg index="4">
     <list>
         <value>80.0</value>
         <value>90.0</value>
         <value>81.0</value>
         <value>82.0</value>
     </list>
 </constructor-arg>
</bean>
Set集合
@Data
public class Person {

 private String name;

 private Set<String> friendNames;
}
<bean name="p" class="com.qf.spring.ioc.model.Person">
 <property name="name" value="李刚" />
 <property name="friendNames">
     <set>
         <value>李四</value>
         <value>王五</value>
     </set>
 </property>
</bean>

注入Map
@Data
public class Person {

 private String name;

 private List<String> friendNames;

 private Map<String, Object> map;
}

<bean name="p" class="com.qf.spring.ioc.model.Person">
 <property name="name" value="李刚" />
 <property name="friendNames">
     <set>
         <value>李四</value>
         <value>王五</value>
     </set>
 </property>
 <property name="map">
     <map>
         <entry key="hobby" value="聊天" />
         <entry key="clazz" value-ref="clazz"/>
     </map>
 </property>
 <property name="props">
     <props>
         <prop key="desc">我很帅</prop>
         <prop key="secret">我有两个女朋友</prop>
     </props>
 </property>
</bean>

注入Properties
@Data
public class Person {

 private String name;

 private List<String> friendNames;

 private Properties props;
}

<bean name="p" class="com.qf.spring.ioc.model.Person">
 <property name="name" value="李刚" />
 <property name="friendNames">
     <set>
         <value>李四</value>
         <value>王五</value>
     </set>
 </property>
 <property name="props">
     <props>
         <prop key="desc">我很帅</prop>
         <prop key="secret">我有两个女朋友</prop>
     </props>
 </property>
</bean>

构造注入

构造注入指的是通过构造放入为属性注入值。

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {

    private String name;

    private String sex;

    private int age;

    private Date birthday;
}

<!--application.xml-->
<bean name="s" class="com.qf.spring.ioc.model.Student">
    <!--这里按照顺序为属性注入值-->
    <constructor-arg index="0" value="李四" />
    <constructor-arg index="1" value="" />
    <constructor-arg index="2" value="22" />
    <constructor-arg index="3" value="2020/05/05" />
</bean>

@Test
public void studentConstructorTest(){
    //应用上下文使用的是类路径下XML文档作为当前应用上下文
    ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
    //从上下文中根据bean的名称或者ID获取bean对象
    Student stu = context.getBean("s", Student.class);
    System.out.println(stu);
}

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

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

相关文章

【一】m2芯片的mac中安装ubuntu24虚拟机集群

文章目录 1. 虚拟机配置2. 复制虚拟机2.1 修改主机名2.2 修改网络 1. 虚拟机配置 在官方网站下载好ubuntu24-arm版镜像开始安装&#xff0c;安装使用VMWare Fusion的社区免费授权版,使用一台m2芯片的mac电脑作为物理机平台。 为什么选择ubuntu24&#xff1f;因为centOS7目前已…

process.env 管理 Vue 项目的环境变量(Vue项目中环境变量的配置及调用)

简述&#xff1a;在构建 Vue 应用时&#xff0c;管理配置是开发中的一个重要部分。不同的环境&#xff08;如开发、测试和生产&#xff09;往往需要不同的配置&#xff0c;例如 API、 基础 URL、第三方服务的密钥等。使用环境变量可以帮助我们更好地管理这些配置。这里将介绍如…

MacOS miniconda安装方法

打开macos “终端” 应用 执行命令 mkdir -p ~/miniconda3curl https://mirrors.tuna.tsinghua.edu.cn/anaconda/miniconda/Miniconda3-latest-MacOSX-arm64.sh -o ~/miniconda3/miniconda.shbash ~/miniconda3/miniconda.sh -b -u -p ~/miniconda3rm -rf ~/miniconda3/mini…

nuxt、vue树形图d3.js

直接上代码 //安装 npm i d3 --save<template><div class"d3"><div :id"id" class"d3-content"></div></div> </template> <script> import * as d3 from "d3";export default {props: {d…

MongoDB-社区版-本地安装

系统&#xff1a;win10 1. 下载server:Download MongoDB Community Server | MongoDB 我选的zip包 2. 下载shell&#xff1a;MongoDB Shell Download | MongoDB 我选的zip包 3. 启动server 4. 启动shell, 完成

【基础算法】UE中实现轮播

本期作者&#xff1a;尼克 易知微3D引擎技术负责人 当前N 总数M 从0到M-1 从1到M 感谢阅读&#xff0c;以上内容均由易知微3D引擎团队原创设计&#xff0c;以及易知微版权所有&#xff0c;转载请注明出处&#xff0c;违者必究&#xff0c;谢谢您的合作。申请转载授权后台回复【…

堆叠的作用

一、为什么要堆叠 传统的园区网络采用设备和链路冗余来保证高可靠性&#xff0c;但其链路利用率低、网络维护成本高&#xff0c;堆叠技术将多台交换机虚拟成一台交换机&#xff0c;达到简化网络部署和降低网络维护工作量的目的。 二、堆叠优势 1、提高可靠性 堆叠系统多台成…

【深度学习】图形模型基础(5):线性回归模型第二部分:单变量线性回归模型

1.引言 在统计学与机器学习的广阔领域中&#xff0c;线性回归作为一种基础而强大的预测技术&#xff0c;其核心在于通过输入变量&#xff08;或称预测器、自变量&#xff09;来估计输出变量&#xff08;响应变量、因变量&#xff09;的连续值。本章聚焦于线性回归的一个基本但…

数据洞察:从零到一的数据仓库与Navicat连接全攻略【实训Day04】[完结篇]

一、数据分析 1 实现数据仓库(在hadoop101上) 1) 创建jobdata数据库 # cd $HIVE_HOME # bin/hive hive>create database jobdata; hive>use jobdata; 2) 创建原始职位数据事实表ods_jobdata_orgin(在hadoop101上) create table ods_jobdata_origin( city string CO…

Python爬虫零基础实战,简洁实用!

1.爬虫简介 简单来讲&#xff0c;爬虫就是一个探测机器&#xff0c;它的基本操作就是模拟人的行为去各个网站溜达&#xff0c;点点按钮&#xff0c;查查数据&#xff0c;或者把看到的信息背回来。就像一只虫子在一幢楼里不知疲倦地爬来爬去。 你可以简单地想象&#xff1a;每个…

Stream的获取、中间方法、终结方法

1、获取Stream流 单列集合&#xff1a;foreach完整版 双列集合通过Ketset()、entryset() 数组的&#xff1a;通过Arrays Stream流的中间方法&#xff1a;链式编程&#xff0c;原stream流只能使用一次 filter&#xff1a; limit、skip&#xff1a; distinct(有自定义对象需要重写…

MYSQL 四、mysql进阶 6(索引的创建与设计原则)

一、索引的声明和使用 1.1 索引的分类 MySQL的索引包括普通索引、唯一性索引、全文索引、单列索引、多列索引和空间索引等。 从 功能逻辑 上说&#xff0c;索引主要有 4 种&#xff0c;分别是普通索引、唯一索引、主键索引、全文索引。 按照 物理实现方式 &#xff0c;索引可…

计算机网络之令牌总线

上文内容&#xff1a;什么是以太网 1.令牌总线工作原理 在总线的基础上&#xff0c;通过在网络结点之间有序地传递令牌来分配各结点对共享型总线的访问权利&#xff0c;形成闭合的逻辑环路。 完全采用半双工的操作方式&#xff0c;只有获得令牌的结点才能发送信息&#xff…

【matlab 项目工期优化】基于NSGA2/3的项目工期多目标优化(时间-成本-质量-安全)

一 背景介绍 本文分享了一个通用的项目工期优化的案例&#xff0c;决策变量是每个子项目的工期&#xff0c;优化目标是项目的完成时间最小&#xff0c;项目的总成本现值最小&#xff0c;项目的总安全水平最高&#xff0c;项目的总质量水平最高。采用的算法是NSGA2和NSGA3算法。…

YOLOV++ 详解 | 网络结构、代码解析、YOLOV 论文阅读、初识 VID

前言 代码地址&#xff1a;https://github.com/YuHengsss/YOLOV 本文网络结构按 YOLOV SwinTiny 绘制&#xff0c;不同的模型主要差异在于 Backbone&#xff0c;VID 相关的部分基本相同。 Predict Input 代码基于 vid_demo。首先会读取视频中的所有帧&#xff08;只能用短视频…

kafka系列之消费后不提交offset情况的分析总结

概述 每当我们调用Kafka的poll()方法或者使用KafkaListener(其实底层也是poll()方法)时&#xff0c;它都会返回之前被写入Kafka的记录&#xff0c;即我们组中的消费者还没有读过的记录。 这意味着我们有一种方法可以跟踪该组消费者读取过的记录。 如前所述&#xff0c;Kafka的一…

自闭症儿童的治疗方法有哪些?

身为星贝育园自闭症儿童康复学校的资深教育者&#xff0c;我深知自闭症谱系障碍&#xff08;ASD&#xff09;儿童的教育与治疗需要一个全面、个性化的方案。在星贝育园&#xff0c;我们致力于为孩子们提供一个充满爱与理解的环境&#xff0c;采用多种科学验证的教育方法&#x…

【Linux】动态库的制作与使用

&#x1f490; &#x1f338; &#x1f337; &#x1f340; &#x1f339; &#x1f33b; &#x1f33a; &#x1f341; &#x1f343; &#x1f342; &#x1f33f; &#x1f344;&#x1f35d; &#x1f35b; &#x1f364; &#x1f4c3;个人主页 &#xff1a;阿然成长日记 …

asp.net公交司机管理系统-计算机毕业设计源码96696

摘 要 公交司机是公交运输系统中的重要组成部分&#xff0c;他们的管理和运营对于公交运输的正常运行和服务质量起着至关重要的作用。本文提出了一种基于C#&#xff08;asp.net&#xff09;的公交司机管理系统。该系统利用计算机技术和网络通信技术&#xff0c;实现了公交司机信…

Ollama:本地大模型运行指南_ollama运行本地模型

Ollama 简介 Ollama 是一个基于 Go 语言开发的可以本地运行大模型的开源框架。 官网&#xff1a;ollama.com/ GitHub 地址&#xff1a;github.com/ollama/olla… Ollama 安装 【一一AGI大模型学习 所有资源获取处一一】 ①人工智能/大模型学习路线 ②AI产品经理入门指南 ③…