spring AOP中pointcut表达式详解

news2024/11/27 6:37:58

📢📢📢📣📣📣
哈喽!大家好,我是「奇点」,江湖人称 singularity。刚工作几年,想和大家一同进步🤝🤝
一位上进心十足的【Java ToB端大厂领域博主】!😜😜😜
喜欢java和python,平时比较懒,能用程序解决的坚决不手动解决😜😜😜

✨ 如果有对【java】感兴趣的【小可爱】,欢迎关注我

❤️❤️❤️感谢各位大可爱小可爱!❤️❤️❤️
————————————————

如果觉得本文对你有帮助,欢迎点赞,欢迎关注我,如果有补充欢迎评论交流,我将努力创作更多更好的文章。

由于项目很忙,最近很少有时间更新文章和大家分享,已经很及没更新文章了,让各位久等了。最近忙里偷闲抽空分享一些aop的知识。详细大家对这个很熟悉但也陌生,没有系统的整理过这个知识。

 

本文主要介绍spring aop中9种切入点表达式的写法,相信不少同学跟我一样,没有系统的整理过aop中的pointcut的表达式。今天我们就抽空讲解一下pointcut表达式的用法和含义。

Spring AOP支持的AspectJ表达式概览:

  • execution: 匹配方法执行的切入点。Spring AOP主要使用的切点标识符。
  • within: 限制匹配在特定类型内的连接点。(给定class的所有方法)
  • this: 限制匹配是给定类型的实例的bean引用(Spring AOP proxy)的连接点。(代理类是给定类型的类的所有方法)
  • target: 限制匹配是给定类型的实例的目标对象(被代理对象)的连接点。(目标对象是给定类型的类的所有方法)
  • args: 匹配参数是给定类型的连接点。(方法入参是给定类型的方法)
  • @target: 匹配有给定注解的执行对象的class的连接点。(目标对象class上有给定注解的类的所有方法)
  • @args: 匹配实际传递的参数的运行时类型有给定的注解的连接点。(方法入参上有给定注解)
  • @within: 匹配有给定注解的类型的连接点。(class上有给定注解的class的所有方法)
  • @annotation: 匹配连接点的subject有给定注解的连接点。(方法上有给定注解的方法)
     

1.execute表达式

execution(* com.xx.web.controller..*.*(..))

参数说明

符号  含义
execution() 表达式的主体;
第一个”*“符号  表示返回值的类型任意;
com.sample.service.impl AOP所切的服务的包名
包名后面的”..“ 表示当前包及子包
第二个”*“符号  表示类名,*即所有类
.*(..) 表示任何方法名,括号表示参数,两个点表示任何参数类型

基本语法格式为: execution(<修饰符模式>?<返回类型模式><方法名模式>(<参数模式>)<异常模式>?)

  • 修饰符匹配(modifier-pattern?)
  • 返回值匹配(ret-type-pattern):可以为*,表示任何返回值,全路径的类名等
  • 类路径匹配(declaring-type-pattern?)
  • 方法名匹配(name-pattern):可以指定方法名 或者*,代表所有。
  • set*, 代表以set开头的所有方法
  • 参数匹配((param-pattern)):可以指定具体的参数类型,多个参数间用“,”隔开,各个参数也可以用“*”来表示匹配任意类型的参数
  • String表示匹配一个String参数的方法;
  • *,String 表示匹配有两个参数的方法,第一个参数可以是任意类型,而第二个参数是String类型;
  • 可以用..表示零个或多个任意参数
  • 异常类型匹配(throws-pattern?)
     

下面是官网中的一些实例:

Aspect Oriented Programming with Spring :: Spring Framework

拦截任意公共方法

execution(public * *(..))

拦截以set开头的任意方法

execution(* set*(..))

拦截类或者接口中的方法

execution(* com.xyz.service.AccountService.*(..))
拦截 AccountService(类、接口)中定义的所有方法

拦截包中定义的方法,不包含子包中的方法

execution(* com.xyz.service.*.*(..))
拦截 com.xyz.service包中所有类中任意方法,不包含子包中的类

拦截包或者子包中定义的方法

execution(* com.xyz.service..*.*(..))
拦截 com.xyz.service包或者子包中定义的所有方法

// 带?的表示可选
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern)
            throws-pattern?)
  1. 方法修饰符匹配 modifier-pattern(可选)
  2. 方法返回值匹配 ret-type-pattern
  3. 类路径匹配 declaring-type-pattern(可选)
  4. 方法名和参数匹配 name-pattern(param-pattern)
  5. 异常类型匹配 throws-pattern(可选)

 

简单事例

下面是execution的简单例子:

有两个IService接口分别有m1和m2方法,现在

ServiceImpl实现两个接口

实现切面Interceptor 切点如下

@Pointcut("execution(* com.ms.aop.execution.ServiceImpl.*(..))")
Interceptor1

public interface IService {
    void m1();
}

 

public interface IService2 {
    void m2();
}
package com.ms.aop.execution;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;


@Aspect
@Component
@Slf4j
public class Interceptor1 {

    @Pointcut("execution(* com.ms.aop.execution.ServiceImpl.*(..))")
    public void pointcut() {
    }

    @Around("pointcut()")
    public Object invoke(ProceedingJoinPoint invocation) throws Throwable {
        log.info("方法执行之前");
        Object result = invocation.proceed();
        log.info("方法执行完毕");
        return result;
    }
}
@Slf4j
@Component
public class ServiceImpl implements IService, IService2 {
    @Override
    public void m1() {
        log.info("切入点m1的execution测试!");
    }

    @Override
    public void m2() {
        log.info("切入点m2的execution测试!");
    }
}

 

测试类

@ComponentScan(basePackageClasses={Client.class})
@EnableAspectJAutoProxy
public class Client {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(Client.class);
        IService2 service = annotationConfigApplicationContext.getBean(IService2.class);
        IService service1 = annotationConfigApplicationContext.getBean(IService.class);
        service.m2();
        service1.m1();
    }
}

执行结果:

15:07:00.304 [main] INFO com.ms.aop.execution.Interceptor1 - 方法执行之前
15:07:00.304 [main] INFO com.ms.aop.execution.ServiceImpl - 切入点m2的execution测试!
15:07:00.304 [main] INFO com.ms.aop.execution.Interceptor1 - 方法执行完毕


15:07:00.305 [main] INFO com.ms.aop.execution.Interceptor1 - 方法执行之前
15:07:00.305 [main] INFO com.ms.aop.execution.ServiceImpl - 切入点m1的execution测试!
15:07:00.305 [main] INFO com.ms.aop.execution.Interceptor1 - 方法执行完毕

分析:

  1. @EnableAspectJAutoProxy:表示若spring创建的对象如果实现了接口,默认使用jdk动态代理,如果没有实现接口,使用cglib创建代理对象
  2. 所以 service 是使用jdk动态代理生成的对象,service instanceof ServiceImpl 为 false
  3. @Pointcut("this(com.ms.aop.jthis.demo1.ServiceImpl)")表示被spring代理之后生成的对象必须为com.ms.aop.jthis.demo1.ServiceImpl才会被拦截,但是service不是ServiceImpl类型的对象了,所以不会被拦截
  4. 修改代码

    @EnableAspectJAutoProxy(proxyTargetClass = true)
    proxyTargetClass=true表示使用cglib来生成代理对象

执行结果

17:34:43.297 [main] INFO com.ms.aop.execution.Interceptor1 - 方法执行之前
17:34:43.307 [main] INFO com.ms.aop.execution.ServiceImpl - 切入点m2的execution测试!
17:34:43.308 [main] INFO com.ms.aop.execution.Interceptor1 - 方法执行完毕


17:34:43.308 [main] INFO com.ms.aop.execution.Interceptor1 - 方法执行之前
17:34:43.308 [main] INFO com.ms.aop.execution.ServiceImpl - 切入点m1的execution测试!
17:34:43.308 [main] INFO com.ms.aop.execution.Interceptor1 - 方法执行完毕

使用cglib方式和jdk代理的方式效果是一致的。

排除和包含

实现某些的排除:@Pointcut切入点排除某一些类或者方法不进行拦截

	// 扫描controller层
    @Pointcut("execution(* com.xx.web.controller..*.*(..)) ")
    public void includePointcat() {
    }

    // 排除controller类
    @Pointcut("execution(* com.xx.web.controller.TempController.*(..)) ")
    public void excludePointcut() {
    }

    //切面配置
    @AfterReturning("includePointcat() && !excludePointcut()")
    public void saveSysLog(JoinPoint joinPoint) throws IOException {
        String className = joinPoint.getSignature().getDeclaringType().getSimpleName();
	    String methodName = joinPoint.getSignature().getName();
	    logger.info("{}.{} start", className, methodName);
	}

includePointcat:切入点为controller下所有类。

excludePointcut:切入点为controller下TempController类。

saveSysLog:切入点为满足 includePointcat且不满足excludePointcut的切入点的范围


2.within表达式

表达式格式:包名.* 或者 包名..*

拦截包中任意方法,不包含子包中的方法

within(com.xyz.service.*)
拦截service包中任意类的任意方法

拦截包或者子包中定义的方法

within(com.xyz.service..*)
拦截service包及子包中任意类的任意方法

within与execution相比,粒度更大,仅能实现到包和接口、类级别。而execution可以精确到方法的返回值,参数个数、修饰符、参数类型等

3.this表达式

代理对象为指定的类型会被拦截

目标对象使用aop之后生成的代理对象必须是指定的类型才会被拦截,注意是目标对象被代理之后生成的代理对象和指定的类型匹配才会被拦截
this(com.xyz.service.AccountService)

例如下面的例子

package com.ms.aop.jthis.demo1;
​
public interface IService {
    void m1();
}

package com.ms.aop.jthis.demo1;
​
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
​
@Slf4j
@Component
public class ServiceImpl implements IService {
    @Override
    public void m1() {
        log.info("切入点this测试!");
    }
}

package com.ms.aop.jthis.demo1;
​
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Aspect
@Component
@Slf4j
public class Interceptor1 {
​
    @Pointcut("this(com.ms.aop.jthis.demo1.ServiceImpl)")
    public void pointcut() {
    }
​
    @Around("pointcut()")
    public Object invoke(ProceedingJoinPoint invocation) throws Throwable {
        log.info("方法执行之前");
        Object result = invocation.proceed();
        log.info("方法执行完毕");
        return result;
    }
}

package com.ms.aop.jthis.demo1;
​
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
​
@ComponentScan(basePackageClasses = {Client.class})
@EnableAspectJAutoProxy
@Slf4j
public class Client {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(Client.class);
        IService service = annotationConfigApplicationContext.getBean(IService.class);
        service.m1();
        log.info("{}", service instanceof ServiceImpl);
    }
}

注意这里的代理对象类型的定义

 @Pointcut("this(com.ms.aop.jthis.demo1.ServiceImpl)")

结果:

10:27:12.277 [main] INFO com.ms.aop.jthis.demo1.ServiceImpl - 切入点this测试!
10:27:12.277 [main] INFO com.ms.aop.jthis.demo1.Client - false
  1. @EnableAspectJAutoProxy:表示若spring创建的对象如果实现了接口,默认使用jdk动态代理,如果没有实现接口,使用cglib创建代理对象
  2. 所以 service 是使用jdk动态代理生成的对象,service instanceof ServiceImpl 为 false
  3. @Pointcut("this(com.ms.aop.jthis.demo1.ServiceImpl)")表示被spring代理之后生成的对象必须为com.ms.aop.jthis.demo1.ServiceImpl才会被拦截,但是service不是ServiceImpl类型的对象了,所以不会被拦截
  4. 修改代码

    @EnableAspectJAutoProxy(proxyTargetClass = true)
    proxyTargetClass=true表示使用cglib来生成代理对象
    执行结果:

    10:34:50.736 [main] INFO com.ms.aop.jthis.demo1.Interceptor1 - 方法执行之前
    10:34:50.755 [main] INFO com.ms.aop.jthis.demo1.ServiceImpl - 切入点this测试!
    10:34:50.756 [main] INFO com.ms.aop.jthis.demo1.Interceptor1 - 方法执行完毕
    10:34:50.756 [main] INFO com.ms.aop.jthis.demo1.Client - true
    service 为 ServiceImpl类型的对象,所以会被拦截

4.target表达式

目标对象为指定的类型被拦截

target(com.xyz.service.AccountService)
目标对象为AccountService类型的会被代理
package com.ms.aop.target;
​
public interface IService {
    void m1();
}

package com.ms.aop.target;
​
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
​
@Slf4j
@Component
public class ServiceImpl implements IService {
    @Override
    public void m1() {
        log.info("切入点target测试!");
    }
}

package com.ms.aop.target;
​
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
​
@Aspect
@Component
@Slf4j
public class Interceptor1 {
​
    @Pointcut("target(com.ms.aop.target.ServiceImpl)")
    public void pointcut() {
    }
​
    @Around("pointcut()")
    public Object invoke(ProceedingJoinPoint invocation) throws Throwable {
        log.info("方法执行之前");
        Object result = invocation.proceed();
        log.info("方法执行完毕");
        return result;
    }
}

package com.ms.aop.target;
​
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
​
@ComponentScan(basePackageClasses = {Client.class})
@EnableAspectJAutoProxy
public class Client {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(Client.class);
        IService service = annotationConfigApplicationContext.getBean(IService.class);
        service.m1();
    }
}

执行结果

10:49:01.674 [main] INFO com.ms.aop.target.Interceptor1 - 方法执行之前
10:49:01.674 [main] INFO com.ms.aop.target.ServiceImpl - 切入点target测试!
10:49:01.674 [main] INFO com.ms.aop.target.Interceptor1 - 方法执行完毕

this 和 target 的不同点

  1. this作用于代理对象,target作用于目标对象
  2. this表示目标对象被代理之后生成的代理对象和指定的类型匹配会被拦截,匹配的是代理对象
  3. target表示目标对象和指定的类型匹配会被拦截,匹配的是目标对象

5.args 表达式

匹配方法中的参数

@Pointcut("args(com.ms.aop.args.demo1.UserModel)")
匹配只有一个参数,且类型为 com.ms.aop.args.demo1.UserModel

匹配多个参数

args(type1,type2,typeN)

匹配任意多个参数

@Pointcut("args(com.ms.aop.args.demo1.UserModel,..)")
匹配第一个参数类型为 com.ms.aop.args.demo1.UserModel的所有方法,  .. 表示任意个参数

6.@target表达式

匹配的目标对象的类有一个指定的注解

@target(com.ms.aop.jtarget.Annotation1)
目标对象中包含 com.ms.aop.jtarget.Annotation1注解,调用该目标对象的任意方法都会被拦截

7.@within表达式

指定匹配必须包含某个注解的类里的所有连接点

@within(com.ms.aop.jwithin.Annotation1)
声明有 com.ms.aop.jwithin.Annotation1注解的类中的所有方法都会被拦截

@target 和 @within 的不同点

  1. @target(注解A):判断被调用的目标对象中是否声明了注解A,如果有,会被拦截
  2. @within(注解A): 判断被调用的方法所属的类中是否声明了注解A,如果有,会被拦截
  3. @target关注的是被调用的对象,@within关注的是调用的方法所在的类

8.@annotation表达式

匹配有指定注解的方法(注解作用在方法上面)

@annotation(com.ms.aop.jannotation.demo2.Annotation1)
被调用的方法包含指定的注解

9.@args表达式

方法参数所属的类型上有指定的注解,被匹配

注意:是 方法参数所属的类型上有指定的注解,不是方法参数中有注解

  • 匹配1个参数,且第1个参数所属的类中有Anno1注解
@args(com.ms.aop.jargs.demo1.Anno1)
  • 匹配多个参数,且多个参数所属的类型上都有指定的注解
@args(com.ms.aop.jargs.demo1.Anno1,com.ms.aop.jargs.demo1.Anno2)
  • 匹配多个参数,且第一个参数所属的类中有Anno1注解
@args(com.ms.aop.jargs.demo2.Anno1,..)

 

项目实战:

下面是切面的一个项目应用 实现服务日志的记录

@Aspect
@Component
@Slf4j
public class SealServiceControllerAspect {

    @Autowired
    private InterfaceLogDao interfaceLogDao;
    /**
     * 日志入库异步模式,线程池用fk pool
     */
    private static ForkJoinPool LOG_THREAD_POOL = new ForkJoinPool(4);

    @Pointcut("" +
            "execution(* com.xx.seal.RestSignContractResource.*(..))" +
            "|| execution(* com.xx.controller.seal.ContractProcessSignResource.*(..))"
            
    )
    public void pointCuts() {
    }

    @Around("pointCuts()")
    public Object invoke(ProceedingJoinPoint invocation) throws Throwable {
        final InterfaceLogPO po = new InterfaceLogPO();
        Object[] inParam = invocation.getArgs();
        JSONArray inParams = new JSONArray();
        if (inParam != null) {
            Arrays.stream(inParam).forEach(p -> {
                try {
                    if (p instanceof String||
                        p instanceof Number ||
                        p instanceof Boolean
                    ){
                        inParams.add(p);
                    }else if (JSONUtils.isArray(p)) {
                        try {
                            inParams.add(JSONArray.fromObject(p));
                        } catch (Exception e) {
                            log.warn("==>this aspect[{}] can not get input param ", invocation.getSignature().getName());
                        }
                    } else {
                        try {
                            inParams.add(JSONObject.fromObject(p));
                        } catch (Exception e) {
                            log.warn("==>this aspect[{}] can not get input param ", invocation.getSignature().getName());
                        }

                    }

                } catch (Exception e) {
                    log.warn("==>aspect error :can not fetch args --->{}", e.getMessage());
                }
            });
        }
        if (invocation.getTarget().getClass().getName().endsWith("Resource") ||
                invocation.getTarget().getClass().getName().endsWith("Controller")
        ) {
            po.setCategory("REST");
        } else {
            po.setCategory("SERVICE");
        }
        po.setAction(invocation.getTarget().getClass().getName() + "@" + invocation.getSignature().getName());
        po.setActionDesc("");// 从swagger的@Api注解中取
        po.setInputParam(inParams.toString());
        po.setTs(new Date());
        po.setCallStatus("OK");
        po.setUserId(InvocationInfoProxy.getUserid());
        
        po.setUserName(InvocationInfoProxy.getUsername());
        Object result = null;
        try {
            result = invocation.proceed();
        } catch (Throwable throwable) {
            po.setCallStatus("ERR");
            StringBuilder sb = new StringBuilder( throwable.getMessage()+"\n");
            sb.append(ExceptionUtils.getFullStackTrace(throwable)).append("\n");
            po.setErrorMessage(sb.toString());
            throw throwable;
        } finally {
            if (result != null) {
                if (result instanceof String  ||
                        result instanceof Number ||
                        result instanceof Boolean){
                    po.setOutputResult(result.toString());
                }else if (JSONUtils.isArray(result)) {
                    try {
                        po.setOutputResult(
                                JSONArray.fromObject(result).toString()
                        );
                    } catch (Exception e) {
                        log.warn("==>this aspect[{}] can not get output result ", invocation.getSignature().getName());
                    }

                } else {
                    try {
                        po.setOutputResult(
                                JSONObject.fromObject(result).toString()
                        );
                    } catch (Exception e) {
                        log.warn("==>this aspect[{}] can not get output result", invocation.getSignature().getName());
                    }

                }
                /*
                这部分以后要改造成基于接口的插件式!!!
                 */
                if (result instanceof Result && ((Result) result).getData() != null) {
                    //后续考虑引入策略模式
                    if (((Result) result).getData() instanceof ResultContractProcessDTO
                    ) {
                        String bizKey = ((ResultContractProcessDTO) ((Result) result).getData()).getProcessId();
                        po.setBizKey(bizKey);
                    } else {
                        try {
                            JSONObject outputResult = JSONObject.fromObject(((Result) result).getData());
                            po.setBizKey(outputResult.getString("id"));
                        } catch (Exception e) {
                            log.warn("==>this aspect[{}] can not get biz key", invocation.getSignature().getName());
                        }
                    }
                }
                if (result instanceof  ResultContractProcessDTO){
                    String bizKey = ((ResultContractProcessDTO) result).getProcessId();
                    po.setBizKey(bizKey);
                }
            }
           
            interfaceLogDao.save(po);
        }
        return result;
    }


}

希望这个文章能让大家有所收获,哪怕有一点点的收获,这样我写这个文章也就值得了。

创作不易,请给小编点个赞吧,一个字一个字敲下来很费时间和精力的😄 

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

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

相关文章

纹理过滤方式

纹理坐标不依赖于分辨率&#xff0c;opengl需要知道如何将纹理像素映射到纹理坐标。 纹理像素和纹理坐标的区别&#xff1a; 纹理像素是有限的。 纹理坐标的精度是无限的&#xff0c;可以是任意的浮点值。 一个像素需要一个颜色。 因此&#xff0c;所谓采样就是通过纹理坐标获取…

图像处理之图像灰度化

图像灰度化 将彩色图像转化成为灰度图像的过程成为图像的灰度化处理。彩色图像中的每个像素的颜色有R、G、B三个分量决定&#xff0c;而每个分量有255中值可取&#xff0c;这样一个像素点可以有1600多万 (255255255)的颜色的变化范用。而灰度图像是R、G、B三个分量相同的一种特…

分析 jsjiami.v6 代码混淆和加密技术:特点、优点和缺点

当涉及到 JavaScript 代码混淆和加密技术时&#xff0c;开发人员常常寻求方法来保护他们的代码免受逆向工程和未授权访问的威胁。这种技术可以增加代码的复杂性&#xff0c;使其难以理解和修改&#xff0c;同时也可以隐藏关键信息和保护数据的安全性。本文将探讨 JavaScript 代…

echarts在vue上使用模版可粘贴!!!一站式搞定以及动态数据渲染关键问题解决(附带模版)

阿丹&#xff1a; 之前的项目中涉及到echarts都是使用jsp写的&#xff0c;但是现在越来普及vue。所以使用在vue上使用echarts就成了问题。 导入相关依赖 在官网上面有说明npm安装Echarts依赖 在vue中使用Echarts 上模版!!!!!!!!!!!!使用了mounted&#xff08;&#xff09;这个…

在使用yum时报错Loaded plugins: fastestmirror

在使用yum时报错Loaded plugins: fastestmirror 在我安装更高版本的gcc编译器时&#xff0c;发现了这个问题 意思是加载插件&#xff0c;加载后面两个插件&#xff0c;查阅得知其中fastestmirror是yum的一个加速插件&#xff0c;这个提示意思是这个插件不能使用,那么就先把他禁…

4 款非常好用的AI生成图片软件

AI生成图片最近是越来越火了&#xff0c;越来越多的AI生成图片工具上线。 本文就给你推荐4款非常好用的AI生成图片工具&#xff0c;避免你碰雷。 即时灵感 「即时灵感」是通过文字描述等方式生成精致图像的AI绘图工具。输入文字&#xff0c;即可将创意变为现实&#xff01; …

centos7升级openssh修复安全漏洞

查看linux版本 cat /etc/redhat-releaseCentOS Linux release 7.9.2009 (Core) 升级 yum update openssh -y升级openssl和openssh 下载openssl https://ftp.openssl.org/source/ 下载openssh http://ftp.openbsd.org/pub/OpenBSD/OpenSSH/portable/ yum update openssh…

盘点spring-boot-3-jwt-security中如何使用jwt+security

目录 SecurityApplication类中 AuthenticationService register方法 authenticate方法 saveUserToken略 revokeAllUserTokens方法 refreshToken方法 ApplicationConfig Bean&#xff1a;UserDetailsService Bean&#xff1a;AuthenticationProvider Bean&#xff1a…

浅谈智能集成电力电容器的应用意义

安科瑞 华楠 摘要&#xff1a;该文通过智能集成电力电容器研发背景的说明&#xff0c;介绍该产品基于数字信号处理器&#xff08;DSP&#xff09;和单片机相结合的双CPU技术智能无功补偿技术&#xff0c;集成复合开关的过零投切与计算机智能网络模块技术&#xff0c;实现了现代…

语音信号的采集--电脑录音

准备条件&#xff1a; 1.电脑麦克风打开。&#xff08;联想小新AIR2021 打开方式&#xff1a;点击电脑右下角“电源”&#xff0c;点击“麦克风打开”&#xff09; 2.打开电脑自带的录音机。&#xff08;电脑界面搜索框中键入“录音机”&#xff09; 3.打开音乐播放软件&…

【JavaEE】Linux基本使用

Linux基本使用 文章目录 【JavaEE】Linux基本使用1. 如何获取一个Linux的环境2. 终端软件来远程登录3. Linux基本使用3.1 命令提示符3.2 ls3.3 cd3.4 pwd3.5 touch3.6 cat3.7 man3.8 echo3.9 vim3.9.1 打开文件3.9.2 编辑文件3.9.3 保存退出3.9.4 vimtutor 3.10 mkdir3.11 rm3.…

【设计模式】23种设计模式——单例模式(原理讲解+应用场景介绍+案例介绍+Java代码实现)

单例模式(Singleton) 介绍 所谓类的单例设计模式&#xff0c;就是采取一定的方法&#xff0c;保证在整个的软件系统中&#xff0c;对某个类只能存在一个对象实例&#xff0c;并且该类只提供一个取得其对象实例的方法&#xff08;静态方法&#xff09;。比如Hibernate的Sessio…

UE5《Electric Dreams》项目PCG技术解析 之 PCGCustomNodes详解(三)SG_CopyPointsWithHierarchy

继续解析《Electric Dreams》项目中的自定义节点和子图&#xff1a;SG_CopyPointsWithHierarchy和PostCopyPoints-OffsetIndices 文章目录 前导文章标准组合拳SG_CopyPointsWithHierarchyPostCopyPoints-OffsetIndices使用情景Execute with ContextIteration Loop Body 小结 前…

看懂Congestion Map

往期文章链接: ​​​​​​ICC2:工具是如何平衡std cell利用率和congestion的? ICC2:使用report_placement检查floorplan ICC2:使用global route分析绕线 ICC2:congestion的解决办法

postman 自动化测试

postman 自动化测试 0、写在前面1、变量引用1.1、如何在请求体中引变量 0、写在前面 在有些时候看官方文档 比网上搜索效率要高&#xff0c; 比如网上搜一通还是不知道用法或者没有搜索到你想要的用法的时候。 postman官方文档 : https://learning.postman.com/docs/introduct…

从 robot 坐标系到 orb2 坐标系的变换

机器人坐标系 在机器人学和机器人导航中,通常使用右手坐标系,其定义如下: X轴:通常定义为机器人的"前"或"向前"方向。在移动机器人中,这通常是机器人行进的方向。 Y轴:在右手坐标系中,Y轴通常定义为机器人的"左"或"向左"方向。…

有趣的命令——————用shell脚本实现与电脑猜价格游戏,小朋友比较合适哟~~~

vim games.sh 输入以下内容#!/bin/bashecho "这是一个小游戏&#xff0c;猜价格&#xff08;1--1000&#xff09;" pc$(expr $RANDOM % 1000 1) cs0 while true doread -p "请输入你猜的价格&#xff1a;" intlet csif [ $int -gt $pc ]thenecho "价…

docker+jenkins 实现自动化部署

前期准备工作 工欲善其事必先利其器&#xff0c;所以前期的准备工作必须做好&#xff0c;那么我们要实现自动化部署&#xff0c;必须提前准备什么呢&#xff1f;哈哈哈&#xff0c;可能有一些小伙伴已经猜到了&#xff0c;我们需要一台自己的服务器&#xff0c;这里博主使用的…

你不会还不知道什么是企业博客吧?

企业博客是指由企业或组织创建的在线平台&#xff0c;主要是用于发布与其业务、产品、行业和相关主题相关的文章、信息和观点。通过企业博客可以实现促进品牌推广、客户培养和业务发展等&#xff0c;对于企业发展有极其重要的作用。 企业博客的目的 1.提供有关企业产品和服务的…

Pytorch基本使用—参数初始化

深度学习模型参数初始化是指在训练深度神经网络时&#xff0c;对网络的权重和偏置进行初始化的过程。合适的参数初始化可以加速模型的收敛&#xff0c;并提高模型的性能。 ✨ 1 基本介绍 在深度学习中&#xff0c;常用的参数初始化方法有以下几种&#xff1a; 零初始化&#…