手写Srping11(实现AOP切面)

news2024/12/26 11:15:29

文章目录

  • 目标
  • 设计
  • 项目结构
  • 一、代理方法的案例
  • 二、代理方法案例拆解
  • 实现
    • 1、切点表达式——Pointcut
    • 2、类匹配器——ClassFilter
    • 3、方法匹配器——MethodMatcher
    • 4、实现切点表达式类——AspectJExpressionPointcut
      • 4.1、匹配验证
    • 5、包装切面信息——AdvisedSupport
      • 5.1、被代理的目标对象——TargetSource
    • 6、反射方法调用的包装类——ReflectiveMethodInvocation
    • 7、 代理抽象——AopProxy
      • 7.1、JDK代理实现——JdkDynamicAopProxy
      • 7.2、Cglib代理实现——Cglib2AopProxy
  • 测试
    • 1、事先准备
    • 2、自定义拦截方法
    • 3、单元测试
  • 总结


目标

在软件行业,AOP 意为:面向切面编程,通过预编译的方式运行期间动态代理实现程序功能功能的统一维护。

在 Spring 框架中是一个非常重要的内容,使用 AOP 可以对业务逻辑的各个部分进行隔离,从而使各模块间的业务逻辑耦合度降低,提高代码的可复用性,同时也能提高开发效率。

关于 AOP 的核心技术实现主要是动态代理的使用,就像你可以给一个(IUserService )接口的实现类,使用代理的方式替换掉这个实现类,使用代理类来处理你需要的逻辑。比如

public interface IUserService {

    String queryUserInfo();

    String register(String userName);
}

用代理类处理自定义的逻辑

@Test
public void test_proxy_class() {
    IUserService userService = (IUserService) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), 
    new Class[]{IUserService.class}, 
    (proxy, method, args) -> "你被代理了!");
    
    String result = userService.queryUserInfo();
    System.out.println("测试结果:" + result);
}

本章的目标是给方法做代理,而不是代理类。所以需要思考如何给符合规则的方法做代理?如果能代理所有类的方法,则可以做一个方法拦截器,给所有被代理的方法添加自定义处理,如打印日志、监控异常等。


设计

1、如何给符合规则的方法做代理
2、做完代理方法的案例后,把类的职责拆分出来
而这两个功能点的实现,都是以切面的思想进行设计和开发

在这里插入图片描述
简单的说就是,只处理一些需要被拦截的方法。在拦截方法后,执行你对方法的扩展操作

那么我们就需要先来实现一个可以代理方法的 Proxy,其实代理方法主要是使用到方法拦截器类处理方法的调用 MethodInterceptor#invoke,而不是直接使用 invoke 方法中的入参 Method method 进行 method.invoke(targetObj, args) 这块是整个使用时的差异。

除了以上的核心功能实现,还需要使用到 org.aspectj.weaver.tools.PointcutParser 处理拦截表达式 “execution(* springframework.test.bean.IUserService.*(…))”,有了方法代理和处理拦截,我们就可以完成设计出一个 AOP 的雏形了。


项目结构

在这里插入图片描述

在这里插入图片描述

主要分为几块内容

1、AopProxy 是代理的抽象对象,它的实现主要是基于 JDK 的代理和 Cglib 代理

2、方法匹配、类匹配、切点表达式的实现(AspectJExpressionPointcut 的核心功能主要依赖于 aspectj 组件并处理 Pointcut、ClassFilter,、MethodMatcher 接口实现,专门用于处理类和方法的匹配过滤操作。)

3、切面的包装类、反射方法调用的包装类、被代理的目标对象等


一、代理方法的案例

在实现 AOP 的核心功能之前,我们先做一个代理方法的案例,通过这样一个可以概括代理方法的核心全貌,可以让更好的理解,后续拆解各个方法,设计成解耦功能的 AOP 实现过程。

测试需要的代码

public interface IUserService {

    String queryUserInfo();

    String register(String userName);
}
public class UserService implements IUserService {

    public String queryUserInfo() {
        try {
            Thread.sleep(new Random(1).nextInt(100));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "ljc,100001,上海";
    }

    public String register(String userName) {
        try {
            Thread.sleep(new Random(1).nextInt(100));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "注册用户:" + userName + " success!";
    }

}

单元测试

 /**
     * @desc: 代理方法的核心实现
     **/
    @Test
    public void test_proxy_method() {
        // 目标对象(可以替换成任何的目标对象)
        Object targetObj = new UserService();

        // AOP 代理
        IUserService proxy = (IUserService) java.lang.reflect.Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                targetObj.getClass().getInterfaces(),
                new InvocationHandler() {
            // 方法匹配器
            MethodMatcher methodMatcher = new AspectJExpressionPointcut("execution(* springframework.test.bean.IUserService.*(..))");

            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                if (methodMatcher.matches(method, targetObj.getClass())) {
                    // 方法拦截器
                    MethodInterceptor methodInterceptor = invocation -> {
                        long start = System.currentTimeMillis();
                        try {
                            return invocation.proceed();
                        } finally {
                            System.out.println("监控 - Begin By AOP");
                            System.out.println("方法名称:" + invocation.getMethod().getName());
                            System.out.println("方法耗时:" + (System.currentTimeMillis() - start) + "ms");
                            System.out.println("监控 - End\r\n");
                        }
                    };
                    // 反射调用
                    return methodInterceptor.invoke(new ReflectiveMethodInvocation(targetObj, method, args));
                }
                return method.invoke(targetObj, args);
            }
        });

        String result = proxy.queryUserInfo();
        System.out.println("测试结果:" + result);

    }

首先整个案例的目标是给一个 UserService 当成目标对象,对类中的所有方法进行拦截添加监控信息打印处理

从案例中你可以看到
代理的实现 Proxy.newProxyInstance

方法的匹配 MethodMatcher

方法拦截器 MethodInterceptor

反射的调用 invoke(Object proxy, Method method, Object[] args)

也有用户自己拦截方法后的操作

这样一看其实和我们使用的 AOP 就非常类似了,只不过你在使用 AOP 的时候是框架已经提供更好的功能,这里是把所有的核心过程给你展示出来了。

测试结果

监控 - Begin By AOP
方法名称:queryUserInfo
方法耗时:87ms
监控 - End

测试结果:ljc,100001,上海

Process finished with exit code 0

从测试结果可以看到我们已经对 UserService#queryUserInfo 方法进行了拦截监控操作,其实后面我们实现的 AOP 就是现在体现出的结果,只不过我们需要把这部分测试的案例解耦更具有扩展性的各个模块实现


二、代理方法案例拆解

在这里插入图片描述

上述代码执行顺序
1、创建被代理目标对象
2、创建代理对象,并实现InvocationHandler#invoke(用户自定义处理业务逻辑)
3、创建方法匹配器,如果方法名匹配,则进行发射调用,执行自定义的业务代码执行
4、方法不匹配,则执行被代理的方法调用

接下来需要做的事情,对上述代码进行解耦
1、我们需要把代理对象拆解出来,因为它可以是 JDK 的实现也可以是 Cglib 的处理。

2、方法匹配器操作其实已经是一个单独的实现类了,不过我们还需要把传入的目标对象、方法匹配、拦截方法,都进行统一的包装,方便外部调用时进行一个入参透传。

3、最后其实是 ReflectiveMethodInvocation(反射方法的调用) 的使用,它目前已经是实现 MethodInvocation 接口的一个包装后的类,参数信息包括:调用的对象、调用的方法、调用的入参


实现

1、切点表达式——Pointcut

/**
 * @desc 切点表达式接口定义
 */
public interface Pointcut {

    /**
     * 返回这个切入点的类过滤器。
     */
    ClassFilter getClassFilter();

    /**
     * 返回这个切入点的方法匹配器
     */
    MethodMatcher getMethodMatcher();
}

切入点接口,定义用于获取 ClassFilter、MethodMatcher 的两个类,这两个接口获取都是切点表达式提供的内容。


2、类匹配器——ClassFilter

/**
 * @desc 定义类匹配类
 */
public interface ClassFilter {
    /**
     * Should the pointcut apply to the given interface or target class?
     * @param clazz the candidate target class
     * @return whether the advice should apply to the given target class
     */
    boolean matches(Class<?> clazz);
}

定义类匹配类,用于切点找到给定的接口和目标类。


3、方法匹配器——MethodMatcher

/**
 * @desc 方法匹配器
 */
public interface MethodMatcher {
    /**
     * 方法匹配
     */
    boolean matches(Method method, Class<?> targetClass);
}

方法匹配,找到表达式范围内匹配下的目标类和方法。在上文的案例中有所体现:methodMatcher.matches(method, targetObj.getClass())


4、实现切点表达式类——AspectJExpressionPointcut

/**
 * @desc 实现切点表达式类
 */
public class AspectJExpressionPointcut implements Pointcut, ClassFilter, MethodMatcher {

    private static final Set<PointcutPrimitive> SUPPORTED_PRIMITIVES = new HashSet<PointcutPrimitive>();

    static {
        SUPPORTED_PRIMITIVES.add(PointcutPrimitive.EXECUTION);
    }

    private final PointcutExpression pointcutExpression;

    public AspectJExpressionPointcut(String expression) {
        PointcutParser pointcutParser = PointcutParser.getPointcutParserSupportingSpecifiedPrimitivesAndUsingSpecifiedClassLoaderForResolution(SUPPORTED_PRIMITIVES, this.getClass().getClassLoader());
        pointcutExpression = pointcutParser.parsePointcutExpression(expression);
    }


    @Override
    public boolean matches(Class<?> clazz) {
        return pointcutExpression.couldMatchJoinPointsInType(clazz);
    }

    @Override
    public boolean matches(Method method, Class<?> targetClass) {
        return pointcutExpression.matchesMethodExecution(method).alwaysMatches();
    }

    @Override
    public ClassFilter getClassFilter() {
        return this;
    }

    @Override
    public MethodMatcher getMethodMatcher() {
        return this;
    }
}

这个类主要实现了切点表达式实现了 Pointcut、ClassFilter、MethodMatcher,三个接口定义方法,同时这个类主要是对 aspectj 包提供的表达式校验方法使用。

匹配 matches:pointcutExpression.couldMatchJoinPointsInType(clazz) ——类匹配

pointcutExpression.matchesMethodExecution(method).alwaysMatches()——方法匹配

这部分内容可以单独测试验证。

4.1、匹配验证

@Test
    public void test_aop() throws NoSuchMethodException {
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut("execution(* springframework.test.bean.UserService.*(..))");
        Class<UserService> clazz = UserService.class;
        Method method = clazz.getDeclaredMethod("queryUserInfo");

        System.out.println(pointcut.matches(clazz));
        System.out.println(pointcut.matches(method, clazz));

        // 返回结果:true、true
    }

这里单独提供出来一个匹配方法的验证测试,可以看看你拦截的方法与对应的对象是否匹配。


5、包装切面信息——AdvisedSupport

/**
 * @desc 包装切面通知信息
 */
public class AdvisedSupport {

    // 被代理的目标对象
    private TargetSource targetSource;

    // 方法拦截器
    private MethodInterceptor methodInterceptor;

    // 方法匹配器(检查目标方法是否符合通知条件)
    private MethodMatcher methodMatcher;


 //  get/set方法 
}

AdvisedSupport,主要是用于把被代理的对象、拦截的方法、方法匹配器的各项属性包装到一个类中,方便在 Proxy 实现类进行使用。这和你的业务开发中包装入参是一个道理

TargetSource,是一个目标对象,在目标对象类中提供 Object 入参属性,以及获取目标类 TargetClass 信息。

MethodInterceptor,是一个具体拦截方法实现类,由用户自己实现 MethodInterceptor#invoke 方法,做具体的处理。像我们本文的案例中是做方法监控处理

MethodMatcher,是一个匹配方法的操作,这个对象由 AspectJExpressionPointcut 提供服务


5.1、被代理的目标对象——TargetSource

/**
 * @desc: 被代理的目标对象
 **/
public class TargetSource {

    private final Object target;

    public TargetSource(Object target) {
        this.target = target;
    }

    public Class<?>[] getTargetClass(){
        return this.target.getClass().getInterfaces();
    }

    public Object getTarget(){
        return this.target;
    }
    
}

被代理的对象的包装类,主要提供参数Object的target,以及获取被代理的接口获取被代理对象


6、反射方法调用的包装类——ReflectiveMethodInvocation

public class ReflectiveMethodInvocation implements MethodInvocation {

    // 目标对象
    protected final Object target;
    // 方法
    protected final Method method;
    // 入参
    protected final Object[] arguments;

    public ReflectiveMethodInvocation(Object target, Method method, Object[] arguments) {
        this.target = target;
        this.method = method;
        this.arguments = arguments;
    }

    @Override
    public Method getMethod() {
        return method;
    }

    @Override
    public Object[] getArguments() {
        return arguments;
    }

    @Override
    public Object proceed() throws Throwable {
        return method.invoke(target, arguments);
    }

    @Override
    public Object getThis() {
        return target;
    }

    @Override
    public AccessibleObject getStaticPart() {
        return method;
    }

}

提供了入参对象:目标对象、方法、入参
在实现jdk和cglib的时候,调用invoke方法时,会传入


7、 代理抽象——AopProxy

public interface AopProxy {

    Object getProxy();

}

定义一个标准接口,用于获取代理类。因为具体实现代理的方式可以有 JDK 方式,也可以是 Cglib 方式,所以定义接口会更加方便管理实现类。


7.1、JDK代理实现——JdkDynamicAopProxy

public class JdkDynamicAopProxy implements AopProxy, InvocationHandler {
	
    private final AdvisedSupport advised;

    public JdkDynamicAopProxy(AdvisedSupport advised) {
        this.advised = advised;
    }

    @Override
    public Object getProxy() {
        return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),advised.getTargetSource().getTargetClass(),this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    	// 方法名匹配判断,相同则,获取到方法拦截器,进行反射调用
        if(advised.getMethodMatcher().matches(method,advised.getTargetSource().getTarget().getClass())){
            MethodInterceptor methodInterceptor = advised.getMethodInterceptor();
            // 反射调用
            return methodInterceptor.invoke(new ReflectiveMethodInvocation(advised.getTargetSource().getTarget(),method,args));
        }
        // 正常调用被代理方法
        return method.invoke(advised.getTargetSource().getTarget(),args);
    }
}

基于 JDK 实现的代理类,需要实现接口 AopProxy、InvocationHandler,这样就可以把代理对象 getProxy反射调用方法 invoke 分开处理了。

getProxy 方法中的是代理一个对象的操作,需要提供入参 ClassLoaderAdvisedSupport、和当前这个类 this,因为这个类提供了 invoke 方法。

invoke 方法中主要处理匹配的方法后,使用用户自己提供的方法拦截实现,做反射调用 methodInterceptor.invoke

这里还有一个 ReflectiveMethodInvocation,它就是一个入参的包装信息,提供了入参对象:目标对象、方法、入参


7.2、Cglib代理实现——Cglib2AopProxy

public class Cglib2AopProxy implements AopProxy{

    private final AdvisedSupport advised;

    public Cglib2AopProxy(AdvisedSupport advised) {
        this.advised = advised;
    }

	
	// 创建代理对象
    @Override
    public Object getProxy() {
        Enhancer enhancer = new Enhancer();
        // 被代理的类
        enhancer.setSuperclass(advised.getTargetSource().getTarget().getClass());
        // 被代理的类实现的接口类
        enhancer.setInterfaces(advised.getTargetSource().getTargetClass());
        // 扩展需要拦截的处理
        enhancer.setCallback(new DynamicAdvisedInterceptor(advised));
        return enhancer.create();
    }


	// 动态拦截器
    private static class DynamicAdvisedInterceptor implements MethodInterceptor{

        private final AdvisedSupport advised;

        public DynamicAdvisedInterceptor(AdvisedSupport advised) {
            this.advised = advised;
        }

		// 动态拦截实现
        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            CglibMethodInvocation cglibMethodInvocation = new CglibMethodInvocation(advised.getTargetSource().getTarget(),method,objects,methodProxy);
            // 方法匹配则进行拦截处理
            if(advised.getMethodMatcher().matches(method,advised.getTargetSource().getTarget().getClass())){
                return advised.getMethodInterceptor().invoke(cglibMethodInvocation);
            }
            return cglibMethodInvocation.proceed();
        }
    }

	// cglib方法调用
    private static class CglibMethodInvocation extends ReflectiveMethodInvocation {

        private final MethodProxy methodProxy;

        public CglibMethodInvocation(Object target, Method method, Object[] arguments, MethodProxy methodProxy) {
            super(target, method, arguments);
            this.methodProxy = methodProxy;
        }

        @Override
        public Object proceed() throws Throwable {
            return this.methodProxy.invoke(this.target,this.arguments);
        }
    }
}

getProxy的实现:基于 Cglib 使用 Enhancer 代理的类可以在运行期间为接口使用底层 ASM 字节码增强技术,处理对象的代理对象生成,因此被代理类不需要实现任何接口。

关于扩展进去的用户拦截方法,主要是在 Enhancer#setCallback 中处理,用户自己的新增的拦截处理。这里可以看到 DynamicAdvisedInterceptor#intercept 匹配方法后做了相应的反射操作


测试

1、事先准备

public interface IUserService {

    String queryUserInfo();

    String register(String userName);
}
public class UserService implements IUserService {

    public String queryUserInfo() {
        try {
            Thread.sleep(new Random(1).nextInt(100));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "ljc,100001,上海";
    }

    public String register(String userName) {
        try {
            Thread.sleep(new Random(1).nextInt(100));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "注册用户:" + userName + " success!";
    }
}

在 UserService 中提供了2个不同方法,后面我们的测试过程,会给这个两个方法添加我们的拦截处理,打印方法执行耗时。


2、自定义拦截方法

public class UserServiceInterceptor implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        long start = System.currentTimeMillis();
        try {
            return invocation.proceed();
        } finally {
            System.out.println("监控 - Begin By AOP");
            System.out.println("方法名称:" + invocation.getMethod());
            System.out.println("方法耗时:" + (System.currentTimeMillis() - start) + "ms");
            System.out.println("监控 - End\r\n");
        }
    }
}

用户自定义的拦截方法需要实现 MethodInterceptor 接口的 invoke 方法,使用方式与 Spring AOP 非常相似,也是包装 invocation.proceed() 放行,并在 finally 中添加监控信息。


3、单元测试

@Test
    public void test_dynamic() {
        // 目标对象
        IUserService userService = new UserService();

        // 组装代理信息
        AdvisedSupport advisedSupport = new AdvisedSupport();
        advisedSupport.setTargetSource(new TargetSource(userService));
        advisedSupport.setMethodInterceptor(new UserServiceInterceptor());
        advisedSupport.setMethodMatcher(new AspectJExpressionPointcut("execution(* springframework.test.bean.IUserService.*(..))"));

        // 代理对象(JdkDynamicAopProxy)
        IUserService proxyJdk = (IUserService) new JdkDynamicAopProxy(advisedSupport).getProxy();
        // jdk测试调用
        System.out.println("测试结果:" + proxyJdk.queryUserInfo());

        System.out.println();

        // 代理对象(Cglib2AopProxy)
        IUserService proxyCglib = (IUserService) new Cglib2AopProxy(advisedSupport).getProxy();
        System.out.println("测试结果:" + proxyCglib.register("yaya"));
    }

整个案例测试了 AOP 在于 Spring 结合前的核心代码,包括什么是目标对象、怎么组装代理信息、如何调用代理对象

AdvisedSupport,包装了目标对象、用户自己实现的拦截方法以及方法匹配表达式。

之后就是分别调用 JdkDynamicAopProxyCglib2AopProxy,两个不同方式实现的代理类,看看是否可以成功拦截方法

测试结果

监控 - Begin By AOP
方法名称:public abstract java.lang.String springframework.test.bean.IUserService.queryUserInfo()
方法耗时:87ms
监控 - End

测试结果:ljc,100001,上海

监控 - Begin By AOP
方法名称:public java.lang.String springframework.test.bean.UserService.register(java.lang.String)
方法耗时:95ms
监控 - End

测试结果:注册用户:yaya success!

如 AOP 功能定义一样,我们可以通过这样的代理方式、方法匹配和拦截后,在对应的目标方法下,做了拦截操作进行监控信息打印。


总结

到这里AOP的切面实现已经完成了,只不过还没有和spring结合。

也可以看到JDK的代理实现,需要实现AopProxy, InvocationHandler,实现AopProxy#getProxyInvocationHandler#invoke,jdk的代理生成主要通过Proxy.newProxyInstance

而cglib的代理实现,只需要实现AopProxy#getProxygetProxy通过EnhancerASM字节码增强技术动态)生成对象,
用户的拦截器扩展,则是通过setCallback,设置需要执行的自定义方法拦截器

内部类DynamicAdvisedInterceptor(动态拦截器),通过实现MethodInterceptor#intercept(方法拦截器),来进行判断进行匹配

内部类CglibMethodInvocation(cglib方法执行),继承ReflectiveMethodInvocation(反射方法调用包装类),调用执行

切面实战逻辑代码其实并不多,主要是进行了解耦,进行了各种类的拆分

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

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

相关文章

Zynq PS之MIO、EMIO调试

目录 原理框图 Vivado中添加&配置Zynq UltraScale MPSoc IP UART设置&#xff08;仅用于调试&#xff0c;非必需&#xff09; MIO、EMIO设置 DDR配置 执行Generate Output Products 执行Create HDL Wrapper 执行File -> Export ->Export Hardware 执行Launch S…

Springboot中配置文件application.yaml的位置

文章目录位置一&#xff1a;整个项目的config包下位置二&#xff1a;整个项目的根目录下位置三&#xff1a;resources文件夹下config包中位置四&#xff1a;resources文件夹下四个位置的优先级位置一&#xff1a;整个项目的config包下 前些天发现了一个巨牛的人工智能学习网站&…

SQL优化实战-0002:select查询不建议使用星号(select *),最好指定具体查询字段

文章目录1.查询时的普遍写法2.问题分析2.1 计算负担2.2 IO负担2.3 覆盖索引失效2.4 缓存压力3.总结1.查询时的普遍写法 select * from the_table_name where ...2.问题分析 2.1 计算负担 数据库需要去解析更多的对象字段、权限、属性&#xff0c;查询数据字典将"*"…

dp刷题(二)分割回文串(详细推导+O(N^3)=>O(N^2)优化)

目录 分割回文串-ii_牛客题霸_牛客网 ​编辑 描述 示例1 思路 状态F(i)&#xff1a;即为第i个字符时所需要切割的最小次数 状态转移方程&#xff1a;F(i) min(F(i), F(j)1) 优化&#xff1a; 注意点 分割回文串-ii_牛客题霸_牛客网 描述 给出一个字符串s&#xff0…

Android---Bottom Sheet

目录​​​​​​​ Bottom Sheet BottomSheetBehavior BottomSheetDialog 完整 Demo Bottom Sheet Bottom Sheet 是 Design Support Library 23.2 版本引入的一个类似于对话框的控件&#xff0c;可以暂且叫做底部弹出框。Bottom Sheet 中的内容默认是隐藏起来的&#xff0…

Python爬虫登录后token处理

今天继续给大家介绍Python爬虫相关知识&#xff0c;本文主要内容是Python爬虫登录后token处理。 一、网页token及token作用 在上文Python爬虫登录后cookie处理中&#xff0c;我们介绍过使用使用Python爬虫解决cookie及网页登录访问问题。 然而&#xff0c;有的网站&#xff0…

一文读懂Linux内核进程及调度时机原理

前言0.1 进程概要进程是对物理世界的建模抽象&#xff0c;每个进程对应一个 task_struct 数据结构&#xff0c;这个数据结构包含了进程的所有的信息。在 Linux 内核中&#xff0c;不会区分线程和进程的概念&#xff0c;线程也是通过进程来实现的&#xff0c;线程和进程的唯一区…

Leetcode:257. 二叉树的所有路径(C++)

目录 问题描述&#xff1a; 实现代码与解析&#xff1a; 递归&#xff1a; 原理思路&#xff1a; 迭代&#xff08;前序&#xff09;&#xff1a; 思路原理&#xff1a; 问题描述&#xff1a; 给你一个二叉树的根节点 root &#xff0c;按 任意顺序 &#xff0c;返回所有…

计算机网络第一章

目录 1.概念 2.标准化工作及其相关组织 3.速率相关的性能指标 4.分层的基本原则&#xff1a; 5.参考模型 1.OSI七层参考模型 2.TCP/IP参考模型 3.五层参考模型 1.概念 计算机网络是网络中的一个分支&#xff0c;组成包括了计算机系统&#xff0c;通信设备&#xff0c;线路…

app逆向 || xx合伙人登陆参数

声明 本文仅供学习参考&#xff0c;如有侵权可私信本人删除&#xff0c;请勿用于其他途径&#xff0c;违者后果自负&#xff01; 如果觉得文章对你有所帮助&#xff0c;可以给博主点击关注和收藏哦&#xff01; 本文适用于对安卓开发和Java有了解的同学 前言 本人最近一直在…

学习笔记5:关于操作符与表达式的求值

目录​​​​​​​ 一.移位操作符 1.左移操作符 2.右移操作符 二.位操作符 1.位运算基本知识 2.位运算的巧妙运用 三.其他操作符 1.算术操作符 2.单目操作符 3.关于逻辑操作符 四.表达式求值 隐式类型转换 (1)整形提升(短整型家族数据的二进制序列补位转换) (2).算…

【最新】SpringBoot集成Dubbo3

最近在学习dubbo&#xff0c;构建一个简单的springboot集成dubbo&#xff0c;中间也是出了好多问题&#xff0c;在这记录下整体的过程。 1. 构建SpringBoot环境 一个简单的聚合工程 dubbo-consumer&#xff1a;是服务消费方dubbo-provider&#xff1a;是服务提供方dubbo-inte…

机器学习笔记之前馈神经网络(二)非线性问题

机器学习笔记之前馈神经网络——非线性问题引言回顾&#xff1a;关于非线性问题解决非线性问题的三种方式引言 上一节介绍了从机器学习到深度学习的过渡&#xff0c;并介绍了深度学习的发展过程。本节将主要介绍如何使用神经网络处理非线性问题 回顾&#xff1a;关于非线性问…

决策树生成、决策树可视化、决策树算法api、泰坦尼克号乘客生存预测案例代码

一、决策树算法api class sklearn.tree.DecisionTreeClassifier(criterion’gini’,max_depthNone,random_stateNone) criterion&#xff1a;特征选择标准&#xff0c;"gini"或者"entropy"&#xff0c;前者代表基尼系数&#xff0c;后者代表信息增益&…

来自 GitHub 2022 的趋势和见解

《Github 2022 发展趋势和见解》发布了这件事小伙伴们知道了吧&#xff1f;这是每个程序员不能错过的年度报告&#xff0c;因为里面详细介绍了语言的发展趋势和热门领域的介绍。那就让我们来看看吧 目录 编程语言 地理分布 贡献时间分配 技术发展趋势 最受欢迎的存储库 …

GoogLeNet详解

入门小菜鸟&#xff0c;希望像做笔记记录自己学的东西&#xff0c;也希望能帮助到同样入门的人&#xff0c;更希望大佬们帮忙纠错啦~侵权立删。 ✨完整代码在我的github上&#xff0c;有需要的朋友可以康康✨ https://github.com/tt-s-t/Deep-Learning.git 目录 一、GoogLeNet…

C++入门——auto、范围for、nullptr

下一篇就要类和对象了&#xff0c;剩了点零碎的知识点就浅浅水一篇把 一. auto关键字 在早期C/C中auto的含义是&#xff1a;使用auto修饰的变量&#xff0c;是具有自动存储器的局部变量&#xff0c;但遗憾的 是一直没有人去使用它&#xff0c;这是由于变量本身就具备生命周期…

算法及时间、空间复杂度

算法 算法是对问题求解过程的一种描述&#xff0c;是为解决一个或一类问题给出的一个确定的、有限长的操作序列。严格说来&#xff0c;一个算法必须满足以下5个重要特性&#xff1a; &#xff08;1&#xff09;有穷性&#xff1a;对于任意一组合法的输入值&#xff0c;在执行有…

【数据结构与算法——C语言版】5. 排序算法(2)——冒泡排序

前言 上篇文章【数据结构与算法——C语言版】4. 排序算法&#xff08;1&#xff09;——选择排序我们介绍了排序算法中的选择排序&#xff0c;其时间复杂度是O(n2)&#xff0c;本篇文章我们将介绍另一种同样时间复杂度是O(n2)的排序算法——冒牌排序&#xff0c;这两种算法思路…

ChatGPT背后的开源AI框架Ray,现在值10亿美元

Ray 被 OpenAI、亚马逊等科技公司用来开发大模型&#xff0c;是最近异军突起的框架。 最近一段时间&#xff0c;文本生成的人工智能在互联网上掀起了一阵风暴&#xff1a;ChatGPT 因为可以对人们能想到的几乎任何问题提供非常详细、近乎逼真的回答而受到追捧。大模型应用的出现…