结构型设计模式-1.代理设计模式

news2024/11/24 0:30:29

结构型设计模式-1.代理设计模式

结构型设计模式:利用类与类之间的关系(继承、组合),形成一种类与类之间的结构,通过这种结构提高代码的可拓展性、可维护性和可重用性。

一、简介

代理设计模式(Proxy Design Pattern)是一种结构型设计模式,它为其他对象提供一个代理,以控制对这个对象的访问。根据不同时期生成的代理对象,分为:

  • 静态代理:指代理类在编译时就已经确定。
  • 动态代理:指代理类在运行时动态生成。

代理模式可以用于实现懒加载、安全访问控制、日志记录等功能,其核心就是:屏蔽掉对原始对象的直接访问,为原始对象的能力提高增强。

其大致流程如下:

  1. 创建一个接口,定义代理类和被代理类共同实现的方法。
  2. 创建被代理类,实现这个接口,并且在其中定义实现方法。
  3. 创建代理类,也要实现这个接口,同时在其中定义一个被代理类的对象作为成员变量。
  4. 在代理类中实现接口中的方法,方法中调用被代理类中的对应方法。
  5. 通过创建代理对象,并调用其方法,方法增强。 这样,被代理类的方法就会被代理类所覆盖,实现了对被代理类的增强或修改。

二、静态代理

1、简介

在静态代理中,需要手动创建代理类和被代理类,并且它们实现相同的接口或继承相同的父类。

2、基本流程

  1. 创建一个接口 / 抽象父类 / 父类:定义代理类和被代理类共同实现的方法。
  2. 创建被代理类:实现上述接口,并在其中定义实现方法。
  3. 创建代理类:同样实现上述接口,并在其中定义一个被代理类的对象作为成员变量。
  4. 在代理类中实现接口中的方法:在这些方法中,调用被代理类对象的对应方法。
  5. 通过创建代理对象并调用其方法,实现对被代理类方法的增强或修改。

3、简单示例

当涉及到继承关系时,我们可以使用静态代理来实现对继承类的功能增强。以下是一个示例代码,演示了如何使用静态代理来实现继承类的功能增强:

首先,我们有一个基础类 BaseClass,它定义了一些基本的操作:

// 基础类
class BaseClass {
    public void performOperation() {
        System.out.println("Performing base operation...");
    }
}

接下来,我们创建一个代理类 ProxyClass,它继承自基础类,并在其方法中添加额外的逻辑:

// 代理类,继承自基础类
class ProxyClass extends BaseClass {
    @Override
    public 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;
    }

    @Override
    public 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);
    }

    @Override
    public 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学习中回答的使用场景和代码示例:

  1. 访问控制和安全性:
    静态代理可以用于控制对被代理对象的访问权限,确保只有具有合适权限的客户端可以访问被代理对象。
  2. 日志记录:
    静态代理可以用于记录对被代理对象的操作日志,方便后续的分析和监控。
  3. 性能监控:
    静态代理可以用于监控被代理对象的性能,统计方法的执行时间、调用次数等指标。
  4. 缓存:
    静态代理可以用于实现对被代理对象的结果进行缓存,提高系统响应速度。
  5. 事务管理:
    静态代理可以用于实现对被代理对象的事务管理,保证操作的原子性和一致性。
  6. 远程代理:
    静态代理可以用于实现远程对象的访问,隐藏底层的网络通信细节。

**【示例-缓存代理】**当涉及到数据库查询时,可以使用静态代理来实现查询缓存的功能。下面是一个简单的示例代码,演示了如何使用静态代理来实现数据库查询缓存的功能:

首先,我们需要定义一个共同的接口,代表数据库操作:

// 定义数据库操作的接口
interface Database {
    String queryData(String query);
}

然后,我们创建一个具体的数据库操作类,实现上述接口,用于执行实际的数据库查询:

// 实现数据库操作的具体类
class DatabaseImpl implements Database {
    @Override
    public 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<>();
    }

    @Override
    public 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 {
    @Override
    public 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;
    }

    @Override
    public 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 {
    @Override
    public void performTask() {
        System.out.println("Performing remote task...");
    }
}

接下来,我们创建一个代理类,用于封装远程通信的逻辑:

// 创建代理类,封装远程通信的逻辑
class RemoteProxy implements RemoteService {
    private RemoteService remoteService;

    public RemoteProxy() {
        // 在代理类中创建远程服务对象
        this.remoteService = new RemoteServiceImpl();
    }

    @Override
    public 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、基本流程

  1. 定义一个接口,该接口是被代理类和代理类共同实现的接口。
  2. 创建一个实现了InvocationHandler接口的代理处理器类,该类中包含对方法的增强逻辑。
  3. 使用Proxy类的静态方法newProxyInstance()来创建代理对象,该方法接收三个参数:类加载器、被代理类实现的接口数组、代理处理器对象。
  4. 通过代理对象调用方法,代理处理器中的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 {
    @Override
    public String query(String query) {
        // 模拟执行数据库查询
        System.out.println("Executing database query: " + query);

        // 返回查询结果
        return "Result for query: " + query;
    }

    @Override
    public 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 异常信息
     */
    @Override
    public 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,包含了 querydelete 两个方法。

public class DatabaseMethodInterceptor implements MethodInterceptor {
    private DataBaseImpl dataBase;

    public DatabaseMethodInterceptor() {
        this.dataBase = new DataBaseImpl();
    }

    public DatabaseMethodInterceptor(DataBaseImpl dataBase) {
        this.dataBase = dataBase;
    }

    @Override
    public 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通过enhancer
        Enhancer 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

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

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

相关文章

从零开始制作婚礼策划展示小程序

随着移动互联网的发展&#xff0c;小程序已经成为各行各业展示和推广自己的重要工具之一。对于婚礼策划行业来说&#xff0c;制作一个专属的婚礼策划展示小程序&#xff0c;不仅能提升服务的专业性和便利性&#xff0c;还能吸引更多的客户。下面将介绍从零开始制作婚礼策划展示…

【MySQL技术专题】「问题实战系列」深入探索和分析MySQL数据库的数据备份和恢复实战开发指南(系统底层优化篇)

深入探索和分析MySQL数据库的全方位的优化实战开发指南&#xff08;数据库底层优化篇&#xff09; 硬件层面优化数据库物理机分析底层技术优化磁盘性能优化随机IO能力的能力支持 RAID磁盘阵列RAID10RAID10的优势 网卡优化网络设备坑点问题建议 服务器硬件配置调整服务器BIOS调整…

Layout软件中的焊盘的一般命名方法是什么呢?

答&#xff1a;对于不同的焊盘有不同命名方法&#xff0c;这里给大家介绍一下普遍的命名方法&#xff0c;具体如下所示&#xff1a; 贴片类焊盘命名方式&#xff1a; 1&#xff09;圆焊盘circle &#xff1a;SC 直径&#xff0c;如&#xff1a; SC1R00&#xff0c;即直径为1m…

多种语言示例采集数据【淘宝天猫1688拼多多API系列】可高并发线程

长话短说&#xff0c;节约彼此宝贵时间&#xff0c;我们以淘宝商品详情数据为例&#xff1a; 请求方式&#xff1a;HTTPS POST GET 请求地址&#xff1a;https://o0b.cn/anzexi taobao.item_get 公共参数 名称类型必须描述技术交流18179014480keyString是调用key&#xf…

探寻智能化未来:AI与Web3共创金融领域巨大潜力

人工智能&#xff08;AI&#xff09;和Web3技术的迅猛发展为我们带来了许多新的机遇和影响。在数字经济和社会的浪潮中&#xff0c;结合了AI的智能化能力和Web3的去中心化与区块链技术&#xff0c;我们将进入一个智能化的Web3时代。人工智能和Web3技术是开拓生产力极限和重新定…

Docker 数据管理

Docker 数据管理 一、docker数据管理 1.数据卷 数据卷是一个供容器使用的特殊目录&#xff0c;位于容器中。可将宿主机的目录挂载到数据卷上&#xff0c;对数据卷的修改操作立刻可见&#xff0c;并且更新数据不会影响镜像&#xff0c;从而实现数据在宿主机与容器之间的迁移。…

QT构建套件(Kit)黄色感叹号问题解决

构建套件&#xff08;Kit&#xff09;黄色感叹号问题 1:看下面的图出现了黄色警告&#xff0c;此时这个构建套件 就是不允许使用的 2&#xff1a;查看一下MSVC的dedbug调试器(cdb.exe) 如果没有&#xff0c;我们需要下载cdb.exe cdb.exe下载方法 2.1首先我们可以打开我们系…

【Linux】- 进程管理

进程管理 1.1 基本介绍1.2 显示系统执行的进程1.3 服务(service)管理2.1 chkconfig 指令2.2 动态监控进程2.3 监控网络状态 1.1 基本介绍 在 LINUX 中&#xff0c;每个执行的程序都称为一个进程。每一个进程都分配一个 ID 号(pid,进程号)。>windows > linux每个进程都可…

哪种电容笔比较好用?一般电容笔和Apple pencil区别

和苹果的Pencil不同的是&#xff0c;一般的电容笔并没有具备重力压感&#xff0c;只有一种倾斜的压感。如果你是一个不常用于绘画的用户&#xff0c;那么可以使用一支价格相对实惠的平替电容笔。这款电容笔&#xff0c;既能用在办公中&#xff0c;又能用来做笔记&#xff0c;还…

勘探开发人工智能技术:地震层位解释

1 地震层位解释 层位解释是地震构造解释的重要内容&#xff0c;是根据目标层位的地震反射特征如振幅、相位、形态、连续性、特征组合等信息在地震数据体上进行追踪解释获得地震层位数据的方法。 1.1 地震信号、层位与断层 图1.1 所示为地震信号采集的过程&#xff0c;地面炮…

spring复习:(54)注解配置和xml配置时在bean实例化时的区别

一、使用配置文件定义bean时&#xff0c;会调用无参的构造方法&#xff1a; 而使用ConfigurationBean注解时&#xff0c;在bean定义解析时设置了 factoryBeanName和factoryMethodName 实例化bean时会走如下逻辑&#xff1a; instantiateUsingFactoryMethod方法代码如下&…

怎么学习JavaWeb开发? - 易智编译EaseEditing

学习JavaWeb开发可以按照以下步骤进行&#xff1a; 掌握Java基础&#xff1a; 在学习JavaWeb开发之前&#xff0c;确保你对Java编程语言有一定的掌握&#xff0c;包括面向对象编程、基本语法、数据类型、流程控制等。 学习HTML、CSS和JavaScript&#xff1a; JavaWeb开发主要…

Thanos工作原理及组件简介

Thanos 简介 Thanos 是一个「开源的&#xff0c;高可用的 Prometheus 系统&#xff0c;具有长期存储能力」。很多知名公司都在使用 Thanos&#xff0c;也是 CNCF 孵化项目的一部分。 Thanos 的一个主要特点就是通过使用对象存储&#xff08;比如 S3&#xff09;可以允许 “无…

苹果safari浏览器播放不了video标签视频

今天遇到了个神奇的问题&#xff0c;视频文件在pc端和安卓手机上播放都没问题&#xff0c;但是在ios上就是播放不了&#xff0c;大概代码如下&#xff1a; 前端代码&#xff1a; <video id"video" width"350" height"500" controls><s…

PMP和软考应该考哪个?

1、信息系统项目管理师和PMP都是国家或国际级别的考试&#xff0c;证书具有通用性。但从考试难度上来说&#xff0c;PMP相对更容易&#xff0c;因为只有一场考试&#xff0c;且题型相对单一。而信息系统项目管理师则需要通过三科考试&#xff0c;包括综合知识、案例分析、论文等…

Hybird App技术解析:混合应用热更新原理

原生应用与混合应用的开发发布 1、原生应用 我们都知道传统原生 APP &#xff0c;整个开发成本非常高、发布周期也比较长。对于需要频繁迭代业务内容的企业来说&#xff0c;无论是更新功能模块还是修复有缺陷的版本&#xff0c;都需要重新测试、重新发版、重新提交第三方应用…

金融领域:产业链知识图谱包括上市公司、行业和产品共3类实体,构建并形成了一个节点10w+,关系边16w的十万级别产业链图谱

项目设计集合&#xff08;人工智能方向&#xff09;&#xff1a;助力新人快速实战掌握技能、自主完成项目设计升级&#xff0c;提升自身的硬实力&#xff08;不仅限NLP、知识图谱、计算机视觉等领域&#xff09;&#xff1a;汇总有意义的项目设计集合&#xff0c;助力新人快速实…

智能井盖:科技赋能城市脚下安全

在智能化飞速发展的今天&#xff0c;智能井盖作为城市基础设施的一部分&#xff0c;正逐渐走进人们的视野。它利用现代科技手段&#xff0c;实现了对城市井盖的实时监控、及时响应和高效管理&#xff0c;为城市管理、市民出行等方面带来了诸多便利。 城市中井盖数量庞大&#x…

ES6基础知识六:你是怎么理解ES6中 Promise的?使用场景?

一、介绍 Promise&#xff0c;译为承诺&#xff0c;是异步编程的一种解决方案&#xff0c;比传统的解决方案&#xff08;回调函数&#xff09;更加合理和更加强大 在以往我们如果处理多层异步操作&#xff0c;我们往往会像下面那样编写我们的代码 doSomething(function(resu…

Springboot+vue 集成openAI智能助手源码分享(适配手机端)

- 演示站&#xff1a;http://tlzcf.vip:8679/ 源码地址&#xff1a;链接&#xff1a;https://pan.baidu.com/s/1a_sGUGW1Q4n3VKbRqlpHrw?pwdtl2u 提取码&#xff1a;tl2u在这里插入代码片