结构型设计模式-1.代理设计模式
结构型设计模式-1.代理设计模式
结构型设计模式:利用类与类之间的关系(继承、组合),形成一种类与类之间的结构,通过这种结构提高代码的可拓展性、可维护性和可重用性。
一、简介
代理设计模式(Proxy Design Pattern)是一种结构型设计模式,它为其他对象提供一个代理,以控制对这个对象的访问。根据不同时期生成的代理对象,分为:
- 静态代理:指代理类在编译时就已经确定。
- 动态代理:指代理类在运行时动态生成。
代理模式可以用于实现懒加载、安全访问控制、日志记录等功能,其核心就是:屏蔽掉对原始对象的直接访问,为原始对象的能力提高增强。
其大致流程如下:
- 创建一个接口,定义代理类和被代理类共同实现的方法。
- 创建被代理类,实现这个接口,并且在其中定义实现方法。
- 创建代理类,也要实现这个接口,同时在其中定义一个被代理类的对象作为成员变量。
- 在代理类中实现接口中的方法,方法中调用被代理类中的对应方法。
- 通过创建代理对象,并调用其方法,方法增强。 这样,被代理类的方法就会被代理类所覆盖,实现了对被代理类的增强或修改。
二、静态代理
1、简介
在静态代理中,需要手动创建代理类和被代理类,并且它们实现相同的接口或继承相同的父类。
2、基本流程
- 创建一个
接口/抽象父类/父类:定义代理类和被代理类共同实现的方法。 - 创建被代理类:实现上述接口,并在其中定义实现方法。
- 创建代理类:同样实现上述接口,并在其中定义一个被代理类的对象作为成员变量。
- 在代理类中实现接口中的方法:在这些方法中,调用被代理类对象的对应方法。
- 通过创建代理对象并调用其方法,实现对被代理类方法的增强或修改。
3、简单示例
当涉及到继承关系时,我们可以使用静态代理来实现对继承类的功能增强。以下是一个示例代码,演示了如何使用静态代理来实现继承类的功能增强:
首先,我们有一个基础类 BaseClass,它定义了一些基本的操作:
// 基础类
class BaseClass {public void performOperation() {System.out.println("Performing base operation...");}
}
接下来,我们创建一个代理类 ProxyClass,它继承自基础类,并在其方法中添加额外的逻辑:
// 代理类,继承自基础类
class ProxyClass extends BaseClass {@Overridepublic void performOperation() {// 在调用父类方法之前添加额外的逻辑System.out.println("Before performing operation...");// 调用父类方法super.performOperation();// 在调用父类方法之后添加额外的逻辑System.out.println("After performing operation...");}
}
在代理类中,我们重写了基础类的 performOperation() 方法,并在方法中通过调用 super.performOperation() 来执行基础类的功能。同时,在调用父类方法之前和之后,我们添加了额外的逻辑。
最后,我们可以使用代理类来执行操作,并观察功能增强的效果:
public class Main {public static void main(String[] args) {ProxyClass proxy = new ProxyClass();proxy.performOperation();}
}
在上述示例中,我们创建了 ProxyClass 的实例,并调用其 performOperation() 方法。在执行该方法时,代理类将在调用父类方法之前和之后添加额外的逻辑。这样,我们就实现了对继承类功能的增强,而不需要修改基础类的代码。
通过静态代理,我们可以在继承关系中对基础类的功能进行增强,而不影响基础类的原有实现。这样,我们可以通过代理类在调用父类方法之前或之后添加额外的逻辑,实现功能的灵活扩展。
下面是一个使用接口实现静态代理的示例代码:
首先,我们定义一个共同的接口 Image,它包含一个方法 display():
// 共同的接口
interface Image {void display();
}
接下来,我们创建一个具体的接口实现类 RealImage,实现了 Image 接口:
// 接口实现类
class RealImage implements Image {private String filename;public RealImage(String filename) {this.filename = filename;}@Overridepublic void display() {System.out.println("Displaying image: " + filename);}
}
然后,我们创建一个代理类 ImageProxy,它同时实现了 Image 接口,并拥有一个 RealImage 对象作为成员变量:
// 代理类
class ImageProxy implements Image {private RealImage realImage;public ImageProxy(String filename) {this.realImage = new RealImage(filename);}@Overridepublic void display() {System.out.println("Loading image: " + realImage.getFilename());realImage.display();}
}
在代理类中,我们在 display() 方法中先输出加载图片的信息,然后调用 RealImage 对象的 display() 方法来显示图片。
最后,我们可以使用代理类来显示图片,并观察输出结果:
public class Main {public static void main(String[] args) {Image image = new ImageProxy("example.jpg");image.display();}
}
在上述示例中,我们创建了 ImageProxy 的实例,并调用其 display() 方法来显示图片。在执行该方法时,代理类会输出加载图片的信息,并通过调用 RealImage 对象的 display() 方法来实际显示图片。
通过使用接口实现静态代理,我们可以在代理类中控制对实现接口的对象的访问,并在调用其方法前后添加额外的逻辑。这样,我们可以对接口实现对象的方法进行增强、修改或限制,以满足特定的需求。
4、优点和缺点
优点:
- 可以在不修改原始代码的情况下,通过代理对象对被代理对象进行功能增强、安全访问控制、日志记录等操作;也可以在代理对象中进行一些额外的操作,如记录日志、缓存等,以增强被代理对象的功能。
- 代理对象可以隐藏被代理对象的具体实现,实现了客户端和被代理对象的解耦。
缺点:
- 静态代理在编译时就已经确定代理类,后续维护可能修改源代码
- 每个被代理类都需要手动创建一个代理类,当代理类较多或变动频繁时,会增加代码量和维护成本。
5、使用场景
下列是使用chatgpt学习中回答的使用场景和代码示例:
- 访问控制和安全性:
静态代理可以用于控制对被代理对象的访问权限,确保只有具有合适权限的客户端可以访问被代理对象。 - 日志记录:
静态代理可以用于记录对被代理对象的操作日志,方便后续的分析和监控。 - 性能监控:
静态代理可以用于监控被代理对象的性能,统计方法的执行时间、调用次数等指标。 - 缓存:
静态代理可以用于实现对被代理对象的结果进行缓存,提高系统响应速度。 - 事务管理:
静态代理可以用于实现对被代理对象的事务管理,保证操作的原子性和一致性。 - 远程代理:
静态代理可以用于实现远程对象的访问,隐藏底层的网络通信细节。
**【示例-缓存代理】**当涉及到数据库查询时,可以使用静态代理来实现查询缓存的功能。下面是一个简单的示例代码,演示了如何使用静态代理来实现数据库查询缓存的功能:
首先,我们需要定义一个共同的接口,代表数据库操作:
// 定义数据库操作的接口
interface Database {String queryData(String query);
}
然后,我们创建一个具体的数据库操作类,实现上述接口,用于执行实际的数据库查询:
// 实现数据库操作的具体类
class DatabaseImpl implements Database {@Overridepublic String queryData(String query) {// 模拟执行数据库查询System.out.println("Executing database query: " + query);// 返回查询结果return "Result for query: " + query;}
}
接下来,我们创建一个代理类,用于添加查询缓存的逻辑:
// 创建代理类,添加查询缓存的逻辑
class DatabaseProxy implements Database {private Database database;private Map<String, String> cache; // 查询缓存public DatabaseProxy() {this.database = new DatabaseImpl();this.cache = new HashMap<>();}@Overridepublic String queryData(String query) {// 先检查缓存中是否存在查询结果if (cache.containsKey(query)) {System.out.println("Retrieving cached result for query: " + query);return cache.get(query);}// 如果缓存中不存在查询结果,则执行实际的数据库查询String result = database.queryData(query);// 将查询结果存入缓存cache.put(query, result);return result;}
}
在代理类中,我们在queryData()方法中先检查缓存中是否存在查询结果。如果存在,直接从缓存中返回结果;如果不存在,代理类会调用实际的数据库操作类执行查询,并将查询结果存入缓存中。
最后,我们可以使用代理类来执行数据库查询,并观察缓存的效果:
public class Main {public static void main(String[] args) {Database database = new DatabaseProxy();// 第一次执行查询,将结果存入缓存String result1 = database.queryData("SELECT * FROM table1");System.out.println("Result 1: " + result1);// 第二次执行相同的查询,从缓存中获取结果String result2 = database.queryData("SELECT * FROM table1");System.out.println("Result 2: " + result2);}
}
在上述示例中,第一次执行查询时,会调用实际的数据库操作类执行查询,并将结果存入缓存。第二次执行相同的查询时,直接从缓存中获取结果,而不会再次执行数据库查询。

通过静态代理,我们实现了数据库查询缓存的功能,可以提高查询性能,减少对数据库的访问。这样,在相同的查询被频繁执行时,可以直接从缓存中获取结果,避免了重复的数据库查询操作。
【示例-安全代理】当涉及到安全性验证时,可以使用静态代理来实现安全代理的功能。下面是一个简单的示例代码,演示了如何使用静态代理来实现安全代理:
首先,我们需要定义一个共同的接口,代表敏感操作:
// 定义敏感操作的接口
interface SensitiveOperation {void performOperation();
}
然后,我们创建一个具体的敏感操作类,实现上述接口,用于执行实际的敏感操作:
// 实现敏感操作的具体类
class SensitiveOperationImpl implements SensitiveOperation {@Overridepublic void performOperation() {System.out.println("Performing sensitive operation...");}
}
接下来,我们创建一个代理类,用于添加安全验证的逻辑:
// 创建代理类,添加安全验证的逻辑
class SecurityProxy implements SensitiveOperation {private SensitiveOperation sensitiveOperation;private String password; // 安全验证密码public SecurityProxy(String password) {this.sensitiveOperation = new SensitiveOperationImpl();this.password = password;}@Overridepublic void performOperation() {// 进行安全验证if (authenticate()) {sensitiveOperation.performOperation();} else {System.out.println("Access denied! Invalid password.");}}private boolean authenticate() {// 进行安全验证的逻辑,比较输入密码和预设密码是否匹配String inputPassword = getPasswordFromUser();return inputPassword.equals(password);}private String getPasswordFromUser() {// 模拟从用户输入获取密码的逻辑Scanner scanner = new Scanner(System.in);System.out.print("Enter password: ");return scanner.nextLine();}
}
在代理类中,我们在performOperation()方法中进行安全验证。首先,用户需要输入密码进行验证;如果验证通过,则调用实际的敏感操作类执行敏感操作;如果验证失败,则拒绝访问。
最后,我们可以使用代理类来执行敏感操作,并观察安全验证的效果:
public class Main {public static void main(String[] args) {String password = "password123"; // 设置安全验证密码SensitiveOperation operation = new SecurityProxy(password);// 执行敏感操作,需要通过密码验证operation.performOperation();}
}
在上述示例中,执行敏感操作时,用户需要输入密码进行安全验证。只有当输入的密码与预设密码匹配时,才能执行实际的敏感操作。否则,将拒绝访问。
通过静态代理,我们实现了安全代理的功能,可以在执行敏感操作前进行安全验证,保护敏感操作的安全性。这样,在需要对敏感操作进行访问控制和验证的场景下,可以使用安全代理来确保只有经过验证的用户才能执行敏感操作。
【示例-远程代理】当涉及到远程对象的访问时,可以使用静态代理来实现远程代理的功能。下面是一个简单的示例代码,演示了如何使用静态代理来实现远程代理:
首先,我们需要定义一个共同的接口,代表远程服务:
// 定义远程服务的接口
interface RemoteService {void performTask();
}
然后,我们创建一个具体的远程服务类,实现上述接口,用于执行实际的远程任务:
// 实现远程服务的具体类
class RemoteServiceImpl implements RemoteService {@Overridepublic void performTask() {System.out.println("Performing remote task...");}
}
接下来,我们创建一个代理类,用于封装远程通信的逻辑:
// 创建代理类,封装远程通信的逻辑
class RemoteProxy implements RemoteService {private RemoteService remoteService;public RemoteProxy() {// 在代理类中创建远程服务对象this.remoteService = new RemoteServiceImpl();}@Overridepublic void performTask() {// 在代理类中添加远程通信的逻辑,模拟网络请求System.out.println("Sending request to remote server...");// 调用远程服务对象的方法remoteService.performTask();// 在代理类中添加远程通信的逻辑,模拟网络响应System.out.println("Received response from remote server...");}
}
在代理类中,我们在performTask()方法中添加远程通信的逻辑,模拟网络请求和响应的过程。首先,发送请求到远程服务器;然后,调用远程服务对象的方法执行远程任务;最后,接收远程服务器的响应。
最后,我们可以使用代理类来执行远程任务,并观察远程通信的效果:
public class Main {public static void main(String[] args) {RemoteService remoteService = new RemoteProxy();// 执行远程任务remoteService.performTask();}
}
在上述示例中,执行远程任务时,代理类将负责封装远程通信的逻辑。在调用远程服务对象的方法之前和之后,代理类会进行网络请求和响应的模拟操作。
通过静态代理,我们实现了远程代理的功能,可以封装远程通信的逻辑,隐藏底层的网络细节。这样,在需要访问远程对象时,可以使用远程代理来进行网络请求和响应的处理,简化了远程通信的操作。
三、动态代理
1、简介
动态代理(Dynamic Proxy)是一种在运行时动态生成代理类的设计模式。与静态代理不同,动态代理不需要手动编写代理类,而是通过Java的反射机制在运行时动态生成代理类,从而实现对被代理对象的代理。
- 基于JDK实现的动态代理,基于
接口实现。 - 基于CGLIB使用的动态代理,基于
继承实现。
2、基本流程
- 定义一个接口,该接口是被代理类和代理类共同实现的接口。
- 创建一个实现了InvocationHandler接口的代理处理器类,该类中包含对方法的增强逻辑。
- 使用Proxy类的静态方法newProxyInstance()来创建代理对象,该方法接收三个参数:类加载器、被代理类实现的接口数组、代理处理器对象。
- 通过代理对象调用方法,代理处理器中的invoke()方法会被触发,并执行相应的增强逻辑。
3、优点和缺点
优点:
- 动态代理可以在运行时动态地创建代理对象,适用于不同的接口和被代理类。
- 它允许在不修改现有代码的情况下,对方法进行统一的增强或拦截,比如性能监控、事务管理、缓存等。
- 动态代理可以减少代码量,避免手动编写大量的代理类。
缺点:
- 动态代理的性能相对较低,因为在运行时需要使用反射机制来生成代理类和调用方法。
- 动态代理只能代理接口,无法代理具体类,因为Java的单继承限制。
4、使用场景
- 动态代理常用于AOP(面向切面编程)领域,可以通过动态代理在运行时动态地为目标对象添加横切逻辑,如日志记录、事务管理等。
- 动态代理还可以用于远程方法调用(RMI)、缓存代理、延迟加载等场景,以实现更灵活、可扩展的系统架构。
5、基于 JDK 实现的动态代理
这个例子是基于 JDK 实现的动态代理示例。它演示了如何使用动态代理在运行时为目标对象添加额外的逻辑处理,而无需修改目标对象的代码:
/*** Author: shawn* Description: 定义一个共同的接口,代表数据库操作*/
public interface DataBase {/*** 执行数据库查询操作** @param query 查询语句* @return 查询结果*/String query(String query);/*** 执行数据库删除操作** @param delete 删除语句* @return 删除结果*/String delete(String delete);
}/*** Author: shawn* Description: 数据库操作的具体实现类*/
public class DataBaseImpl implements DataBase {@Overridepublic String query(String query) {// 模拟执行数据库查询System.out.println("Executing database query: " + query);// 返回查询结果return "Result for query: " + query;}@Overridepublic String delete(String delete) {// 模拟执行数据库删除System.out.println("Executing database delete: " + delete);// 返回删除结果return "Result for delete: " + delete;}
}/*** Author: shawn* Description: 该代理处理器类负责处理代理对象的方法调用,并在方法调用前后执行额外的逻辑操作。*/
public class DatabaseInvocationHandler implements InvocationHandler {private Object target;public DatabaseInvocationHandler(Object target) {this.target = target;}/*** invoke()方法用于处理代理对象的方法调用。** @param proxy 代理对象本身* @param method 被调用的方法对象* @param args 方法的参数数组* @return 方法的返回结果* @throws Throwable 异常信息*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 在方法调用前的逻辑处理// 如:判断方法名,查看是否需要做记录日志String methodName = method.getName();if ("delete".equals(methodName)) {//是删除操作才做增强,不然还是调用原方法System.out.println("Recording deletion operation log");}System.out.println("Before method: " + methodName);System.out.println("Arguments: " + args[0]);// 调用被代理对象的方法Object result = method.invoke(target, args);// 在方法调用后的逻辑处理System.out.println("Result: " + result);System.out.println("After method: " + method);// 返回方法的返回结果return result;}
}public class Main {public static void main(String[] args) {// 创建目标对象DataBase target = new DataBaseImpl();// 创建代理处理器DatabaseInvocationHandler handler = new DatabaseInvocationHandler();// 创建代理对象DataBase proxy = (DataBase) Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),handler);// 调用代理对象的方法String queryResult = proxy.query("SELECT * FROM table");System.out.println("------------------------");String deleteResult = proxy.delete("DELETE FROM table WHERE id = 1");System.out.println("------------------------");// 输出方法的返回结果System.out.println("Query Result: " + queryResult);System.out.println("------------------------");System.out.println("Delete Result: " + deleteResult);}
}
在上面的代码中,我们使用了动态代理来为 DataBase 接口生成了一个代理类,并在代理类中增加了日志记录的功能。
具体地说,在 DatabaseInvocationHandler 类中的 invoke 方法中,我们根据方法名进行判断,只有当方法名是 "delete" 时才会进行日志记录。这样,在调用代理对象的 delete 方法时,会先输出 “Recording deletion operation log” 的提示信息,表示进行了删除操作的日志记录。
其他方法(比如 query 方法)没有满足条件,所以不会进行日志记录,只会输出方法调用前的提示信息和参数信息。
通过这种方式,我们实现了对原有接口的增强,根据不同的方法名来决定是否进行日志记录,从而实现了按需添加日志记录功能的动态代理类。这样的设计使得我们可以在不修改原有接口和实现类的情况下,为特定方法或特定场景添加额外的功能。
【下列是运行输出】:
Before method: query
Arguments: SELECT * FROM table
Executing database query: SELECT * FROM table
Result: Result for query: SELECT * FROM table
After method: public abstract java.lang.String structuralDesignPattern.proxy.dynamicProxy.DataBase.query(java.lang.String)
------------------------
Recording deletion operation log
Before method: delete
Arguments: DELETE FROM table WHERE id = 1
Executing database delete: DELETE FROM table WHERE id = 1
Result: Result for delete: DELETE FROM table WHERE id = 1
After method: public abstract java.lang.String structuralDesignPattern.proxy.dynamicProxy.DataBase.delete(java.lang.String)
------------------------
Query Result: Result for query: SELECT * FROM table
------------------------
Delete Result: Result for delete: DELETE FROM table WHERE id = 1
6、基于 CGLIB 实现的动态代理
这是一个基于CGLIB实现的动态代理示例。下面是代码的解析:
public class DataBaseImpl {public String query(String query) {// 模拟执行数据库查询System.out.println("Executing database query: " + query);// 返回查询结果return "Result for query: " + query;}public String delete(String delete) {// 模拟执行数据库删除System.out.println("Executing database delete: " + delete);// 返回删除结果return "Result for delete: " + delete;}
}
上述代码定义了一个数据库操作的具体实现类 DataBaseImpl,包含了 query 和 delete 两个方法。
public class DatabaseMethodInterceptor implements MethodInterceptor {private DataBaseImpl dataBase;public DatabaseMethodInterceptor() {this.dataBase = new DataBaseImpl();}public DatabaseMethodInterceptor(DataBaseImpl dataBase) {this.dataBase = dataBase;}@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {// 在方法调用前的逻辑处理// 如:判断方法名,查看是否需要做记录日志String methodName = method.getName();if ("delete".equals(methodName)) {//是删除操作才做增强,不然还是调用原方法System.out.println("Recording deletion operation log");}System.out.println("Before method: " + methodName);System.out.println("Arguments: " + objects[0]);// 调用被代理对象的方法Object result = method.invoke(dataBase, objects);// 在方法调用后的逻辑处理System.out.println("Result: " + result);System.out.println("After method: " + method);// 返回方法的返回结果return result;}
}
上述代码是基于CGLIB的方法拦截器 DatabaseMethodInterceptor,实现了 MethodInterceptor 接口。拦截器中的 intercept 方法用于在方法调用前后进行逻辑处理,包括记录日志和调用被代理对象的方法。
public class Main {public static void main(String[] args) {//cglib通过enhancerEnhancer enhancer = new Enhancer();//设置他的父类-要继承谁,给谁做代理enhancer.setSuperclass(DataBaseImpl.class);//设置方法拦截器,用于拦截方法,对方法做增强enhancer.setCallback(new DatabaseMethodInterceptor());// 创建代理对象DataBaseImpl proxy = (DataBaseImpl) enhancer.create();// 调用代理对象的方法String queryResult = proxy.query("SELECT * FROM table");System.out.println("--------cglib----------------");String deleteResult = proxy.delete("DELETE FROM table WHERE id = 1");System.out.println("--------cglib----------------");// 输出方法的返回结果System.out.println("Query Result: " + queryResult);System.out.println("--------cglib----------------");System.out.println("Delete Result: " + deleteResult);}
}
上述代码是 Main 类,包含了代理对象的创建和方法调用的示例。使用 Enhancer 创建代理对象,并设置父类和方法拦截器,最终通过 create 方法创建代理对象。然后,通过代理对象调用方法。
在运行该示例代码时,会输出方法调用的前后日志和结果。
请注意,为了使该示例代码正常运行,你需要在项目的依赖中添加 CGLIB 的相关依赖,或者将项目修改为spring boot项目。你可以将以下依赖添加到你的 pom.xml 文件中:
在运行代码时,你将看到方法调用的输出结果和日志记录,以及代理对象的增强效果。
Before method: query
Arguments: SELECT * FROM table
Executing database query: SELECT * FROM table
Result: Result for query: SELECT * FROM table
After method: public java.lang.String structuralDesignPattern.proxy.dynamicProxy.cglib.DataBaseImpl.query(java.lang.String)
--------cglib----------------
Recording deletion operation log
Before method: delete
Arguments: DELETE FROM table WHERE id = 1
Executing database delete: DELETE FROM table WHERE id = 1
Result: Result for delete: DELETE FROM table WHERE id = 1
After method: public java.lang.String structuralDesignPattern.proxy.dynamicProxy.cglib.DataBaseImpl.delete(java.lang.String)
--------cglib----------------
Query Result: Result for query: SELECT * FROM table
--------cglib----------------
Delete Result: Result for delete: DELETE FROM table WHERE id = 1
相关文章:
结构型设计模式-1.代理设计模式
结构型设计模式-1.代理设计模式 结构型设计模式:利用类与类之间的关系(继承、组合),形成一种类与类之间的结构,通过这种结构提高代码的可拓展性、可维护性和可重用性。 一、简介 代理设计模式(Proxy Des…...
Python(四十九)获取列表指定元素的索引
❤️ 专栏简介:本专栏记录了我个人从零开始学习Python编程的过程。在这个专栏中,我将分享我在学习Python的过程中的学习笔记、学习路线以及各个知识点。 ☀️ 专栏适用人群 :本专栏适用于希望学习Python编程的初学者和有一定编程基础的人。无…...
年轻人的第一套海景房
前段时间新房装修,我把书房设计成工作室的风格,并自己装配了一台电脑,本文是对电脑选购与装配的一则经验贴,仅包含我对计算机硬件的浅薄理解。 配件选购 装机契源 事实上,很多电脑店都提供装配和测试服务,…...
Vue输入内容/链接生成二维码
方式一:qrcode(无 icon 图标) npm i qrcodejs2 --save完整代码 <template><div class"flex-box"><div>qrcode(无 icon 图标)</div><div class"qr-code" ref"qrCo…...
使用langchain与你自己的数据对话(二):向量存储与嵌入
之前我以前完成了“使用langchain与你自己的数据对话(一):文档加载与切割”这篇博客,没有阅读的朋友可以先阅读一下,今天我们来继续讲解deepleaning.AI的在线课程“LangChain: Chat with Your Data”的第三门课:向量存储与嵌入。 …...
No105.精选前端面试题,享受每天的挑战和学习
文章目录 手写new手写Mapget和post区别发起post请求的时候,服务端是怎么解析你的body的(content-type),常见的content-type都有哪些,发文件是怎么解析的(FormData),如果多个文件&…...
【计算机网络】第 3 课 - 计算机网络体系结构
欢迎来到博主 Apeiron 的博客,祝您旅程愉快 ! 时止则止,时行则行。动静不失其时,其道光明。 目录 1、常见的计算机网络体系结构 2、计算机网络体系结构分层的必要性 2.1、物理层 2.2、数据链路层 2.3、网路层 2.4、运输层 2…...
精细呵护:如何维护自己的电脑,提升性能和寿命
导语: 在当今数字化时代,电脑已经成为我们日常生活和工作的必需品。然而,就像任何其他设备一样,电脑需要得到适当的维护和保养,以保持良好的性能和延长使用寿命。在本文中,我们将分享一些简单而有效的方法&…...
DevOps-Jenkins
Jenkins Jenkins是一个可扩展的持续集成引擎,是一个开源软件项目,旨在提供一个开放易用的软件平台,使软件的持续集成变成可能。 官网 应用场景 场景一 研发人员上传开发好的代码到github代码仓库需要将代码下载nginx服务器部署手动下载再…...
Jasper裁员,成也GPT,败也GPT
大家好! 我是老洪。 今天来聊一聊人工智能(artificial intelligence),简称AI。 当前的AI可谓是热火朝天, 自从ChatGPT发布以来,引起了广泛的关注和热情, 许多公司和研究者都试图将其应用于自己的产品或研究中。 按理说…...
安卓开发后台应用周期循环获取位置信息上报服务器
问题背景 最近有需求,在APP启动后,退到后台,还要能实现周期获取位置信息上报服务器,研究了一下实现方案。 问题分析 一、APP退到后台后网络请求实现 APP退到后台后,实现周期循环发送网络请求。目前尝试了两种方案是…...
为什么你的独立站有流量没转化?如何做诊断检查?
新店的创业初期,即使网站有流量,但是销售额为零的情况也常有发生。如果你确定流量是高质量的,寻找阻止潜在客户购买的具体因素可能会感到困难重重。 从“立即购买”按钮的色彩选择这样的细节,到构建品牌故事这样的大计划…...
【深度学习】【三维重建】windows10环境配置PyTorch3d详细教程
【深度学习】【三维重建】windows10环境配置PyTorch3d详细教程 文章目录 【深度学习】【三维重建】windows10环境配置PyTorch3d详细教程前言确定版本对应关系源码编译安装Pytorch3d总结 前言 本人windows10下使用【Code for Neural Reflectance Surfaces (NeRS)】算法时需要搭…...
【算法和数据结构】257、LeetCode二叉树的所有路径
文章目录 一、题目二、解法三、完整代码 所有的LeetCode题解索引,可以看这篇文章——【算法和数据结构】LeetCode题解。 一、题目 二、解法 思路分析:首先看这道题的输出结果,是前序遍历。然后需要找到从根节点到叶子节点的所有路径ÿ…...
yolov5的后处理解析
由于最近实习项目使用到了yolov5, 发现对yolov5的后处理部分不太熟悉,为防止忘记,这里简单做个记录。 在yolov5里,利用FPN特征金字塔,可以得到三个加强特征层,每一个特征层上每一个特征点存在3个先验框&am…...
Java中注解应用场景
1.Parameter注解 Parameter(names "-browser", description "browser name, supported scope [chrome]", required true) Param注解的用法解析_parameter_fFee-ops的博客-CSDN博客 Public User selectUser(param(“userName”) String name, param(“…...
verilog
数据类型 reg reg [3:0] counter; counter是一个寄存器,这个寄存器有4bit大小; reg [3:0] byte1 [7:0]; 有8个寄存器,每个4bit大小; wire 有符号整数 interge 无符号 reg clk_temp (小数)verilog中称实数…...
基于springboot+mybatis+vue进销存管理信息系统
基于springbootmybatisvue进销存管理信息系统 一、系统介绍二、功能展示1.个人中心2.企业信息管理3.商品信息管理4.客户信息管理5.入库记录管理6.出库记录管理7.出库记录管理8.操作日志管理9.库存盘点管理 四、获取源码 一、系统介绍 系统主要功能: 普通用户&#…...
Keepalived 在CentOS安装
下载 有两种下载方式,一种为yum源下载,另一种通过源代码下载,本文章使用源代码编译下载。 官网下载地址:https://www.keepalived.org/download.html wget https://www.keepalived.org/software/keepalived-2.0.20.tar.gz --no-…...
Lua语法学习
Lua 文章目录 Lua变量数据类型nilbooleanstringtable 循环if函数运算符Table -- Events local StateEvents ReplicatedStorage:WaitForChild("StateEvents"); local AddMoneyEvent StateEvents:WaitForChild("AddMoneyEvent");AddMoneyEvent:FireServer(…...
<6>-MySQL表的增删查改
目录 一,create(创建表) 二,retrieve(查询表) 1,select列 2,where条件 三,update(更新表) 四,delete(删除表…...
k8s从入门到放弃之Ingress七层负载
k8s从入门到放弃之Ingress七层负载 在Kubernetes(简称K8s)中,Ingress是一个API对象,它允许你定义如何从集群外部访问集群内部的服务。Ingress可以提供负载均衡、SSL终结和基于名称的虚拟主机等功能。通过Ingress,你可…...
在rocky linux 9.5上在线安装 docker
前面是指南,后面是日志 sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo dnf install docker-ce docker-ce-cli containerd.io -y docker version sudo systemctl start docker sudo systemctl status docker …...
DBAPI如何优雅的获取单条数据
API如何优雅的获取单条数据 案例一 对于查询类API,查询的是单条数据,比如根据主键ID查询用户信息,sql如下: select id, name, age from user where id #{id}API默认返回的数据格式是多条的,如下: {&qu…...
【HTML-16】深入理解HTML中的块元素与行内元素
HTML元素根据其显示特性可以分为两大类:块元素(Block-level Elements)和行内元素(Inline Elements)。理解这两者的区别对于构建良好的网页布局至关重要。本文将全面解析这两种元素的特性、区别以及实际应用场景。 1. 块元素(Block-level Elements) 1.1 基本特性 …...
CMake 从 GitHub 下载第三方库并使用
有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...
微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据
微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据 Power Query 具有大量专门帮助您清理和准备数据以供分析的功能。 您将了解如何简化复杂模型、更改数据类型、重命名对象和透视数据。 您还将了解如何分析列,以便知晓哪些列包含有价值的数据,…...
视觉slam十四讲实践部分记录——ch2、ch3
ch2 一、使用g++编译.cpp为可执行文件并运行(P30) g++ helloSLAM.cpp ./a.out运行 二、使用cmake编译 mkdir build cd build cmake .. makeCMakeCache.txt 文件仍然指向旧的目录。这表明在源代码目录中可能还存在旧的 CMakeCache.txt 文件,或者在构建过程中仍然引用了旧的路…...
算法:模拟
1.替换所有的问号 1576. 替换所有的问号 - 力扣(LeetCode) 遍历字符串:通过外层循环逐一检查每个字符。遇到 ? 时处理: 内层循环遍历小写字母(a 到 z)。对每个字母检查是否满足: 与…...
排序算法总结(C++)
目录 一、稳定性二、排序算法选择、冒泡、插入排序归并排序随机快速排序堆排序基数排序计数排序 三、总结 一、稳定性 排序算法的稳定性是指:同样大小的样本 **(同样大小的数据)**在排序之后不会改变原始的相对次序。 稳定性对基础类型对象…...
