2.SpringAop的jdkcglib动态代理xml注解实现切面

news2024/9/20 18:41:01

1.Spring 的 AOP 简介

1.1 什么是 AOP

AOP 为 Aspect Oriented Programming 的缩写,意思为面向切面编程,是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。

AOP 是 OOP 的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

1.2 AOP 的作用及其优势

作用:在程序运行期间,在不修改源码的情况下对方法进行功能增强

优势:减少重复代码,提高开发效率,并且便于维护

1.3 AOP 的底层实现

实际上,AOP 的底层是通过 Spring 提供的的动态代理技术实现的。在运行期间,Spring通过动态代理技术动态的生成代理对象,代理对象方法执行时进行增强功能的介入,在去调用目标对象的方法,从而完成功能的增强。

1.4 AOP 的动态代理技术

常用的动态代理技术

JDK 代理 : 基于接口的动态代理技术

cglib 代理:基于父类的动态代理技术

 

1.5 JDK 的动态代理

①目标类接口

public interface TargetInterface {
    public void method();
}

②目标类

public class Target implements TargetInterface {
    @Override
    public void method() {
        System.out.println("Target running....");
    }
}

③动态代理代码

Target target = new Target(); //创建目标对象
//创建代理对象
TargetInterface proxy = (TargetInterface) Proxy.newProxyInstance(target.getClass()
.getClassLoader(),target.getClass().getInterfaces(),new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) 
            throws Throwable {
                System.out.println("前置增强代码...");
                Object invoke = method.invoke(target, args);
                System.out.println("后置增强代码...");
                return invoke;
            }
        }
);

④ 调用代理对象的方法测试

// 测试,当调用接口的任何方法时,代理对象的代码都无序修改
proxy.method();

1.6 cglib 的动态代理

①目标类

public class Target {
    public void method() {
        System.out.println("Target running....");
    }
}

②动态代理代码

Target target = new Target(); //创建目标对象
Enhancer enhancer = new Enhancer();   //创建增强器
enhancer.setSuperclass(Target.class); //设置父类
enhancer.setCallback(new MethodInterceptor() { //设置回调
    @Override
    public Object intercept(Object o, Method method, Object[] objects, 
    MethodProxy methodProxy) throws Throwable {
        System.out.println("前置代码增强....");
        Object invoke = method.invoke(target, objects);
        System.out.println("后置代码增强....");
        return invoke;
    }
});
Target proxy = (Target) enhancer.create(); //创建代理对象

③调用代理对象的方法测试

//测试,当调用接口的任何方法时,代理对象的代码都无序修改
proxy.method();

 

1.7 AOP 相关概念

Spring 的 AOP 实现底层就是对上面的动态代理的代码进行了封装,封装后我们只需要对需要关注的部分进行代码编写,并通过配置的方式完成指定目标的方法增强。

在正式讲解 AOP 的操作之前,我们必须理解 AOP 的相关术语,常用的术语如下:

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

  • Proxy (代理):一个类被 AOP 织入增强后,就产生一个结果代理类

  • Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点

  • Pointcut(切入点):所谓切入点是指我们要对哪些 Joinpoint 进行拦截的定义

  • Advice(通知/ 增强):所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知

  • Aspect(切面):是切入点和通知(引介)的结合

  • Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程。spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入

1.8 AOP 开发明确的事项

1.8 AOP 开发明确的事项

1.8 AOP 开发明确的事项

1)需要编写的内容

  • 编写核心业务代码(目标类的目标方法)

  • 编写切面类,切面类中有通知(增强功能方法)

  • 在配置文件中,配置织入关系,即将哪些通知与哪些连接点进行结合

2)AOP 技术实现的内容

Spring 框架监控切入点方法的执行。一旦监控到切入点方法被运行,使用代理机制,动态创建目标对象的代理对象,根据通知类别,在代理对象的对应位置,将通知对应的功能织入,完成完整的代码逻辑运行。

3)AOP 底层使用哪种代理方式

在 spring 中,框架会根据目标类是否实现了接口来决定采用哪种动态代理的方式。

1.9 知识要点

  • aop:面向切面编程

  • aop底层实现:基于JDK的动态代理 和 基于Cglib的动态代理

  • aop的重点概念:

Pointcut(切入点):被增强的方法

Advice(通知/ 增强):封装增强业务逻辑的方法

Aspect(切面):切点+通知

Weaving(织入):将切点与通知结合的过程

开发明确事项:

谁是切点(切点表达式配置)

谁是通知(切面类中的增强方法)

将切点和通知进行织入配置

2. 基于 XML 的 AOP 开发

2.1 快速入门

①导入 AOP 相关坐标

②创建目标接口和目标类(内部有切点)

③创建切面类(内部有增强方法)

④将目标类和切面类的对象创建权交给 spring

⑤在 applicationContext.xml 中配置织入关系

⑥测试代码

①导入 AOP 相关坐标

<!--导入spring的context坐标,context依赖aop-->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context</artifactId>
  <version>5.0.5.RELEASE</version>
</dependency>
<!-- aspectj的织入 -->
<dependency>
  <groupId>org.aspectj</groupId>
  <artifactId>aspectjweaver</artifactId>
  <version>1.8.13</version>
</dependency>

②创建目标接口和目标类(内部有切点)

public interface TargetInterface {
    public void method();
}

public class Target implements TargetInterface {
    @Override
    public void method() {
        System.out.println("Target running....");
    }
}

③创建切面类(内部有增强方法)

public class MyAspect {
    //前置增强方法
    public void before(){
        System.out.println("前置代码增强.....");
    }
}

④将目标类和切面类的对象创建权交给 spring

<!--配置目标类-->
<bean id="target" class="com.itheima.aop.Target"></bean>
<!--配置切面类-->
<bean id="myAspect" class="com.itheima.aop.MyAspect"></bean>

⑤在 applicationContext.xml 中配置织入关系

导入aop命名空间

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

⑤在 applicationContext.xml 中配置织入关系

配置切点表达式和前置增强的织入关系

<aop:config>
    <!--引用myAspect的Bean为切面对象-->
    <aop:aspect ref="myAspect">
        <!--配置Target的method方法执行时要进行myAspect的before方法前置增强-->
        <aop:before method="before" pointcut="execution(public void com.itheima.aop.Target.method())"></aop:before>
    </aop:aspect>
</aop:config>

⑥测试代码

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AopTest {
    @Autowired
    private TargetInterface target;
    @Test
    public void test1(){
        target.method();
    }
}

⑦测试结果

 

2.2 XML 配置 AOP 详解

1) 切点表达式的写法

表达式语法:

execution([修饰符] 返回值类型 包名.类名.方法名(参数))
  • 访问修饰符可以省略

  • 返回值类型、包名、类名、方法名可以使用星号* 代表任意

  • 包名与类名之间一个点 . 代表当前包下的类,两个点 .. 表示当前包及其子包下的类

  • 参数列表可以使用两个点 .. 表示任意个数,任意类型的参数列表

例如:

execution(public void com.itheima.aop.Target.method())	
execution(void com.itheima.aop.Target.*(..))
execution(* com.itheima.aop.*.*(..))
execution(* com.itheima.aop..*.*(..))
execution(* *..*.*(..))

2) 通知的类型

通知的配置语法:

<aop:通知类型 method=“切面类中方法名” pointcut=“切点表达式"></aop:通知类型>

 

3) 切点表达式的抽取

当多个增强的切点表达式相同时,可以将切点表达式进行抽取,在增强中使用 pointcut-ref 属性代替 pointcut 属性来引用抽取后的切点表达式。

<aop:config>
    <!--引用myAspect的Bean为切面对象-->
    <aop:aspect ref="myAspect">
        <aop:pointcut id="myPointcut" expression="execution(* com.itheima.aop.*.*(..))"/>
        <aop:before method="before" pointcut-ref="myPointcut"></aop:before>
    </aop:aspect>
</aop:config>

2.3 知识要点

  • aop织入的配置

<aop:config>
    <aop:aspect ref=“切面类”>
        <aop:before method=“通知方法名称” pointcut=“切点表达式"></aop:before>
    </aop:aspect>
</aop:config>
  • 通知的类型:前置通知、后置通知、环绕通知、异常抛出通知、最终通知

  • 切点表达式的写法:

execution([修饰符] 返回值类型 包名.类名.方法名(参数))

3.基于注解的 AOP 开发

3.1 快速入门

基于注解的aop开发步骤:

①创建目标接口和目标类(内部有切点)

②创建切面类(内部有增强方法)

③将目标类和切面类的对象创建权交给 spring

④在切面类中使用注解配置织入关系

⑤在配置文件中开启组件扫描和 AOP 的自动代理

⑥测试

①创建目标接口和目标类(内部有切点)

public interface TargetInterface {
    public void method();
}

public class Target implements TargetInterface {
    @Override
    public void method() {
        System.out.println("Target running....");
    }
}

②创建切面类(内部有增强方法)

public class MyAspect {
    //前置增强方法
    public void before(){
        System.out.println("前置代码增强.....");
    }
}

③将目标类和切面类的对象创建权交给 spring

@Component("target")
public class Target implements TargetInterface {
    @Override
    public void method() {
        System.out.println("Target running....");
    }
}
@Component("myAspect")
public class MyAspect {
    public void before(){
        System.out.println("前置代码增强.....");
    }
}

④在切面类中使用注解配置织入关系

@Component("myAspect")
@Aspect
public class MyAspect {
    @Before("execution(* com.itheima.aop.*.*(..))")
    public void before(){
        System.out.println("前置代码增强.....");
    }
}

⑤在配置文件中开启组件扫描和 AOP 的自动代理

<!--组件扫描-->
<context:component-scan base-package="com.itheima.aop"/>

<!--aop的自动代理-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

⑥测试代码

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AopTest {
    @Autowired
    private TargetInterface target;
    @Test
    public void test1(){
        target.method();
    }
}

⑦测试结果

 

3.2 注解配置 AOP 详解

1) 注解通知的类型

通知的配置语法:@通知注解(“切点表达式")

2) 切点表达式的抽取

同 xml配置aop 一样,我们可以将切点表达式抽取。抽取方式是在切面内定义方法,在该方法上使用@Pointcut注解定义切点表达式,然后在在增强注解中进行引用。具体如下:

@@Component("myAspect")
@Aspect
public class MyAspect {
    @Before("MyAspect.myPoint()")
    public void before(){
        System.out.println("前置代码增强.....");
    }
    @Pointcut("execution(* com.itheima.aop.*.*(..))")
    public void myPoint(){}
}

3.3 知识要点

  • 注解aop开发步骤

①使用@Aspect标注切面类

②使用@通知注解标注通知方法

③在配置文件中配置aop自动代理aop:aspectj-autoproxy/

通知注解类型

 代码案例

pom.xml文件

jdk

增强对象

package com.java.proxy.jdk;

public class Advice {
    public void before(){
        System.out.println("前置增强");
    }

    public void afterReturning() {
        System.out.println("后置增强");
    }
}

 增强目标

package com.java.proxy.jdk;

public class Target implements TargetInterface{


    @Override
    public void save() {
        System.out.println("save running...");
    }
}

ProxyText 测试类

package com.java.proxy.jdk;

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

public class ProxyText {
    public static void main(String[] args) {
//        目标对象
        final Target target = new Target();
        //增强对象
        Advice advice = new Advice();

        //返回值   就是动态生成的代理对象
        TargetInterface proxy = (TargetInterface) Proxy.newProxyInstance(
            target.getClass().getClassLoader(), //目标对象类加载器
            target.getClass().getInterfaces(),//目标对象相同的接口字节码对象数组
            new InvocationHandler() {
                //调用代理对象的任何方法   实质
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    advice.before();  //前置增强
                    Object invoke = method.invoke(target, args);//执行目标方法
                    advice.afterReturning(); //后置增强
                    return invoke;
                }
            }
        );
        //调用代理对象的方法
        proxy.save();
    }
}

cglib

增强对象

package com.java.proxy.cglib;

public class Advice {
    public void before(){
        System.out.println("前置增强");
    }

    public void afterReturning() {
        System.out.println("后置增强");
    }
}

增强目标

package com.java.proxy.cglib;



public class Target {



    public void save() {
        System.out.println("save running...");
    }
}

Proxy Text 测试类

package com.java.proxy.cglib;


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

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

public class ProxyText {
    public static void main(String[] args) {
//        目标对象
        final Target target = new Target();
        //增强对象
        Advice advice = new Advice();

        //返回值   就是动态生成的代理对象 基于chlib
        //1创建增强器
        Enhancer enhancer = new Enhancer();
        //2设置父类(目标)
        enhancer.setSuperclass(Target.class);
        //3设置回调
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
                //执行前置
                advice.before();
                //执行目标
                Object invoke = method.invoke(target, args);
                //执行后置
                advice.afterReturning();
                return invoke;
            }
        });
        //4创建代理对象
        Target proxy = (Target)enhancer.create();
        proxy.save();
    }
}

aop

applicationContext.xml        配置文件

<?xml version="1.0" encoding="UTF-8"?>
<!--引入 aop-->
<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 http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop   http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--目标对象-->
    <bean id="target" class="com.java.aop.Target"/>
    <!--切面对象-->
    <bean id="myAspect" class="com.java.aop.MyAspect"/>

    <!--配置织入:告诉spring框架 哪些方法(切点)需要进行哪些增强(前置,后置)-->
    <aop:config>
        <!--声明切面-->
        <aop:aspect ref="myAspect">
            <!--抽取切点表达式-->
            <aop:pointcut id="myPointcut" expression="execution(* com.java.aop.*.*())"/>
            <!--切面:切点+通知  切点表达式:execution(public void com.java.aop.Target.save())-->
          <!--  <aop:before method="before" pointcut="execution(public void com.java.aop.Target.save())"/>-->
            <!--前置-->
<!--            <aop:before method="before" pointcut="execution(* com.java.aop.*.*())"/>-->
            <!--后置-->
<!--            <aop:after-returning method="before" pointcut="execution(* com.java.aop.*.*())"/>-->
            <!--<aop:around method="around" pointcut="execution(* com.java.aop.*.*())"/>
            <aop:after-throwing method="afterThrowing" pointcut="execution(* com.java.aop.*.*())"/>
            <aop:after method="after" pointcut="execution(* com.java.aop.*.*())"/>-->
            <aop:around method="around" pointcut-ref="myPointcut"/>
            <aop:after method="after" pointcut-ref="myPointcut"/>
         </aop:aspect>
    </aop:config>
</beans>

Target 增强目标

package com.java.aop;

public class Target implements TargetInterface {


    @Override
    public void save() {
//        int i = 1 / 0;
        System.out.println("save running...");
    }
}

MyAspect        切面类

package com.java.aop;

import org.aspectj.lang.ProceedingJoinPoint;

public class MyAspect {

    public void before(){
        System.out.println("前置增强...");
    }
    public void afterReturning(){
        System.out.println("后置增强...");
    }
    //Proceeding JoinPoint:  正在执行的连接点 === 切点
    public Object around(ProceedingJoinPoint pjp) throws Throwable  {
        System.out.println("环绕前增强....");
        Object proceed = pjp.proceed();//切点方法
        System.out.println("环绕后增强....");
        return proceed;
    }
    public void afterThrowing() {
        System.out.println("异常抛出增强...");
    }
    public void after() {
        System.out.println("最终增强...");
    }

}

AopTest        测试类

package com.java.test;

import com.java.aop.TargetInterface;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AopTest {

    @Autowired
    private TargetInterface target;
    @Test
    public  void test1(){
        target.save();
    }

}

anno

applicationContext-anno.xml        配置文件

<?xml version="1.0" encoding="UTF-8"?>
<!--引入 aop-->
<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 http://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
">
  <!--开启组建扫描-->
  <context:component-scan base-package="com.java.anno"/>
  <!--aop自动代理-->
  <aop:aspectj-autoproxy/>

</beans>

Target        增强目标对象

package com.java.anno;

import org.springframework.stereotype.Component;

@Component("target")
public class Target implements TargetInterface {


    @Override
    public void save() {
//        int i = 1 / 0;
        System.out.println("save running...");
    }
}

MyAspect        切面类

package com.java.anno;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component("myAspect")
@Aspect //标注当前MyAspect是一个切面类
public class MyAspect {


    /* <!--抽取切点表达式-->
                <aop:pointcut id="myPointcut" expression="execution(* com.java.aop.*.*())"/>*/
//    private String myPointcut = "execution(* com.java.anno.*.*(..))";
//    @Before("execution(* com.java.anno.*.*(..))")
    @Before("MyAspect.myPoint()")
    public void before(){
        System.out.println("前置增强...");
    }
//    @AfterReturning("execution(* com.java.anno.*.*(..))")
    @AfterReturning("MyAspect.myPoint()")
    public void afterReturning(){
        System.out.println("后置增强...");
    }
    //Proceeding JoinPoint:  正在执行的连接点 === 切点
    @Around("MyAspect.myPoint()")
//    @Around("execution(* com.java.anno.*.*(..))")
    public Object around(ProceedingJoinPoint pjp) throws Throwable  {
        System.out.println("环绕前增强....");
        Object proceed = pjp.proceed();//切点方法
        System.out.println("环绕后增强....");
        return proceed;
    }
//    @AfterThrowing("execution(* com.java.anno.*.*(..))")
    @AfterThrowing("MyAspect.myPoint()")
    public void afterThrowing() {
        System.out.println("异常抛出增强...");
    }
//    @After("execution(* com.java.anno.*.*(..))")
    @After("MyAspect.myPoint()")
    public void after() {
        System.out.println("最终增强...");
    }
    @Pointcut("execution(* com.java.anno.*.*(..))")
    public void myPoint() {}

}

AnnoTest

package com.java.test;

import com.java.anno.TargetInterface;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext-anno.xml")
public class AnnoTest {

    @Autowired
    private TargetInterface target;
    @Test
    public  void test1(){
        target.save();
    }

}

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

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

相关文章

Linux常用命令——rsync命令

在线Linux命令查询工具(http://www.lzltool.com/LinuxCommand) rsync 远程数据同步工具 补充说明 rsync命令是一个远程数据同步工具&#xff0c;可通过LAN/WAN快速同步多台主机间的文件。rsync使用所谓的“rsync算法”来使本地和远程两个主机之间的文件达到同步&#xff0c…

ARMv8 AArch64异常处理机制概览

1 处理机制概述 相对于ARMv7中的异常向量表&#xff08;Exception Vector Table&#xff09;&#xff0c;ARMv8异常处理机制更为复杂&#xff0c;涉及处理器的异常等级&#xff08;Exception Levels, ELn&#xff09;、运行状态&#xff08;Execution States&#xff09;和安全…

汉字乱码状态下的编码转换将导致的问题

实验工具notepad编辑器实验过程步骤1&#xff1a;打开notepad&#xff0c;新建一个文本文件&#xff0c;在其中输入一段汉字文本&#xff0c;查看当前编码格式&#xff0c;如下&#xff1a;分析&#xff1a;由上图可见&#xff0c;从右下角可知当前文件是以UTF-8解码显示的&…

微信小程序——页面事件,.启用下拉刷新监听页面的下拉刷新事件,上拉触底事件,停止下拉刷新的效果

一.页面事件1.什么是下拉刷新下拉刷新是移动端的专有名词&#xff0c;指的是通过手指在屏幕上的下拉滑动操作&#xff0c;从而重新加载页面数据的行为。2.启用下拉刷新启用下拉刷新有两种方式&#xff1a;a.全局开启下拉刷新在 app.json 的window 节点中&#xff0c;将 enableP…

python 蓝桥杯 矩阵拼接

问题描述已知 3 个矩形的大小依次是 a_{1} \times b_{1}, a_{2} \times b_{2}a1b1,a2b2 和 a_{3} \times b_{3}a3b3 。用这 3 个矩形能拼 出的所有多边形中, 边数最少可以是多少?例如用 3 \times 232 的矩形&#xff08;用 A 表示)、 4 \times 141 的矩形 (用 BB 表示) 和 2 \…

法律常识(一)婚姻法全文

目录 参考 第一章 总 则 第二章 结 婚 第三章 家庭关系 第四章 离 婚 第五章 救助措施与法律责任 第六章 附 则 参考 中华人民共和国婚姻法http://www.gqb.gov.cn/node2/node3/node5/node9/node101/userobject7ai1290.html 《图解中华人民共和国婚姻法》 &#xff…

[架构之路-92]:《软件架构设计:程序员向架构师转型必备》-2-解析软件架构的概念

前言&#xff1a;什么是软件架构&#xff1f;不同的人&#xff0c;有不同的答案。因为架构无处不再&#xff0c;架构又有不同层面。很多人都给架构定义&#xff0c;不同的人&#xff0c;对架构有不同的理解&#xff0c;很难统一。本文是按照作者个人的理解&#xff0c;来展现一…

React是不是MVVM架构吗?

首先说结论&#xff1a;不是 一、MVVM Model-View-ViewModel&#xff1a;一句话概括MVVM&#xff0c;操作数据&#xff0c;就是操作视图&#xff0c;就是操作DOM。开发者只需要完成包含申明绑定的视图模板&#xff0c;编写ViewModel中业务数据变更逻辑&#xff0c;View层则完…

Lua 协同程序(coroutine)

Lua 协同程序(coroutine) 参考文章&#xff1a; 菜鸟教程 https://zhuanlan.zhihu.com/p/480357405 https://zhuanlan.zhihu.com/p/76249973 Lua 协同程序(coroutine)与线程比较类似&#xff1a;拥有独立的堆栈&#xff0c;独立的局部变量&#xff0c;独立的指令指针&#xff0…

贪心算法(例题详细解说)

日升时奋斗&#xff0c;日落时自省 目录 1、选择排序 2、分割平衡字符串 3、买卖股票的最佳时机 II 4、跳跃游戏 5、钱币找零 6、多机调度问题 7、活动选择 8、无重复区间 贪心思想&#xff1a;顾名思义 贪 是该算法的一大特点&#xff0c;如何贪&#xff1f;&#x…

2023-01-28 clickhouse-聚合函数的源码再梳理

笔者在源码笔记1之中分析过ClickHouse的聚合函数的实现&#xff0c;但是对于各个接口函数的实际如何共同工作的源码&#xff0c;回头看并没有那么明晰&#xff0c;主要原因是没有结合Aggregator的类来一起分析聚合函数的是如果工作起来的。所以决定重新再完成一篇聚合函数的源码…

梦熊杯-十二月月赛-白银组题解-A.自由

A. Problem A.自由&#xff08;freedom.cpp&#xff09; 内存限制&#xff1a;256 MiB 时间限制&#xff1a;1000 ms 标准输入输出 题目类型&#xff1a;传统 评测方式&#xff1a;文本比较 题目描述: 「蒙德」是「自由」的国度。 巴巴托斯认为&#xff0c;如果一个数的…

ch1_2 计算机的基本组成

计算机的基本组成 1. 冯 诺依曼计算机的特点 计算机由五大部件组成指令和数据 以同等地位 存于存储器&#xff0c; 可按地址寻访。指令和数据用二进制 表示指令由操作码 和 地址码 组成&#xff1b;存储程序&#xff1b;以运算器 为中心&#xff1b; 2. 硬件框图 存储器&am…

【Java集合】HashSet源码分析

目录 一、Set简介 二、HashSet简介 2.1 简介 2.2 HashSet继承关系 三、源码分析 3.1 成员属性 3.2 构造方法 3.3 添加元素 3.3.1 add()方法 3.3.2 addAll()方法 3.4 删除元素 3.4.1 remove()方法 3.4.2 removeAll()方法 3.5 查询元素 3.5.1 contains()方法 3.5.2 containsAll方…

项目管理:如何编写高质量的Makefile?

文章目录背景熟练掌握 Makefile 语法规划 Makefile 要实现的功能设计合理的 Makefile 结构掌握 Makefile 编写技巧技巧 1&#xff1a;善用通配符和自动变量技巧 2&#xff1a;善用函数技巧 3&#xff1a;依赖需要用到的工具技巧 4&#xff1a;把常用功能放在 /Makefile 中&…

nodeJS - 切换使用淘宝镜像【临时切换、 长期切换】

一、文章引导 #mermaid-svg-zWQadgqvTsLhAes4 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-zWQadgqvTsLhAes4 .error-icon{fill:#552222;}#mermaid-svg-zWQadgqvTsLhAes4 .error-text{fill:#552222;stroke:#55222…

自动驾驶感知——视觉感知经典算法

文章目录1. 车道线检测技术1.1 基于规则的车道线检测技术1.1.1 流程框架1.1.2 预处理模块1.1.3 车道线识别感兴趣区域提取1.1.4 灰度图转化1.1.5 灰度图去噪1.1.6 二值化操作1.1.7 鲁棒性参数估计——RANSAC1.1.8 后处理模块1.1.9 输出1.2 车道线检测技术发展路线2. 目标检测技…

10.图和树基础

一、基本介绍 1.图 图描述的是一些个体之间的关系。这些个体之间既不是前驱后继的顺序关系&#xff0c;也不是祖先后代的层次关系&#xff0c;而是错综复杂的网状关系。我们一般用图G(V,E)G(V,E)G(V,E)来表示&#xff0c;VVV表示结点&#xff0c;EEE表示边。 根据边是否有权值…

爱快软路由安装Docker插件

在爱快云 插件应用中开启Docker插件 在爱快web端页面的[系统设置]->[磁盘管理]->[磁盘分区]设置磁盘分区&#xff0c;选择普通存储&#xff0c;挂载路径名可以随便取。 点击[高级应用]->[插件管理] 点击页面的Docker图标。 启用Docker服务 点击中间的[镜像管理]&…

n皇后问题

n皇后问题 题目&#xff1a; 按照国际象棋的规则&#xff0c;皇后可以攻击与之处在同一行 或同一列 或同一斜线上的棋子。 n 皇后问题 研究的是如何将 n 个皇后放置在 nn 的棋盘上&#xff0c;并且使皇后彼此之间不能相互攻击。 给你一个整数 n &#xff0c;返回所有不同的…