【Java校招面试】基础知识(二)——Spring Framework AOP

news2024/11/25 2:40:13

目录

  • 前言
  • 一、Spring Framewwork基础知识
  • 二、Spring AOP基础概念
    • 1. 切面(Aspect)
    • 2. 织入(Weaving)
    • 3. 增强(Advice)
    • 4. 动态代理
  • 三、JDK动态代理
    • 1. 基本用法
    • 2. 原理分析
  • 四、CGLib动态代理
    • 1. 基本用法
    • 2. 原理分析
    • 3. MethodProxy
    • 4. FastClass机制
  • 五、SDK动态代理和CGLib动态代理的区别
  • 六、AspectJ
    • 1. 基本用法
    • 2. AspectJ提供的几种增强注解
  • 后记

前言

本篇主要介绍Spring FrameworkAOP面向切面编程的源码解析。

“基础知识”是本专栏的第一个部分,本篇博文是第二篇博文,如有需要,可:

  1. 点击这里,返回本专栏的索引文章
  2. 点击这里,返回上一篇《【Java校招面试】基础知识(一)——Java常用类库》

一、Spring Framewwork基础知识

1. IoC、DL、DI的关系
在这里插入图片描述
IoC即控制反转,DL即依赖查找,DI即依赖注入

从上图中可见,DL和DI是IoC的两种实现方式。DL因为对业务代码具有入侵性,所以已被抛弃。 而实现DI的方式又分为Set注入接口注入注解注入构造器注入4种。

2. IoC容器
在这里插入图片描述
从图中可知:

  1. IoC容器首先从各种配置文件、注解中读取Bean的配置信息,存放在Bean定义注册表中。
  2. 接着根据Bean注册表去实例化Bean。
  3. 然后将实例化好的Bean实例存放在Bean缓存池中。
  4. 在应用程序需要用到Bean实例时,从Bean缓存池取出对应的实例,注入进去。

3. BeanFactory
它是Spring框架最核心的接口,其作用有:

  • 提供IoC的配置机制;
  • 包含各种Bean的定义,便于实例化Bean;
  • 建立Bean之间的依赖关系;
  • 控制Bean的生命周期。

4. ApplicationContext接口
它继承了多个接口:

  • BeanFactory:管理、装配Bean;
  • ResourcePatternResolver:加载资源文件;
  • MessageSource:实现国际化等功能;
  • ApplicationEventPublisher:注册监听器,实现监听机制。

5. Bean的作用域

  • singleton:Spring的默认作用域,容器里拥有唯一的Bean实例;
  • prototype:针对每个getBean请求,容器都会创建一个Bean实例;
  • request:会为每个Http请求创建一个Bean实例;
  • session:会为每个session创建一个Bean实例;
  • globalSession:会为每个全局Http Session创建一个Bean实例,该作用域仅针对Portlet有效。

6. Bean的生命周期
在这里插入图片描述
从图中可以看出,Bean的生命周期分为4个部分:
1)Bean的实例化
2)Bean的初始化
3)Bean的使用
4)Bean的销毁


二、Spring AOP基础概念

AOP的出现并不是要完全替代OOP,而仅是作为OOP的有益补充。 AOP的应用场合是受限的,它一般只适合于那些具有横切逻辑的应用场合:如性能监测、访问控制、事务管理以及日志记录。

以下是Spring AOP的一些基础概念:

1. 切面(Aspect)

1)静态切面: 指在生成代理对象时,就确定了增强是否需要织入到目标类连接点上;
2)动态切面: 指必须在运行期根据方法入参的值来判断增强是否需要织入到目标类连接点上;
3)流程切点: 代表由某个方法直接或间接发起调用的其他方法,流程切面由流程切点和相应的增强组成;
4)复合切面: 有时,一个切点难以描述目标连接点的信息,我们就需要创建复合切点切面;
5)引介切面: 是一种比较特殊的增强类型,它不是在目标方法周围织入增强,而是为目标类创建新的方法和属性,所以引介增强的链接点是类级别的,而非方法级别的。通过引介增强,我们可以为一个目标类添加一个接口实现。

2. 织入(Weaving)

织入是将增强添加到目标类具体连接点上的过程。AOP有3种织入的方式:
1)编译器织入: 这要求使用特殊的Java编译器;
2)类装载期织入: 这要求使用特殊的类装载器;
3)动态代理织入: 在运行期为目标类添加增强生成子类的方式。
Spring Framework采用动态代理织入

3. 增强(Advice)

增强即在一些业务逻辑外,需要频繁来写的非业务逻辑代码,如有一类方法执行前都要执行一些通用的初始化步骤;有一类方法执行完成后都要执行一些通用的反初始化步骤;甚至有的方法两者都需要。这些通用的代码有:性能监测、访问控制、事务管理以及日志记录等。

增强的类型分为:

  • 方法级别的增强: 前置增强、后置增强、环绕增强、异常抛出增强
  • 类级别的增强: 引介增强

4. 动态代理

Spring AOP使用了两种代理机制:

  • 基于JDK的动态代理;
  • 基于CGLib的动态代理。

之所以需要两种代理机制,很大程度上是因为JDK本身只提供接口的代理,而不支持类的代理。


三、JDK动态代理

JDK1.3以后,Java提供了动态代理的技术,允许开发者在运行期间创建接口的代理实例。

JDK的动态代理主要涉及到java.lang.reflect包中的两个类:ProxyInvocationHandler

  • InvocationHandler是一个接口,可以通过实现该接口定义横切逻辑,并通过反射机制调用目标类的代码,动态将横切逻辑和业务逻辑编织在一起。
  • Proxy利用InvocationHandler动态创建一个符合某一接口的实例,生成目标类的代理对象。

1. 基本用法

以下对JDK动态代理列举一段代码进行示例:

1)我们自己的,需要织入增强的业务接口:

	public interface Business {
	    void doBusiness();
	}

2)业务逻辑实现类:

	public class BusinessImpl implements Business {
	    @Override
	    public void doBusiness() {
	        System.out.println("I'm doing business");
	    }
	}

这里的业务逻辑中,因为只做演示,我们只输出了一句话I'm doing business,实际开发中的业务逻辑可以是任意代码。

3)业务逻辑代理类:

	public class BusinessProxy implements InvocationHandler {
		//增强的目标,这里指的是业务逻辑实现类BusinessImpl
	    private Object target;
	    public BusinessProxy(Object target) {
	        this.target = target;
	    }
		
		//获取代理对象
	    public <T> T getProxy() {
	        return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
	    }
	
		//实现增强逻辑
	    @Override
	    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
	        System.out.println("Doing pre-advice.");
	        Object result = method.invoke(target, args);
	        System.out.println("Doing post-advice.");
	        return result;
	    }
	}

这里的增强逻辑我们实现了一个环绕增强,即在业务逻辑前输出一句话Doing pre-advice.,在业务逻辑后输出一句话Doing post-advice.。实际开发中的增强逻辑也可以是性能监测、访问控制、事务管理以及日志记录等逻辑。

4)业务逻辑调用类:

	public class CallBusiness {
	    public static void main(String[] args) {
	        Business business = new BusinessProxy(new BusinessImpl()).getProxy();
	        business.doBusiness();
	    }
	}

5)输出结果

Doing pre-advice.
I’m doing business
Doing post-advice.

可以看到,通过JDK动态代理,我们实现了业务逻辑的增强。

2. 原理分析

1)其大致流程为:
① 为接口创建代理类的字节码文件;
② 使用ClassLoader将字节码文件加载到JVM;
③ 创建代理类实例对象,执行对象的目标方法。

2)生成的目标字节码文件内容:

	package com.sun.proxy;
	
	import java.lang.reflect.InvocationHandler;
	import java.lang.reflect.Method;
	import java.lang.reflect.Proxy;
	import java.lang.reflect.UndeclaredThrowableException;
	import test.jdkproxytest.Business;
	
	public final class $Proxy0 extends Proxy implements Business {
	    private static Method m1;
	    private static Method m2;
	    private static Method m0;
	    private static Method m3;
	
	    public $Proxy0(InvocationHandler var1) throws  {
	        super(var1);
	    }
	
	    public final boolean equals(Object var1) throws  {
	        try {
	            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
	        } catch (RuntimeException | Error var3) {
	            throw var3;
	        } catch (Throwable var4) {
	            throw new UndeclaredThrowableException(var4);
	        }
	    }
	
	    public final String toString() throws  {
	        try {
	            return (String)super.h.invoke(this, m2, (Object[])null);
	        } catch (RuntimeException | Error var2) {
	            throw var2;
	        } catch (Throwable var3) {
	            throw new UndeclaredThrowableException(var3);
	        }
	    }
	
	    public final int hashCode() throws  {
	        try {
	            return (Integer)super.h.invoke(this, m0, (Object[])null);
	        } catch (RuntimeException | Error var2) {
	            throw var2;
	        } catch (Throwable var3) {
	            throw new UndeclaredThrowableException(var3);
	        }
	    }
	
	    public final void doBusiness() throws  {
	        try {
	            super.h.invoke(this, m3, (Object[])null);
	        } catch (RuntimeException | Error var2) {
	            throw var2;
	        } catch (Throwable var3) {
	            throw new UndeclaredThrowableException(var3);
	        }
	    }
	
	    static {
	        try {
	            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
	            m2 = Class.forName("java.lang.Object").getMethod("toString");
	            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
	            m3 = Class.forName("test.jdkproxytest.Business").getMethod("doBusiness");
	        } catch (NoSuchMethodException var2) {
	            throw new NoSuchMethodError(var2.getMessage());
	        } catch (ClassNotFoundException var3) {
	            throw new NoClassDefFoundError(var3.getMessage());
	        }
	    }
	}

根据生成的字节码文件,我们得出以下结论:

生成的代理类继承了我们定义的代理类并且实现了要代理的接口,由于Java不支持多继承,所以JDK动态代理不能代理类;
② 有一个静态代码块,通过反射或者代理类的所有方法;
③ 通过invoke执行代理类中的目标方法;


四、CGLib动态代理

使用JDK创建代理有一个限制,即它只能为接口创建代理实例。对于没有通过接口定义业务方法的类,CGLib作为一个替代者,填补了这个空缺。

CGLib采用非常底层的字节码技术,可以为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,并顺势织入横切逻辑。

1. 基本用法

同样,以下对JDK动态代理列举一段代码进行示例:

业务接口业务逻辑实现类沿用前文中的的代码

1)业务逻辑代理类

	public class BusinessProxy implements MethodInterceptor {
	    private Enhancer enhancer = new Enhancer();
	    public Object getProxy(Class clazz) {
	        enhancer.setSuperclass(clazz);
	        enhancer.setCallback(this);
	        return enhancer.create();
	    }
	
	    @Override
	    public Object intercept(Object o, Method method, Object[] os, MethodProxy mp) throws Throwable {
	        System.out.println("Doing pre-advice");
	        Object result = mp.invokeSuper(o, os);
	        System.out.println("Doing post-advice");
	        return result;
	    }
	}

2)业务逻辑调用类:

	public class CallBusiness {
		public static void main(String[] args) {
	        BusinessProxy proxy = new BusinessProxy();
	        BusinessImpl businessImpl = (BusinessImpl) proxy.getProxy(BusinessImpl.class);
	        businessImpl.doBusiness();
	    }
	}

输出结果相同,此处不再赘述。

2. 原理分析

1)生成的代理类:
调用CallBusiness之后,会生成3个代理类的Class文件:
在这里插入图片描述
我们先打开第二个文件:

	public class BusinessImpl$$EnhancerByCGLIB$$d53a0f55 extends BusinessImpl implements Factory {
	    ......
	    private MethodInterceptor CGLIB$CALLBACK_0;  //拦截器
	    ......
	    private static final Method CGLIB$doBusiness$0$Method;  //被代理方法
	    private static final MethodProxy CGLIB$doBusiness$0$Proxy;  //代理方法
	    ......
	
	    static void CGLIB$STATICHOOK1() {
			......
			//代理类
	        Class var0 = Class.forName("test.cglibproxytest.BusinessImpl$$EnhancerByCGLIB$$d53a0f55");
	        //被代理类
	        Class var1;
	        ......
	        CGLIB$doBusiness$0$Method = ReflectUtils.findMethods(new String[]{"doBusiness", "()V"}, (var1 = Class.forName("test.cglibproxytest.BusinessImpl")).getDeclaredMethods())[0];
	        CGLIB$doBusiness$0$Proxy = MethodProxy.create(var1, var0, "()V", "doBusiness", "CGLIB$doBusiness$0");
	    }
	}

通过代理类的源码可以看到,代理类会获得所有从父类继承来的方法,并且会有MethodProxy与之对应,比如Method:CGLIB$doBusiness$0$Method,MethodProxy:CGLIB$doBusiness$0$Proxy

2)方法的调用

		//代理方法(methodProxy.invokeSuper会调用)
	    final void CGLIB$doBusiness$0() {
	        super.doBusiness();
	    }
		
		//被代理方法(methodProxy.invoke会调用,在拦截器中调用methodProxy.invoke会死循环,因为其会调用拦截器)
	    public final void doBusiness() {
	        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
	        if (this.CGLIB$CALLBACK_0 == null) {
	            CGLIB$BIND_CALLBACKS(this);
	            var10000 = this.CGLIB$CALLBACK_0;
	        }
	
	        if (var10000 != null) {
	        	//调用拦截器
	            var10000.intercept(this, CGLIB$doBusiness$0$Method, CGLIB$emptyArgs, CGLIB$doBusiness$0$Proxy);
	        } else {
	            super.doBusiness();
	        }
	    }

由此可以从中看出方法的调用链: 代理对象调用this.doBusiness方法 --> 调用拦截器 --> methodProxy.invokeSuper --> CGLIB$doBusiness$0 --> 被代理对象doBusiness方法

3. MethodProxy

拦截器MethodInterceptor中就是由MethodProxy的invokeSuper方法调用代理方法的,MethodProxy非常关键,需要分析一下它具体做了什么。

1)创建MethodProxy

	public class MethodProxy {
	    private Signature sig1;
	    private Signature sig2;
	    private MethodProxy.CreateInfo createInfo;
	    private final Object initLock = new Object();
	    private volatile MethodProxy.FastClassInfo fastClassInfo;
	    /** 
	     * @param c1 被代理对象Class
	     * @param c2 代理对象Class
	     * @param desc 入参类型列表字符串
	     * @param name1 被代理方法名
	     * @param name2 代理方法名
	     */
	    public static MethodProxy create(Class c1, Class c2, String desc, String name1, String name2) {
	        MethodProxy proxy = new MethodProxy();
	        // 被代理方法签名
	        proxy.sig1 = new Signature(name1, desc);
	        // 代理方法签名
	        proxy.sig2 = new Signature(name2, desc);
	        proxy.createInfo = new MethodProxy.CreateInfo(c1, c2);
	        return proxy;
	    }
	
		private static class CreateInfo {
		    Class c1;
		    Class c2;
		    NamingPolicy namingPolicy;
		    GeneratorStrategy strategy;
		    boolean attemptLoad;
		
		    public CreateInfo(Class c1, Class c2) {
		        this.c1 = c1;
		        this.c2 = c2;
		        AbstractClassGenerator fromEnhancer = AbstractClassGenerator.getCurrent();
		        if(fromEnhancer != null) {
		            this.namingPolicy = fromEnhancer.getNamingPolicy();
		            this.strategy = fromEnhancer.getStrategy();
		            this.attemptLoad = fromEnhancer.getAttemptLoad();
		        }
		    }
		}
	}

2)invokeSuper调用

		public Object invokeSuper(Object obj, Object[] args) throws Throwable {
		        try {
		            this.init();
		            MethodProxy.FastClassInfo fci = this.fastClassInfo;
		            return fci.f2.invoke(fci.i2, obj, args);
		        } catch (InvocationTargetException var4) {
		            throw var4.getTargetException();
		        }
		    }
		
		private static class FastClassInfo {
		    FastClass f1;  //被代理类FastClass
		    FastClass f2;  //代理类FastClass
		    int i1;   //被代理类的方法签名(index)
		    int i2;  //代理类的方法签名
		
		    private FastClassInfo() {}
		}

上面代码调用过程就是获取到代理类对应的FastClass,并执行了代理方法。之前生成3个class文件中:

  • 第一个就是代理类的FastClass
  • 第三个就是被代理类的FastClass

4. FastClass机制

Cglib动态代理执行代理方法效率之所以比JDK的高是因为Cglib采用了FastClass机制,它的原理简单来说就是:为代理类和被代理类各生成一个Class,这个Class会为代理类或被代理类的方法分配一个index(int类型)。

这个index当做一个入参,FastClass就可以直接定位要调用的方法直接进行调用,这样省去了反射调用,所以调用效率比JDK动态代理通过反射调用高。

public class BusinessImpl$$FastClassByCGLIB$$e73ca2d3 extends FastClass {
    public BusinessImpl$$FastClassByCGLIB$$e73ca2d3(Class var1) {
        super(var1);
    }

    public int getIndex(Signature var1) {
        String var10000 = var1.toString();
        switch(var10000.hashCode()) {
        case 765831722:
            if (var10000.equals("doBusiness()V")) {
                return 0;
            }
            break;
		......
        return -1;
    }

    public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
        BusinessImpl var10000 = (BusinessImpl)var2;
        int var10001 = var1;

        try {
            switch(var10001) {
            case 0:
                var10000.doBusiness();
                return null;
            ......
            }
        } catch (Throwable var4) {
            throw new InvocationTargetException(var4);
        }
        throw new IllegalArgumentException("Cannot find matching method/constructor");
    }
}

FastClass并不是跟代理类一块生成的,而是在第一次执行MethodProxy invoke/invokeSuper时生成的并放在了缓存中。

		// MethodProxy invoke/invokeSuper都调用了init()
		private void init() {
	        if(this.fastClassInfo == null) {
	            Object var1 = this.initLock;
	            synchronized(this.initLock) {
	                if(this.fastClassInfo == null) {
	                    MethodProxy.CreateInfo ci = this.createInfo;
	                    MethodProxy.FastClassInfo fci = new MethodProxy.FastClassInfo();
						// 如果缓存中就取出,没有就生成新的FastClass
	                    fci.f1 = helper(ci, ci.c1);
	                    fci.f2 = helper(ci, ci.c2);
						// 获取方法的index
	                    fci.i1 = fci.f1.getIndex(this.sig1);                      					fci.i2 = fci.f2.getIndex(this.sig2);
	                    this.fastClassInfo = fci;
	                    this.createInfo = null;
	                }
	            }
	        }
	    }

五、SDK动态代理和CGLib动态代理的区别

  1. JDK动态代理实现了被代理对象的接口,CGLib动态代理继承了被代理对象;
  2. 两者都是在运行期生成字节码,JDK直接写Class字节码,CGLib使用ASM框架写Class字节码,CGLib实现更复杂,生成代理类的效率比JDK低;
  3. 调用代理方法时,JDK是通过反射机制调用,CGLib是通过FastClass机制直接调用方法,执行效率比JDK更高。

六、AspectJ

它是一个基于Java的AOP框架,比Spring AOP更复杂,性能更好。

1. 基本用法

同样,以下对AspectJ列举一段代码进行示例:

业务接口沿用前文中的的代码

1)业务逻辑切面类

	@Aspect
	public class BusinessAspect {
	    @Before("execution(* doBusiness(..))")
	    public void beforeDoingBusiness(){
	        System.out.println("Doing pre-advice");
	    }
	    ......
	}

这里的@Before("execution(* doBusiness(..))")称为前置增强注解。表达的意思是,在execution:执行*:任何类的doBusiness(..):方法@Before:之前,都执行下面的beforeDoingBusiness方法。

同样的@AfterReturning后置增强注解的代码省略。

2)业务逻辑调用类

	public class CallBusiness {
	    public static void main(String[] args) {
	        BusinessImpl business = new BusinessImpl();
	        AspectJProxyFactory proxyFactory = new AspectJProxyFactory();
	        proxyFactory.setTarget(business);
	        proxyFactory.addAspect(BusinessAspect.class);
	        BusinessImpl proxy = proxyFactory.getProxy();
	        proxy.doBusiness();
	    }
	}

输出结果相同,此处不再赘述。

可见,AspectJ在用法,代码更简洁,便于记忆。唯一的难点是:引入的切面规则语法,需要一定的学习和记忆。

2. AspectJ提供的几种增强注解

  1. @Before: 前置增强,相当于BeforeAdvice的功能;
  2. @AfterReturning: 后置增强,相当于AfterReturningAdvice;
  3. @Around: 环绕增强,相当于MethodInterceptor;
  4. @AfterThrowing: 抛出增强,相当于ThrowsAdvice;
  5. @After: Final增强,不管抛出异常还是正常退出,该增强都会得到执行,该增强没有对应的增强接口,可以把它看成ThrowsAdvice和AfterReturningAdvice的混合物,一般用于释放资源,相当于try{}finally{}的控制流;
  6. @DeclareParents: 引介增强,相当于IntroductionInterceptor。

后记

这一篇内容也不少,不过大部分是代码实例。

Spring Framework相关内容,我的免费专栏 《Spring学习笔记》 中有更为详尽的概念、原理及实践方法,如有需要可移步。

本篇中:
Spring Framework部分: 仅列举了核心的概念。

Spring AOP部分: 概念和原理确实应该知道以应对面试,不过这里也具有很高的实践价值,列举一些场景:如使用环绕增强实现接口耗时统计、使用后置增强上报接口调用、安卓端用户点击某些功能按钮的比例上报统计等。AOP面向切面编程虽然不能替代OOP面向对象编程,但作为其补充,则可以省略很多重复的代码,大有用处。

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

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

相关文章

【五一创作】使用Resnet残差网络对图像进行分类(猫十二分类,模型定义、训练、保存、预测)(二)

使用Resnet残差网络对图像进行分类 &#xff08;猫十二分类&#xff0c;模型定义、训练、保存、预测&#xff09;(二&#xff09; 目录 &#xff08;6&#xff09;、数据集划分 &#xff08;7&#xff09;、训练集增强 &#xff08;8&#xff09;、装载数据集 &#xff08…

山东专升本计算机第十一章-新一代信息技术

新一代信息技术 物联网 概念 物联网就是物物相连的互联网&#xff0c;其核心和基础仍然是互联网 计算机&#xff0c;互联网之后信息产业发展的第三次浪潮 推入人类进入智能时代&#xff0c;又称物联时代 三大特征 全面感知 可靠传递 智能处理 • 物联网的最核心 技术架…

阿里云g8i服务器ECS通用型服务器CPU处理器性能测评

阿里云服务器ECS通用型实例规格族g8i采用2.7 GHz主频的Intel Xeon(Sapphire Rapids) Platinum 8475B处理器&#xff0c;3.2 GHz睿频&#xff0c;g8i实例采用阿里云全新CIPU架构&#xff0c;可提供稳定的算力输出、更强劲的I/O引擎以及芯片级的安全加固。阿里云百科分享阿里云服…

JavaScript 入门(1)

script 标签 <scrtipt> 标签可以插入到HTML中的任何位置在很老的代码中需使用type属性&#xff0c;但是现在的代码中不需要 <script type"text/javascript"><!-- ... //--></script>外部脚本 通过src 属性将脚本添加到HTML中 <script …

Maven的全面讲解及如何安装使用

Maven是一种流行的Java项目管理工具&#xff0c;可用于构建、测试、打包和部署Java应用程序。本文将介绍Maven的概念、安装配置、使用方法、生命周期以及IDEA集成Maven的方法。 Maven的概念 Maven是一种基于项目对象模型&#xff08;POM&#xff09;的构建工具。POM是一个XML…

【C++】位运算类题目总结

文章目录 一. 位运算符脑图二. 相关题目1. 统计二进制数中0的个数2. 数组中只出现一次的数字3. 数组中只出现一次的数字 II4. 不用加减乘除做加法 一. 位运算符脑图 二. 相关题目 1. 统计二进制数中0的个数 解题思路&#xff1a;x & (x-1)&#xff1b;它的作用是每次循环…

系统集成项目管理工程师 笔记(第18章:项目风险管理)

文章目录 18.1.2 风险的分类 54318.1.3 风险的性质 544项目风险管理6个过程&#xff08;风险管理、识别风险、实施定性风险分析、实施定量风险分析、规划风险应对、控制风险&#xff09;组织和干系人的风险态度影响因素18.3.3 规划风险管理的输出 550风险识别的原则18.4.2 识别…

针对Vue前后端分离项目的渗透思路

引言 在目前的开发环境下&#xff0c;越来越多的厂商选择 Vue.js 来实现前端功能的编写&#xff0c;且成熟的前端框架已经可以实现后端代码实现的功能&#xff0c;导致后端目前只负责提供 Api 接口和文档&#xff0c;方便前端的同时去调用。本文主要介绍如何针对这类前后端分离…

如何利用几何坐标变换后纠正技术实现倾斜摄影三维模型数据拼接?

如何利用几何坐标变换后纠正技术实现倾斜摄影三维模型数据拼接&#xff1f; 倾斜摄影三维模型数据拼接是指将多个倾斜摄影数据集合并为一个完整的三维模型。在这个过程中&#xff0c;由于不同数据集之间的相对位置和姿态不同&#xff0c;需要进行几何坐标变换以实现数据拼接。…

借用AI工具为视频添加中文字幕,消除语言障碍,母语环境最快速地学习

由于chatgpt的启动&#xff0c;感觉语言已经完全不会成为学习的障碍&#xff0c;突发奇想&#xff0c;在我们查看youtube视频的时候&#xff0c;有没有方便的工具能够将其字幕翻译为中文。这样能够极大提高在youtube学习的效率&#xff0c;于是顺手问了一下ChatGPT&#xff0c;…

Nginx—在linux的ubuntu系统上的安装使用

前言: 有关Nginx的基础知识和使用都在这里Nginx简介和快速入门_北岭山脚鼠鼠的博客-CSDN博客 常用命令: cd /usr/local/nginx/sbin/ ./nginx 启动 ./nginx -s stop 停止 ./nginx -s quit 安全退出 ./nginx -s reload 重新加载配置文件(常用) //在修改配置文件之后使用 p…

教你部署chatgpt商业版源码,支持卡密开通国内使用

教你部署chatgpt商业版源码&#xff0c;支持卡密开通国内使用 当今&#xff0c;人工智能技术在各个领域的应用越来越广泛&#xff0c;其中自然语言处理是非常重要的一环。OpenAI 的 GPT 模型是自然语言处理领域的一项重要技术&#xff0c;它可以根据已有的文本数据&#xff0c;…

Java 怎样实现代理模式,有什么优缺点

一、介绍 代理模式是一种常见的设计模式&#xff0c;它可以为其他对象提供一种代理以控制对这个对象的访问。代理对象具有与被代理对象相同的接口&#xff0c;客户端无需知道代理对象和被代理对象的区别。代理模式可以应用于各种不同的场景&#xff0c;例如远程代理、虚拟代理…

Ubantu docker学习笔记(九)容器监控 自带的监控+sysdig+scope+cAdvisor+prometheus

文章目录 一、Docker命令监控二、Sysdig2.1介绍2.2 基本操作2.2.1 切换视图2.2.2 查看标签含义2.2.3 排序2.2.4 查看内部进程2.2.5 查找2.2.6 暂停2.2.7 上一级2.2.8 退出 三、Weave Scope3.1介绍3.2基本操作3.2.1 显示容器3.2.2 选择容器3.2.3 按照CPU使用情况排序3.2.4 控制容…

手动开发 简单的 Spring 基于 XML 配置的程序

目录 手动开发- 简单的 Spring 基于 XML 配置的程序 需求说明 思路分析 WyxApplicationContextTest xml配置 注意 手动开发- 简单的 Spring 基于 XML 配置的程序 需求说明 1. 自己写一个简单的 Spring 容器, 通过读取 beans.xml&#xff0c;获取第 1 个 JavaBean: Mon…

【建议收藏】Pandas(一)——初见Series

文章目录 &#x1f4da;引言&#x1f4d6;库的安装以及一些说明&#x1f4d1;库的安装&#x1f4d1;一些说明 &#x1f4d6;Series&#x1f4d1;创建一个Series&#x1f516;从列表创建Series&#x1f516;从字典创建Series&#x1f516;标量创建Series &#x1f4d1;Series的特…

SpringSecurity认证原理和自定义认证

认证原理和自定义认证 认证配置表单认证注销登录前后端分离认证添加验证码 自定义认证 自定义资源权限规则 /index 公共资源/hello … 受保护资源 权限管理 在项目中添加如下配置就可以实现对资源权限规则设定: Configuration public class WebSecurityConfigurer extend…

node笔记_http服务搭建(渲染html、json)

文章目录 ⭐前言⭐初始化项目调整npm 的script运行入口搭建hello world的http服务npm run dev执行主函数的http服务 ⭐http返回类型html模板文件返回安装express渲染html的字符串 渲染html文件 sendFile渲染json返回数据类型 res.json ⭐结束 ⭐前言 大家好&#xff0c;我是ym…

CTF权威指南 笔记 -第二章二进制文件- 2.2 -ELF文件格式

目录 ELF的文件类型 ELF文件的结构 ELF文件头 节头表 代码节 数据节和只读数据节 bss节 字符串表 符号表 重定位 可执行文件的装载 常见的段 ELF就是可执行可连接格式 为linux运行文件格式 ELF的文件类型 我们使用复杂的例子进行演示 #include<stdio.h>int gl…

成功解决长时间挂起虚拟机后再次打开无法连接网络,并提示网络激活失败(亲测有效)

成功解决长时间挂起虚拟机后再次打开无法连接网络&#xff0c;并提示网络激活失败&#xff08;亲测有效&#xff01;&#xff09; 之前做区块链的一个虚拟机很久没打开&#xff0c;一直处于挂起状态&#xff0c;一直提示网络连接激活失败。试了很多种方法没解决&#xff0c;更…