day53_spring

news2025/4/27 6:36:43

今日内容

零、 复习昨日

零、 复习昨日

一、代理设计模式

代理的设计理念是限制对象的直接访问,即不能通过 new 的方式得到想要的对象,而是访问该对象的代理类。

这样的话,我们就保护了内部对象,如果有一天内部对象因为 某个原因换了个名或者换了个方法字段等等,那对访问者来说 一点不影响,因为他拿到的只是代理类而已,从而使该访问对 象具有高扩展性。

代理类可以实现拦截方法,修改原方法的参数和返回值,满足 了代理自身需求和目的,也就是代理的方法增强性。

按照代理的创建时期,代理可分为:静态代理动态代理

静态代理由开发者手动创建,在程序运行前,已经存在;

动态代理不需要手动创建,它是在程序运行时动态的创建代理类。


总结:代理模式–给某个目标对象提供一个代理,以改变对该对象的访问方式,以便于对目标方法的增强

图…

1.1 静态代理

静态代理,在运行之前,提前先把代理创建好.

需求: 目标类(Fangdong),目标方法(chuzu()),来一个代理FangdongProxy(中介),中介会在目标方法执行前后,实现一些增强的功能.

演示:

目标接口和实现类

/**
 * --- 天道酬勤 ---
 *
 * @author QiuShiju
 * @desc 房东接口
 */
public interface Fangdong {
    void chuzu();
}

public class FangdongImpl implements Fangdong{
    public void chuzu() {
        System.out.println("房东出租房!" );
    }
}

代理

/**
 * --- 天道酬勤 ---
 *
 * @author QiuShiju
 * @desc 房东代理
 */
public class FangdongProxy {

    private Fangdong fangdong;

    public FangdongProxy(){
        this.fangdong = new FangdongImpl();
    }

    // 代理执行方法
    public void chuzu(){

        // 前: 功能增强
        System.out.println("租房前: 中介发布消息");
        // 目标方法:出租房
        fangdong.chuzu();
        // 后: 功能增强
        System.out.println("中介签合同/后期水电气的维修" );
    }
}

测试

public static void main(String[] args) {

    // 找代理
    FangdongProxy proxy = new FangdongProxy();
    proxy.chuzu();
}

租房前: 中介发布消息
房东出租房!
中介签合同/后期水电气的维修

以上就是静态代理,FangdongProxy在程序运行前,是提前创建好的.

现在需要有一人需要买车,有汽车厂商,就需要一个汽车代理商,代理商在真正执行卖车前后可以做一些增强.即需要再创建汽车厂的代理

但是.如果需要有一人需要买酒,有酒厂,就需要酒厂的代理,代理商在真正执行卖酒前,后可以做一些增强,即需要再创建酒厂的代理

…依次类推

这样下去,每做一件事情,就需要一个代理,代理类越来越多,多个代理类的增强的功能代码冗余


既然是这样,那能不能根据目标类,动态产生代理呢?

答案是可以的,那就是动态的为程序生成代理!

1.2 动态代理

动态代理: 在程序运行过程中,动态的为目标类产生代理类

实现动态代理有两种方案:

  • jdk动态代理(JDK自带)
    • jdk动态代理,只能代理接口,即目标类必须有接口
  • cglib动态代理(第三方技术,需要导入第三方jar包)
    • cglib动态代理,目标类可以是接口也可以是实现类

1.2.1 JDK动态代理

在java的java.lang.reflect.InvocationHandler包下有一个InvocationHandler接口,可以实现动态产生代理对象.

package com.qf.proxy.dynamic.jdk;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * --- 天道酬勤 ---
 *
 * @author QiuShiju
 * @desc
 */
public class MyInvocationHandler implements InvocationHandler {

    private Object target;

    public MyInvocationHandler(Object target) {
        this.target = target;
    }
    /**
     * @param proxy   代理对象
     * @param method  目标方法
     * @param args    目标方法执行需要的参数
     * @return 目标方法执行后返回值
     * @throws Throwable
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 目标方法前:
        System.out.println("前期宣传" );

        // 目标方法执行
        Object ret = method.invoke(target,args);

        // 目标方法后:
        System.out.println("后期维护" );
        return ret;
    }
}

测试

public class TestJDKDynamic {

    public static void main(String[] args) {

        // 动态代理,只需要给定目标类,就会产生目标类的代理对象

        // Class clazz = FangdongImpl.class;
        // ClassLoader loader = clazz.getClassLoader( );
        // Class[] interfaces = clazz.getInterfaces( );
        // MyInvocationHandler handler = new MyInvocationHandler(new FangdongImpl( ));
        // /**
        //  * @param   loader 被代理的类(目标类)的类加载器
        //  * @param   interfaces 被代理的类(目标类)的实现的接口数组
        //  * @param   handler 刚才自己创建的MyInvocationHandler
        //  * @return 返回代理对象
        //  */
        // Fangdong proxy = (Fangdong) Proxy.newProxyInstance(loader, interfaces, handler);
        //
        // proxy.chuzu();


        Class clazz = CarFactoryImpl.class;
        ClassLoader loader = clazz.getClassLoader( );
        Class[] interfaces = clazz.getInterfaces( );
        MyInvocationHandler handler = new MyInvocationHandler(new CarFactoryImpl( ));
        /**
         * @param   loader 被代理的类(目标类)的类加载器
         * @param   interfaces 被代理的类(目标类)的实现的接口数组
         * @param   handler 刚才自己创建的MyInvocationHandler
         * @return 返回代理对象
         */
        CarFactory proxy = (CarFactory) Proxy.newProxyInstance(loader, interfaces, handler);

        proxy.sellCar();

        //  JDK的动态代理,目标类必须实现接口,否则代理失败
    }
}

1.2.2 CGLIB动态代理

CGLIB是第三方技术,需要导入第三方jar包

但是Spring框架已经整合了CGLIB技术,即在导入spring依赖时,已经导入了cglib包了

image-20221220161141801
package com.qf.proxy.dynamic.cglib;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * --- 天道酬勤 ---
 *
 * @author QiuShiju
 * @desc
 */
public class MyProxyInterceptor implements MethodInterceptor {

    // cglib增强器
    private Enhancer enhancer = new Enhancer();

    public MyProxyInterceptor(Class targetClass){
        enhancer.setSuperclass(targetClass);
        enhancer.setCallback(this);
    }

    // 获得代理对象
    public Object getProxyBean() {
        return enhancer.create();
    }


    /**
     * @param target  目标对象(被代理对象)
     * @param method  目标方法
     * @param args  目标方法的参数
     * @param methodProxy 代理目标方法
     * @return 目标方法执行后,返回数据
     * @throws Throwable
     */
    public Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {

        // 前:
        System.out.println("前期宣传" );

        // 目标方法
        Object ret = methodProxy.invokeSuper(target, args);

        // 后:
        System.out.println("后期维护" );

        return ret;
    }
}
package com.qf.proxy.dynamic.cglib;

/**
 * --- 天道酬勤 ---
 *
 * @author QiuShiju
 * @desc
 */
public class TestCGLIB {

    public static void main(String[] args) {

        // 动态代理,只需要给定目标类,就会产生目标类的代理对象

        // MyProxyInterceptor myProxyInterceptor = new MyProxyInterceptor(FangdongImpl.class);
        // Fangdong proxy = (Fangdong) myProxyInterceptor.getProxyBean( );
        // proxy.chuzu();

        MyProxyInterceptor myProxyInterceptor = new MyProxyInterceptor(CarFactoryImpl.class);
        CarFactoryImpl proxy = (CarFactoryImpl) myProxyInterceptor.getProxyBean( );
        proxy.sellCar();

        // CGLIB代理的好处,就在于目标类即可以实现接口,也可以不用实现接口,都可以代理

    }
}

1.3 代理总结

需要理解: 什么是代理? 为什么需要代理? 代理的类型?

动态代理的代码,不需要去记,知道两种不同动态代理方案的特点.


理解几个词汇

  • 目标类
  • 目标方法
  • 代理类
  • 增强

二、AOP

2.1 概念

Spring中另外一个核心功能,AOP


AOP(Aspect Oriented Programming),即面向切面编程.

OOP(Object Oriented Programming ),即面向对象编程.

AOP面向切面编程,利用 一种称为"横切"的技术,剖开封装的对象内部,并将那些影响了 多个类的公共行为抽取出封装到一个可重用模块,并将其命名 为"Aspect",即切面。所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。

image-20230612114224473

面向切面编程的作用:就是将项目中与核心逻辑无关的代码横向抽取成切面类,通过织入作用到目标方法,以使目标方法执行前后达到增强的效果.

原理: AOP底层使用的就是动态代理,给AOP指定哪些类型(目标类)需要增强,就会产生对应的代理对象,代理对象执行方法前后会先执行增 强的方法.

好处:减少系统的重复代码,降低模块之间的耦合度,便于维护,可以只关注核心业务

2.2 AOP术语

连接点(Joinpoint):连接点是程序类中客观存在的方法,可被Spring拦截并切入内容。即每个方法在切入之前,都是连接点

切入点(Pointcut):被Spring切入连接点。即真正会增强的目标方法

通知、增强(Advice):可以为切入点添加额外功能,分为:前置通知、后置通知、异常通知、环绕通知等。

目标对象(Target):被代理的目标对象

织入(Weaving):把通知应用到具体的类,进而创建新的代理类的过程。

代理(Proxy):被AOP织入通知后,产生的结代理类。

切面(Aspect):由切点和通知组成

2.3 应用场景

  • 事务管理
    • 后续spring管理事务用的AOP原理
  • 权限校验
    • 后期使用Spring Security注解开发时,其实利用了AOP思想
  • 日志记录
  • 性能检测
  • 等等

2.4 AOP入门编程【重点】

需求: UserService以及UserServiceImpl,在其中方法执行前,后执行增强的方法

编程的过程:

  • 创建项目
  • 导入依赖
    • 核心依赖
    • 切面依赖
  • 目标类
  • 切面类
  • 织入(配置文件设置)
  • 测试

2.4.1导入依赖

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.6.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.1.6.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.1</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

2.4.2 目标类

public interface UserService {

    void findUserById();

    void deleteUserById();

}
public class UserServiceImpl implements UserService {

    @Override
    public void findUserById() {

        System.out.println("核心业务---> 查询用户" );

    }
    @Override
    public void deleteUserById() {

        System.out.println("核心业务---> 删除用户" );

    }
}

2.4.3 切面类

演示的环绕通知

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
public class MyAspect {

    /**
     * 定义增强的方法:如果前后都要增强的方法
     * 那么就还使用环绕通知/增强
     */
    /**
     * 自定义环绕通知
     * @param joinPoint 连接点/切入点,即目标方法
     * @return 目标方法的返回值
     */
    public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable {
        // 目标方法前:
        System.out.println("开启事务/权限校验" );

        // 目标方法执行
        Object ret = joinPoint.proceed( );
        System.out.println("目标方法返回值---> " + ret );
        // 目标方法后:
        System.out.println("提交事务/日志记录" );
        return ret;
    }

}

2.4.4 配置(织入)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       https://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- 目标类,UserService -->
    <bean id="userService" class="com.qf.service.impl.UserServiceImpl"/>

    <!-- 切面类 -->
    <bean id="myAspect" class="com.qf.aspect.MyAspect"/>

    <!-- 织入 -->
    <aop:config>
        <!-- 确定切面-->
        <aop:aspect ref="myAspect">
            <!-- 1确定通知/增强类型 -->
            <!-- 2确定切面中增强的方法 -->
            <!-- 3确定目标类以及目标方法
               execution(返回值类型 包路径.类名.方法名(参数))
               返回值类型任意,写 *
               包路径,写com.qf
               类名,UserServiceImpl,如果是所有类,写 *
               方法名,findUserById,如果是所有方法,写 *
               () 参数列表
               (..) 代表任意参数
            -->
            <aop:around method="myAround"
                        pointcut="execution(* com.qf.service.impl.*.*(..))"/>
        </aop:aspect>
    </aop:config>
</beans>

2.4.5 测试

@Test
public void testAspect() {
    String path = "applicationContext.xml";
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(path);

    UserService userService = context.getBean("userService", UserService.class);
    userService.findUserById();

    System.out.println("-----------------" );

    userService.deleteUserById();
}
开启事务/权限校验
核心业务---> 查询用户
目标方法返回值---> null
提交事务/日志记录
-----------------
开启事务/权限校验
核心业务---> 删除用户
目标方法返回值---> null
提交事务/日志记录

练习: 重复

2.5 其他增强【重点】

  • 环绕通知: 目标方法执行前,后都有增强的方法(已经演示过)
    • 常用于事务管理,性能检测
  • 前置通知
    • 常用于权限检验
  • 后置通知
    • 常用于日志记录,或者释放资源
  • 后置返回通知
    • 常用于获得目标方法的返回值,处理返回值
  • 异常通知
    • 常用于代码有异常后去执行一些功能,处理异常

2.5.1 各种增强

package com.qf.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;

/**
 * --- 天道酬勤 ---
 *
 * @author QiuShiju
 * @desc
 */
public class MyAspect {

    /**
     * 定义增强的方法:如果前后都要增强的方法
     * 那么就还使用环绕通知/增强
     */

    /**
     * 自定义环绕通知
     * @param joinPoint 连接点/切入点,即目标方法
     * @return 目标方法的返回值
     */
    public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable {
        // 目标方法前:
        System.out.println("开启事务/权限校验" );

        // 目标方法执行
        Object ret = joinPoint.proceed( );
        System.out.println("目标方法返回值---> " + ret );
        // 目标方法后:
        System.out.println("提交事务/日志记录" );
        return ret;
    }


    /**
     * 自定义前置通知
     * @param joinPoint,目标对象
     */
    public void myBefore(JoinPoint joinPoint) {
        // 目标对象
        Object target = joinPoint.getTarget( );
        System.out.println("target = " + target);
        // 获得目标方法签名(方法名)
        Signature signature = joinPoint.getSignature( );
        System.out.println("signature = " + signature);

        System.out.println("前置通知--->权限校验--->OK" );

        // 假设权限校验没有通过,通过抛出异常让代码停下,不再执行目标方法
        // System.out.println("前置通知--->权限校验--->ERROR" );
        // throw new RuntimeException("权限校验--->ERROR");
    }


    /**
     * 自定义后置通知
     */
    public void myAfter() {
        System.out.println("后置通知--->记录日志,释放资源" );
    }

    /**
     * 自定义后置返回通知
     * @param ret 用于接收目标方法返回的数据
     * @return
     */
    public Object myAfterRet(Object ret) {
        System.out.println("后置返回通知,接收到目标方法返回值--->" + ret);
        return ret;
    }

    public void myException(Exception e) {
        System.out.println("目标方法报的错---> " + e.getMessage());
    }
}

2.5.2 配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       https://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- 目标类,UserService -->
    <bean id="userService" class="com.qf.service.impl.UserServiceImpl"/>
    <bean id="houseService" class="com.qf.service.impl.HouseServiceImpl"/>


    <!-- 切面类 -->
    <bean id="myAspect" class="com.qf.aspect.MyAspect"/>

    <!-- 织入 -->
    <aop:config>
        <!-- 确定切面-->
        <aop:aspect ref="myAspect">
            <!-- 1确定通知/增强类型 -->
            <!-- 2确定切面中增强的方法 -->
            <!-- 3确定目标类以及目标方法
               execution(返回值类型 包路径.类名.方法名(参数))
               返回值类型任意,写 *
               包路径,写com.qf
               类名,UserServiceImpl,如果是所有类,写 *
               方法名,findUserById,如果是所有方法,写 *
               () 参数列表
               (..) 代表任意参数
            -->
            <!--<aop:around method="myAround"
                        pointcut="execution(* com.qf.service.impl.*.*(..))"/>
            -->


            <!--<aop:before method="myBefore"
                        pointcut="execution(* com.qf.service.impl.*.*(..))"/>
            -->
            
            <!--<aop:after method="myAfter" pointcut="execution(* com.qf.service.impl.*.*(..))"/>-->

            <!--
                returning用来绑定目标方法执行后返回值,ret是myAfterRet方法的参数名
            -->
            <!--<aop:after-returning returning="ret" method="myAfterRet"
                                 pointcut="execution(* com.qf.service.impl.*.*(..))"/>-->

            <!-- throwing绑定目标方法抛出的异常,要传入给增强方法myException的参数 -->
            <aop:after-throwing throwing="e" method="myException" pointcut="execution(* com.qf.service.impl.*.*(..))"/>

        </aop:aspect>
    </aop:config>

</beans>

2.6 总结

  • 通过AOP提供的编码流程,更便利的定制切面,更方便的定制了动态代理。
  • 进而彻底解决了辅助功能(事务管理,日志记录,权限校验)冗余的问题;
  • 业务类中职责单一性得到更好保障;
  • 辅助功能也有很好的复用性。

2.7 AOP底层动态代理【面试】

AOP的底层使用 的就是动态代理,使用的技术

jdk+cglib都用了

  • 当目标类有接口的时候使用JDK动态代理
  • 当目标类没有实现接口时,使用CGLIB动态代理

三、注解开发AOP【重点】

3.1 注解介绍

IOC,DI,AOP全部使用注解开发

注解解释
@Aspect声明类为切面类
@Around环绕通知
@Before前置通知
@After后置通知
@AfterReturning后置返回通知
@AfterThrowing异常通知
@Pointcut抽取切入点表达式

业务层类加注解@Service,@Autowired

3.2 代码添加注解

重点是给切面类加注解

package com.qf.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

/**
 * --- 天道酬勤 ---
 *
 * @author QiuShiju
 * @desc 切面
 */
@Component // 该注解,spring创建该类对象
@Aspect    // 该注解,该类是切面类
public class MyAspectAnno {

    /**
     * 定义一个切入点表达式公共方法
     */
    @Pointcut("execution(* com.qf.service.impl.*.*(..))")
    public void myPointcut(){}


    @Around("myPointcut()")
    public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable {
        // 目标方法前:
        System.out.println("开启事务/权限校验" );

        // 目标方法执行
        Object ret = joinPoint.proceed( );
        System.out.println("目标方法返回值---> " + ret );
        // 目标方法后:
        System.out.println("提交事务/日志记录" );
        return ret;
    }

    @Before("myPointcut()")
    public void myBefore(JoinPoint joinPoint) {
        // 目标对象
        Object target = joinPoint.getTarget( );
        System.out.println("target = " + target);
        // 获得目标方法签名(方法名)
        Signature signature = joinPoint.getSignature( );
        System.out.println("signature = " + signature);

        System.out.println("前置通知--->权限校验--->OK" );

        // 假设权限校验没有通过,通过抛出异常让代码停下,不再执行目标方法
        // System.out.println("前置通知--->权限校验--->ERROR" );
        // throw new RuntimeException("权限校验--->ERROR");
    }

    @After("myPointcut()")
    public void myAfter() {
        System.out.println("后置通知--->记录日志,释放资源" );
    }

    @AfterReturning(value = "myPointcut()",returning = "ret")
    public Object myAfterRet(Object ret) {
        System.out.println("后置返回通知,接收到目标方法返回值--->" + ret);
        return ret;
    }

    @AfterThrowing(value = "myPointcut()",throwing = "e")
    public void myException(Exception e) {
        System.out.println("目标方法报的错---> " + e.getMessage());
    }
}

3.3 配置文件扫描注解,让注解生效

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       https://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">


    <!-- 扫描IOC,DI注解 -->
    <context:component-scan base-package="com.qf"/>

    <!-- AOP开启自动代理,注解生效 -->
    <aop:aspectj-autoproxy/>

</beans>

测试即可

    @Test
    public void testAspectByAnno() {
        String path = "applicationContextAnno.xml";
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(path);
        // 使用注解,对象名,默认是类名首字母小写
        UserService userService = context.getBean("userServiceImpl", UserService.class);
        userService.findUserById();

        System.out.println("-----------------" );

        userService.deleteUserById();

        System.out.println("-----------------" );
        HouseService houseService = context.getBean("houseServiceImpl", HouseService.class);
        int i = houseService.deleteHouse( );
        System.out.println("i = " + i);
    }

任务

配置文件编写aop
注解开发aop
关于代理的几个问题,要介绍出来

------
复习mybatis的核心配置文件,测试代码
阿里巴巴数据库连接池druid
----
spring整合mybatis,实现crud

利用aop实现事务管理
-----------------------
明天早上
1 读单词
2 读昨日学习的总结
3 jvm

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

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

相关文章

MIT 6.S081 (BOOK-RISCV-REV1)教材第三章内容

MIT 6.S081 教材第三章内容 引言页表分页硬件内核地址空间物理内存分配代码&#xff08;物理内存分配&#xff09;kinit函数kfree函数kalloc函数 代码&#xff1a;创建一个地址空间kvminit 函数kvmmap函数walk函数kvminithart函数procinit函数 进程地址空间代码&#xff1a;sbr…

Lua 脚本语法学习

文章目录 Lua 基础语法单行注释和多行注释数据类型标识符运算符关系运算符if条件循环whiledorepeatuntil数值for泛型for 函数1. 固定参数函数2. 可变参函数3. 多返回值4. 函数作参数5.匿名函数 Lua 语法进阶table1. 数组2. map3. 数组-map 混合结构4. table操作函数 迭代器模块…

Makerbase SimpleFOC ESP32 例程9 在线电流检测测试

Makerbase SimpleFOC ESP32 例程9 在线电流检测测试 第一部分 硬件介绍 1.1 硬件清单 序号品名数量1 ESP32 FOC V1.0 主板 12 YT2804电机 23 12V电源适配器 14 USB 线 1 注意&#xff1a;YT2804是改装的云台无刷电机,带有AS5600编码器&#xff0c;可实现360连续运转。 主要…

java:实现用户扫码二维码自动跳转指定链接功能

0. 引言 近来接到要实现链接转二维码的需求&#xff0c;通过提供二维码给用户&#xff0c;让用户扫描后自动访问指定的H5页面&#xff0c;从而实现业务流转&#xff0c;这样的功能其实在其他很多场景也会用到&#xff0c;比如资产管理系统中&#xff0c;扫码资产二维码&#x…

uniapp实现tab切换可以滚动的效果

实现效果 当 tab 切换的内容很多时&#xff0c;需要用到滚动&#xff0c;希望在点击 tab 的时候可以自动滑动到对应的tab下 知识点 scrollIntoView&#xff1a;该scrollIntoView()方法将调用它的元素滚动到浏览器窗口的可见区域。 语法 element.scrollIntoView&#xff08…

2023 年企业 Java 面试前复习的正确姿势(已助力319人入职大厂)

作为 Java 程序员&#xff0c;选择学习什么样的技术&#xff1f;什么技术该不该学&#xff1f;去招聘网站上搜一搜、看看岗位要求就十分清楚了&#xff0c;自己具备的技术和能力&#xff0c;直接影响到你工作选择范围和能不能面试成功。 如果想进大厂&#xff0c;那就需要在 Ja…

ARM-Linux开发与MCU开发的不同之处分析

目录 一、ARM-Linux应用开发和单片机开发的不同 二、Arm-Linux基本开发环境 针对ARM-Linux程序的开发&#xff0c;主要分为三类&#xff1a;应用程序开发、驱动程序开发、系统内核开发。针对不同种类的软件开发&#xff0c;有其不同的特点。 今天&#xff0c;我们来看看ARM-L…

B-6:逆向分析及隐写

任务环境说明: 服务器场景: FTPServer20220509(关闭链接) FTP用户名:PE01密码: PE01 C语言:渗透机Windows7 (Embarcadero Dev-C++) 1,从靶机服务器的FTP上下载PE01文件,对PE01. exe二进制文件进行静态调试,将 main 函数的入口地址作为 Flag 值提交; 双击渗透机kali桌面上…

【数字图像处理】3.对比度增强

目录 3.1 灰度直方图 3.2 线性变换 3.3 直方图正规化 3.4 伽马变换 3.5 全局直方图均衡化 3.6 CLAHE 对比度增强是图像增强的一种&#xff0c;它主要解决的是图像的灰度级范围较小造成的对比度较低的问题&#xff0c;目的是将图像的灰度级增强到指定范围&#xff0c;使得…

Unity Metaverse(八)、RTC Engine 基于Agora声网SDK实现音视频通话

文章目录 简介创建应用构建应用场景API调用与回调事件测试 简介 本文介绍如何在Unity中接入声网SDK&#xff0c;它可以应用的场景有许多&#xff0c;例如直播、电商、游戏、社交等&#xff0c;音视频通话是其实时互动的基础能力。 如下图所示&#xff0c;可以在官网中选择Unit…

【Python实用基础整合(三)】儒略日计算、Pandas写Excel文件多Sheet以及datetime64[ns]时间格式处理

一、儒略日计算 儒略日&#xff08;Julian Day&#xff09;是在儒略周期内以连续的日数计算时间的计时法&#xff0c;主要用于天文学领域&#xff0c;SMOKE、CMAQ、CAMx等模型中也有使用。Linux中主要使用IOAPI库中的juldate和jul2greg来进行常规日期和儒略日的相互转化。Pyth…

MongoDB索引详解-03

MongoDB索引 索引是一种用来快速查询数据的数据结构。BTree 就是一种常用的数据库索引数据结构&#xff0c; MongoDB采用BTree 做索引 &#xff0c;索引创建在colletions上。MongoDB不使用索引的查 询&#xff0c;先扫描所有的文档&#xff0c;再匹配符合条件的文档。 使用索…

docker注意事项和https

docker容器安全注意&#xff1a; 尽量别做的事&#xff1a; 尽量不用 --privileged 运行容器授权容器root用户拥有宿主机的root权限 尽量不在 容器中运行 ssh 服务 尽量不用 --network host 使用 host 网络模式运行容器 尽量要做的事&#xff1a; 尽量使用最小化的镜像 尽量…

四六级套路总结

四六级 四级六级作文背多分 四级 英语四级&#xff08;必过12.10&#xff09; 听力&#xff1a; 全出现 头尾出现 &#xff08;直接选&#xff09; 长篇&#xff1a; 勾 8分钟&#xff0c;边勾边默念 念 勾完五句念一遍&#xff0c;十句通读&#xff08;时间够不要慌&#xff…

SpringSecurity从入门到精通

简介 ​ Spring Security 是 Spring 家族中的一个安全管理框架。相比与另外一个安全框架Shiro&#xff0c;它提供了更丰富的功能&#xff0c;社区资源也比Shiro丰富。 ​ 一般来说中大型的项目都是使用SpringSecurity 来做安全框架。小项目有Shiro的比较多&#xff0c;因为相比…

Three.js--》实现3D汽车展厅效果展示

目录 项目搭建 初始化three.js基础代码 加载汽车模型 设置展厅效果 设置GUI面板动态控制车身操作 车门操作与车身视角展示 设置手动点击打开关闭车门 设置图片背景 今天简单实现一个three.js的小Demo&#xff0c;加强自己对three知识的掌握与学习&#xff0c;只有在项目…

【振奋人心】中科院芯片突破,中国ai将逆袭

最近&#xff0c;中国科学院在人工智能芯片领域取得了一项重大突破。中科院计算技术研究所和中国电子科技集团公司第五十三研究所联合研发的新型神经网络加速器芯片&#xff0c;成功实现高效率和低功耗的特性&#xff0c;而且在典型人工智能测试中获取了高达1000倍计算效率的提…

滤波器设计总结

滤波器的主要参数 中心频率&#xff08;Center Frequency&#xff09;&#xff1a;滤波器通带的频率f0&#xff0c;一般取f0&#xff08;f1f2&#xff09;/2&#xff0c;f1、f2为带通或带阻滤波器左、右相对下降1dB或3dB边频点。窄带滤波器常以插损最小点为中心频率计算通带带…

开源大型语言模型(llm)总结

大型语言模型&#xff08;LLM&#xff09;是人工智能领域中的一个重要研究方向&#xff0c;在ChatGPT之后&#xff0c;它经历了快速的发展。这些发展主要涉及以下几个方面&#xff1a; 模型规模的增长&#xff1a;LLM的规模越来越大&#xff0c;参数数量显著增加。这种扩展使得…