【博学谷学习记录】超强总结,用心分享 | 架构师 Spring源码学习总结

news2024/9/24 7:24:10

文章目录

  • Spring的循环依赖
    • 1.循环依赖的定义&&原因
    • 2.循环依赖的场景
      • 1.构造器注入引起循环依赖
      • 2.Field属性setter注入的循环依赖
      • 3.循环依赖解决思路
      • 4.三级缓存
      • 5.面试题[三级缓存]
  • AOP源码深度剖析
    • 概述
    • Spring AOP的前世今生
    • 实现机制
      • **JDK 动态代理**
      • **CGLIB 代理**
    • 流程
    • 总结
  • MVC流程源码剖析
    • Servlet生命周期
    • DispatcherServlet 类图
    • 源码剖析-根容器初始化【父容器】
      • Web应用部署初始化过程 (Web Application Deployement)
      • ContextLoaderListener的初始化过程
      • ServletContextListener接口源码:
    • 源码剖析-DispatcherServlet初始化【子容器&9大组件】
      • DispatcherServlet类图
      • 为什么需要多个IOC容器呢?
      • DispatcherServlet初始化

1.Spring中核心组件
2.IOC流程:bean对象是如何创建出来的
3.Bean生命周期
4.循环依赖:循坏依赖为什么要通过三级缓存来解决
5.AOP
6.事务
7.MVC

Spring的循环依赖

1.循环依赖的定义&&原因

定义:一个或多个对象实例之间存在直接或间接的依赖关系,这种依赖关系构成了一个环形调用(闭环)

在这里插入图片描述

在Spring中,一个对象并不是简单new出来的,而是会经过一系列的Bean的生命周期,就是因为Bean的生命周期,所以才出现了循环依赖的问题

在Bean生命周期的属性赋值阶段,A依赖了B,从单例池中找B对象,没有则创建,创建B的途中依赖了A,此处形成了环形调用。

2.循环依赖的场景

1.构造器注入引起循环依赖

在这里插入图片描述

2.Field属性setter注入的循环依赖

在这里插入图片描述

对于以上两种场景的循环依赖,Spring下测试效果:

1.构造器注入引起的循环依赖(不能解决

2.单例Bean的Setter注入产生的循环依赖(能解决

补充:多例Bean的Setter注入产生的循环依赖(不能解决

3.循环依赖解决思路

Spring的循环依赖的理论依据是:基于Java的引用传递

即:可以先实例化对象,实例化对象之后,在内存中就有了该对象的内存地址,我们就可以先从内存中获取到该对象而对象的具体属性,是可以延后设置的

核心:创建和属性赋值分开,达到对象的提前暴露效果

A创建时,先创建A原始对象放入缓存中

因为依赖于B,在创建B时需要A,从缓存中取A,缓存中有A则B创建完成,赋值给A

4.三级缓存

一级缓存

存放完整的Bean

Bean执行了一系列生命周期,存入单例池SingletonObjects中

二级缓存

存放半成品Bean

类似于A:{b=null},实例化但未赋值的状态,存入earlySingletonObjects

三级缓存

存放ObjectFactory对象

将Bean进行包装成ObjectFactory,存入singletonFactories

取对象时,可能取A的原始对象/A的代理对象

bean将A的地址传入进来,如果现在针对A进行AOP配置的话,A对象的方法增强,则在三级缓存中取A的时候,会提前生成A对象的代理对象proxy,如果没有配置则返回A的原始对象。

当有AOP配置时,Spring根据beanName在已注册的Advisor集合中找匹配到的拦截面,生成代理对象,把生成的代理对象存入二级缓存,并删除三级缓存: this.singletonFactories.remove(beanName)

5.面试题[三级缓存]

1.构造器注入引起的循环依赖能够解决吗?

不能。因为创建A对象时候是通过A的有参构造去构建的——new A(B),这个对象的创建和属性赋值是没有分开来的,所以没有办法进行A对象的提前暴露。

2.多例Bean对象setter注入产生的循环依赖能够解决吗?

不能。多例Bean的生命周期不由Spring管理

3.循环依赖,只有一级缓存,能够解决循环依赖吗?

单从循环依赖的角度,能解决,但使用过程会有问题,因为此时成品对象、半成品对象都存到一个map中,如果另一个请求需要调用A对象的B属性的方法,他并不知道此时A对象是成品对象还是半成品对象,如果是半成品对象,调用B属性方法(此时B为null),则会报空指针异常。

4.循环依赖,只有一级缓存、二级缓存,能够解决循环依赖吗?

如果不存在AOP代理的情况,是可以解决的。

如果对A进行了AOP配置,要生成A的代理对象,A在初始化通过BeanPostProcessor#after方法生成proxy代理对象并存储缓存,但B的A属性还是指向A的原始对象,这里就产生了问题。

针对以上问题解决思路:在A放缓存之前,判断是否需要生成代理对象,如果需要,则基于原始对象生成代理对象,再把代理对象存入缓存中(等同于违背了Spring的设计原则,无论Bean是否产生循环依赖问题,所有Bean对象产生代理对象的时机都提前了(所有的Bean都得多一个判断))

5.循环依赖,只有一级缓存、三级缓存,能够解决循环依赖吗?

简单的A、B循环依赖没问题,但如果此时出现复杂的循环依赖会出现问题:比如A、B、C相互依赖场景

在这里插入图片描述

B依赖于A,生成了A的代理对象Proxy1,C依赖于A,又生成了A的代理对象Proxy2,此时B、C引用了不同的A对象地址。

通过二级缓存解决这种现象:Spring先查二级缓存,发现有A对象代理生成,则不会再去生成新的A对象代理。

生成代理对象的时候有判断:该bean之前是否生成过代理,不重复生成,通过集合判断

6.三级缓存为什么存ObjectFactory?为什么需要三级缓存解决循环依赖问题?

本身违背了Spring设计原则,Spring很多拓展点设计都无法应用,只有产生循环依赖,才会把产生循环依赖的对象要生成的代理对象做一个提前操作,否则就把没有循环依赖,但需要产生代理对象按照Spring的设计规范生成

总结

三级缓存作用:在没有循环依赖的情况下,能够包装bean的初始化的最后阶段再生成代理对象,遵循Spring设计原则。

个人理解:三级缓存就是判断对象是否有AOP配置生成proxy对象,将生成代理对象提前了,但Spring找缓存对象时是按照顺序:一级缓存 -> 二级缓存 -> 三级缓存 查找的,不一定每个Bean都走到三级缓存,所以不违背Spring设计原则。

AOP源码深度剖析

概述

AOP(Aspect Orient Programming):面向切面编程;

用途:用于系统中的横切关注点,比如日志管理,事务管理;

实现:利用代理模式,通过代理对象对被代理的对象增加功能。

所以,关键在于AOP框架自动创建AOP代理对象,代理模式分为静态代理和动态代理;

框架:

​ AspectJ使用静态代理,编译时增强,在编译期生成代理对象;

​ SpringAOP使用动态代理,运行时增强,在运行时,动态生成代理对象;

Spring AOP的前世今生

目前 Spring AOP 一共有三种配置方式,Spring 做到了很好地向下兼容,所以可以放心使用。

  • Spring 1.2 基于接口的配置:最早的 Spring AOP 是完全基于几个接口的
  • Spring 2.0 schema-based 配置:Spring 2.0 以后使用 XML 的方式来配置,使用 命名空间
  • Spring 2.0 @AspectJ 配置:使用注解的方式来配置,这种方式感觉是最方便的,还有,这里虽然叫做 @AspectJ,但是这个和 AspectJ 其实没啥关系。

要说明的是,这里介绍的 Spring AOP 是纯的 Spring 代码,和 AspectJ 没什么关系,但是 Spring 延用了 AspectJ 中的概念,包括使用了 AspectJ 提供的 jar 包中的注解,但是不依赖于其实现功能。

如 @Aspect、@Pointcut、@Before、@After 等注解都是来自于 AspectJ,但是功能的实现是纯 Spring AOP 自己实现的。

实现机制

Spring AOP 底层实现机制目前有两种:JDK 动态代理、CGLIB 动态字节码生成。在阅读源码前对这两种机制的使用有个认识,有利于更好的理解源码。

JDK 动态代理

在这里插入图片描述

Proxy.newProxyInstance()第三个参数,实现InvocationHandler接口重写invoke方法,在每次调用方法时,都会进入invoke方法实现增强。

Spring为Bean对象创建代理时,会判断当前Bean对象有没有实现接口,如果实现接口,使用JDK动态代理生成对象,否则使用CGLIB代理。

如果proxy-target-class=“true”,则再判断当前对象是否为接口对象,是接口的话依然使用JDK动态代理,不是接口使用CGLIB;如果proxy-target-class不设置或者为false,则默认为JDK动态代理

CGLIB 代理

在这里插入图片描述

new Enhance对象,设置属性,调用create()方法创建proxy对象,每次调用方法时,会进入intercept方法,可以在proxy.invokeSuper()前后进行逻辑增强。

流程

Spring对标签<aop:aspectj-autoproxy @EnableAspectJAutoProxy/>的解析(作用):

​ 注册AnnotationAwareAspectJAutoProxyCreator后置处理器

AnnotationAwareAspectJAutoProxyCreator类图:实现了BeanPostProcessor

在这里插入图片描述

AnnotationAwareAspectJAutoProxyCreator 实现了几个重要的扩展接口(可能是在父类中实现):
1)实现了 BeanPostProcessor 接口:实现了 postProcessAfterInitialization 方法。
2)实现了 InstantiationAwareBeanPostProcessor 接口:实现了 postProcessBeforeInstantiation 方法。
3)实现了 SmartInstantiationAwareBeanPostProcessor 接口:实现了 predictBeanType 方法、getEarlyBeanReference 方法。
4)实现了 BeanFactoryAware 接口,实现了 setBeanFactory 方法。
对于 AOP 来说,postProcessAfterInitialization 是我们重点分析的内容,因为在该方法中,会对 bean 进行代理,该方法由父类 AbstractAutoProxyCreator 实现。

AbstractAutoProxyCreator#postProcessAfterInitialization

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    if (bean != null) {
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        // 1.判断当前bean是否需要被代理,如果需要则进行封装
        if (!this.earlyProxyReferences.contains(cacheKey)) {
            return wrapIfNecessary(bean, beanName, cacheKey);
        }
    }
    return bean;
}

AnnotationAwareAspectJAutoProxyCreator的BeanPostProcessor#after方法执行的时候,经过wrapIfNecessary方法来判断是否需要针对当前类生成代理对象;

wrapIfNecessary

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    // 1.判断当前bean是否在targetSourcedBeans缓存中存在(已经处理过),如果存在,则直接返回当前bean
    if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
        return bean;
    }
    // 2.在advisedBeans缓存中存在,并且value为false,则代表无需处理
    if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
        return bean;
    }
    // 3.bean的类是aop基础设施类 || bean应该跳过,则标记为无需处理,并返回
    if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }
 
    // Create proxy if we have advice.
    // 4.获取当前bean的Advices和Advisors
    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    // 5.如果存在增强器则创建代理
    if (specificInterceptors != DO_NOT_PROXY) {
        this.advisedBeans.put(cacheKey, Boolean.TRUE);
        // 5.1 创建代理对象:这边SingletonTargetSource的target属性存放的就是我们原来的bean实例(也就是被代理对象),
        // 用于最后增加逻辑执行完毕后,通过反射执行我们真正的方法时使用(method.invoke(bean, args))
        Object proxy = createProxy(
                bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
        // 5.2 创建完代理后,将cacheKey -> 代理类的class放到缓存
        this.proxyTypes.put(cacheKey, proxy.getClass());
        // 返回代理对象
        return proxy;
    }
    // 6.标记为无需处理
    this.advisedBeans.put(cacheKey, Boolean.FALSE);
    return bean;
}

getAdvicesAndAdvisorsForBean

@Override
protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, TargetSource targetSource) {
    // 1.找到符合条件的Advisor
    List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
    if (advisors.isEmpty()) {
        // 2.如果没有符合条件的Advisor,则返回null
        return DO_NOT_PROXY;
    }
    return advisors.toArray();
}

getAdvicesAndAdvisorsForBean针对当前程序中所有切面类@Aspect进行解析,找到切面类中@Before/@After/@Around注解标注的方法,把这些方法创建成Advice对象,并且针对@Before(“pointcut()”)中的切点信息进行解析成Pointcut对象,将Advice对象和Pointcut对象进行封装,形成Advisor对象。

总结

1.标签<aop:aspectj-autoproxy @EnableAspectJAutoProxy/> 注册了一个AnnotationAwareAspectJAutoProxyCreator后置处理器,当代理对象生成的时候是调用AnnotationAwareAspectJAutoProxyCreator后置处理器的after方法生成。

2.创建代理对象时,after方法对于当前Bean对象去找对应的Advisor,如果有对应的Advisor,则生成代理对象(判断对象有没有实现接口,如果实现接口,采用JDK动态代理,否则采用CGLIB动态代理);当代理对象调用接口中任意方法时,都是执行底层的invoke方法,invoke方法执行时涉及拦截器链的依次执行(如果是后置通知/最终通知:先放行,再在方法返回时执行对应逻辑)。

MVC流程源码剖析

* 问题1SpringSpringMVC整合使用时,会创建一个容器还是两个容器(父子容器?)
答:会创建两个容器对象,并且是有父子容器关系,对于Spring容器主要管理业务层/持久层/事务层对象,对于SpringMVC容器主要负责Web层对象的维护,SpringMVC容器为子容器,Spring容器为父容器(其实就是设置了SpringMVC的一个parent属性)

* 问题2DispatcherServlet初始化过程中做了什么?
答:在DispatcherServlet初始化init方法时,首先构建了子容器对象,并且根据spring-mvc.xml进行文件解析,这里关注<mvc:annotation-driven>标签(开启注解模式驱动),它向当前的BeanDefinitionMap中注册了一些Bean定义,其中有RequestMappingHandlerMappingRequestMappingHandlerAdapterExceptionHandlerExceptionResolver,以支持对使用了@RequestMapping@ExceptionHandler及其他注解的控制器方法的请求处理。

RequestMappingHandlerMapping:生命周期中,父类实现了initializingBean#AfterPropertiesSet,这里进行了映射关系的注册:
	1.获取容器中所有的BeanName,根据BeanName获得对应的BeanType,判断有没有@Controller/@RequestMapping注解,如果有 说明是处理器(Handler)对象,将这些Bean封装到Map集合中(key:Method,value:RequestMappingInfo)(RequestMappingInfo是针对@RequestMapping注解的属性解析封装)
    2.根据Map注册映射关系(key:RequestMappingInfo,value:HandlerMethod)(这里HandlerMethod是上一步Method的封装,比如还封装了这个Method的所在类的对象)
    3.RequestMappingInfo再封装到urlLookup中(key:url,value:RequestMappingInfoRequestMappingHandlerAdapter:在它的initializingBean#AfterPropertiesSet方法中:
    初始化了参数解析器、返回值处理器

* 问题3:请求的执行流程是怎么样的?
答:
    1.根据请求地址,定位到Controller中的方法,可能有拦截器拦截,所以返回的是执行器链,根据顺序:拦截器目标方法、拦截器后置方法执行;
    2.(参数如何绑定)通过不同的参数解析器完成参数值的解析,再反射调用方法
    3.(不同返回值如何处理)通过不同的返回值处理器将结果变为ModelAndView对象,再进行视图渲染,最终以转发的形式完成视图的跳转

SpringMVC是基于Servlet和Spring容器设计的Web框架

Servlet生命周期

1</> //将随着服务的启动进行实例化,并调用init方法且只调用一次。

在这里插入图片描述

ServletConfig 是一个和 Servlet 配置相关的接口:

在配置 Spring MVC 的 DispatcherServlet 时,会通过 ServletConfig 将配置文件的位置告知 DispatcherServlet。

例:

<servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:springmvc.xml</param-value>
    </init-param>
</servlet>

如上,标签内的配置信息最终会被放入 ServletConfig 实现类对象中。DispatcherServlet 通过 ServletConfig 接口中的方法,就能获取到 contextConfigLocation 对应的值。

DispatcherServlet 类图

在这里插入图片描述

请求入口:DispatcherServlet类的dpDispatch()方法 (找service()方法一步一步找到的核心方法)

源码剖析-根容器初始化【父容器】

Web应用部署初始化过程 (Web Application Deployement)

Web应用部署初始化流程执行图:

在这里插入图片描述

可以发现,在tomcatweb应用的初始化流程是,先初始化listener接着初始化filter最后初始化servlet,当我们清楚认识到Web应用部署到容器后的初始化过程后,就可以进一步深入探讨SpringMVC的启动过程。

ContextLoaderListener的初始化过程

首先定义了<context-param>标签,用于配置一个全局变量,<context-param>标签的内容读取后会被放进application中,做为Web应用的全局变量使用,接下来创建listener时会使用到这个全局变量,因此,Web应用在容器中部署后,进行初始化时会先读取这个全局变量,之后再进行上述讲解的初始化启动过程。

接着定义了一个ContextLoaderListener类listener。查看ContextLoaderListener的类声明源码如下图:

在这里插入图片描述

ServletContextListener接口源码:

public interface ServletContextListener extends java.util.EventListener {
    
    void contextInitialized(javax.servlet.ServletContextEvent servletContextEvent);

    void contextDestroyed(javax.servlet.ServletContextEvent servletContextEvent);
}

该接口只有两个方法contextInitializedcontextDestroyed,这里采用的是观察者模式,也称为为订阅-发布模式,实现了该接口的listener会向发布者进行订阅,当Web应用初始化或销毁时会分别调用上述两个方法。

继续看ContextLoaderListener,该listener实现了ServletContextListener接口,因此在Web应用初始化时会调用该方法,该方法的具体实现如下:

  /**
     * Initialize the root web application context.
     */
    @Override
    public void contextInitialized(ServletContextEvent event) {
        initWebApplicationContext(event.getServletContext());
    }

ContextLoaderListenercontextInitialized()方法直接调用了initWebApplicationContext()方法,这个方法是继承自ContextLoader类,通过函数名可以知道,该方法是用于初始化Web应用上下文,即IoC容器,这里使用的是代理模式

源码剖析-DispatcherServlet初始化【子容器&9大组件】

DispatcherServlet类图

Web应用启动的最后一个步骤就是创建和初始化相关Servlet,我们配置了DispatcherServlet类前端控制器,前端控制器作为中央控制器是整个Web应用的核心,用于获取分发用户请求并返回响应。

其类图如下所示:

在这里插入图片描述

通过类图可以看出DispatcherServlet类的间接父类实现了Servlet接口,因此其本质上依旧是一个Servlet

为什么需要多个IOC容器呢?

答:父子容器类似于类的继承关系,子类可以访问父类中的成员变量,而父类不可访问子类的成员变量,同样的,子容器可以访问父容器中定义的Bean,但父容器无法访问子容器定义的Bean。

根IoC容器做为全局共享的IoC容器放入Web应用需要共享的Bean,而子IoC容器根据需求的不同,放入不同的Bean,这样能够做到隔离,保证系统的安全性。

DispatcherServlet类的子IoC容器创建过程,如果当前Servlet存在一个IoC容器则为其设置根IoC容器作为其父类,并配置刷新该容器,用于构造其定义的Bean,这里的方法与前文讲述的根IoC容器类似,同样会读取用户在web.xml中配置的中的值,用于查找相关的xml配置文件用于构造定义的Bean,这里不再赘述了。如果当前Servlet不存在一个子IoC容器就去查找一个,如果仍然没有查找到则调用 createWebApplicationContext()方法去创建一个,查看该方法的源码如下图所示:

protected WebApplicationContext createWebApplicationContext(@Nullable WebApplicationContext parent) {
    return createWebApplicationContext((ApplicationContext) parent);
}


protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
    Class<?> contextClass = getContextClass();
    if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
        throw new ApplicationContextException(
                "Fatal initialization error in servlet with name '" + getServletName() +
                "': custom WebApplicationContext class [" + contextClass.getName() +
                "] is not of type ConfigurableWebApplicationContext");
    }
    ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);

    wac.setEnvironment(getEnvironment());
    wac.setParent(parent);
    String configLocation = getContextConfigLocation();
    if (configLocation != null) {
        wac.setConfigLocation(configLocation);
    }
    configureAndRefreshWebApplicationContext(wac);

    return wac;
}

该方法用于创建一个子IoC容器并将根IoC容器做为其父容器,接着进行配置和刷新操作用于构造相关的Bean。至此,根IoC容器以及相关Servlet的子IoC容器已经配置完成,子容器中管理的Bean一般只被该Servlet使用,因此,其中管理的Bean一般是“局部”的,如SpringMVC中需要的各种重要组件,包括Controller、Interceptor、Converter、ExceptionResolver等。

DispatcherServlet初始化

DispatcherServlet的内置组件及其作用。

在这里插入图片描述




如 @Aspect、@Pointcut、@Before、@After 等注解都是来自于 AspectJ,但是功能的实现是纯 Spring AOP 自己实现的。

颜色

颜色

颜色

颜色

颜色

颜色

颜色

颜色

颜色

颜色

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

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

相关文章

六十分之十三——黎明前

目录一、目标二、计划三、完成情况四、提升改进(最少3点)五、意外之喜(最少2点)六、总结一、目标 明确可落地&#xff0c;对于自身执行完成需要一定的努力才可以完成的 1.8本技术管理书籍阅读(使用番茄、快速阅读、最后输出思维导图)2.吴军系列硅谷来信1听书、香帅的北大金融…

成都哪家机构的Java培训比较好,求一个不坑的?

关于这个问题&#xff0c;相信你会得到很多条答案&#xff0c;以及很多家机构的自荐。既然如此&#xff0c;不如也了解一下老牌IT职业教育机构&#xff1a;有足够丰富的教学经验&#xff0c;丰富的教学产品资源以及成熟的就业保障体系&#xff0c;还有就是承担风险的能力。 很…

计算机网络7:传输层相关

目录1-传输层1.1 UDP 和 TCP 的特点1.1.1 UDP用户数据报格式1.1.2 TCP首部格式1.1.2.1 常用端口号1.2 TCP的三次握手1.2.1 三次握手的原因1.3 TCP四次挥手1.3.1 四次挥手的原因1.3.2 TIME_WAIT为什么是2MSL1.4 TCP的可靠传输有效机制1.4.1 TCP可靠传输-超时重传1.4.2 TCP流量控…

封装、继承、Super、重写、多态instanceof类型转换的使用以及个人见解

这里写目录标题封装继承supersuper和this的区别重写多态instanceof类型转换封装 之前我们调用共有的属性&#xff0c;是直接可以调用的 但是属性私有后&#xff0c;无法在直接.调用 只能通过getset调用 继承 super 可以直接调用父类中属性和方法&#xff0c;私有的无法做 其…

TCP详解及面试相关问题

文章目录1、计算机模型2、客户端和服务端通信——TCP协议&#xff08;1&#xff09;socket套接字&#xff08;2&#xff09;TCP三次握手——创建socket&#xff08;3&#xff09;连接的本质&#xff08;4&#xff09;TCP四次挥手——释放socket资源&#xff08;5&#xff09;TC…

如何用PHP实现消息推送

什么是消息推送 通过服务器自动推送消息到客户端(浏览器&#xff0c;APP&#xff0c;微信)的应用技术。 2. 为什么要使用消息推送技术 通常情况下都是用户发送请求浏览器显示用户需要的信息。推送技术通过自动传送信息给用户&#xff0c;来减少用于网络上搜索的时间。它根据…

SSH 服务详解 (八)-- vscode 通过 SSH 远程连接 linux 服务器

vscode 通过 SSH 远程连接 linux 服务器 SSH服务详解(一)–Linux SSH 服务器与客户端的安装与启动 SSH服务详解(二)–使用私钥登录 SSH 服务器(免密登录) SSH 服务详解 (三)-- 使用 SSH 代理 SSH 服务详解 (四)-- 本地调用远程主机的命令 SSH 服务详解 (五)-- 远程文件拷贝…

零信任-微软零信任介绍(2)

微软零信任是什么&#xff1f; Microsoft Zero Trust 是一种安全架构&#xff0c;旨在在没有信任任何设备、用户或网络的情况下保护网络。这种架构使用多重验证和分段技术&#xff0c;以确保每个请求和资源的安全性。 零信任不假定任何内部用户或设备是安全的&#xff…

硬件工程师入门基础知识(一)基础元器件认识(一)

硬件工程师入门基础知识 &#xff08;一&#xff09;基础元器件认识&#xff08;一&#xff09; 今天水一篇hhh。介绍点基础但是实用的东西。 tips&#xff1a;学习资料和数据来自《硬件工程师炼成之路》、百度百科、网上资料。 1.贴片电阻 2.电容 3.电感 4.磁珠 1.贴片电…

Android 基础知识4-2.4程序签名打包

Android APP都需要我们用一个证书对应用进行数字签名&#xff0c;不然的话是无法安装到Android手机上的&#xff0c;平时我们调试运行时到手机上时&#xff0c;是AS会自动用默认的密钥和证书来进行签名&#xff1b;但是我们实际发布编译时&#xff0c;则不会自动签名&#xff0…

IDEA设置只格式化本次迭代变更的代码

趁着上海梅雨季节&#xff0c;周末狠狠更新一下。平常工作在CR的时候&#xff0c;经常发现会有新同事出现大量代码变更行..一看原因竟是在格式化代码时把历史代码也格式化掉了这样不仅坑了自己&#xff08;覆盖率问题等&#xff09;&#xff0c;也可能会影响原始代码责任到人&a…

python 字典的概念叙述和使用方法

文章目录1. 一个简单的字典2. 使用字典2.1 访问字典中的值2.2 添加键-值对2.3 修改字典中的值2.4 删除键-值对3. 遍历字典3.1 遍历所有键-值对3.2 遍历字典中的所有键3.3 按顺序遍历字典中的所有键3.4 遍历字典中的所有值4.嵌套4.1 字典列表4.2 range() 函数4.3 在字典中存储列…

Java中导入、导出Excel——HSSFWorkbook 使用

一、介绍 当前B/S模式已成为应用开发的主流&#xff0c;而在企业办公系统中&#xff0c;常常有客户这样子要求&#xff1a;你要把我们的报表直接用Excel打开(电信系统、银行系统)。或者是&#xff1a;我们已经习惯用Excel打印。这样在我们实际的开发中&#xff0c;很多时候需要…

自动化测试岗位求职简历编写规范+注意事项,让你的简历脱颖而出

目录 前言 1.个人信息 2.教育背景(写最高学历) 3.个人技能(按精通/掌握/熟练/了解层次来写) 4.工作经历 5.工作经验/项目经历 6.自我评价 总结 前言 挑选一个阅读舒适度不错的模板 HR和面试官看的简历多&#xff0c;都是快速阅读&#xff0c;舒适度特别重要&#xff1b…

ctfshow 每周大挑战 极限命令执行

《简单的命令执行题目》 这里感叹一下&#xff0c;g4佬是真好厉害&#xff0c;这次题目十分的难&#xff0c;嗯&#xff0c;对我这种菜鸡来说是这样的&#xff0c;想了一天&#xff0c;最后结束了&#xff0c;也还是没有想明白第五题的解法&#xff0c;我真是fw&#xff0c;到最…

Oracle Trace File Analyzer 介绍及简单使用

一、什么是Oracle Trace File Analyzer Oracle Autonomous Health Framework(AHF) 包含 Oracle ORAchk, Oracle EXAchk, and Oracle Trace File Analyzer(TFA). AHF工具包包含了Oracle常用的多种诊断工具&#xff0c;如 ORAchk, Oracle EXAchk, and Oracle Trace File Analyzer…

简单的密码加密

用户的密码必须被加密后再存储到数据库, 否则就存在用户账号安全问题用户使用的原始密码通常称之为"原文"或"明文", 经过算法的运算, 得到的结果通常称之为"密文"在处理密码加密时, 不可以使用任何加密算法, 因为所有加密算法都是可以被逆向运算…

内网渗透(十九)之Windows协议认证和密码抓取-网络认证(基于挑战响应认证的NTLM协议)

系列文章第一章节之基础知识篇 内网渗透(一)之基础知识-内网渗透介绍和概述 内网渗透(二)之基础知识-工作组介绍 内网渗透(三)之基础知识-域环境的介绍和优点 内网渗透(四)之基础知识-搭建域环境 内网渗透(五)之基础知识-Active Directory活动目录介绍和使用 内网渗透(六)之基…

尚医通(十一)医院模拟系统接口

目录一、第三方医院系统简介及运行二、上传医院接口1、数据分析2、添加service接口3、添加repository接口4、添加controller接口5、添加帮助类6、图片base64说明7、base64码通过http传输问题三、查询医院接口1、添加service方法2、添加controller四、上传科室接口1、添加科室基…

C语言学习笔记(二): 简单的C程序设计

数据的表现形式 常量 在C语言中常量有以下几种&#xff1a; 整型常量&#xff1a; 0,-1,100实型常量&#xff1a; 小数形式(12.12)&#xff1b;指数形式(12.1e312.110312.1\times 10^312.1103)字符常量&#xff1a; 普通字符(’a’,’Z’,’#’)&#xff1b;转义字符(’\n’…