Spring整合MyBatis底层原理

news2024/11/9 9:52:20

Spring整合MyBatis底层原理

项目结构图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kpbVmnfT-1687513246717)(C:\Users\13301\AppData\Roaming\Typora\typora-user-images\image-20230623170457676.png)]

项目代码

  1. build.gradle需要进入的依赖

    //    testImplementation(platform("org.junit:junit-bom:5.9.1"))
    //    testImplementation("org.junit.jupiter:junit-jupiter")
    	implementation("org.aspectj:aspectjweaver:1.9.7")
        annotationProcessor("org.aspectj:aspectjweaver:1.9.7")
        implementation("javax.annotation:javax.annotation-api:1.3.2")
        annotationProcessor("javax.annotation:javax.annotation-api:1.3.2")
        implementation("javax.inject:javax.inject:1")
        annotationProcessor("javax.inject:javax.inject:1")
        implementation(project(":spring-context"))
        implementation(project(":spring-jdbc"))
        implementation(project(":spring-tx"))
        implementation(project(":spring-webmvc"))  // mvc
        implementation(project(":spring-context-support")) // freemarker
    
        implementation("mysql:mysql-connector-java:5.1.40")
        implementation("org.mybatis:mybatis:3.5.7")
        implementation("org.mybatis:mybatis-spring:1.3.2")
    
  2. 自定义注解XiaogeScan

    package com.xiaoge.springmybatis.annotation;
    
    import com.xiaoge.springmybatis.springintegrationmybatis.XiaogeBeanDefinitionRegister;
    import org.springframework.context.annotation.Import;
    
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    
    /**
     * TODO
     *
     * @author <a href="mailto:1330137071@qq.com">Zhang Xiao</a>
     * @since
     */
    @Retention(RetentionPolicy.RUNTIME)
    @Import(XiaogeBeanDefinitionRegister.class)  // import注解是可以这样用的
    public @interface XiaogeScan {
    
    	String value() default "";
    
    }
    
  3. 定义三个mapper

    package com.xiaoge.springmybatis.mapper;
    
    import org.apache.ibatis.annotations.Select;
    
    /**
     * TODO
     *
     * @author <a href="mailto:1330137071@qq.com">Zhang Xiao</a>
     * @since
     */
    public interface UserMapper {
    
    	@Select("select user")
    	String selectById(Integer id);
    }
    
    package com.xiaoge.springmybatis.mapper;
    
    import org.apache.ibatis.annotations.Select;
    
    /**
     * TODO
     *
     * @author <a href="mailto:1330137071@qq.com">Zhang Xiao</a>
     * @since
     */
    public interface MemberMapper {
    
    	@Select("select member")
    	String selectById(Integer id);
    
    }
    
    package com.xiaoge.springmybatis.mapper;
    
    import org.apache.ibatis.annotations.Select;
    
    /**
     * TODO
     *
     * @author <a href="mailto:1330137071@qq.com">Zhang Xiao</a>
     * @since
     */
    public interface OrderMapper {
    
    	@Select("select order")
    	String selectById(Integer id);
    
    }
    
  4. 定义一个service

    package com.xiaoge.springmybatis.service;
    
    import com.xiaoge.springmybatis.mapper.MemberMapper;
    import com.xiaoge.springmybatis.mapper.OrderMapper;
    import com.xiaoge.springmybatis.mapper.UserMapper;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    /**
     * TODO
     *
     * @author <a href="mailto:1330137071@qq.com">Zhang Xiao</a>
     * @since
     */
    @Component
    public class UserService {
    
    	@Autowired
    	private UserMapper userMapper; // 代理对象赋值给这个属性, spring属性注入, 对象
    
    	@Autowired
    	private OrderMapper orderMapper;
    
    	@Autowired
    	private MemberMapper memberMapper;
    
    
    	public void test() {
    		System.out.println(userMapper);
    		System.out.println(userMapper.getClass().getSimpleName());
    		System.out.println(orderMapper);
    		System.out.println(orderMapper.getClass().getSimpleName());
    		System.out.println(memberMapper);
    		System.out.println(memberMapper.getClass().getSimpleName());
    		userMapper.selectById(1);
    		orderMapper.selectById(1);
    		memberMapper.selectById(1);
    	}
    
    }
    
  5. 重点整合mybatis

    package com.xiaoge.springmybatis.springintegrationmybatis;
    
    import org.springframework.beans.factory.FactoryBean;
    import org.springframework.stereotype.Component;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    /**
     * TODO  spring 整合 mybatis
     *
     * @author <a href="mailto:1330137071@qq.com">Zhang Xiao</a>
     * @since
     */
    public class SpringIntegrationMybatis implements FactoryBean {
    	// todo FactoryBean 本身就是一个特殊bean
    	private Class mapper; // 灵活设置属性
    
    	public SpringIntegrationMybatis(Class mapper) {
    		this.mapper = mapper;
    	}
    
    	@Override
    	public Object getObject() throws Exception {
    		// mybatis 代理对象
    		Object o = Proxy.newProxyInstance(SpringIntegrationMybatisTwo.class.getClassLoader(), new Class[]{mapper}, new InvocationHandler() {
    			@Override
    			public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    				if (Object.class.equals(method.getDeclaringClass())) {
    					System.out.println(method.getName());
    					return method.invoke(this, args);
    				}
    				System.out.println(method.getName());
    				return null;
    			}
    		});
    		return o; // bean
    	}
    
    	@Override
    	public Class<?> getObjectType() {
    		return mapper;
    	}
    }
    
  6. 重点怎么把扫描到的mapper生成BeanDefinition的

    package com.xiaoge.springmybatis.springintegrationmybatis;
    
    import com.xiaoge.springmybatis.annotation.XiaogeScan;
    import org.springframework.beans.factory.support.AbstractBeanDefinition;
    import org.springframework.beans.factory.support.BeanDefinitionBuilder;
    import org.springframework.beans.factory.support.BeanDefinitionRegistry;
    import org.springframework.beans.factory.support.BeanNameGenerator;
    import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
    import org.springframework.core.type.AnnotationMetadata;
    
    import java.beans.Introspector;
    import java.io.File;
    import java.net.URL;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Map;
    
    /**
     * TODO 该类通过 import 注解导入, 它会去执行 该类下面的方法, 原因可以去看@Import注解源码
     *
     * @author <a href="mailto:1330137071@qq.com">Zhang Xiao</a>
     * @since
     */
    public class XiaogeBeanDefinitionRegister implements ImportBeanDefinitionRegistrar {
    
    	@Override
    	public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {
    		// 获取我们扫描注解上面的路径
    		Map<String, Object> annotationAttributes = importingClassMetadata.getAnnotationAttributes(XiaogeScan.class.getName());
    		String path = (String) annotationAttributes.get("value");
    
    		// 扫描
    		List<Class> mappers = new ArrayList<>();
    		doScan(path, mappers);
    
    		mappers.forEach(mapper -> {
    			BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition();
    			AbstractBeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
    
    			// 给构造方法添加参数
    			beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(mapper);  // new SpringIntegrationMybatisTwo(mapper)
    			// 设置类型
    			beanDefinition.setBeanClass(SpringIntegrationMybatis.class);
    
    			// 注册BeanDefinition  (beanName是根据spring源码用到的方法生成的)
    			registry.registerBeanDefinition(Introspector.decapitalize(mapper.getSimpleName()), beanDefinition);
    
    		});
    	}
    
    	/**
    	 * 扫描当前包下所有带@Mapper注解的接口
    	 */
    	private void doScan(String path, List<Class> mappers) {
    
    		// 获取路径
    		path = path.replace(".", "/");
    
    		// 获取资源路径
    		ClassLoader classLoader = XiaogeBeanDefinitionRegister.class.getClassLoader();
    		URL resource = classLoader.getResource(path);
    
    		// 拿到当前目录下面所有的文件
    		File files = new File(resource.getFile());
    
    		// 当前文件是不是目录
    		if (files.isDirectory()) {
    			// 获取目录下所有文件
    			for (File file : files.listFiles()) {
    				// 获取文件绝对路径
    				String fileName = file.getAbsolutePath();
    				// 获取com->.class结尾的名称
    				String className = fileName.substring(fileName.indexOf("com"), fileName.indexOf(".class"));
    				// 获取权限定类名
    				className = className.replace("\\", ".");
    
    				try {
    					// 加载class
    					Class<?> clazz = classLoader.loadClass(className);
    					mappers.add(clazz);
    				} catch (ClassNotFoundException e) {
    
    				}
    			}
    		}
    	}
    
    }
    
  7. 配置类

    package com.xiaoge.springmybatis;
    
    import com.xiaoge.springmybatis.annotation.XiaogeScan;
    import org.mybatis.spring.annotation.MapperScan;
    import org.springframework.context.annotation.ComponentScan;
    
    /**
     * TODO
     *
     * @author <a href="mailto:1330137071@qq.com">Zhang Xiao</a>
     * @since
     */
    @ComponentScan
    @XiaogeScan("com.xiaoge.springmybatis.mapper")
    public class AppConfig {
    }
    
  8. 启动类

    package com.xiaoge.springmybatis;
    
    import com.xiaoge.springmybatis.mapper.UserMapper;
    import com.xiaoge.springmybatis.service.UserService;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    
    /**
     * TODO
     *
     * @author <a href="mailto:1330137071@qq.com">Zhang Xiao</a>
     * @since
     */
    public class SpringIntegrationMybatisMain {
    	public static void main(String[] args) {
    
    		// 创建 ApplicationContext 容器
    		AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
    		UserService userService = applicationContext.getBean(UserService.class);
    		userService.test();
    
    		System.out.println(applicationContext.getBean("userMapper"));
    
    	}
    }
    
    
    // 执行结果
    toString
    com.xiaoge.springmybatis.springintegrationmybatis.SpringIntegrationMybatisTwo$1@2aa5fe93
    $Proxy11
    toString
    com.xiaoge.springmybatis.springintegrationmybatis.SpringIntegrationMybatisTwo$1@5c1a8622
    $Proxy12
    toString
    com.xiaoge.springmybatis.springintegrationmybatis.SpringIntegrationMybatisTwo$1@5ad851c9
    $Proxy13
    selectById
    selectById
    selectById
    toString
    com.xiaoge.springmybatis.springintegrationmybatis.SpringIntegrationMybatisTwo$1@2aa5fe93
    

mybatis-spring源码它是怎么整合的

  1. 先通过MapperScan找到对应的MapperScannerRegistrar

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cUDGGZdm-1687513246718)(C:\Users\13301\AppData\Roaming\Typora\typora-user-images\image-20230623171512324.png)]

  2. 主要看这个类的这个方法registerBeanDefinitions

    @Override
      public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    
          // 获取MapperScan注解相关信息, 就是MapperScan里面我们会配置路径什么啊等
        AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
         // 获取spring扫描器
         ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
    
        // this check is needed in Spring 3.1
        if (resourceLoader != null) {
          scanner.setResourceLoader(resourceLoader);
        }
    
        // 解析MapperScan annotationClass 属性
        Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
        if (!Annotation.class.equals(annotationClass)) {
          scanner.setAnnotationClass(annotationClass);
        }
    
        // 解析MapperScan markerInterface 属性
        Class<?> markerInterface = annoAttrs.getClass("markerInterface");
        if (!Class.class.equals(markerInterface)) {
          scanner.setMarkerInterface(markerInterface);
        }
    
        // 解析MapperScan nameGenerator 属性
        Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");
        if (!BeanNameGenerator.class.equals(generatorClass)) {
          scanner.setBeanNameGenerator(BeanUtils.instantiateClass(generatorClass));
        }
    
        // 解析MapperScan factoryBean 属性
        Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
        if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
          scanner.setMapperFactoryBean(BeanUtils.instantiateClass(mapperFactoryBeanClass));
        }
    
        // 解析MapperScan sqlSessionTemplateRef 属性
        scanner.setSqlSessionTemplateBeanName(annoAttrs.getString("sqlSessionTemplateRef"));
        // 解析MapperScan sqlSessionFactoryRef 属性
        scanner.setSqlSessionFactoryBeanName(annoAttrs.getString("sqlSessionFactoryRef"));
    
        // 添加扫描包路径
        List<String> basePackages = new ArrayList<String>();
        for (String pkg : annoAttrs.getStringArray("value")) {
          if (StringUtils.hasText(pkg)) {
            basePackages.add(pkg);
          }
        }
        
        // 添加扫描包路径
        for (String pkg : annoAttrs.getStringArray("basePackages")) {
          if (StringUtils.hasText(pkg)) {
            basePackages.add(pkg);
          }
        }
        // 添加class权限定类名
        for (Class<?> clazz : annoAttrs.getClassArray("basePackageClasses")) {
          basePackages.add(ClassUtils.getPackageName(clazz));
        }
        // 添加一些过滤器, 因为spring扫描到了也不一定注册到BeanDefinitionMap中它还需要通过过滤器条件
        scanner.registerFilters();
        scanner.doScan(StringUtils.toStringArray(basePackages));
      }
    
    @Override
      public Set<BeanDefinitionHolder> doScan(String... basePackages) {
        // 扫描该包, 拿到是所有的BeanDefinition
        Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
    
        if (beanDefinitions.isEmpty()) {
          logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
        } else {
          processBeanDefinitions(beanDefinitions);
        }
    
        return beanDefinitions;
      }
    
    private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
        GenericBeanDefinition definition;
        // 循环beanDefinitions
        for (BeanDefinitionHolder holder : beanDefinitions) {
          // 拿到对应的beanDefinitions
          definition = (GenericBeanDefinition) holder.getBeanDefinition();
    
          if (logger.isDebugEnabled()) {
            logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName() 
              + "' and '" + definition.getBeanClassName() + "' mapperInterface");
          }
    
          // 给beanDefinition设置相关属性
          definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName());
          
          // 设置bean类型
          definition.setBeanClass(this.mapperFactoryBean.getClass());
    
          definition.getPropertyValues().add("addToConfig", this.addToConfig);
    
          boolean explicitFactoryUsed = false;
          if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
            definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
            explicitFactoryUsed = true;
          } else if (this.sqlSessionFactory != null) {
            definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
            explicitFactoryUsed = true;
          }
    
          if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
            if (explicitFactoryUsed) {
              logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
            }
            definition.getPropertyValues().add("sqlSessionTemplate", new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
            explicitFactoryUsed = true;
          } else if (this.sqlSessionTemplate != null) {
            if (explicitFactoryUsed) {
              logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
            }
            definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
            explicitFactoryUsed = true;
          }
    
          if (!explicitFactoryUsed) {
            if (logger.isDebugEnabled()) {
              logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
            }
            // 设置它的依赖注入类型
            definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
          }
        }
      }
    
    package org.mybatis.spring.mapper;
    
    import static org.springframework.util.Assert.notNull;
    
    import org.apache.ibatis.executor.ErrorContext;
    import org.apache.ibatis.session.Configuration;
    import org.mybatis.spring.SqlSessionTemplate;
    import org.mybatis.spring.support.SqlSessionDaoSupport;
    import org.springframework.beans.factory.FactoryBean;
    
    /**
     * 可以看到它跟我们写的那个差不多, 都是实现了FactoryBean  
     * mapper都是class类型 private Class<T> mapperInterface;
     * 看getObject方法getMapper它是怎么代理mapper的
     */
    public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
    
      private Class<T> mapperInterface;
    
      private boolean addToConfig = true;
    
      public MapperFactoryBean() {
        //intentionally empty 
      }
      
      public MapperFactoryBean(Class<T> mapperInterface) {
        this.mapperInterface = mapperInterface;
      }
    
      /**
       * {@inheritDoc}
       */
      @Override
      protected void checkDaoConfig() {
        super.checkDaoConfig();
    
        notNull(this.mapperInterface, "Property 'mapperInterface' is required");
    
        Configuration configuration = getSqlSession().getConfiguration();
        if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
          try {
            configuration.addMapper(this.mapperInterface);
          } catch (Exception e) {
            logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", e);
            throw new IllegalArgumentException(e);
          } finally {
            ErrorContext.instance().reset();
          }
        }
      }
    
      /**
       * {@inheritDoc}
       */
      @Override
      public T getObject() throws Exception {
        return getSqlSession().getMapper(this.mapperInterface);
      }
    
      /**
       * {@inheritDoc}
       */
      @Override
      public Class<T> getObjectType() {
        return this.mapperInterface;
      }
    
      /**
       * {@inheritDoc}
       */
      @Override
      public boolean isSingleton() {
        return true;
      }
    
      //------------- mutators --------------
    
      /**
       * Sets the mapper interface of the MyBatis mapper
       *
       * @param mapperInterface class of the interface
       */
      public void setMapperInterface(Class<T> mapperInterface) {
        this.mapperInterface = mapperInterface;
      }
    
      /**
       * Return the mapper interface of the MyBatis mapper
       *
       * @return class of the interface
       */
      public Class<T> getMapperInterface() {
        return mapperInterface;
      }
    
      /**
       * If addToConfig is false the mapper will not be added to MyBatis. This means
       * it must have been included in mybatis-config.xml.
       * <p/>
       * If it is true, the mapper will be added to MyBatis in the case it is not already
       * registered.
       * <p/>
       * By default addToCofig is true.
       *
       * @param addToConfig
       */
      public void setAddToConfig(boolean addToConfig) {
        this.addToConfig = addToConfig;
      }
    
      /**
       * Return the flag for addition into MyBatis config.
       *
       * @return true if the mapper will be added to MyBatis in the case it is not already
       * registered.
       */
      public boolean isAddToConfig() {
        return addToConfig;
      }
    }
    
    
    @Override
    public <T> T getMapper(Class<T> type) {
        return configuration.getMapper(type, this);
    }
    
    public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
        return mapperRegistry.getMapper(type, sqlSession);
    }
    

    这里就用了动态代理

    public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
        final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
        if (mapperProxyFactory == null) {
          throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
        }
        try {
          return mapperProxyFactory.newInstance(sqlSession);
        } catch (Exception e) {
          throw new BindingException("Error getting mapper instance. Cause: " + e, e);
        }
    }
    
    public T newInstance(SqlSession sqlSession) {
        final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
        return newInstance(mapperProxy);
    }
    
    protected T newInstance(MapperProxy<T> mapperProxy) {
        return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
    }
    

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

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

相关文章

电池SOC和动力电池OCV功率联合估计研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

爱心方程(理科生的浪漫?)

目录 前言 C/C 源代码 扩展 Java Python HTML 前言 这个在大一的时候就想找了&#xff0c;然后后面是找到了一个&#xff0c;但是忘记出处了。我决定把可以找到的所有爱心给整理一下&#xff0c;为了实现“理科生的浪漫”&#xff01;&#xff01;&#xff01; C/C 首先…

你认为年轻人存款难吗?难啊难,难如上青天!!!

近日&#xff0c;有调查称“大概五分之一的年轻人存款在一万元以内。10万元存款是一个“坎”&#xff0c;存款超过10万就会超过53.7%的人。”“年轻人”“存款”两个词碰撞在一起&#xff0c;引来了广泛的关注和讨论。你认为年轻人存款难吗&#xff1f;可以从以下几个角度发表你…

Linux Nacos 设置systemctl service 并添加为开机启动

为方便在启动服务器时&#xff0c;不需要一个一个手动启动服务&#xff0c;需要把nacos设置为开机启动。方法如下&#xff1a; 在/usr/lib/systemd/system 目录下面添加nacos.service脚本&#xff1a; # 下面这一行必须有&#xff0c;不然会报错 #vim /usr/lib/systemd/system…

libevent(3)IO模型基础知识

一、用户态和内核态 我们知道现在的操作系统是分层的&#xff0c;内核封装了与底层的接口&#xff0c;通过系统调用提供给上层应用使用。 当进程运行在内核空间时&#xff0c;它就处于内核态&#xff1b;当进程运行在用户空间时&#xff0c;它就处于用户态。 当我们需要进行IO操…

【小沐学Python】网络爬虫之requests

文章目录 1、简介2、requests方法2.1 get2.2 post 3、requests响应信息4、requests的get方法4.1 url4.2 headers4.3 params4.4 proxies4.5 verify4.6 timeout4.7 cookies4.8 身份验证 3、测试代码3.1 获取网页HTML&#xff08;get&#xff09;3.2 获取网页HTML&#xff08;带he…

【LeetCode热题100】打卡第26天:最大矩形

文章目录 最大矩形⛅前言&#x1f512;题目&#x1f511;题解 最大矩形 ⛅前言 大家好&#xff0c;我是知识汲取者&#xff0c;欢迎来到我的LeetCode热题100刷题专栏&#xff01; 精选 100 道力扣&#xff08;LeetCode&#xff09;上最热门的题目&#xff0c;适合初识算法与数…

随着ChatGPT、文言一心的大火,未来可能的生活工作方式

前面的文章笼统的扯了一些ChatGPT、文言一心的差异化&#xff0c;感觉还是不够明白直观。特地找了一份资料&#xff0c;通过基础能力、进阶能力、和一些垂直领域的几百个各种问题&#xff0c;来对比分析两者的回答情况&#xff0c;让大家可以有个更接地气的了解。 由于问题太多…

无限脉动:释放音乐和区块链在音乐领域的力量

音乐是一种永恒的通用语言&#xff0c;它将人们聚集在一起&#xff0c;超越了边界&#xff0c;在我们灵魂深处产生共鸣&#xff0c;创造联系。在当今数字时代&#xff0c;随着区块链技术和去中心化网络的出现&#xff0c;音乐世界正在经历一场深刻的变革。 我们在与艺术家合作&…

动态规划 DP (二)

3.二维动态规划 1) 力扣https://leetcode.cn/problems/minimum-path-sum/第一行的的路径只与左边的元素有关&#xff0c;第一列的路径只与上面的元素有关。 除了第一行和第一列&#xff0c;其他元素的路径取决于左边和上面元素的最小值。 只要每次都选择值最小的路径&#…

2021电工杯数学建模B题解题思路(光伏建筑一体化板块指数发展趋势分析及预测)

目录 一、前言 二、问题背景 三、具体问题 四、解题思路 &#xff08;一&#xff09;整体思路 &#xff08;二&#xff09;问题一 &#xff08;三&#xff09;问题二 &#xff08;四&#xff09;问题三 &#xff08;五&#xff09;问题四 &#xff08;六&#xff09;…

2023最新谷粒商城笔记之秒杀服务篇(全文总共13万字,超详细)

秒杀服务 秒杀具有瞬间高并发的特点&#xff0c;针对这一特点&#xff0c;必须要做限流异步缓存(页面静态化)独立部署 限流方式&#xff1a; 前端限流&#xff0c;一些高并发的网站直接在前端页面开始限流&#xff0c;例如&#xff1a;小米的验证码设计Nginx 限流&#xff0c…

ChatGPT从入门到精通,深入认识Prompt

ChatGPT从入门到精通&#xff0c;一站式掌握办公自动化/爬虫/数据分析和可视化图表制作 全面AI时代就在转角 道路已经铺好了 “局外人”or“先行者” 就在此刻 等你决定让ChatGPT帮你高效实现职场办公&#xff01;行动起来吧1、ChatGPT从入门到精通&#xff0c;一站式掌握办公…

Unity 中的旋转、targetFrameRate、 vSyncCount、Time

1. 旋转&#xff1a; Unity 中的旋转用eulerAngle 表示&#xff0c;但在内部是以quaternion存储。欧拉角旋转围绕三个轴进行三次独立旋转&#xff0c;依次是z、x、y。To convert from Euler angles to quaternions, you can use the Quaternion.Euler function.To convert a q…

Linux 文件操作

文章目录 一、task_struct 和 file 的关系二、文件操作的系统调用三、进程默认打开的三个文件四、文件重定向五、Linux 下一切皆文件 文件是在磁盘上创建出来的&#xff0c;当我们想进行文件操作时&#xff0c;根据冯诺依曼体系结构&#xff0c;CPU 只和内存交互&#xff0c;为…

【裸机开发】Reset 中断服务函数(汇编实现)

目录 一、Reset 中断服务函数的实现步骤 二、汇编实现 Reset 中断服务函数 1、禁止/打开全局中断 2、设置SP指针 3、清除 .bss 段 4、完整 Reset 中断服务函数 一、Reset 中断服务函数的实现步骤 实现 Reset 中断服务函数的基本步骤如下&#xff1a; 设置各个模式下的S…

关联式容器set和map

文章目录 一.容器二.set的介绍1.insert2.lower_bound&&upper_bound3.find和countfindcount 三. multiset四.map最特别的operator[] 四.multimap&#xff0c;因为允许键值冗余&#xff0c;所以它没有operator[]&#xff0c;它的find返回的是中序遍历第一次遇到的节点五.…

ChatGPT办公自动化实战

ChatGPT从入门到精通&#xff0c;一站式掌握办公自动化/爬虫/数据分析和可视化图表制作 全面AI时代就在转角 道路已经铺好了 “局外人”or“先行者” 就在此刻 等你决定 让ChatGPT帮你高效实现职场办公&#xff01;行动起来吧 1、ChatGPT从入门到精通&#xff0c;一站式掌握办…

对象的销毁

析构函数 C 中的类可以定义一个特殊的清理函数 这个特殊的清理函数叫做析构函数析构函数的功能与构造函数相反 定义&#xff1a;~ClassName() 析构函数没有参数也没有返回值类型声明析构函数在对象销毁时自动被调用 析构函数使用初探 #include <stdio.h>class Test …

Threadlocal 必会的9个知识点

1.什么是ThreadLocal&#xff1f;它在多线程环境下有什么用处&#xff1f; ThreadLocal是在多线程环境下提供的一种简单的机制&#xff0c;使得每个线程都能拥有一个独立的变量副本。它避免了线程安全问题&#xff0c;并提高了程序的并发性能。 2.ThreadLocal是如何工作的&am…