当前位置: 首页 > news >正文

Spring常见面试题总结

什么是Spring

Spring是一个轻量级Java开发框架,目的是为了解决企业级应用开发的业务逻辑层和其他各层的耦合问题,以提高开发效率。它是一个分层的JavaSE/JavaEE full-stack(一站式)轻量级开源框架,为开发Java应用程序提供全面的基础架构支持。Spring负责基础架构,因此Java开发者可以专注于应用程序的开发。
Spring最根本的使命是解决企业级应用开发的复杂性,即简化Java开发,提高下发效率。

为什么要使用Spring

根据Spring官网所述,Spring可以实现更快更轻松更安全地编程。 Spring对速度简单性生产力的关注使其成为世界上最受欢迎的Java 开发框架。具体来说,主要体现在以下六个方面:
(1) Spring无处不在。Spring受到全世界开发人员的信任。各个领域,各个企业都在广泛使用。
(2) Spring灵活性强。Spring提供的扩展能力和启动的粘合第三方库的能力,可以使开发人员轻松构建几乎任何可以想象的应用程序。Spring框架的控制反转(IoC)能力,为广泛的功能集奠定了基础。
(3) Spring可以提高生产力。Spring Boot改变了Java编程的方式,从根本上简化了编程体验。Spring Cloud使微服务开发变得轻松。
(4) Spring快速。Spring可以快速启动,快速关闭并支持执行的优化。Spring项目也越来越多地支持反应式(非阻塞)编程模型,以提高效率。
(5) Spring注重安全。Spring代码提交者与安全专家合作,修补和测试所有报告的漏洞。Spring密切关注关联的第三方类库,并定期发布更新以帮助确保数据和应用程序尽可能安全。此外,Spring Security可以更轻松地与行业标准安全方案集成。
(6) Spring社区支持较好。Spring社区是一个庞大,全球化,多元的社区,涵盖了从初学者到经验丰富的职业人士的各个阶段需要内容,拥有带入新高度所需的支持和资源:快速入门,指南和教程,视频,聚会,支持等等。

Spring 框架简介

请添加图片描述
(1) Spring Core:提供 Spring 框架的基本功能。核心容器的主要组件是BeanFactory,它是工厂模式的实现。BeanFactory 使用控制反转(IOC)模式将应用程序的配置和依赖性规范与实际的应用程序代码分开。
(2) Spring AOP:提供 Spring 框架面向切面的编程能力:将面向切面的编程功能集成到 Spring 框架中,可以将 Spring 框架管理的任何对象实现 AOP;提供了事务管理服务:通过使用 Spring AOP,不用依赖 EJB 组件,就可以将声明性事务管理集成到应用程序中。
(3) Spring Context:向 Spring 框架提供上下文信息。Spring 上下文支持消息源和观察者模式。Spring 上下文包括企业服务,例如 JNDI、EJB、电子邮件、国际化、校验和调度功能。
(4) Spring ORM:Spring 框架整合了若干个 ORM 框架,从而提供了 ORM 的对象关系工具,其中包括 JDO、Hibernate 和 iBatis SQL Map。所有这些都遵从 Spring 的通用事务和 DAO 异常层次结构。
(5) Spring DAO:提供数据访问能力(访问数据库);提供了有意义的异常层次结构:可用该结构来管理异常处理和不同数据库供应商抛出的错误消息。异常层次结构简化了错误处理,并且极大地降低了需要编写的异常代码数量(例如打开和关闭连接)。
(6) Spring Web:Web 上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文。所以,Spring 框架支持与 Jakarta Struts 的集成;Web 模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。
(7) Spring MVC:MVC 框架是一个全功能的构建 Web 应用程序的 MVC 实现。通过策略接口,MVC 框架变成为高度可配置的,MVC 容纳了大量视图技术,其中包括 JSP、Velocity、Tiles、iText 和 POI。

Spring Bean

Bean创建的容器类

Spring 有两个容器类: BeanFactory 和 ApplicationContext。
BeanFactory:这是一个最简单的容器,它主要的功能是为依赖注入(DI)提供支持。
ApplicationContext:Application Context 是 Spring 中的高级容器。和 BeanFactory 类似,它可以加载和管理配置文件中定义的 Bean。 另外,它还增加了企业所需要的功能,比如,从属性文件中解析文本信息和将事件传递给所指定的监听器。

BeanFactory 和 ApplicationContext 的区别?

二者都是 Spring 框架的两大核心接口,都可以当做 Spring 的容器。其中 ApplicationContext 是 BeanFactory 的子接口。
BeanFactory 是 Spring 里面最底层的接口,包含了各种 Bean 的定义,读取配置文档,管理 Bean 的加载、实例化,控制 Bean 的生命周期,维护对象之间的依赖关系等功能。
ApplicationContext 接口作为 BeanFactory 的派生,除了提供 BeanFactory 所具有的功能外,还提供了更完整的框架功能:
(1) 继承 MessageSource,支持国际化。
(2) 统一的资源文件访问方式。
(3) 提供在监听器中注册 Bean 的事件。
(4) 支持同时加载多个配置文件。
(5) 载入多个(有继承关系)上下文,使得每一个上下文都专注于一个特定的层次,如应用的 Web 层。
具体区别体现在以下三个方面:
(a) 加载方式不同
BeanFactroy 采用的懒加载方式注入 Bean,即只有在使用到某个 Bean 时才对该 Bean 实例化。这样,我们就不能在程序启动时发现一些存在的 Spring 的配置问题。
ApplicationContext 是在启动时一次性创建了所有的 Bean。
(b) 创建方式不同
BeanFactory 通常以编程的方式被创建,ApplicationContext 还能以声明的方式创建,如使用 ContextLoader。
(c) 注册方式不同
二者都支持 BeanPostProcessor、BeanFactoryPostProcessor 的使用,但 BeanFactory 需要手动注册,而 ApplicationContext 则是自动注册。

Bean的创建过程

(1) Spring Bean的容器类通过解析注解类或者以其他方式定义的类,将Bean标签解析成BeanDefinition(Bean定义信息)
(2) BeanFactory通过获取到的BeanDefinition,利用反射创建Bean对象。
(3) 通过populateBean()方法对Bean对象进行属性填充。
(4) 通过invokeAwareMethods()方法对Bean对象进行赋值。
(5) 调用BeanPostProcessor的初始化前置方法。
(6) 调用init-method方法,进行初始化操作。
(7) 调用BeanPostProcessor的初始化后置方法(AOP在此处进行)。
(8) 将创建好的Bean对象放入BeanDefinitionMap容器中。
(9) 通过Context.getBean()方法获得Bean对象并使用。–正常是通过依赖注入的方式使用
(10) spring容器关闭时会调用DisposableBean的destory()方法销毁Bean对象(如果配置了destory-method属性,spring会自动调用指定的销毁方法)。
单例bean的初始化以及依赖注入一般都在容器初始化阶段进行,只有懒加载(lazy-init为true)的单例bean是在应用第一次调用getBean()时进行初始化和依赖注入。
多例bean 在容器启动时不实例化,即使设置 lazy-init 为 false 也没用,只有调用了getBean()才进行实例化。

Spring Bean 作用域

Spring 提供以下五种 Bean 的作用域:
(1) Singleton: Bean 在每个 Spring Ioc 容器中只有一个实例,也是 Spring 的默认配置。
(2) Prototype:一个 Bean 的定义可以有多个实例。
(3) Request:每次 Http 请求都会创建一个 Bean,故该作用域仅在基于 Web 的 Spring ApplicationContext情形下有效。
(4) Session:在一个 Http Session 中,一个 Bean 对应一个实例。该作用域同样仅在基于 Web 的 Spring ApplicationContext 情形下有效。
(5) Global-session:在一个全局的 Http Session 中,一个 Bean 定义对应一个实例。
值的注意的是:使用 Prototype 作用域时需要慎重的思考,因为频繁创建和销毁 Bean 会带来很大的性能开销。

Spring 自动装配

Spring的自动装配有三种模式:byType(根据类型),byName(根据名称)、constructor(根据构造函数)。

@Autowired是按类型匹配的(byType),如果需要按名称(byName)匹配的话,可以使用@Qualifier注解与@Autowired结合。
@Resource,默认按 byName模式自动注入。@Resource有两个中重要的属性:name和type。Spring容器对于@Resource注解的name属性解析为bean的名字,type属性则解析为bean的类型。因此使用name属性,则按byName模式的自动注入策略,如果使用type属性则按 byType模式自动注入策略。倘若既不指定name也不指定type属性,Spring容器将通过反射技术默认按byName模式注入。
上述两种自动装配的依赖注入并不适合简单值类型,如int、boolean、long、String以及Enum等,对于这些类型,Spring容器也提供了@Value注入的方式。@Value接收一个String的值,该值指定了将要被注入到内置的java类型属性值,Spring 容器会做好类型转换。一般情况下@Value会与properties文件结合使用。

Spring 的单例是否线程安全?

Spring 中的单例 Bean 并不是线程安全的。
但我们日常使用时往往并未做多线程并发处理,那又是如何保证线程安全的呢?
实际上大部分时候我们定义的 Bean 是无状态的(如 dao 类),所有某种程度上来说 Bean 也是安全的,但如果 Bean 有状态的话(比如 model 对象),那就要开发者自己去保证线程安全了。
其中有状态就是有数据存储功能,无状态就是不会。

对于有状态的Bean,如何保证线程安全

使用ThreadLocal来保证。也可使用多例的模式,但是不推荐(在高并发场景下)。

Spring Bean 生命周期

Bean 在 Spring 容器中从创建到销毁经历了若干阶段,每一阶段都可以进行个性化定制。
请添加图片描述
(1)实例化:Spring 对 Bean 进行实例化;
(2)属性注入:Spring 将配置和 Bean 的引用注入到对应的属性中;
(3)如果 Bean 实现了 BeanNameAware 接口,Spring 将 Bean 的 ID 传递给 setBeanName() 方法;
(4)如果 Bean 实现了 BeanFactoryAware 接口,Spring 将调用 setBeanFactory() 方法将 BeanFactory 容器实例传入;
(5)如果 Bean 实现了 ApplicationContextAware 接口,Spring 将调用 setApplicationContext() 方法将 Bean 所在的应用上下文的引用传入进来;
(6)BeanPostProcessor前置处理:如果 Bean 实现了 BeanPostProcessor 接口,Spring 将调用它们的 postProcessBeforeInitialization() 方法;
(7)初始化:如果 Bean 实现了 InitializingBean 接口,Spring 将调用它们的 afterPropertiesSet() 方法。类似地,如果 Bean 使用 initmethod 声明了初始化方法,该方法也会被调用;
(8)BeanPostProcessor后置处理:如果 Bean 实现了 BeanPostProcessor 接口,Spring 将调用它们的postProcessAfterInitialization()方法;
(9)使用:此时,Bean 已经准备就绪,可以被应用程序使用了,它们将一直驻留在应用上下文中,直到该应用上下文被销毁;
(10)销毁:如果 Bean 实现了 DisposableBean 接口,Spring 将调用它的 destroy() 接口方法。同样,如果使用 destroymethod 声明了销毁方法,该方法也会被调用。

Spring 常用的Bean注解

(1) @Configuration、@Component、@Service、@Repository、@Controller:用于标识Bean的角色。
(2) @Autowired、@Qualifier、@Resource、@Value:用于自动装配Bean。
(3) @PostConstruct、@PreDestroy:用于指定初始化和销毁方法。
(4) @Transactional:用于声明式事务管理。
(5) @Scope:用于指定Bean作用域。
(6) @Async:用于实现异步编程。

@Bean和@Component有什么区别?

都是使用注解定义 Bean。@Bean 是使用 Java 代码装配 Bean,@Component 是自动装配 Bean。
@Component 注解用在类上,表明一个类会作为组件类,并告知Spring要为这个类创建bean,每个类对应一个 Bean。
@Bean 注解用在方法上,表示这个方法会返回一个 Bean。@Bean 需要在配置类中使用,即类上需要加上@Configuration注解。
@Bean 注解更加灵活。当需要将第三方类装配到 Spring 容器中,因为没办法源代码上添加@Component注解,只能使用@Bean 注解的方式。

Spring Bean 循环依赖及解决办法

循环依赖指两个或多个Bean之间相互引用,形成了一个无限循环调用的情况。在Spring容器中,如果存在循环依赖,但是都是单例模式的Bean,则可以通过Spring容器提前暴露正在创建的Bean,从而避免循环依赖问题。如果存在循环依赖且其中一方是原型模式的Bean,则Spring无法处理这种情况。
临时规避:显式配置参数。

循环依赖问题在Spring中主要有三种情况:
(1)通过构造方法进行依赖注入时产生的循环依赖问题。
(2)通过setter方法进行依赖注入且是在多例(原型)模式下产生的循环依赖问题。
(3)通过setter方法进行依赖注入且是在单例模式下产生的循环依赖问题。
在Spring中,只有第(3)种方式的循环依赖问题被解决了,其他两种方式在遇到循环依赖问题时都会产生异常。这是因为:
第一种构造方法注入的情况下,在new对象的时候就会堵塞住了,其实也就是”先有鸡还是先有蛋“的历史难题。
第二种setter方法(多例)的情况下,每一次getBean()时,都会产生一个新的Bean,如此反复下去就会有无穷无尽的Bean产生了,最终就会导致OOM问题的出现。
Spring在单例模式下的setter方法依赖注入引起的循环依赖问题,主要是通过二级缓存和三级缓存来解决的,其中三级缓存是主要功臣。解决的核心原理就是:在对象实例化之后,依赖注入之前,Spring提前暴露的Bean实例的引用在第三级缓存中进行存储

解释一下 Spring IoC

IoC简介

IoC(Inversion of Control,即"控制反转"),不是一种技术,而是一种设计思想。传统程序设计,直接在对象内部通过new的方式创建对象。而这种方式会导致程序间的耦合。假如类A依赖于接口IB,且在其内部创建了类B(实现接口IB)的一个实例。这种方式会将类B的实例的创建耦合到了类A中,当需要使用接口IB的另一个实现时,就需要更改代码。而IoC则反转依赖对象的获取方式。IoC提供一个容器来创建这些对象,即由Ioc容器来控制对象的创建。这样,当类A需要依赖接口IB时,仅需声明对IB的引用,具体的实例化由IoC容器负责。

DI简介

DI(Dependency Injection,依赖注入),就是让容器去决定依赖关系,即容器全权负责的组件的装配,它会把符合依赖关系的对象通过属性或者构造函数传递给需要的对象。常见的依赖注入实现有三种:构造方法注入、setter方法注入、接口注入(从 Spring4 开始已被废弃)等。
(1) 构造方法注入:即被注入对象可以通过在其构造方法中声明依赖对象的参数列表,让外部(通常是IoC容器)知道它需要哪些依赖对象,然后外部会检查被注入对象的构造方法,取得其所需要的依赖对象列表,进而为其注入相应对象。
(2) setter方法注入:即当前对象只需要为其依赖对象所对应的属性添加setter方法,IoC容器通过此setter方法将相应的依赖对象设置到被注入对象的方式即setter方法注入。
(3) 接口注入:被注入对象如果想要IoC容器为其注入依赖对象,就必须实现某个接口,这个接口提供一个方法,用来为被注入对象注入依赖对象,IoC容器通过接口方法将依赖对象注入到被注入对象中去。相对于前两种注入方式,接口注入比繁琐和死板,被注入对象必须声明和实现另外的接口。

解释一下AOP

AOP(Aspect-Oriented Programming,面向切面编程),可以看成OOP(Object-Oriented Programing,面向对象编程)的补充和完善。面向对象编程时,建立了一种对象层次结构,可以定义从上到下的关系,但不适合定义从左到右的关系。如日志功能。日志代码水平地散布在所有的对象层次中,也就是说,多个对象,使用相似或相同的日志代码。对于其他类型的代码,如安全性、异常处理等也是如此。这种散布在各个对象中的业务逻辑无关的代码被称为横切(cross-cutting)代码,在面向对象设计中,这种代码导致了大量的代码重复,不利于模块的重用。
简单来说,面向对象编程的自上而下的关系,不适合自左向右的关系,会带来横切代码的重复。而AOP技术主要针对这个问题进行优化。
AOP技术可以将那些影响了多个类的公共行为封装到一个可重用模块,并将其名为“Aspect”(即切面)。所谓“切面”,就是将那些与业务无关但业务模块所共同调用的逻辑或责任封装起来,从而减少系统的重复代码,降低模块间的耦合度。
AOP把软件系统分为两个部分:核心关注点和横切关注点。核心关注点主要指业务处理的主要流程,横切关注点则指与业务处理的主要流程关系不大的部分。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处都基本相似。比如权限认证、日志、事务处理。AOP的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离。正如Avanade公司的高级方案构架师Adam Magee所说,AOP的核心思想就是“将应用程序中的商业逻辑同对其提供支持的通用服务进行分离。”
请添加图片描述
(1) 切面(Aspect):切面是通知和切入点的结合。通知和切入点共同定义了切面的全部内容。
(2) 连接点(Join point):指方法,在Spring AOP中,一个连接点总是代表一个方法的执行。连接点是在应用执行过程中能够插入切面的一个点。这个点可以是调用方法时、抛出异常时、甚至修改一个字段时。切面代码可以利用这些点插入到应用的正常流程之中,并添加新的行为。
(3) 通知(Advice):在AOP术语中,切面的工作被称为通知。
(4) 切入点(Pointcut):切入点的定义会匹配通知所要织入的一个或多个连接点。通常使用明确的类和方法名称,或是利用正则表达式定义所匹配的类和方法名称来指定这些切点,最经典的使用方式是使用注解。
(5) 引入(Introduction):引入允许我们向现有类添加新方法或属性。
(6)目标对象(Target Object): 被一个或者多个切面(aspect)所通知(advise)的对象。它通常是一个代理对象。
(7) 织入(Weaving):织入是把切面应用到目标对象并创建新的代理对象的过程。在目标对象的生命周期里有以下时间点可以进行织入:
(a) 编译期:切面在目标类编译时被织入。AspectJ的织入编译器是以这种方式织入切面的。
(b) 类加载期:切面在目标类加载到JVM时被织入。需要特殊的类加载器,它可以在目标类被引入应用之前增强该目标类的字节码。AspectJ5的加载时织入就支持以这种方式织入切面。
(c) 运行期:切面在应用运行的某个时刻被织入。一般情况下,在织入切面时,AOP容器会为目标对象动态地创建一个代理对象。Spring AOP就是以这种方式织入切面。

Spring AOP应用场景

AOP常见于以下场景:
(1) 日志记录
(2) 权限校验和管理
(3) 缓存
(4) 事务

Spring AOP 实现原理

Spring AOP是基于动态代理实现。通过动态代理,可以对被代理对象的方法进行增强。Spring AOP用到了两种动态代理技术:JDK动态代理、CGLIB库。
JDK动态代理只能对实现了接口的类生成代理,而不能针对类。
CGLIB(Code Generation Library) 是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法(继承)。
Spring AOP默认策略是如果目标类是接口,则使用 JDK 动态代理技术,否则使用 Cglib 来生成代理。
更多动态代理的介绍可参考笔者之前的文章。

Spring AOP 使用方式

Spring Boot简化了AOP的使用。这里仅介绍如何在Spring Boot中使用AOP。
(1) 引入Maven依赖

<!--引用AOP注解功能开始-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!--引用AOP注解功能结束-->

(2) 自定义注解

/*** @Author: courage007* @Date: 2021/02/05/下午3:54* @Description:*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CustomAnnotation {/**** @return 指定值*/String value() default "";/**** @return 指定名称*/String name() default "";}

(3) 自定义切面并声明切入点并实现AOP通知

/*** @Author: courage007* @Date: 2021/02/05/下午3:47* @Description:*/
@Aspect
@Component
public class CustomAspect implements Ordered  {/*** * 切入点*/@Pointcut("@annotation(com.github.courage007.springdemo.annotation.CustomAnnotation)")public void pointCutCustomAnnotation() {// 无需内容System.out.println("Test Pointcut Method");}@Before("pointCutCustomAnnotation()")public void before(JoinPoint joinPoint) {try {Signature signature  = joinPoint.getSignature();//获取方法参数Object[] arguments = joinPoint.getArgs();//获取目标类Class<?> aClass = joinPoint.getTarget().getClass();//获取类名String targetName = aClass.getName();//获取当前方法名insertString methodName = signature.getName();//获取方法数组Method[] methods = aClass.getMethods();String operation = "";//获取当前方法1Class<?>[] argTypes = new Class[arguments.length];for (int i = 0; i < arguments.length; i++) {argTypes[i] = arguments[i] == null ? null : arguments[i].getClass();}Method method = null;try {method = aClass.getMethod(methodName, argTypes);operation = method.getAnnotation(CustomAnnotation.class).value();} catch (NoSuchMethodException e) {}//获取当前方法2MethodSignature signature1 = (MethodSignature)joinPoint.getSignature();Method method1 = signature1.getMethod();//获取方法参数类型数组Class<?>[] parameterTypes = method1.getParameterTypes();//获取当前方法3for (Method mm : methods) {if (mm.getName().equals(methodName)) {Class<?>[] clazzs = mm.getParameterTypes();if (clazzs.length == arguments.length) {//获取代理方法上注解的operation参数的值operation = mm.getAnnotation(CustomAnnotation.class).value();break;}}}//通过方法获取注解参数值CustomAnnotation serviceLog= method.getAnnotation(CustomAnnotation.class);String item= serviceLog.value();String biz = serviceLog.name();StringBuilder paramsBuf = new StringBuilder();for (Object arg : arguments) {paramsBuf.append(arg);paramsBuf.append("&");}} catch (Throwable e) {}}@After("pointCutCustomAnnotation()")public void after(JoinPoint joinPoint) {try {String targetName = joinPoint.getTarget().getClass().getName();String methodName = joinPoint.getSignature().getName();Object[] arguments = joinPoint.getArgs();Class<?> targetClass = Class.forName(targetName);Method[] methods = targetClass.getMethods();String operation = "";for (Method method : methods) {if (method.getName().equals(methodName)) {Class<?>[] clazzs = method.getParameterTypes();if (clazzs.length == arguments.length) {operation = method.getAnnotation(CustomAnnotation.class).value();break;}}}StringBuilder paramsBuf = new StringBuilder();for (Object arg : arguments) {paramsBuf.append(arg);paramsBuf.append("&");}} catch (Throwable e) {}}/*** 环绕通知处理处理** @param* @throws Throwable*/@Around("pointCutCustomAnnotation()")public Object around(ProceedingJoinPoint point) throws Throwable {// 先执行业务,注意:业务这样写业务发生异常不会拦截日志。Object result = point.proceed();try {handleAround(point);// 处理日志} catch (Exception e) {}return result;}@AfterReturning(pointcut = "pointCutCustomAnnotation()")public void doAfterReturning(JoinPoint joinPoint) {System.out.println("TTEESSTT");}/*** around日志记录** @param point* @throws SecurityException* @throws NoSuchMethodException*/public void handleAround(ProceedingJoinPoint point) throws Exception {Signature sig = point.getSignature();MethodSignature msig = null;if (!(sig instanceof MethodSignature)) {throw new IllegalArgumentException("该注解只能用于方法");}msig = (MethodSignature) sig;Object target = point.getTarget();Method currentMethod = target.getClass().getMethod(msig.getName(), msig.getParameterTypes());// 方法名称String methodName = currentMethod.getName();// 获取注解对象CustomAnnotation aLog = currentMethod.getAnnotation(CustomAnnotation.class);// 类名String className = point.getTarget().getClass().getName();// 方法的参数Object[] params = point.getArgs();StringBuilder paramsBuf = new StringBuilder();for (Object arg : params) {paramsBuf.append(arg);paramsBuf.append("&");}}@AfterThrowing(pointcut = "pointCutCustomAnnotation()", throwing = "e")public void doAfterThrowing(JoinPoint joinPoint, Throwable e) {try {String targetName = joinPoint.getTarget().getClass().getName();String className = joinPoint.getTarget().getClass().getName();String methodName = joinPoint.getSignature().getName();Object[] arguments = joinPoint.getArgs();Class<?> targetClass = Class.forName(targetName);Method[] methods = targetClass.getMethods();String operation = "";for (Method method : methods) {if (method.getName().equals(methodName)) {Class<?>[] clazzs = method.getParameterTypes();if (clazzs.length == arguments.length) {operation = method.getAnnotation(CustomAnnotation.class).value();break;}}}StringBuilder paramsBuf = new StringBuilder();for (Object arg : arguments) {paramsBuf.append(arg);paramsBuf.append("&");}} catch (Exception ex) {}}
}

(4) 使用自定义注解

/*** @Author: courage007* @Date: 2021/02/05/下午3:15*/
public class CustomController {@Autowiredprivate ICustomService customService;@GetMapping("/xxx")@CustomAnnotation(value = "测试", name = "test")public Integer getXXX() {return customService.getXXX();}
}

Spring AOP通知顺序

AOP支持的通知类型有:Before、After、Around、AfterReturning、AfterThrowing等。接下来详细说明下:
(1) Before:在目标方法被调用之前做增强处理,@Before只需要指定切入点表达式即可。
(2) AfterReturning:在目标方法正常完成之后做增强,@AfterReturning除了指定切入点表达式后,还可以指定一个返回值形参名returning,代表目标方法的返回值。
(3) AfterThrowing:主要用来处理程序中未处理的异常,@AfterThrowing除了指定切入点表达式后,还可以指定一个throwing的返回值形参名,可以通过该形参名来访问目标方法中所抛出的异常对象。
(4) After:在目标方法完成之后做增强,无论目标方法时候成功完成。@After可以指定一个切入点表达式。
(5) Around:环绕通知,在目标方法完成前后做增强处理,环绕通知是最重要的通知类型,像事务、日志等都是环绕通知,注意编程中核心是一个ProceedingJoinPoint。
注意,spring aop在方法正常返回和抛出异常时,各个通知的执行顺序不同。此外,Spring版本不一样,通知执行顺序也会存在差异。
(1) Spring4.0
正常情况:环绕前置=====@Before目标方法执行=环绕返回=环绕最终===@After=====@AfterReturning
异常情况:环绕前置=====@Before目标方法执行=环绕异常=环绕最终===@After=====@AfterThrowing
(2) Spring5.28
正常情况:环绕前置=====@Before=目标方法执行=@AfterReturning=====@After=环绕返回=环绕最终
异常情况:环绕前置=====@Before=目标方法执行=@AfterThrowing=====@After=环绕异常=环绕最终
可见,Spring 5 提前了@AfterXXX的执行位置。

多个切面的执行顺序

上面的例子仅描述单个切面的执行顺序,如果在同一个方法有多个AOP,其执行不会存在任何顺序。也就是说,这些代码会随机生成。如果需要按照指定的顺序运行,还需手动设置优先级。
请添加图片描述
Spring aop就是一个同心圆,要执行的方法为圆心,最外层的order最小。从最外层按照AOP1、AOP2的顺序依次执行doAround方法,doBefore方法。然后执行method方法,最后按照AOP2、AOP1的顺序依次执行doAfterReturn、doAfter方法。也就是说对多个AOP来说,先before的,一定后after

配置多个切面的执行顺序的方式

Spring支持多种方式配置AOP执行顺序,如实现实现org.springframework.core.Ordered接口、使用@Ordered注解、配置文件添加配置等。
(1) 实现org.springframework.core.Ordered接口

@Aspect  
@Component  
public class CustomAspectWithOrder implements Ordered {@Override  public int getOrder() {  return 1;  }  }  

(2) 使用@Ordered注解

@Aspect  
@Component  
@Order(1)  
public class CustomAspectWithOrder {}  

(3) 配置文件添加配置

<aop:config expose-proxy="true">  <aop:aspect ref="aopBean" order="0">    <aop:pointcut id="testPointcut"  expression="@annotation(xxx.xxx.xxx.annotation.xxx)"/>    <aop:around pointcut-ref="testPointcut" method="doAround" />    </aop:aspect>    
</aop:config> 

@Pointcut注解

@Pointcut注解用于声明切入点。在@Pointcut注解中,通过书写切入点表达式来描述需要处理的连接点集合。@Pointcut注解定义如下:

 /*** Pointcut declaration** @author Alexandre Vasseur*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Pointcut {/*** @return the pointcut expression* We allow "" as default for abstract pointcut*/String value() default "";/*** When compiling without debug info, or when interpreting pointcuts at runtime,* the names of any arguments used in the pointcut are not available.* Under these circumstances only, it is necessary to provide the arg names in * the annotation - these MUST duplicate the names used in the annotated method.* Format is a simple comma-separated list.* * @return argNames the argument names (should match those in the annotated method)*/String argNames() default "";
}

@DeclareParents注解

在面向切面的编程中,对方法的增强叫做Wearing(织入),而对类的增强叫introduction(引入)。Introduction Advice(引入增强)就是对已有类添加方法,它也是面向切面编程提供的一种特殊增强。
在Spring AOP中,使用@DeclareParents注解实现引入增强。
(1) 定义增强方法及对应类

/*** @Author: courage007* @Date: 2021/02/05/下午8:49* @Description:*/
public interface ICustomServiceProxy {Integer getYYY();
}
/*** @Author: courage007* @Date: 2021/02/05/下午8:50* @Description:*/
public class CustomServiceProxyImpl implements ICustomServiceProxy {@Overridepublic Integer getYYY() {return 222;}
}

(2) 切面中声明@DeclareParents注解

/*** @Author: courage007* @Date: 2021/02/05/下午3:47* @Description:*/
@Order(1)
@Aspect
@Component
public class CustomAspect {@DeclareParents(value = "com.github.courage007.springdemo.service.impl.CustomService*", defaultImpl = CustomServiceProxyImpl.class)private ICustomServiceProxy customServiceProxy;
}

(3) 使用引入增强的方法

/*** @Author: courage007* @Date: 2021/02/05/下午3:15* @Description:*/
@RestController
@RequestMapping("custom")
public class CustomController {@Autowiredprivate ICustomService customService;@GetMapping("/xxx")@CustomAnnotation(value = "测试", name = "test")public Integer getXXX() {// 使用强制类型转换ICustomServiceProxy customServiceProxy = (ICustomServiceProxy) customService;customServiceProxy.getYYY();return customService.getXXX();}
}

Spring 事务

Spring事务的本质就是数据库对事务的支持。Spring框架提供统一的事务抽象,通过统一的编程模型使得应用程序可以很容易地在不同的事务框架之间进行切换。
无论是JTA、JDBC、Hibernate/JPA、Mybatis/Mybatis-Plus,Spring都使用统一的编程模型,使得应用程序可以很容易地在不同的事务框架之间进行切换。这也符合面向接口编程思想。Spring事务框架的代码在org.springframework:spring-tx中。Spring事务抽象的核心类图如下:
请添加图片描述
Spring事务管理的核心接口是PlatformTransactionManager。接口PlatformTransactionManager定义事务操作的行为,PlatformTransactionManager依赖TransactionDefinition和TransactionStatus接口。TransactionDefinition接口定义与Spring兼容的事务属性(如隔离级别、事务传播行为等)。TransactionStatus接口则定义事务的状态(如是否回滚、是否完成、是否包含安全点(Save Point)、将基础会话刷新到数据存储区(如果适用)等)。

PlatformTransactionManager简介

PlatformTransactionManager是Spring事务框架的核心接口。应用程序可以直接使用PlatformTransactionManager,但它并不是主要用于API:应用程序将借助事务模板(TransactionTemplate)或声明式事务(Declarative Transaction)。
对于需要实现PlatformTransactionManager接口的应用程序,可通过继承AbstractPlatformTransactionManager抽象类的方式实现。AbstractPlatformTransactionManager类已实现事务传播行为和事务同步处理。子类需要实现针对事务特定状态(如:begin,suspend,resume,commit)的模板方法。Spring事务框架已经实现了JtaTransactionManager(JPA)和DataSourceTransactionManager(JDBC)。应用程序可以参考以上方法实现事务管理器。PlatformTransactionManager事务继承示例如下:
请添加图片描述

Spring事务隔离级别和传播级别

TransactionDefinition接口中定义了Spring事务隔离级别和Spring事务传播级别。隔离级别主要控制事务并发访问时隔离程度。Spring支持的隔离级别如下:
请添加图片描述
除了使用ISOLATION_DEFAULT表示使用数据库默认的隔离级别外,其余四个隔离级别与数据库规范的隔离级别一致。
需要注意的是,隔离级别越高,意味着数据库事务并发执行性能越差。JDBC规范虽然定义了事务支持的以上行为,但是各个JDBC驱动、数据库厂商对事务的支持程度可能各不相同。出于性能的考虑我们一般设置READ_COMMITTED级别。针对READ_COMMITTED隔离级别无法避免的脏读,通常使用数据库的锁来处理。
传播级别主要控制含事务方法的调用(如一个事务方法调用另一个事务方法)时,Spring对事务的处理方式。Spring事务传播级别共七类。它们是:
(1)PROPAGATION_REQUIRED:支持当前事务,如果当前有事务则加入,如果当前没有事务则新建一个。这种方式是默认的事务传播方式。(一般直接使用,不需要调整)
(2)PROPAGATION_SUPPORTS:支持当前事务,如果当前有事务则加入,如果当前没有事务则以非事务方式执行。
(3)PROPAGATION_MANDATORY:支持当前事务,如果当前有事务则加入,如果当前没有事务则抛出异常。(当前必须有事务)
(4)PROPAGATION_REQUIRES_NEW:不支持当前事务,如果当前有事务则挂起当前事务,然后新创建一个事务,如果当前没有事务则自己创建一个事务。
(5)PROPAGATION_NOT_SUPPORTED:不支持当前事务,如果当前有事务则把当前事务挂起,执行完后恢复事务(忽略当前事务)。
(6)PROPAGATION_NEVER:不支持当前事务,如果当前存在事务,则抛出异常。(当前必须不能有事务)
(7)PROPAGATION_NESTED:如果当前存在事务,则嵌套在当前事务中。如果当前没有事务,则新建一个事务自己执行。对嵌套事务来说,内部事务回滚时不会影响外部事务的提交;但是外部事务回滚会把内部事务一起回滚。(这个和新建一个事务的区别)

Spring事务失效

常见的事务失效场景有:
(1) 异常类型不对:默认支持回滚的是 Runtime 异常,或异常被业务捕获。
(2) 数据源不支持事务:如 MySQL 未开启事务或使用 MyISAM 存储引擎。
(3) 非 Public 方法不支持事务。
(4) Spring传播类型不支持事务。
(5) 事务未被 Spring 接管。

Spring事务有优势

(1) 为不同的事务API 如 JTA,JDBC,Hibernate,JPA 和JDO,提供一个不变的编程模式。
(2) 为编程式事务管理提供了一套简单的API而不是一些复杂的事务API
(3) 支持声明式事务管理。
(4) 和Spring各种数据访问抽象层很好得集成。

参考

https://blog.csdn.net/adminpd/article/details/123016872 常见Java后端面试题系列——Spring篇
https://blog.csdn.net/qq_57434877/article/details/123714044 spring创建Bean的流程以及Bean的生命周期
https://thinkwon.blog.csdn.net/article/details/104397516 Spring面试题
https://blog.csdn.net/adminpd/article/details/123016872 Java后端面试题系列——Spring篇
https://zhuanlan.zhihu.com/p/623502268 Spring面试题详解—从基础到进阶
https://zhuanlan.zhihu.com/p/493343355 Spring面试题
https://blog.csdn.net/q982151756/article/details/80513340 细说Spring——AOP详解(AOP概览)
https://blog.51cto.com/5914679/2092253#h10 Spring AOP 切点(pointcut)表达式
https://blog.csdn.net/weixin_46009162/article/details/113333311 Spring boot AOP结合注解的使用
https://cloud.tencent.com/developer/article/1441626 spring aop概念、使用、动态代理原理
https://www.jianshu.com/p/5b9a0d77f95f spring aop 及实现方式
https://www.cnblogs.com/orzjiangxiaoyu/p/13869747.html Spring-AOP-基于注解的AOP通知执行顺序
https://blog.csdn.net/hxpjava1/article/details/55504513 spring多个AOP执行先后顺序
https://www.jianshu.com/p/f7238613c877 Spring AOP注解@DeclareParents的使用
https://www.cnblogs.com/chihirotan/p/7365890.html Spring AOP中引入增强
https://www.cnblogs.com/wangshen31/p/9383828.html 用注解@DelcareParents实现引用增强
https://blog.csdn.net/a745233700/article/details/80959716 Spring常见面试题总结

相关文章:

Spring常见面试题总结

什么是Spring Spring是一个轻量级Java开发框架&#xff0c;目的是为了解决企业级应用开发的业务逻辑层和其他各层的耦合问题&#xff0c;以提高开发效率。它是一个分层的JavaSE/JavaEE full-stack&#xff08;一站式&#xff09;轻量级开源框架&#xff0c;为开发Java应用程序…...

Git全套命令使用

日升时奋斗&#xff0c;日落时自省 目录 1、Git安装 1.1、创建git本地仓库 1.2、配置Git 1.3、认识Git内部区分 2、Git应用操作 2.1、添加文件 2.2、查看日志 2.3、查看修改信息 2.4、查看添加信息 3、版本回退 4、撤销修改 4.1、工作区撤销 4.2、已经add&#xf…...

【陕西理工大学-数学软件实训】数学实验报告(8)(数值微积分与方程数值求解)

目录 一、实验目的 二、实验要求 三、实验内容与结果 四、实验心得 一、实验目的 1. 掌握求数值导数和数值积分的方法。 2. 掌握代数方程数值求解的方法。 3. 掌握常微分方程数值求解的方法。 二、实验要求 1. 根据实验内容&#xff0c;编写相应的MATLAB程序&#xff0c…...

Vue3为什么推荐使用ref而不是reactive

为什么推荐使用ref而不是reactive reactive本身具有很大局限性导致使用过程需要额外注意,如果忽视这些问题将对开发造成不小的麻烦;ref更像是vue2时代option api的data的替代,可以存放任何数据类型,而reactive声明的数据类型只能是对象; 先抛出结论,再详细说原因:非必要不用rea…...

JavaScript函数this指向

一、this的指向规则 1.this到底指向什么呢&#xff1f; 我们先来看一个让人困惑的问题&#xff1a; 定义一个函数&#xff0c;我们采用三种不同的方式对它进行调用&#xff0c;它产生了三种不同的结果 // 定义函数 function foo(name) {console.log("foo函数:", …...

Java的序列化

写在前面 本文看下序列化和反序列化相关的内容。 源码 。 1&#xff1a;为什么&#xff0c;什么是序列化和反序列化 Java对象是在jvm的堆中的&#xff0c;而堆其实就是一块内存&#xff0c;如果jvm重启数据将会丢失&#xff0c;当我们希望jvm重启也不要丢失某些对象&#xff…...

计算机二级python简单应用题刷题笔记(一)

计算机二级python简单应用题刷题笔记&#xff08;一&#xff09; 1、词频统计&#xff1a;键盘输入一组我国高校所对应的学校类型&#xff0c;以空格分隔&#xff0c;共一行。2、找最大值、最小值、平均分&#xff1a;键盘输入小明学习的课程名称及考分等信息&#xff0c;信息间…...

Spring注解家族介绍: @RequestMapping

前言&#xff1a; 今天我们来介绍RequestMapping这个注解&#xff0c;这个注解的内容相对来讲比较少&#xff0c;篇幅会比较短。 目录 前言&#xff1a; RequestMapping 应用场景&#xff1a; 总结&#xff1a; RequestMapping RequestMapping 是一个用于映射 HTTP 请求…...

系统架构设计师(第二版)学习笔记----信息安全系统及信息安全技术

【原文链接】系统架构设计师&#xff08;第二版&#xff09;学习笔记----信息加解密技术 文章目录 一、信息安全系统的组成框架1.1 信息安全系统组成框架1.2 信息安全系统技术内容1.3 常用的基础安全设备1.4 网络安全技术内容1.5 操作系统安全内容1.6 操作系统安全机制1.7 数据…...

交换机的工作原理(含实例,华为ensp操作)

目录​​​​​​​ ​​​​​​​1.交换机学习和转发 案例 1.设置静态地址表项 2.配置黑洞mac地址表项 1.交换机学习和转发 交换机工作在数据链路层。当交换机从某个端口收到一个帧时&#xff0c;它并不是向所有的接口转发此帧&#xff0c;而是根据此帧的目的MAC地址&a…...

从字符串中删除指定字符

任务描述 编写一个函数实现功能&#xff1a;从字符串中删除指定的字符。同一字母的大、小写按不同字符处理。例如&#xff1a;程序执行时输入字符串&#xff1a;turbo c and Borland c&#xff0c;从键盘输入字符n&#xff0c;则输出后变为&#xff1a;turbo c ad Borlad c。如…...

Xcode14.3.1 真机调试iOS17的方法(无iOS17 DeviceSupport)

由于iOS17需要使用Xcode15 才能调试&#xff0c;而当前Xcode15都是beta&#xff0c;正式版还未出&#xff0c;那么要真机调试iOS17的方式一般有两种&#xff1a; 方法一&#xff1a; 一种是下载新的Xcode15 beta版 &#xff08;但Xcode包一般比较大&#xff0c;好几个G&#…...

JWT基础

概念 JSON Web Token本质上就是一串字符串&#xff0c;一串包含了很多信息的字符串令牌拥有三个部分头部-包含加密算法和令牌类型{"alg":"算法名称","type":"JWT"}负载-包含数据和信息-七个官方默认-也可以自己定义内容{iss&#xff…...

关于远程工作的面试可能存在的陷阱

附上看到的完整帖子地址&#xff1a;面试 POPER 的后端开发工程师的离奇经历 分享一下我遇到过的&#xff0c;我至少面试过10个远程工作&#xff0c;其中有3个的面试是直接让我完成一个需求的&#xff0c;前两次都耐心做了&#xff0c;第3次看到相同要求时我都懒得回复了&…...

Qt5开发及实例V2.0-第一章Qt概述

Qt5开发及实例V2.0-第一章-Qt概述 第一章-Qt概述1.1 什么是Qt1.2 Qt 5的安装1.2.1 下载安装Qt 51.2.2 运行Qt 5 Creator1.2.3 Qt 5开发环境 1.3 Qt 5开发步骤及实例1.3.1 设计器Qt 5 Designer实现1.3.2 代码实现简单实例 L1.2 Qt 5安装&#xff1a;概念解析L1.3 Qt 5开发步骤及…...

matlab检索相似图像

在Matlab中检索相似图像通常需要使用图像处理和计算机视觉技术。以下是一种常见的方法&#xff0c;可以帮助您在Matlab中进行相似图像检索&#xff1a; 准备图像数据库&#xff1a; 首先&#xff0c;您需要有一个包含待检索图像的图像数据库。这些图像应该经过预处理&#xff0…...

ArrayBlockingQueue 带有三个参数的构造函数为何需要加锁?

哪一个构造函数 public ArrayBlockingQueue(int capacity, boolean fair,Collection<? extends E> c) {this(capacity, fair);final ReentrantLock lock = this.lock;lock.lock(); // Lock only for visibility, not mutual exclusiontry {final Object[] items = this…...

实训笔记——Spark计算框架

实训笔记——Spark计算框架 Spark计算框架一、Spark的概述二、Spark的特点三、Spark的安装部署&#xff08;安装部署Spark的Cluster Manager-资源调度管理器的&#xff09;3.1 本地安装--无资源管理器3.2 Spark的自带独立调度器Standalone3.2.1 主从架构的软件3.2.2 Master/wor…...

自定义类型:结构体

自定义类型&#xff1a;结构体 一&#xff1a;引入二&#xff1a;结构体类型的声明1&#xff1a;正常声明2&#xff1a;特殊声明 三&#xff1a;结构体变量的创建和初始化1:结构体变量的创建2&#xff1a;结构体变量的初始化 三&#xff1a;结构体访问操作符四&#xff1a;结构…...

postman如何设置才能SwitchHosts切换host无缓存请求到指定ip服务

开发测试中,遇到多版本同域名的服务使用postman进行测试,一般会搭配SwitchHosts切换host类似工具进行请求,postman缓存比较重,如何做到无缓存请求呢,下面简单记录一下如何实现 首先要知道如何当前请求服务的ip是哪个 打开postman 依次点击/menu/view/show postman console 就…...

LeetCode LCR 103. 零钱兑换【完全背包,恰好装满背包的最小问题】中等

本文属于「征服LeetCode」系列文章之一&#xff0c;这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁&#xff0c;本系列将至少持续到刷完所有无锁题之日为止&#xff1b;由于LeetCode还在不断地创建新题&#xff0c;本系列的终止日期可能是永远。在这一系列刷题文章…...

竞赛 基于深度学习的人脸专注度检测计算系统 - opencv python cnn

文章目录 1 前言2 相关技术2.1CNN简介2.2 人脸识别算法2.3专注检测原理2.4 OpenCV 3 功能介绍3.1人脸录入功能3.2 人脸识别3.3 人脸专注度检测3.4 识别记录 4 最后 1 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 基于深度学习的人脸专注度…...

supervisord 进程管理器 Laravel执行队列

supervisord 进程管理器 执行队列 安装 yum install supervisor修改配置文件 /etc/supervisord.conf 最后一行 ini改为conf files=/etc/supervisor.d/*.conf vim /etc/supervisord.conf/etc/supervisord.d目录下新增配置文件 vim laravel-worker.conf 修改i 粘贴内容 退出修…...

Lnmp架构之mysql数据库实战1

1、mysql数据库编译 编译成功 2、mysql数据库初始化 配置数据目录 全局文件修改内容 生成初始化密码并进行初始化设定 3、mysql主从复制 什么是mysql的主从复制&#xff1f; MySQL的主从复制是一种常见的数据库复制技术&#xff0c;用于将一个数据库服务器&#xff08;称为主…...

ChatGLM 大模型炼丹手册-理论篇

序言一)大还丹的崛起 在修真界,人们一直渴望拥有一种神奇的「万能型丹药」,可包治百病。 但遗憾的是,在很长的一段时间里,炼丹师们只能对症炼药。每一枚丹药,都是特效药,专治一种病。这样就导致,每遇到一个新的问题,都需要针对性的炼制,炼丹师们苦不堪言,修真者们吐…...

Spring Boot集成Redis实现数据缓存

&#x1f33f;欢迎来到衍生星球的CSDN博文&#x1f33f; &#x1f341;本文主要学习Spring Boot集成Redis实现数据缓存 &#x1f341; &#x1f331;我是衍生星球&#xff0c;一个从事集成开发的打工人&#x1f331; ⭐️喜欢的朋友可以关注一下&#x1faf0;&#x1faf0;&…...

CentOS 7 安装Libevent

CentOS 7 安装Libevent 1.下载安装包 新版本是libevent-2.1.12-stable.tar.gz。&#xff08;如果你的系统已经安装了libevent&#xff0c;可以不用安装&#xff09; 官网&#xff1a;http://www.monkey.org/~provos/libevent/ 2.创建目录 # mkdir libevent-stable 3.解压 …...

线性代数的本质——几何角度理解

B站网课来自 3Blue1Brown的翻译版&#xff0c;看完醍醐灌顶&#xff0c;强烈推荐&#xff1a; 线性代数的本质 本课程从几何的角度翻译了线代中各种核心的概念及性质&#xff0c;对做题和练习效果有实质性的提高&#xff0c;下面博主来总结一下自己的理解 1.向量的本质 在物…...

SSH key 运作方式

1、本地创建SSH key pairs 2、把public key上传到网站服务器&#xff08;如GitHub 3、当使用ssh方式连接时 本地SSH client向远端请求ssh连接远端发来random data要求加密本地ssh client用private key加密&#xff0c;把加密的data发送过去&#xff08;不发送private key远端接…...

【基于MBD开发模式的matlab持续集成(一)】

基于MBD开发模式的matlab持续集成 引言 或许是感受到行业内卷的愈加激烈&#xff0c;在传统制造和高新技术相结合的新能源领域对软件工程开发的要求也愈加提高&#xff0c;尤其在互联网已经大行 其道的敏捷开发&#xff0c;便顺其自然的被新能源的老板们所看重。 概述 本文…...

在手机上做网站/个人网站制作模板主页

一、前言 highlight.js是一款轻量级的Web代码语法高亮库。 下载地址&#xff1a;https://highlightjs.org/download/ 下载插件库的时候可以选择你需要的语言&#xff0c;然后再打包&#xff0c;下载后会发现有个style文件夹&#xff0c;里面放了各种不同的显示颜色。 二、使用…...

外包网站推荐/谷歌浏览器安卓下载

1、对windows2000系统&#xff1a;点开始&#xff0c;点运行,输入rundll32 netplwiz.dll,UsersRunDll 把使用本机必须输入用户和密码的勾去掉。 2、对windows2000以上系统&#xff1a;点开始&#xff0c;点运行,输入control userpasswords2把使用本机必须输入用户和密码的勾去掉…...

长春做网站的/南宁seo外包服务

一、Nginx常见面试题Nginx 是如何实现并发的?为什么 Nginx 不使用多线程?Nginx常见的优化手段有哪些?502错误可能原因有哪些?二、面试官心理分析主要是看应聘人员的对NGINX的基本原理是否熟悉&#xff0c;因为大多数运维人员多多少少都懂点NGINX&#xff0c;但是真正其明白…...

宁波网站建设与设计制作/百度商店

加油加油转载于:https://www.cnblogs.com/naliang/p/4974360.html...

邯郸网站建设的地方/新手如何学seo

1、类SQL PLUS窗口:File->New->Command Window&#xff0c;这个类似于oracle的客户端工具sql plus&#xff0c;但比它好用多了。2、设置关键字自动大写:Tools->Preferences->Editor&#xff0c;将Keyword case选择Uppercase。这样在窗口中输入sql语句时&#xff0c…...

安远做网站/线下推广方案

本人&#xff0c;是属于那种心思比较重的人&#xff0c;当然不是那种坏心眼。就是比如本次工作哪里有问题&#xff0c;领导指出来&#xff0c;或是有些事情做的不是很到位&#xff0c;出现失误。在下次做这些事情的时候&#xff0c;我就会特别的紧张&#xff0c;甚至比较害怕会…...