反射第二弹:用注册器动态注册(用自定义的注解标注的)策略,实现策略模式的设计

news2024/11/27 18:33:47

引言

        曾经有人问我,假如有一个业务或者数据处理逻辑,会根据甲方客户繁杂的业务需求,而动态变化,该怎么处理,具体怎么实现?

        将所有策略strategy都写出来,然后放入一个hashMap,根据不同的业务需求,调用不同的策略就行了。

        再问:那如果我的策略动态多变,再将strategy放入Map的代码,不想写死,不想频繁的修改代码怎么实现?比如我将策略代码写入项目下某个文件夹下面某个文件里,或者直接存入数据库某张表里,策略代码用text类型字段存起来,再调用构建接口,将策略逻辑用类加载器,加载出来,动态的创建为一个个新的strategy对象呢?

        读取文件或者text字段,并解析为java代码,编译后,用类加载的方式加载,并且注册进Map,这个稍微复杂,但是我可以实现,只用在代码中添加策略,但是不用修改注册逻辑代码的方式。

设计思路:

  1. 所有需要被自动添加的策略类 Strategy 都必须加注解

  2. 再做一个扫描注解,属性包含:标明需要被扫描的包路径,@Import 标注一个注册器(实现ImportBeanDefinitionRegistrar),

  3. 定义一个注册器,实现ImportBeanDefinitionRegistrar,该注册器可以根据上述注解找到所有被注解标注的类,然后获取到标注的名称,实例化该类,并注册入指定的缓存。

        将此注解加注在springboot启动类上面就可以在项目启动的时候,按照指定的包路径去扫描指定注解的类,然后利用反射机制,获取到扫描到的(被标注注解的)类的元空间,有了元空间,不管是获取该类上的注解,注解属性,还是该类的属性,方法都是没问题的。那么就可以获取到被扫描到的策略strategy的名字,再利用反射 clazz.newInstance()  创建出实例,放入缓存,就解决了动态注册策略的目的了。

    除了对反射的利用,还需要对spring 容器有一定的理解,才能利用spring容器的environment, resourceLoader ,做出扫描器,使用resourceLoader 扫描该环境下,所有带注解的类;再根据指定的注解,过滤出目标类。

代码实现:

标注注解:@StrategyClass

import io.swagger.v3.oas.annotations.tags.Tag;
import java.lang.annotation.*;

/**
 * @Title: StrategyClass
 * @Description: 用来标记需要被注册的strategy
 * @Author: wenrong
 * @Date: 2024/4/2 10:02
 * @Version:1.0
 */

@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface StrategyClass {


    /**
     * 策略名
     * 为空时,会尝试读取 {@link Tag#name()} 属性
     */
    String strategyName() default "";


}

扫描注解:trategyScan

@DynamicStrategyScan注解标记在springboot启动类上,在springboot启动的时候,就会扫描@StrategyClass 注解的策略类,并开始自动添加到Map 缓存。

import org.springframework.context.annotation.Import;
import java.lang.annotation.*;

/**
 * @Title: DynamicStrategyScan
 * @Description: 用来扫描有动态策略注解的类,并初始化成相应的配置,注册到JVM 缓存
 * @Author: wenrong
 * @Date: 2024/4/2 15:27
 * @Version:1.0
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import(DynamicStrategyRegister.class)
@Documented
public @interface DynamicStrategyScan {

    String[] value() default {};

    String[] basePackages() default {};
}

注册器抽象类:

因为可能不光策略需要被注册,其他的也可能需要被注册,所以这里这里做一下抽象:

import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;

import java.lang.annotation.Annotation;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * @Title: AnnotationScanAndRegister
 * @Description:
 * @Author: wenrong
 * @Date: 2024/4/2 15:35
 * @Version:1.0
 */
public abstract class AnnotationScanAndRegister implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {

    /**
     * 资源加载器
     */
    protected ResourceLoader resourceLoader;
    /**
     * 环境
     */
    protected Environment environment;

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        ImportBeanDefinitionRegistrar.super.registerBeanDefinitions(importingClassMetadata, registry);
    }

    /**
     * author: wenrong
     * date: 2024/4/2 15:36
     * 获取base packages
     */
    protected static Set<String> getBasePackages(AnnotationMetadata importingClassMetadata, Class<? extends Annotation> clazz) {
        // 获取到扫描注解所有属性
        Map<String, Object> attributes = importingClassMetadata.getAnnotationAttributes(clazz.getCanonicalName());
        Set<String> basePackages = new HashSet<>();
        assert attributes != null;
        // value 属性是否有配置值,如果有则添加
        for (String pkg : (String[]) attributes.get("value")) {
            if (StringUtils.hasText(pkg)) {
                basePackages.add(pkg);
            }
        }

        // basePackages 属性是否有配置值,如果有则添加
        for (String pkg : (String[]) attributes.get("basePackages")) {
            if (StringUtils.hasText(pkg)) {
                basePackages.add(pkg);
            }
        }

        // 如果上面两步都没有获取到basePackages,那么这里就默认使用当前项目启动类所在的包为basePackages
        if (basePackages.isEmpty()) {
            basePackages.add(ClassUtils.getPackageName(importingClassMetadata.getClassName()));
        }

        return basePackages;
    }

    /**
     * 获取name
     */
    protected static String getAttribute(Map<String, Object> attributes, String field) {
        return (String) attributes.get(field);
    }

    /**
     * 创建扫描器
     * 扫描器是在environment下,遇到带注解的类都扫描,只是后面会根据指定注解,做过滤
     */
    protected ClassPathScanningCandidateComponentProvider getScanner() {
        return new ClassPathScanningCandidateComponentProvider(false, environment) {
            @Override
            protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
                boolean isCandidate = false;
                if (beanDefinition.getMetadata().isIndependent()) {
                    if (!beanDefinition.getMetadata().isAnnotation()) {
                        isCandidate = true;
                    }
                }
                return isCandidate;
            }
        };
    }

    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }

    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }
}

动态策略注册类:

import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Title: DynamicStrategyRegister
 * @Description: 用于系统启动时候,动态策略的初始化注册
 * @Author: wenrong
 * @Date: 2024/4/2 15:25
 * @Version:1.0
 */
@Slf4j
public class DynamicStrategyRegister extends AnnotationScanAndRegister {

    
    public static final Map<String, Strategy> XXXStrategies = new HashMap<>();
    

    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        // 创建scanner
        ClassPathScanningCandidateComponentProvider scanner = getScanner();
        scanner.setResourceLoader(resourceLoader);

        // 设置扫描器scanner扫描的过滤条件
        AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(StrategyClass.class);
        scanner.addIncludeFilter(annotationTypeFilter);

        // 获取指定要扫描的basePackages
        Set<String> basePackages = getBasePackages(metadata, DynamicStrategyScan.class);
        // 遍历每一个basePackages
        for (String basePackage : basePackages) {
            // 通过scanner获取basePackage下的候选类(有标注解的类)
            Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(basePackage);
            // 遍历每一个候选类,如果符合条件就把他们注册到容器
            for (BeanDefinition candidateComponent : candidateComponents) {
                if (candidateComponent instanceof AnnotatedBeanDefinition) {
                    // verify annotated class is an interface
                    AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
                    AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
                    // 获取@StrategyClass注解的属性
                    Map<String, Object> attributes = annotationMetadata.getAnnotationAttributes(StrategyClass.class.getCanonicalName());
                    // 注册配置到缓存
                    registerBean(annotationMetadata, attributes);
                }
            }
        }
    }

    /**
     * 利用factoryBean创建代理对象,并注册到容器
     */
    private void registerBean(AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
        // 获取类
        Class<?> clazz;
        try {
            clazz = Class.forName(annotationMetadata.getClassName());
        } catch (ClassNotFoundException e) {
            log.error(e.getMessage());
            return;
        }
       
        // 解析出@StrategyClass注解的name
        String strategyName = getAttribute(attributes, "strategyName");
        Strategy strategy = (Strategy) clazz.newInstance();
        XXXStrategies.put(strategyName, strategy);
    }

}

        前面说了,除了动态注册策略,我还可以用来动态注册操作日志配置,注册配置后,可以操作配置开关,控制哪些接口开启操作日志:

import io.swagger.v3.oas.annotations.Operation;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Title: OperateOrderRegister
 * @Description: 用于系统启动时候,操作配置的初始化注册
 * @Author: wenrong
 * @Date: 2024/4/2 15:25
 * @Version:1.0
 */
@Slf4j
public class OperateLogConfigRegister extends AnnotationScanAndRegister {

    public static final List<LogConfigDTO> CONFIGS = new ArrayList<>();
    
    static final OperateTypeEnum[] OPERATE_TYPES = {OperateTypeEnum.CREATE, OperateTypeEnum.UPDATE, OperateTypeEnum.DELETE, OperateTypeEnum.IMPORT};
    
    @Resource
    private OperateLogConfigApi operateLogConfigApi;

    public static RequestMethod[] getRequestMethod(Method method) {
        // 使用 Spring 的工具类,可以处理 @RequestMapping 别名注解
        RequestMapping requestMapping = AnnotationUtils.getAnnotation(
                method, RequestMapping.class);
        if (requestMapping == null) {
            throw new RuntimeException("方法上没有加请求方式注解");
        }
        return requestMapping.method();
    }

    @PostConstruct
    private void register() {
        List<LogConfigDTO> configDTOS = CONFIGS.stream().filter(logConfigDTO -> {
            if (logConfigDTO.getOperateType() != null) {
                return operateLogConfigApi.validLogConfig(logConfigDTO);
            }
            return false;
        }).collect(Collectors.toList());
        //注册配置到数据库
        operateLogConfigApi.createOperateLogConfigs(configDTOS);
        // 删除已经没有配置日志注解的配置
        List<LogConfigDTO> allOperateLogConfigs = operateLogConfigApi.getAllOperateLogConfigs();
        Set<String> orders = CONFIGS.stream().map(LogConfigDTO::getOrderName).collect(Collectors.toSet());
        List<Long> config2Remove = allOperateLogConfigs.stream().filter(operateConfig ->
                !orders.contains(operateConfig.getOrderName())).map(LogConfigDTO::getId).collect(Collectors.toList());
        if (!config2Remove.isEmpty() && !CONFIGS.isEmpty()) {
            operateLogConfigApi.remove(config2Remove);
        }
    }

    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        // 创建scanner
        ClassPathScanningCandidateComponentProvider scanner = getScanner();
        scanner.setResourceLoader(resourceLoader);

        // 设置扫描器scanner扫描的过滤条件
        AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(OperateLogForClass.class);
        scanner.addIncludeFilter(annotationTypeFilter);

        // 获取指定要扫描的basePackages
        Set<String> basePackages = getBasePackages(metadata, OperateLogApiScan.class);
        // 遍历每一个basePackages
        for (String basePackage : basePackages) {
            // 通过scanner获取basePackage下的候选类(有标注解的类)
            Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(basePackage);
            // 遍历每一个候选类,如果符合条件就把他们注册到容器
            for (BeanDefinition candidateComponent : candidateComponents) {
                if (candidateComponent instanceof AnnotatedBeanDefinition) {
                    // verify annotated class is an interface
                    AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
                    AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
                    // 获取@OperateLogForClass注解的属性
                    Map<String, Object> attributes = annotationMetadata.getAnnotationAttributes(OperateLogForClass.class.getCanonicalName());
                    // 注册配置到缓存
                    registerBean(annotationMetadata, attributes);
                }
            }
        }
    }

    /**
     * 利用factoryBean创建代理对象,并注册到容器
     */
    private void registerBean(AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
        // 获取类
        Class<?> clazz;
        try {
            clazz = Class.forName(annotationMetadata.getClassName());
        } catch (ClassNotFoundException e) {
            log.error(e.getMessage());
            return;
        }
        // 解析出@OperateLogForClass注解的module
        String module = getAttribute(attributes, "module");
        // 解析出@OperateLogForClass注解的name
        String orderName = getAttribute(attributes, "orderName");
        Method[] declaredMethods = clazz.getDeclaredMethods();
        this.mapMethod(declaredMethods, module, orderName);
    }

    private void mapMethod(Method[] methods, String module, String orderName) {

        for (Method method : methods) {
            try {
                if (!Modifier.isPublic(method.getModifiers())) {
                    continue;
                }
                LogConfigDTO logConfigDTO = new LogConfigDTO();
                logConfigDTO.setModule(module);
                logConfigDTO.setOrderName(orderName);
                logConfigDTO.setConfig(true);
                String name = method.getAnnotation(Operation.class).summary();
                logConfigDTO.setName(name);
                // 设置操作类型
                // setOperateTypeByMethodName(method, logConfigDTO);
                setOperateType(method, logConfigDTO);
                if (logConfigDTO.getOperateType() != null) {
                    OperateLogConfigRegister.CONFIGS.add(logConfigDTO);
                }
            } catch (Exception e) {
                log.error("【操作类型匹配异常:】", e);
            }
        }

    }

    /**
     * @description: 设置操作类型, 虽然可以通过方法名匹配,不依赖注解,
     * 但是要依赖代码编写时候的方法名称的严格格式,如果方法名跳出增删改,
     * 则需要通过 {@link OperateType#type()}自定义方法操作类型
     * 适配非controller层的方法
     * @params: @param
     * @return: null
     * @author: wenrong
     * @time: 2024/4/3 14:11
     */
    private void setOperateTypeByMethodName(Method method, LogConfigDTO logConfigDTO) {
        List<OperateTypeEnum> operateTypes = Arrays.stream(OPERATE_TYPES).filter(operateType ->
                method.getName().startsWith(operateType.getName())
        ).collect(Collectors.toList());
        if (!operateTypes.isEmpty()) {
            logConfigDTO.setOperateType(operateTypes.get(0).getDescription());
        } else if (method.getName().startsWith(OperateTypeEnum.GET.getName())
                || method.getName().startsWith(OperateTypeEnum.EXPORT.getName())) {
            logConfigDTO.setOperateType(null);
        } else if (StringUtils.hasText(method.getAnnotation(OperateType.class).type())) {
            logConfigDTO.setOperateType(method.getAnnotation(OperateType.class).type());
        }

    }

    /**
     * @description: 使用 {@link RequestMapping#method()} 匹配操作类型,不容易出错
     * 但是仅限于controller的 RESTFul风格接口
     * @params: @param
     * @return: null
     * @author: wenrong
     * @time: 2024/4/3 14:28
     */
    private void setOperateType(Method method, LogConfigDTO logConfigDTO) {
        cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog operateLog = method.getAnnotation(cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog.class);
        if (operateLog != null && !operateLog.enable()) {
            return;
        }
        RequestMethod requestMethod = RequestMethodFilter.filterRequestMethod(getRequestMethod(method));
        if (requestMethod == null) {
            logConfigDTO.setOperateType(null);
            return;
        }
        OperateTypeEnum operateTypeEnum = OperateTypeConvertor.convertOperateType(requestMethod);
        OperateType operateType = method.getAnnotation(OperateType.class);

        if (operateTypeEnum == null && operateType == null) {
            logConfigDTO.setOperateType(null);
        } else {
            if (operateTypeEnum == null) {
                if (StringUtils.hasText(method.getAnnotation(OperateType.class).type())) {
                    logConfigDTO.setOperateType(method.getAnnotation(OperateType.class).type());
                } else {
                    logConfigDTO.setOperateType(OperateTypeEnum.OTHER.getDescription());
                }
            } else {
                logConfigDTO.setOperateType(operateTypeEnum.getDescription());
            }
        }
    }


}

AOP类:

这里需要自己去实现,如果需要源码,后给我发消息,留言。

注意:

如果只做专业人员运维日志,保存接口所属功能模块,菜单,接口名称,uri、客户端IP,传参就可以了,但是如果是给用户看的,则需要定义一个上下文类,里面利用ThreadLocal存入两个字符串变量,修改前,修改后,至于新增,修改前为空,删除,修改后为空。(虽然操作肯定不止修改,有新增和删除,看似修改前和修改后的方式并不适合所有操作类型,但这是将技术实现设计向业务功能实现设计妥协的体现,因为不得不对业务代码有侵入。)

需要注意的是,上述出现过两次AnnotationMetadata  元空间,一次是registerBeanDefinitions(AnnotationMetadata metadata, ...)一次是AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();  这两次虽然都是元空间对象,但是前面的元空间是当前整个容器的元空间,类信息都在里面,包括接口和注解的,所以利用元空间、注解的类名,获取类的属性空间,importingClassMetadata.getAnnotationAttributes(clazz.getCanonicalName());  后者的元空间则是扫描的到类的元空间,可以获取该类上的注解,注解的属性等。

总结:

Java的反射机制允许程序在运行时检查和操作类、方法和字段。通过反射,你可以在运行时获取类的信息(如类名、字段、方法等),并且可以动态地创建对象、调用方法和访问/修改字段。

以下是Java反射机制的一些重要概念和用法:

  • class类: Java中的每个类都有一个与之关联的Class对象,它包含了该类的完整信息。你可以使用以下方式获取Class对象:

Class<?> clazz = MyClass.class; // 通过类名 
Class<?> clazz = obj.getClass(); // 通过对象实例 
Class<?> clazz = Class.forName("com.example.MyClass"); // 通过类的完全限定名
  • 获取类的信息: 一旦有了Class对象,你就可以获取关于类的信息,如类名、字段、方法等。

​​​​​​​String className = clazz.getName(); 
Field[] fields = clazz.getDeclaredFields(); 
Method[] methods = clazz.getDeclaredMethods();
  • 创建对象: 可以通过反射来动态创建对象实例。

MyClass obj = (MyClass) clazz.newInstance();
  • 访问和修改字段: 可以通过反射来获取和修改对象的字段值。

Field field = clazz.getDeclaredField("fieldName"); 
field.setAccessible(true); // 如果字段是私有的,需要设置可访问性 
Object value = field.get(obj); // 获取字段值 
field.set(obj, newValue); // 设置字段值
  • 调用方法: 可以通过反射来调用类的方法。

Method method = clazz.getDeclaredMethod("methodName", parameterTypes); 
method.setAccessible(true); // 如果方法是私有的,需要设置可访问性 
Object result = method.invoke(obj, args); // 调用方法
  • 动态代理: 可以使用反射来创建动态代理对象。

MyInterface proxy = (MyInterface) Proxy
.newProxyInstance( MyInterface.class.getClassLoader(), new Class[] { MyInterface.class }, new MyInvocationHandler());

    虽然反射提供了很大的灵活性,但也需要谨慎使用,因为它会降低代码的可读性和性能,而且可能会在编译时捕获不到的错误。

​​​​​​​

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

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

相关文章

VTK+其他布尔运算库

文章目录 一、vtkBool1.1 安装1.2 使用1.3 效果 二、CGAL 本文的主要内容&#xff1a;VTK内置的布尔运算类具有稳定性差、计算时间长等缺点&#xff0c;为了避免使用VTK内置的布尔运算&#xff0c;本文简单介绍了两个比较实用的库用于替代&#xff0c;主要涉及vtkBool和CGAL库的…

私域高效管理指南:提升用户粘性的高效法则

随着越来越多的品牌将目光投向私域流量&#xff0c;如何高效地管理这些用户并提升他们的粘性&#xff0c;成为了营销领域的重要课题。 下面&#xff0c;就分享一些私域高效管理的实用法则&#xff0c;帮助你在私域管理中游刃有余。 1、用户分层 对于新用户而言&#xff0c;他…

redhat7.7 linux 网络配置文件

一、为什么虚拟网卡配置文件是ens33 变更目录至网络脚本&#xff08;network-scripts&#xff09;文件夹&#xff0c;发现网络配置文件名称为“ifcfg-ens33” cd /etc/sysconfig/network-scripts ls扩展&#xff1a;“ifcfg-ens33”文件下面还有一个“ifcfg”前缀的文件&…

LabVIEW程序怎么解决 Bug?

在LabVIEW开发过程中&#xff0c;发现和解决程序中的Bug是确保系统稳定运行的关键环节。由于LabVIEW采用图形化编程方式&#xff0c;Bug的排查和处理与传统编程语言略有不同。以下是解决LabVIEW程序中Bug的常见方法和技巧&#xff0c;涵盖从问题发现到解决的多个步骤和角度&…

LabVIEW裂纹深度在线监测系统

随着铁路运输技术的快速发展&#xff0c;火车安全问题成为重中之重&#xff0c;尤其是轮面裂纹的检测和管理。裂纹的出现可能导致严重的列车事故&#xff0c;因此&#xff0c;建立可靠的在线监测系统&#xff0c;实时掌握裂纹情况&#xff0c;对保障铁路运输安全至关重要。 La…

快速构建 yolo 训练集 测试集

在机器学习工作流程中&#xff0c;数据处理是一个关键步骤。通常我们会使用不同的工具来标注数据&#xff0c;而每种工具都有其特定的格式。在这篇文章中&#xff0c;我们将展示如何从数据集中快速抽取样本&#xff0c;构建训练集和测试集。 YOLO简介 YOLO&#xff08;You On…

响应重定向

响应重定向 重定向也可以跳转servlet,也可以跳转页面 (功能与请求转发类似) 使用的方法: resp.sendRedirect("路径"); 测试: 改造登录Servlet,重定向到ListServlet ps: 可以跳转到ListServlet,但是因为请求域数据不能共享,可能会报错 特点 重定向是响应重定向,是浏…

大语言模型的微调方法_大语言模型六种微调方法,零基础入门到精通,收藏这一篇就够了

01 引言 自2018年BERT发布以来&#xff0c;“预训练微调”成为语言模型的通用范式。以ChatGPT为代表的大语言模型针对不同任务构造Prompt来训练&#xff0c;本质上仍然是预训练与微调的使用范式。千亿规模的参数微调需要大量算力&#xff0c;即使提供了预训练的基座模型&…

jQuery——文档的增删改

1、添加 / 替换元素 append&#xff08;content&#xff09;&#xff1a;向当前匹配的所有元素内部的最后插入指定内容prepend&#xff08;content&#xff09;&#xff1a;向当前匹配的所有元素内部的最前面插入指定内容before&#xff08;content&#xff09;&#xff1a;…

一、机器学习算法与实践_06迭代法和KMeans、线性回归、逻辑回归算法笔记

0 迭代法 迭代法不仅是机器学习、深度学习的核心&#xff0c;也是整个人工智能领域的重要概念&#xff0c;其对于算法的设计和实现至关重要 0.1 适合场景 对于不能一次搞定的问题&#xff0c;将其分成多步来解决&#xff0c;逐步逼近解决方案 0.2 典型应用 KMeans 聚类算法…

开放式耳机哪个品牌好?适合运动的开放式蓝牙耳机分享

如今&#xff0c;开放式耳机的购买量呈现出持续上升的趋势&#xff0c;变得越来越多。而随着人们对音频设备需求的不断提升以及对舒适佩戴体验和自然聆听感受的日益追求&#xff0c;开放式耳机也以其独特的优势逐渐走进大众的视野&#xff0c;成为众多消费者的新宠。 在各大电…

中文llama3仿openai api实战

课程介绍 本次课属于【大模型本地部署应用】&#xff0c;基于Chinese-LLaMA-Alpaca-3&#xff08;GitHub - ymcui/Chinese-LLaMA-Alpaca-3: 中文羊驼大模型三期项目 (Chinese Llama-3 LLMs) developed from Meta Llama 3&#xff09;项目&#xff0c;介绍如何封装一个私有的兼容…

谷歌收录批量查询,谷歌收录批量查询的简单方法

谷歌收录批量查询是网站管理员和SEO优化人员常见的需求&#xff0c;以下提供几种简单且高效的批量查询方法&#xff1a; 一、使用Google Search Console&#xff08;谷歌搜索控制台&#xff09; 注册并验证网站&#xff1a; 首先&#xff0c;确保你已经在Google Search Conso…

常用正则匹配中国手机号码

正则表达式匹配中国的手机号码。 我可以提供一些匹配中国手机号码的常用正则表达式: 仅匹配11位数字的手机号码:^1[3456789]\d{9}$匹配以1开头的11位数字手机号码:^1\d{10}$更宽松的匹配规则,允许0开头的手机号码:^(?:0|86|&#xff0b;?86)?1[3-9]\d{9}$ 这些正则表达式可…

Java基础知识总结(超详细整理)

Java基础知识总结&#xff08;超详细整理&#xff09; Java语言的特点 1.面向对象 面向对象(OOP)就是Java语言的基础&#xff0c;也是Java语言的重要特性。面向对象的概念&#xff1a;生活中的一切事物都可以被称之为对象&#xff0c;生活中随处可见的事物就是一个对象&#…

CMSIS-RTOS V2封装层专题视频,一期视频将常用配置和用法梳理清楚,适用于RTX5和FreeRTOS(2024-09-28)

【前言】 本期视频就一个任务&#xff0c;通过ARM官方的CMSIS RTOS文档&#xff0c;将常用配置和用法给大家梳理清楚。 对于初次使用CMSIS-RTOS的用户来说&#xff0c;通过梳理官方文档&#xff0c;可以系统的了解各种用法&#xff0c;方便大家再进一步的自学或者应用&#x…

【人工智能深度学习应用】妙策API最佳实践

功能概述 在文章创作过程中&#xff0c;用户可以借助AI妙策来辅助创作。AI妙策主要集中在聚合热点话题榜和平台话题榜两个方面。 具体功能 话题分析 AI妙策中的话题分析可以作为创作的灵感来源&#xff0c;通过网页视角选题、热门视角选题、时效视角选题、新颖视角选题&…

网站建设中,如何处理多语言版本?

在全球化的今天&#xff0c;网站多语言版本的处理成为了一项基本要求&#xff0c;尤其是对于面向国际用户的企业来说。以下是一些关于网站建设中如何处理多语言版本问题的建议&#xff1a; 使用URL国际化&#xff1a;通过在URL中添加语言代码&#xff08;如example.com/en/表示…

【文心智能体】猫咪用品购物指南搭建教程

前言 随着科技的飞速发展和人们生活水平的提高&#xff0c;越来越多的家庭开始养宠物&#xff0c;其中猫咪因其可爱、独立和温顺的性格而备受青睐。然而&#xff0c;面对市场上琳琅满目的猫咪用品&#xff0c;如何为自家的“喵星人”挑选最合适的商品&#xff0c;成为了许多宠…

vue3学习:axios输入城市名称查询该城市天气

说来惭愧&#xff0c;接触前端也有很长一段时间了&#xff0c;最近才学习axios与后端的交互。今天学习了一个查询城市天气的案例&#xff0c;只需输入城市名称&#xff0c;点击“查询”按钮便可以进行查询。运行效果如下&#xff1a; 案例只实现了基本的查询功能&#xff0c;没…