java的双亲委派模型-附源码分析

news2025/2/26 23:17:02

1、类加载器

1.1 类加载的概念

  要了解双亲委派模型,首先我们需要知道java的类加载器。所谓类加载器就是通过一个类的全限定名来获取描述此类的二进制字节流,然后把这个字节流加载到虚拟机中,获取响应的java.lang.Class类的一个实例。我们把实现这个动作的代码模块称为“类加载器”。

1.2 类与类加载器

  对于任意的一个类,都需要由加载它的类加载器和这个类本身一同建立其在Java虚拟机中的唯一性,每个类加载器,都拥有一个独立的类名称空间,即:比较两个类是否“相等”,只有在这两个类是由同一个类加载器加载的前提下才有意义,否则,即使这两个类源于同一个Class文件,被同一个虚拟机加载,只要加载它们的类加载器不同,那这两个类就必定不想等。

package com.demo.test;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

/**
 * @author lxc
 * @createTime 2023-02-09 10:20
 * @description
 */
public class ClassLoaderTest {

    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        ClassLoader myloader = new ClassLoader() {
            @Override
            public Class<?> loadClass(String name) throws ClassNotFoundException {
                String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class";
                InputStream is = getClass().getResourceAsStream(fileName);
                if (is == null) {
                    return super.loadClass(name);
                }
                try {
                    byte[] b = new byte[is.available()];
                    is.read(b);
                    return defineClass(name,b,0,b.length);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        };
        Object obj = myloader.loadClass("com.demo.test.ClassLoaderTest").newInstance();
        System.out.println(obj.getClass());
        System.out.println(obj instanceof ClassLoaderTest);
        Class<?> clazz = Class.forName("com.demo.test.ClassLoaderTest");
        Constructor<?> constructor = clazz.getConstructor();
        Object obj1 = constructor.newInstance();
        System.out.println(obj1 instanceof ClassLoaderTest);
    }
}

运行结果如下:

obj对象的Class:class com.demo.test.ClassLoaderTest
obj1对象的Class:class com.demo.test.ClassLoaderTest
false
true

从运行结果来看,第一句和第二句来看,两个对象都是由类class com.demo.test.ClassLoaderTest实例化出来的对象;
从第三句可以看出,这个对象与类class com.demo.test.ClassLoaderTest做所属类型检查时却返回了false,这是因为虚拟机中存在了两个ClassLoaderTest,一个是由我们自定义的类加载器加载的,
另一个是由系统应用程序类加载器加载的,虽然都来自同一个Class文件,但依然是两个独立的类,做对象所属类型检查时结果为false。

2、双亲委派模型

2.1 类加载器的分类

  从虚拟机的角度来看,存在两种不同的类加载器:一种是启动类加载器(Bootstrap ClassLoader),这个类加载器使用C++语言实现,是虚拟机自身的一部分;另一个就是所有的其他类加载器,这些类加载器都是由Java语言实现,独立于虚拟机外部,并且全都继承字抽象类java.lang.ClassLoader。
  从开发者角度来看,类加载器可以分为以下四类:启动类加载器(Bootstrap ClassLoader)、扩展类加载器(Extension ClassLoader)、应用程序类加载(Application ClassLoader)和自定义类加载器。

  • 启动类加载器(Bootstrap ClassLoader):这个类加载器是加载核心java库,负责将<JAVA_HOME>/jre/lib目录中的,或者被-Xbootclasspath参数所指定的路径中的,并且是虚拟机识别的(仅仅按照文件名识别,如rt.jar,名字不符合的类库即使放在lib目录下也不会被加载)类库加载到虚拟机内存中。开发者不能直接使用启动类加载器。
  • 扩展类加载器(Extension ClassLoader):这个类加载器是由sun.misc.Launcher$ExtClassLoader实现,它负责加载<JAVA_HOME>/jre/lib/ext目录中的,或者被java.ext.dirs系统变量所指定的路径中的所有类库,开发者可以直接使用该类加载器。
  • 应用程序类加载器(Application ClassLoader):这个类加载器是由sun.misc.Launcher$AppClassLoader实现。这个类加载器是ClassLoader中的getSystemClassLoader()方法的返回值,所以一般也称之为系统类加载器。它负责加载用户类路径(ClassPath)上所指定的类库,开发者可以直接使用这个类加载器,如果应用程序中没有定义过自己的类加载器,一般情况下这就是程序中默认的类加载器。
      通过上面的分类我们可以看到,这三种类加载器只能加载各自所负责的目录下的类库,而不能加载超过其目录范围的类库,这也就是我们常常说的双亲委派模型中的可见性原则。
      我们平时所写的应用程序都是由这三种类加载器相互配合进行加载的,如果有必要,还可以加上自己定的类加载器。

2.2 双亲委派模型中各类加载器之间的层次关系

在这里插入图片描述
  类加载器之间这种层次关系,我们称之为类加载的双亲委派模型。双亲委派模型中,除了顶层的启动类加载器外,其余的类加载器都应当有自己的父-类加载。这里的父子关系不是以继承关系来实现的,而都是使用组合的关系来复用父-类加载的代码。

2.3 双亲委派模型中类加载的工作过程

  如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父-类加载去完成,每一个层次的类加载器(启动类加载器除外)都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父-类加载器反馈自己无法完成这个加载请求时,子-类加载器才会尝试自己去加载。
下面我们看段源码,从代码角度看一下这个工作过程:

protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            //首先检查请求的类是否被加载过
            Class<?> c = findLoadedClass(name);
            if (c == null) {
            		//如果没有被加载过,就进行加载操作
                long t0 = System.nanoTime();
                try {
                		//加载时,如果存在父-类加载器,就用父-类加载器加载
                		//如果没有父-类加载器,就说明这个类加载器是启动类加载器,就找启动类加载器进行加载
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                    //如果父-类加载器抛出异常,说明父-类加载无法完成加载工作
                }

                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    //在父-类加载器无法完成加载的时候,再调用本身的findClass方法来进行类加载
                    long t1 = System.nanoTime();
                    c = findClass(name);

                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

  双亲委派模型对于保证java的稳定运行很重要,但从上面的源码来看,实现还是比较简单的,双亲委派模型的核心代码主要都在java.lang.ClassLoader的loadClass()方法中,大体逻辑如下:
  先检查是否已经被加载过,若没有加载则调用父-类加载的loadClass()方法,若父类加载为空则默认使用启动类加载器做父-类加载器加载。如果父-类加载器加载失败,抛出ClassNotFoundException异常后,再调用自己的findClass()方法进行加载。

2.4 双亲委派模型的目的

  那么我们思考一下,java为什么采用双亲委派模型呢?从上面双亲委派模型的工作过程,我们看出,java类随着它的类加载器一起具备了带有优先级的层次关系。例如类java.lang.Integer,它存放在核心包rt.jar中,那么无论哪一个类加载器要加载这个类,最终都
是要委派给处于最顶端的启动类加载器进行加载,从而是的Integer类在程序的各中类加载器环境中都是同一个类;相反,如果没有使用双亲委派模型,而是由各个类加载器自行去加载的话,当用户自己编写了一个名为java.lang.Integer类并放到ClassPath中,那么系统将会出现多个不同的Integer类,这样就会造成java体系中最基础的行为都无法保证(连最基本的类型都不唯一),程序将变得一片混乱。你可能会说,我自定义一个类加载去加载java.lang.Integer,直接重写loadClass方法,从而破坏掉双亲委派模型不就行了。
  我们写个简单的例子试下。

public class MyClassLoader extends ClassLoader {
    @Override
    public Class<?> loadClass(String name) throws ClassNotFoundException {
        String className = null;
        if (name.startsWith("java.lang")) {
            className = "/" + name.replace(".", "/") + ".class";
        } else {
            className = name.substring(name.lastIndexOf(".") + 1) + ".class";
        }
        InputStream is = getClass().getResourceAsStream(className);
        if (is == null) {
            return super.loadClass(name);
        }
        try {
            byte[] bytes = new byte[is.available()];
            is.read(bytes);
            return defineClass(name, bytes, 0, bytes.length);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        ClassLoader myLoader = new MyClassLoader();
        Object obj = myLoader.loadClass("java.lang.Integer").newInstance();
        System.out.println(obj);
    }
}

结果输出:

Exception in thread "main" java.lang.SecurityException: Prohibited package name: java.lang
	at java.lang.ClassLoader.preDefineClass(ClassLoader.java:662)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:761)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:642)
	at com.demo.test.MyClassLoader.loadClass(MyClassLoader.java:28)
	at com.demo.test.MyClassLoader.main(MyClassLoader.java:36)

  从源码分析来看,主要是defineClass方法调用的preDefineClass方法异常,在preDefineClass这个方法中我们看到:

	 if ((name != null) && name.startsWith("java.")) {
           throw new SecurityException
         		("Prohibited package name: " +
                 name.substring(0, name.lastIndexOf('.')));
     }

即如果是以java.开头的包下的类,都只能用启动类加载器来加载。

2.5 双亲委派模型的三个原则

  双亲委派模型有三个基本原则:委托性、可见性和唯一性原则。

  • 委托性原则:当子类加载器收到类加载请求时,会将加载请求向上委托给父类加载器;
  • 可见性原则:每种类加载器都有自己可加载类库的范围,超出这个范围是不可见的,即无法加载的;
  • 唯一性原则:这是双亲委派模型的核心,也是最重要的目的。

2.6 为什么要打破双亲委派模型

  我们这里主要说一下JDBC为什么要打破双亲委派模型,其他的方面我后续再分析。
  我们以mysql数据库驱动为例来说明。最早我们使用mysql数据库驱动的时候,一般是这样写代码:

    Class.forName("com.mysql.jdbc.Driver");
	Connection conn = DriverManager.getConnection("jdbc:mysql://host:port/dbname?useUnicode=true&characterEncoding=utf-8&useSSL=false", "username", "password");

  其中com.mysql.jdbc.Driver下的Driver.class的源码如下:

			 package com.mysql.jdbc;
			import java.sql.DriverManager;
			import java.sql.SQLException;
			public class Driver extends NonRegisteringDriver implements java.sql.Driver {
			    public Driver() throws SQLException {
			    }
			
			    static {
			        try {
			            DriverManager.registerDriver(new Driver());
			        } catch (SQLException var1) {
			            throw new RuntimeException("Can't register driver!");
			        }
			    }
			}

  从com.mysql.jdbc的Driver.java源码中看到,在Driver类中向DriverManager注册了对应的驱动实现类。
  而从JDBC4.0以后,开始支持使用SPI的方式来注册这个Driver,这样当我们使用不同jdbc驱动时,就不用手动修改Class.forName加载的驱动类,只需要加入相关的jar包就行了。所以上面的数据库连接代码可以简写成如下:

Connection conn = DriverManager.getConnection("jdbc:mysql://host:port/dbname?useUnicode=true&characterEncoding=utf-8&useSSL=false", "username", "password");

这就不需要Class.forName(“com.mysql.jdbc.Driver”)了。
了解SPI的同学都知道,在DriverManager中,这时候对应的驱动类大体是这么加载的:
  1.通过从META-INF/services/java.sql.Driver文件中获取具体的实现类”com.mysql.jdbc.Driver“;
  2.通过Class.forName(“com.mysql.jdbc.Driver”)将这个类加载进来。
  但是DriverManager是在java.sql中,在rt.jar包中,这个包中的类只能使用启动类加载器进行加载,那么根据类加载的机制,当被装载的类引用了另外一个类的时候,虚拟机就会使用装载第一个类的类装载器装载被引用的类。:启动类加载器还要去加载mysql驱动jar中的类(com.mysql.jdbc.Driver),这显然是不可能的,根据双亲委派模型的可见性原则,启动类加载器找不到这个mysql类库,所以无法加载。
  这个问题更加有适用性的说法应该是:JAVA核心包中的类去调用开发者实现的类的方法,这时候就会出现启动类加载器无法加载到具体实现类的问题。所以想让启动类加载器(顶层类加载器)加载可见范围之外的类库,只能破坏双亲委派模型中的可见性原则,让启动类加载器可以”加载“到可见范围之外的类库。主要这里我加了个引号,因为这个地方并不是真的是由启动类加载器加载了com.mysql.jdbc.Driver这个类库,其实还是由Application ClassLoader系统类加载器加载完成的,只不过从表面上看起来是破坏了可见行原则,实质上并没有破坏双亲委派原则。
  下面我们看DriverManager是怎么实现的。
  DriverManager加载时,会执行静态代码块,在静态代码块中,会执行loadInitialDrivers方法。而这个方法中会加载对应的驱动类。

public class DriverManager {

    static {
        loadInitialDrivers();
        println("JDBC DriverManager initialized");
    }

    private static void loadInitialDrivers() {
        String drivers;
        try {
            drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {
                public String run() {
                    return System.getProperty("jdbc.drivers");
                }
            });
        } catch (Exception ex) {
            drivers = null;
        }

        AccessController.doPrivileged(new PrivilegedAction<Void>() {
            public Void run() {
            		
								// 根据配置文件加载驱动实现类,下面这个方法中说明了所使用的类加载器
                ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
                Iterator<Driver> driversIterator = loadedDrivers.iterator();
                try{
                    while(driversIterator.hasNext()) {
                        driversIterator.next();
                    }
                } catch(Throwable t) {
                // Do nothing
                }
                return null;
            }
        });

        println("DriverManager.initialize: jdbc.drivers = " + drivers);

        if (drivers == null || drivers.equals("")) {
            return;
        }
        String[] driversList = drivers.split(":");
        println("number of Drivers:" + driversList.length);
        for (String aDriver : driversList) {
            try {
                println("DriverManager.Initialize: loading " + aDriver);
                Class.forName(aDriver, true,
                        ClassLoader.getSystemClassLoader());
            } catch (Exception ex) {
                println("DriverManager.Initialize: load failed: " + ex);
            }
        }
    }
}

    public static <S> ServiceLoader<S> load(Class<S> service) {
    		//使用了一个线程上下文类加载器
        ClassLoader cl = Thread.currentThread().getContextClassLoader();
        return ServiceLoader.load(service, cl);
    }
    

  ExtClassLoader和AppClassLoader都是通过Launcher类来创建的,在Launcher类的构造函数中我们可以看到线程上下文类加载器默认是AppClassLoader。Launcher类中无参构造方法:

public Launcher() {
	        ExtClassLoader var1;
	        try {
	            var1 = Launcher.ExtClassLoader.getExtClassLoader();
	        } catch (IOException var10) {
	            throw new InternalError("Could not create extension class loader", var10);
	        }
	
	        try {
	            this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
	        } catch (IOException var9) {
	            throw new InternalError("Could not create application class loader", var9);
	        }
	        //设置当前线程的上下文类加载器就是AppClassLoader
	        Thread.currentThread().setContextClassLoader(this.loader);
	        String var2 = System.getProperty("java.security.manager");
	        if (var2 != null) {
	            SecurityManager var3 = null;
	            if (!"".equals(var2) && !"default".equals(var2)) {
	                try {
	                    var3 = (SecurityManager)this.loader.loadClass(var2).newInstance();
	                } catch (IllegalAccessException var5) {
	                } catch (InstantiationException var6) {
	                } catch (ClassNotFoundException var7) {
	                } catch (ClassCastException var8) {
	                }
	            } else {
	                var3 = new SecurityManager();
	            }
	
	            if (var3 == null) {
	                throw new InternalError("Could not create SecurityManager: " + var2);
	            }
	
	            System.setSecurityManager(var3);
        }

2.7 如何打破双亲委派模型?

  在ClassLoader中有几个核心方法,上面我们已经展示了loadClass的基本源码,下面我们再简略看一下(去掉了一些代码细节):

    package java.lang;
	public abstract class ClassLoader {
    protected Class defineClass(byte[] b); 
    protected Class<?> findClass(String name); 
    protected Class<?> loadClass(String name, boolean resolve) {
        synchronized (getClassLoadingLock(name)) {
            // 1. 检查类是否已经被加载过
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                try {
                    if (parent != null) {
                        //2. 委托给父类加载
                        c = parent.loadClass(name, false);
                    } else {
                        //3. 父类不存在的,交给启动类加载器
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) { }
                if (c == null) {
                    //4. 父类加载器无法完成类加载请求时,调用自身的findClass方法来完成类加载
                    c = findClass(name);
                }
            }
            return c;
    }
}
  • defineClass 方法:调用 native 方法将 字节数组解析成一个 Class 对象;
  • findClass 方法:抽象类ClassLoader中默认抛出ClassNotFoundException,需要继承类自己去实现,目的是通过文件系统或者网络查找类;
  • loadClass 方法: 首先根据类的全限定名检查该类是否已经被加载过,如果没有被加载,那么当子加载器持有父加载器的引用时,那么委托给父加载器去尝试加载,如果父类加载器无法完成加载,再交给子类加载器进行加载。loadClass方法 就是实现了双亲委派机制。
      ClassLoader 的三个重要方法,那么如果需要自定义一个类加载器的话,直接继承 ClassLoader类,一般情况只需要重写 findClass 方法即可,自己定义加载类的路径,可以从文件系统或者网络环境。但是,如果想打破双亲委派机制,那么还要重写 loadClass 方法。

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

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

相关文章

边界层气象学期末复习笔记

边界层气象学期末复习笔记 什么是边界层 广义上的边界层是&#xff1a;气体流动于平板上方&#xff0c;平板表面的摩擦力和垂直速度切变产生的流体内摩擦力阻滞了固体边界处的气体流动&#xff0c;这样一个流速减少&#xff0c;并低于自由气流速度的区域称为边界层 在厚度较少…

uniapp ios证书申请和上架全流程

目前市场上流行着很多多端开发框架&#xff0c;就拿uniapp和react native来比较&#xff0c;uniapp比react native方便很多&#xff0c;react的编译还需要mac电脑&#xff0c;而uniapp则轻量得多&#xff0c;一台windows电脑就可以打包所有端的应用&#xff0c;包括ios版本。 …

nginx隐藏服务器信息以及修改服务器名称

网络安全日益受到关注。尽可能的隐藏信息,是公认的较为安全的做法。 nginx在默认情况下会输出服务软件名+版本号。 在nginx配置文件中添加如下: server_tokens off; 此项设置,可以屏蔽nginx输出版本号。 类似输出如下图: 由上图可见,仅输出了nginx名称。 如果想要修…

SAP S/4HANA 概述

智能企业业务技术平台Business Technology Platform提供数据管理和分析&#xff0c;并支持应用程序开发和集成。它还允许我们的客户使用人工智能、机器学习和物联网等智能技术来推动创新。业务网络Business network帮助客户实现跨公司业务流程的数字化。该网络建立在我们的采购…

Java基础面试题——JavaWeb专题

文章目录1.HTTP响应码有哪些2.Forward和Redirect的区别&#xff1f;3.Get和Post请求的区别4.介绍下OSI七层和TCP/IP四层的关系5.说说TCP和UDP的区别6. 说下HTTP和HTTPS的区别7.说下HTTP、TCP、Socket的关系是什么&#xff1f;8. 说下HTTP的长链接和短连接的区别9.TCP原理10. Co…

Kotlin 28. Kotlin 如何通过 TransitionDrawable 显示颜色渐变效果

Kotlin 如何通过 TransitionDrawable 显示颜色渐变效果 这里&#xff0c;我们通过 TransitionDrawable 显示颜色渐变效果&#xff0c;包括背景颜色的变化&#xff0c;以及图片与图片的渐变效果。 文章目录Kotlin 如何通过 TransitionDrawable 显示颜色渐变效果1 导入需要渐变的…

编译DPDK出现[-Werror=implicit-fallthrough=]错误

我使用ubuntu20.04版本&#xff0c;gcc-7编译dpdk-19.08版本 错误如下&#xff1a; 同样的问题&#xff0c;可以先看一下这篇文章 https://blog.csdn.net/weixin_44260459/article/details/123563091 可以看出[-Werrorimplicit-fallthrough]错误 是由于makefie 的CLFAGS中设…

【C++、数据结构】封装unordered_map和unordered_set(用哈希桶实现)

文章目录&#x1f4d6; 前言1. 复用同一个哈希桶⚡1.1 &#x1f300;修改后结点的定义1.2 &#x1f300;两个容器各自模板参数类型&#xff1a;2. 改造之后的哈希桶⛳3. 哈希桶的迭代器&#x1f525;3.1 &#x1f4a5;哈希桶的begin&#xff08;&#xff09;和 end&#xff08;…

Python自动化测试实战篇(5)优化selenium+unittest+ddt,搞定100条测试用例只执行前50条

这些是之前的文章&#xff0c;里面有一些基础的知识点在前面由于前面已经有写过&#xff0c;所以这一篇就不再详细对之前的内容进行描述 Python自动化测试实战篇&#xff08;1&#xff09;读取xlsx中账户密码&#xff0c;unittest框架实现通过requests接口post登录网站请求&…

【PyQt】树形控件QTreeWidget的复选框实现自动部分选择/半选择状态

为实现如下效果&#xff0c;搜索未得&#xff0c;自己总结。1 效果2 代码以下非完整代码&#xff0c;仅作演示用。2.1 引入包from PyQt5.QtCore import Qt from PyQt5.QtWidgets import QMainWindow, QApplication, QTreeWidgetItem from PyQt5.uic import loadUi import sys2.…

基于zookeeper的Hadoop集群搭建详细步骤

目录 一、一些基本概念 二、集群配置图 三、Hadoop高可用集群配置步骤 1.在第一台虚拟机解压hadoop-3.1.3.tar.gz到/opt/soft/目录 2.修改文件名、属主和属组 3.配置windows四台虚拟机的ip映射 4.修改hadoop配置文件 (1)hadoop-env.sh (2)workers (3)crore-site.xml …

微信小程序+gatewayworker+php+tp框架开发,websocke即时通讯

为了做小程序的即时通讯功能&#xff0c;查了一些资料和视频&#xff0c;记录一下。 gatewayworker在tp框架的安装 下载地址&#xff1a;https://www.workerman.net/doc/gateway-worker/ 由于我先是在本地电脑上做开发的&#xff0c;所以下载的windows的demo 解压之后&#xf…

IPv6的基础配置以及实战案例

IPv6基本配置配置IPv6静态路由[Huawei] ipv6 route-static dest-ipv6-address prefix-length { interface-type interface-number [ nexthop-ipv6-address ] | nexthop-ipv6-address } [ preference preference ]查看接口的IPv6信息[Huawei] display ipv6 interface [ interfac…

Keras实例教程(7)之构建模型的第三种方式

多年以前,在TensorFlow中搭建深度学习模型对于很多人来说其实仍然是比较困难的。相比之下,Keras作为独立于TensorFlow的一种深度学习框架则要简单很多。在TensorFlow与PyTorch的竞争中逐渐式微的情况下,TensorFlow团队终于宣布Keras将成为在tensorflow2.0中构建和训练模型的…

浅谈软件测试需求管理

什么是需求管理&#xff1f; 需求管理&#xff0c;指对产品、系统或工程的开发需求的搜集、定义、分析、评审、整理、维护、追溯和复用等相关的管理工作和流程。通常特指应用程序或软件系统的研发需求。需求管理和配置管理、测试管理、缺陷管理、风险管理、变更管理等管理流程…

Java真的不难(五十四)RabbitMQ的入门及使用

RabbitMQ的入门及使用 一、什么是RabbitMQ&#xff1f; MQ全称为Message Queue&#xff0c;即消息队列。消息队列是在消息的传输过程中保存消息的容器。它是典型的&#xff1a;生产者、消费者模型。生产者不断向消息队列中生产消息&#xff0c;消费者不断的从队列中获取消息。…

redis+token实现登录校验,前后端分离,及解跨域问题的4种方法

目录 一、使用自定义filter实现跨域 1、客户端向服务端发送请求 2、服务端做登录验证了&#xff0c;并生成登路用户对应的token&#xff0c;保存到redis 3、响应&#xff08;报错&#xff09;-----跨域问题 4、解决跨域问题--------服务器端添加过滤器&#xff0c;设置请求…

Mybatis流式游标查询-大数据DB查询OOM查询问题

问题场景Mysql数据处理类型分以下三种com.mysql.cj.protocol.a.result.ResultsetRowsStatic&#xff1a;普通查询&#xff0c;将结果集一次性全部拉取到内存com.mysql.cj.protocol.a.result.ResultsetRowsCursor&#xff1a;游标查询&#xff0c;将结果集分批拉取到内存&#x…

Pytorch入门教程

Pytorch入门教程 Pytorch简介 概念&#xff1a;由Facebook人工智能研究小组开发的一种基于Lua编写的Torch库的Python实现的深度学习库。优势&#xff1a;简洁、上手快、具有良好的文档和社区支持、项目开源、支持代码调试、丰富的扩展库 Pytorch基础知识 1.张量Tensor 分类…

【Ubuntu新手入门2】深度学习环境配置 Anaconda+Pycharm+PyTorch

【Ubuntu新手入门2】深度学习环境配置 AnacondaPycharmpytorch前言安装PyTorch查看cuda版本mobaxterm软件远程连接linux服务器安装安装anaconda安装pycharm安装新环境pytorch前言 本系统&#xff1a;Ubuntu18.04&#xff0c;anaconda最新&#xff0c;Pycharm最新&#xff0c;P…