一)什么是Spring?它有什么优点?
spring是一款顶级的开源框架,他是包含了众多工具方法的IOC容器,Spring中包含了很多模块,比如说Spring-core,Spring-context,Spring-aop,Spring-web,从而更好地帮助我们开发JAVA程序;
1)管理对象的生命周期和依赖关系:Spring提供了IOC容器,它是用来管理应用程序中的对象的生命周期和依赖关系,通过使用DI,可以将对象的依赖关系从代码中抽离出来,使得代码具有可测试性和可维护性;
2)统一业务处理,面向切面编程:Spring本身支持AOP,能够分离横切关注点比如说日志和事务管理和核心业务逻辑;
3)事务管理:手动式事务+编程式事务
4)集成了很多的持久性框架:MyBatis
5)可测试性:
6)丰富的生态系统:提供了更多的第三方插件和库
7)国际化和本地化支持:使得应用程序可以使用不同的语言和地区
二)什么是IOC?对象生命周期的控制权限的反转,是一种思想
IOC是Inversion of Controll翻译成中文是控制反转的意思,它不是一个具体的技术而是用来实现对象解耦的思想,控制反转的意思就是将依赖对象的生命周期的控制权进行反转,传统开发是将当前类new对象是控制依赖对象的生命周期,现在交给Spring IOC框架,这就是控制权发生了反转;
三)IOC的优点有哪些?
3.1)解耦合:IOC将组件之间的依赖关系从代码中抽离出来,实现了解耦合
3.2)代码的简洁性:IOC使得你的代码更加的关注于业务逻辑,而不需要过多的关注与依赖的创建和管理,这就是得代码更加清晰,简洁和易于管理
3.3)对象生命周期的管理:IOC容器可以管理组件的生命周期,确保Bean对象在合适的时间创建,初始化和销毁;
3.4)可重用性:默认的Bean对象都是单例模式,可以实现重用;
3.5)AOP实现基础:IOC是AOP实现的基础,允许将代理对象存放在IOC容器中
四)什么是DI:
DI是Dependency Injection的缩写,翻译成中文是依赖注入的意思,依赖注入不是一种设计实现而是一种具体的技术,它是在程序运行的过程中,动态地将某一个依赖对象注入到当前的对象的过程就叫做依赖注入,在程序运行过程中,动态地从Spring IOC容器中取出之前存放过的对象,也就是该程序运行过程中从SpringIOC容器中依赖的对象,从程序运行的过程中动态地将一个类注入到当前类中
就比如说在A对象中需要依赖B对象,那么在A对象运行过程中,动态地将依赖对象B注入到当前类中,而不是直接通过new的方式来获取到B对象的方式,就是依赖注入
五)IOC和DI有什么区别?
IOC和DI虽然定义不同,但是他们所做的事情都是相同的,都是来实现针对于对象进行解耦合的,IOC是一种思想,但是DI是一种实现,况且IOC除了依赖注入以外还可以通过依赖查找来实现,在Spring框架中可以通过ApplicationContext接口的getBean()方法来实现
对IOC和DI的理解:
IOC:是Inversion of Control,是面向对象编程中的一种常见的设计原则,主要是通过IOC容器,对Bean对象来进行统一的一个管理,以及组织对象的之间的依赖关系,获取依赖对象的过程,原本是由程序自己进行控制的,现在变成了IOC容器进行自动注入,控制权发生了反转,所以叫IOC控制反转
之前是需要我们自己来管理对象的,需要代码当前类来管理对象的,但是现在不是由当前类来进行管理了,而是直接交给IOC容器来进行管理,控制权发生了反转;
DI:就是在IOC容器在运行期间,动态地将某种依赖关系注入到对象里面
DI的实现方式只有两种,构造方法注入和属性注入,依赖注⼊(DI)和控制反转(IoC)是从不同的角度的描述的同⼀件事情,就是指通过引⼊IoC容器,利⽤依赖关系注⼊的⽅式,实现对象之间的解耦
实现原理:主要是依赖于反射或者是ASM字节码框架实现,字节码框架操作字节码更为高效,功能是更强大的;
bean容器和IOC容器:
IOC容器是控制反转,是管理Bean对象的,IOC容器一定是Bean容器,但是Bean容器不一定是IOC容器,IOC范围更小,Bean容器范围更大;
SpringIOC容器主要是对IoC设计模式的实现,主要是使⽤容器来统⼀管理Bean对象,及管理对象之间的依赖关系,但是IOC只是Bean容器实现的一种模式,有可能也有其他的实现模式
创建容器的API主要是BeanFactory和ApplicationContext两种:
BeanFactory相当于是父亲,ApplicationContext相当于是儿子,儿子用于父亲的所有功能,还扩展出了自己所拥有的一些功能;
1)BeanFactory是最底层的容器接⼝,只提供了最基础的容器功能:Bean 的实例化和依赖注⼊,并且使⽤懒加载的⽅式,这意味着 beans 只有在我们通过 getBean() ⽅法直接调⽤它们时才进⾏实例化,更加节省内存;
2.)ApplicationContext(应⽤上下⽂)是BeanFactory的⼦接⼝,与 BeanFactory 懒加载的⽅式不同,它是预加载,所以,每⼀个 bean 都在 ApplicationContext 启动之后实例化,更加注重速度;
3) 除了基础功能,还添加了很多增强:3.1)整合了Bean的⽣命周期管理
3.2)国际化功能(MessageSource)
3.3)载⼊多个(有继承关系)上下⽂ ,使得每⼀个上下⽂都专注于⼀个特定的层次,⽐如应⽤的web层
3.4)事件传播机制和AOP
六)说一下AOP:
AOP面向切面编程可以说是OOP的补充和完善,OOP引入了封装继承多态等概念来建立一种公共对象处理的能力但是当我们进行处理某一个公共行为的时候,OOP就会显得无能为力,但是AOP的出现恰好解决了这个问题,比如说统一的日志处理模块,授权登录模块都可以很好地使用AOP来进行处理的;
1)AOP:是面向切面编程,对针对业务代码横切来实现统一的业务管理,而不用侵入业务代码本身,这样面向切面编程的思想就是AOP
使用场景:日志管理,事务管理,性能统计,安全控制,异常处理
2)优点:实现了代码的解耦,统一业务功能对具体业务没有什么侵入性,这样可扩展性更好,灵活性更高
3)Spring是采取动态代理的方式,具体是基于JDK和CGLIB两种:
JDK动态代理需要被代理类实现接口,使用的是反射的方式;
CGLIB需要被代理类能够被继承,不能被final修饰,底层是基于ASM字节码框架,在运行的时候动态生成代理类,Spring默认的动态代理是JDK proxy,SpringBoot的默认动态代理是CGLIB;
4)SpringAOP如何进行使用:使用@Aspect来进行定义切面,并注册到容器里面,使用@Pointcut定义好切点方法之后,可以对目标方法来进行拦截,然后执行前置通知,后置通知之类的东西
方法名必须相同,代理的方法和目标类被调用的方法必须相同,JDK proxy动态代理类和原来的类的方法名是相同的,这个实现就是依靠接口来实现的;
AOP的优点:
1)集中针对于某一个问题来做集中的处理,方便进行维护
2)逻辑更加清晰
3)解耦合,降低模块之间的复杂度
七)如何将Bean存入到Spring中?
1)通过xml方式来进行注入,就是在Spring容器中通过XML的方式进行注入,在配置文件中加入Bean标签,id是Bean的标识读取的时候来进行使用,class是你要注册到Spring容器中的完整的包名+类名;
2)注解的方式来进行注入:
2.1)通过类注解来进行注入Bean,比如说@Controller,@Service,@Resposity,@Component,@Configuration等等
2.2)通过方法注解来进行注入比如说@Bean,注意方法注解也要配合类注解来使用
八)Spring中@Bean名称冲突怎么办?
默认情况下使用@Bean注解注入对象的时候,Bean的名称是方法名,此时如果一个类中方法重名了,就会导致Spring中只能存储一个Bean,同一个类中多个Bean名称重复也是同样的效果,此时如果想要解决这个问题,就需要手动指定@Bean是不同的名字了,通过@Bean的name属性来进行设置:此时Bean的名称就从默认的getUser变成user1了
九)BeanFactory和FacroryBean有什么区别?
1)BeanFactory是管理和获取Bean的,通过BeanFactory可以来获取到Spring的上下文对象从而来获取Bean
2)FactoryBean是一个特殊的Bean,是用来生成Bean对象,但是使用FactoryBean可以创建普通的Bean对象和AOP的代理对象
BeanFactory是Spring框架提供的核心接口之一,它是一个工厂模式的实现,负责管理bean,它提供了很多的接口来获取到Bean,包括按照名称来获取,按照类型获取等等,大多数的使用场景就是从IOC容器中来获取到Bean对象
FactoryBean本身就是一个普通的Bean,但是它是一个特殊的Bean,它是一个能够生成Bean对象的工厂,它在Spring中比较典型的应用就是创建AOP的代理对象
十)属性注入和构造方法注入和Setter注入都有什么缺点?
1)属性注入的缺点:
1.1)只是适用于IOC容器
1.2)功能性问题:无法注入一个不可变的对象,也就是final修饰的对象,因为在JAVA中final对象要么直接赋值,要么在构造方法中赋值,所以当属性注入注入final对象的时候,他不符合JAVA对于final的使用规范,所以就不能注入成功了;
1.3)通用设计原则问题:更容易违背单一设计原则问题
2)Setter注入的缺点:不可以注入不可变对象和注入对象可以被修改
注入对象可以被修改:Setter注入提供了setXXX的方法,意味着你可以在任何时刻,适合地方来调用setXXX方法来改变注入对象,所以说Setter对象注入的问题就是被注入的对象可能随时被修改
3)构造方法注入的优点:
可以注入不可以变的对象,完全初始化,通用性更好,注入对象不会被修改
十一)Bean的作用域都有哪些:
Bean的作用域指的是Bean的实例在应用程序中的生命周期和可见范围,就比如说单例作用域Singleton,就表示Bean在整个Spring容器中只是存在一份,它是全局共享的,当有人修改了这个值以后,那么另一个人读取到的就是修改以后的值
实例化时系统自动去执行的,初始化是指执行用户写的东西,代码从系统执行到了用户;
在SpringCore项目中默认是单例模式和原型模式,在SpringMVC里面又多了四种;
3.1)singleton:唯一的Bean实例,Spring中的Bean默认都是单例模式
3.2)prototype:每一次请求都会创建一个新的Bean对象
3.3)request:每一次HTTP请求都会创建一个新的Bean,该Bean只会在Http Request中有效
3.4)session:每一次会话中使用一个共享Bean
3.5)application:在一个应用的Servlet上下文生命周期中,产生一个新的Bean
3.6)websocket:在一个WebSocket生命周期中,使用一个共享Bean
如何来设置@Bean的作用域呢?
直接来设置作用域的具体的值@Scope("prototype")或者是直接设置@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE")
十二)Spring解决循环依赖的问题
循环依赖问题解决的就是在对象之间存在着相互之间的依赖关系,形成一个闭环,导致无法精准地完成对象的实例化和初始化,当两个对象之间或者是多个对象之间存在着相互引用,而这种引用关系形成一个闭环的时候,就很有可能出现循环依赖的问题
前提条件是Spring并不能完全解决循环依赖的问题:
1)首先要求相互依赖的Bean必须是单例的Bean,这是因为针对于原型模式的范围的Bean,每一次请求都会创建一个新的Bean实例,导致无限循环,由于没有全局的,单例模式的缓存来引用,因此循环依赖无法得到解决,创建A对象的时候属性赋值发现需要一个B,因为在进行创建B的时候是protype的,所以B需要NEW一个A,这个时候新创建了一个A,A这个时候又创建了一个新的B,就永远不是最开始的A和最开始的B,子子孙孙,无穷无尽,所以只有单例的时候才可以解决循环依赖的问题;
2)依赖注入的方式不能都是构造方法注入:
当时用到构造函数的时候,一个Bean的实例在构造函数被完全调用之前是不会被创建的,如果Bean A的构造函数依赖于Bean B,而BeanB的构造函数有完全依赖于Bean A,那么就会产生一个死锁的情况,那么两者都是不能在对方初始化之前完成初始化
一级缓存:存放的是完全初始化的对象,完全初始化的对象指的是经历过实例化,属性赋值,初始化三个步骤都是完全经历完成的对象;
二级缓存:半成品的Bean源对象,放的就是没有完全初始化好的对象,保存的就是初始化一半的对象;
三级缓存:处理AOP代理对象的特殊情况:但是如果目标对象使用到了拦截器,事务,那么此时向AOP中存放的对象就是代理对象,三级缓存:放的就是FactoryBean对象工厂,因为代理对象就是通过对象工厂产生的,因为代理对象本身有和之前的两级缓存没有任何关系,但是还是需要有一个地方去存放,所以就需要三级缓存了;
简单来说就是A对象依赖于B对象,B对象又依赖于A对象,咱们的类似的代码如下: @Component public class A{ @Autowired private B b; } @Component public class B{ @Autowired private A a; }
spring处理循环依赖的执行流程:
1)A在进行加载的时候,开始实例化,首先会生成一个A的一个ObjectFactory,创建Bean的一个工厂类,存到三级缓存里面,然后我们执行对A的对象属性的填充,发现A里面引用了B对象了,于是就开始尝试执行B创建;
2)此时进行创建B操作,首先在一级缓存里面,二级缓存里面,三级缓存里面发现都没有B,那么就首先在三级缓存里面创建一个B的ObjectFactory,发现初始化B的时候,发现属性要用到A,又要用到A了,我们就会通过A的三级缓存ObjectFactory,生成一个A一半的Bean,因为执行一半,里面的A的初始化还没有执行完呢,这是就是一个A的半成品,我们把半成品存到二级缓存里面,把三级缓存里面的A东西删了;
3)然后B controller继续执行,此时B就已经拿到了半成品的A了,直接把A的内存地址放到B的属性里面,B就会针对本身进行初始化后面的代码了,最后,我们生成了一个完整的B的Bean对象,然后B直接放到一级缓存里面了,然后我们再把B的二级缓存和三级缓存中的半成品和ObjectFactory进行删除;
4)然后A继续初始化,A在这时就可以拿到完整的B了,把A初始化好的Bean放到Spring容器里面,删除A二级缓存和三级缓存的ObjectFactory和一半的A的Bean;
因此,Spring一开始提前暴露的并不是实例化的 Bean,而是将 Bean 包装起来的ObjectFactory,为什么要这么做呢?
1)这实际上涉及到 AOP如果创建的 Bean 是有代理的,那么注入的就应该是代理 Bean,而不是原始的 Bean,但是,Spring一开始并不知道 Bean是否会有循环依赖,通常情况下没有循环依赖的情况,Spring 都会在完成填充属性并且执行完初始化方法之后再为其创建代理,但是,如果出现了循环依赖,Spring 就不得不为其提前创建"代理对象";否则,注入的就是一个原始对象,而不是代理对象,因此,这里就涉及到"应该在哪里提前创建代理对象"
2)Spring 的做法就是:在 ObjectFactory 中去提前创建代理对象
总结:加入缓存中是从下向上加的,查找缓存是从上向下进行查找的,当有二级缓存的时候就没有三级缓存;
十三)Spring单例Bean的线程安全问题:
回答的时候千万不能说安全,因为在Spring容器里面,默认的就是作用于就是Singleton;
在默认情况下,Bean是单例模式,所有人都是在用这一个对象,如果有人针对这个对象进行属性的修改的时候,那么就会存在数据覆盖的问题,所以不是线程安全的;
1)在Bean对象中尽量避免可变的成员变量,Bean属性设置成final,不能被修改,设置成只读,不能修改,比如说SpringMVC后端接收参数的时候通常使用的都是Integer而不是用int,如果前端不传递这个参数那么Integer默认采取null值来进行接收,但是如果后端参数采用int值来进行接收,那么程序就直接会报错;
2)在类中定义一个ThreadLocal成员变量,将需要的可变成员变量保存在ThreadLocal里面,每一个线程设置的是自己的变量,用于处理线程安全的单例Bean
十四)Spring中常用的设计模式有哪些?
1)单例模式:Spring中的Bean默认是单例的,通过一个单一的实例来保存资源,比如说你在Spring中的定义一个Bean的时候,默认情况下他就是单例的;
<bean id="MyBean" com="com.example.MyBean">
2)代理模式:SpringAOP本身就是依靠代理模式来进行实现的
3)工厂模式:Spring中有大量使用到了工厂模式,比如说FactoryBean,通过getObject方法new出对象的实例
4)模板方法模式:模板方法指的是定义一个模板结构,将具体的功能延迟到子类中来实现,模板方法的优点在于其父类实现了算法公共部分的方法来实现和保障算法的稳定性,在抽象类中定义并实现了公共部分算法,在子类中进行扩展,由抽象类和具体的子类来进行实现;
4.1)比如说JdbcTemplate,是SpringJDBC模块和核心类之一,它封装了数据库操作的基本步骤,但是允许子类重写特定方法来实现自定义行为;
4.2)比如说RestTemplate,用于实现Spring的HTTP请求,它也采用了模板方法模式,定义了HTTP请求的通用结构,支持GET,POST,DELETE等方法,用户可以通过自定义回调函数来处理响应数据;
就是定义一个算法的骨架,并且将一些步骤延迟到子类中进行实现,允许子类重写并且继承父类的一些方法,但是不会改变算法整体的结构,父类提供模板,子类提供实现;
5)原型模式:原型模式通过调用原型实例的Clone方法或者是其他手段来创建对象,原型模式属于创建型设计模式,它是以当前对象为模型创建另一个新的对象,而不需要知道其中的细节,原型模式在JAVA中常常使用克隆方式来进行实现;
6)适配器模式:适配器模式是将一个类的接口变成客户端所期望的另一种接口,从而使得原本接口不匹配的而无法工作的两个类能够在一起工作,可以让两个没有关联的类在一起运行
SpringMVC中使用到了适配器模式来进行适配Controller,在DispatcherServlet中的doDispatch方法,通过handlerAdapter和它的子类RequestMappingHandlerAdapter来实现Controller和url映射逻辑;
7)观察者模式:定义对象之间的一对多依赖关系,使得每当一个对象状态发生改变的时候,其相关对象接得到通知会被自动更新;
Spring中有一个机制叫做Spring Event,允许定义一个事件,并且允许有其他的观察者来订阅这个事件,那么当这个事件发生修改的时候,很多订阅者都是可以收到这个数据变更的一个通知,也被称之为是发布订阅模式
十五)SpringMVC的执行流程都有哪些?
1)用户发起url请求到前端控制器:DispatcherServlet
2)前端控制器请求HandlerMapping处理器映射器查找Controller,可以根据xml配置、注解进⾏查找,作⽤根据请求的url查找Controller方法,Handler就是一个Controller;
3)处理器映射器HanderMapping执行之后,会返回一个执行链给DispatcherServlet,因为在这个时候可能会有拦截器,doDisPatch方法applyPreHandler方法执行一些逻辑,比如说登录功能的校验等等,如果有拦截器,会执行拦截器,没有就会继续向下执行,换一种说法就是HanderMapping会把请求映射成HanderExecutionChain对象,这个对象里面就包含了一个Hander(Controller)处理器对象,还有多个HanderInterceptor对象;
4)DispatcherServlet调用处理器适配器HandlerAdapter去执行Hander,然后处理器适配器会根据适配的结果去执行Handler也就是Controller,HandlerAdapter处理业务逻辑,对数据进行处理,最终可能返回一个逻辑视图名或者是逻辑数据;
5)此时如果加上了@RestController或者是@ResponseBody表示此时返回给前端的是一个完整的数据而不是页面
6)适配器HandlerAdapter是去确定最终执行的Controller的,执行完适配器中的Controller,会返回一个ModelAndView,先返回给我们的DispatcherServlet,然后DispatcherServlet再传递请求给我们的视图解析器ViewResolver进行视图解析渲染操作生成最终的HTML
7)最终前端控制器将生成的HTML响应通过前端控制器返回给客户端最终显示在用户界面上
返回的数据有两种模式:返回的是一个静态页面或者后端返回的是一个数据)
十六)SpringMVC是如何根据对应的url来找到对应的Controller的?
1)简单来说就是SpringMVC会将url地址和Controller保存到映射集合中,当有url请求过来的时候,去集合中通过浏览器的url地址来找到对应的Controller即可,如果匹配到了直接进行方法的调用,如果匹配有多个,然后再进行匹配具体的方法类型(GET,POST)
2)具体来说就是当Spring MVC启动的时候,它会将添加了路由映射的比如说@RequestMapping的类和方法封装到MappingRegistry,然后再来通过AbstractHandlerMethodMapping的lookupHandlerMethod来获取到对应的HandlerMethod
十七)Spring MVC有哪些外部组件?
1)DispatcherServlet:前端控制器它是用来接收请求,响应结果,相当于转发器,中央处理器,有了DispatcherServlet减少了其它组件之间的耦合度,是由调度器来进行数据的分发和返回操作的,不需要程序员来进行开发;
2)HanderMapping:根据url来匹配最终的Controller,是根据url来查找Controllerhandler而不是执行;
3)HanderAdapter:叫做处理器适配器,按照特定规则去执行Controller,是Controller中的方法的执行者
4)Handler:执行相关的请求处理逻辑,返回相应的数据或者是视图信息,封装到ModelAndView
5)视图解析器ViewResolver(不需要程序员开发)
作⽤:进⾏视图解析,根据ModelAndView逻辑视图名解析成真正的视图6)View:JSP
十八)谈谈SpringMVC的项目分层:有利于维护代码提升项目可读性和项目具有可维护性
1)Controller层:控制器层,处理请求和响应,用于接收用户的请求,处理请求参数,调用业务逻辑层来进行处理,并将结果返回给用户
2)service服务层:处理具体的业务逻辑,进行数据组装和接口调用的
3)dao数据访问层:和数据库进行交互
4)model:模型层,定义数据模型
5)config:配置层,用于Spring配置
6)resources目录下面的static文件:静态资源
7)resources目录下面的template文件:视图模板,比如说Thymeleaf
8)application.properties:应用配置文件
dao:用户表由用户表的dao,文章表有文章表的dao,每一个表都存在着一个dao,但是具体来调用哪一个dao是由Service层来进行实现的
执行doFiter()调用下一个方法,如果数据校验不通过,可以不调用doFiter(),过滤器加密解密
十九)如何获取Request对象?
1)通过RequestContextGolder的getRequestAttribute方法来获取
ServletRequestAttributes attributes= (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request= attributes.getRequest();
2)通过@Autowried注解或者是参数来获取
二十)SpringMVC是如何实现拦截器的?
基于代理和DispatcherServlet,可以使用@Order来指定拦截器加载的顺序,执行顺序也就是越早的,SpringBoot默认使用的是CGLIB注解
1)在SpringMVC全局配置文件里面,添加如下配置:
<mvc:intercepter> <---配置拦截器的作用路径/**表示拦截所有--> <mvc:mapping path="/**"> <----配置放行的路径----> <mvc:exculde-mapping path=""/> <----指定拦截器---> <bean class="com.example.MyIntercepter"> </mvc:intercepter>
2)添加拦截器业务代码:使用全局配置文件中的bean的Class名称来写一个类,实现HandlerIntercepter接口,重写preHandler方法,重写preHandler和postHandler和afterComplain方法
2.1)preHandler方法:该方法会在控制器方法之前执行前进行执行,它的返回值表示是否继续进行中断操作,当返回值是true的时候,表示继续向下执行,当返回值是false的时候,会中断后续的所有操作,包括调用下一个拦截器和控制器中的类的方法的执行操作;
2.2)postHandler方法:该方法会在控制器方法调用之后,解析视图之前来执行,通过该方法可以针对于请求域中的模型和视图做出进一步的修改
2.3)afterComplain:该方法会在整个请求完成,视图渲染以后执行,可以使用该方法进行资源清理,记录日志等工作;
21)拦截器的常见使用场景有哪些?
1)用户登录权限的校验
2)可用作与日志的记录,信息监控和信息统计
3)统一安全处理:可以用于参数的加密和解密,数据返回之前进行加密,数据传递过来的时候进行解密,
22)如何实现过滤器
使用@WebFiter注解和五大类注解(将过滤器加入到SpringIOC容器里面),通过urlpatterns来实现配置过滤的url规则,然后再来实现Filter接口,重写里面的doFilter方法,里面有一个参数是FilterChain来调用下一个doFilter方法,是调用下一个过滤器或者是调用下一个流程
1)void intit(FitlerConfig config)方法:容器启动的时候会被调用整个程序运行周期中只会执行一次,用于实现Fitler对象的初始化
2)void doFitler(ServletRequest,ServletResponse,FitlerChain chain):具体的就是过滤功能实现代码,通过此方法来实现过滤处理,其中的Fitlerchain时调用下一个过滤器或者是执行下一个流程
3)void destory()用于实现兄安徽前完成相关资源的回收操作;
23)过滤器和拦截器有什么区别?
1)所属框架不同:过滤器是来源于Servlet框架,但是拦截器属于Spring框架
2)执行时机不同:请求的执行顺序是请求进入容器--->进入过滤器----->进入Servlet---->进入拦截器----->进入控制器,先执行过滤器,再来执行拦截器,最后执行调用的方法
3)底层实现不同:过滤器是基于方法回调实现的,但是拦截器是基于Spring中的环绕通知和动态代理来实现的,还有就是在正式调用Controller之前,先进行验证所有的拦截器,从而来判断是否可以继续通行;
4)支持的项目类型不同:过滤器的底层是依据Servlet来实现的,所以过滤器要依赖于Servlet容器,所以他只能依赖于Web项目中,但是拦截器是Spring中的一个组件,因此它不仅仅可以依赖于Web项目中,还可以依赖于Spring Core项目中
5)使用场景不同:拦截器更多用于实现业务判断,但是过滤器更多的用于功能过滤
24)什么是跨域问题?
Ajax为了保证安全性,要求发起的ajax请求的页面,与接收ajax请求的服务器,应该在同一个域名/地址下,如果发起请求的页面对应的域名1和接收ajax(域名2)请求的服务器,如果域名1和域名2是不相同的,那么就认为这是一个跨域请求,例如说自己实现的教务系统,只能通过教务系统才可以进行访问教务系统的服务器来进行获取数据,别的设备是不可以访问教务系统来进行获取数据的,默认情况下是不会被允许跨域的;
1)跨域问题解决的是不同站点之间,ajax无法相互调用的问题,跨域问题本质上就是浏览器的一种保护机制,他的初衷就是为了保护用户的安全,防止恶意网站来窃取数据,但是这个网站也带来了新的问题,他的问题就是不同站点的调用,带来的阻碍,也就是跨域问题,协议不同,域名不同,端口号不同;
2)解决方法就是重写addCorsMapping方法,调用CorsRegistry的addMapping方法,允许来设置跨域请求的拦截规则,后端给前端返回一个响应头:Access-Control-Allow-Orgin是*的响应头而已,告诉浏览器是自己人;
25)Spring和SpringBoot有什么区别?
Spring为JAVA提供了全面的基础框架支持,包含了Spring Core,Spring MVC,Spring AOP,这些模块的出现大大的缩短了应用程序的开发时间,提升了应用开发的效率
SpringBoot框架的出现本质上是为了简化Spring开发不需要依赖于Spring框架的XML配置
26)SprngBoot有什么优点?
1)抛弃繁杂的Bean注入的XML配置:SpringBoot自动进行配置,减少了xmlbean的注入,比如说在Spring时代,要在xml中配置包扫描路径和五大类注解的类才可以注册到SpringIOC容器中,但是在Spring Boot中约定大于配置,会自动扫描启动类以及启动类子包下面的加上五大类注解的类放到IOC中;
2)快速开发:不需要手动导jar包,抛弃pom.xml,内置常用的依赖和配置,使得更使程序员关注与业务开发,加速开发效率;
3)独立运行,内置Tomact的WEB容器,之前Servlet项目和Spring项目是存放在Tomact的webapps目录下进行运行,这些程序共享同一个Tomact的端口,方便进行开发和测试;
4)自动装配:自动根据项目的依赖和配置自动将第三方应用程序的组件的Bean装载到IOC容器里面,不需要开发人员手动再去写Bean的一个相关的配置,在SpringBoot项目中只是需要在启动类上加上@SpringBootApplication注解就可以实现自动装配,@SpringBootApplication是一个复合注解,真正实现自动装配的注解是@EnableAutoConfiguration
27)什么是自动装配?
SpringBoot的自动装配指的是在应用程序进行启动的时候,根据类路径下面的依赖,配置文件以及预定义的规则,自动配置以及初始化Spring应用程序中的各种组件,模块和功能的过程,这种自动配置的方式大大减少了开发人员手动配置的工作,是得开发者更能关注与业务逻辑的实现,同时提供了更高效,快速的应用程序的启动和开发体验;
1)在传统的Spring开发中,你可能需要进行手动配置数据库连接池的Bean,然后指定连接池的一些参数,还需要创建Spring配置文件,并在里面手动进行配置数据库连接池的Bean,之后才可以进行数据库增删改查的方法;
但是在SpringBoot自动装配的过程中只是需要在项目的配置文件中,设置和数据库相关的配置属性,就不需要手动写自动配置DataSourceBean对象了,当应用程序启动的时候,SpringBoot会自动根据这些配置属性,判断你想要使用的数据库连接池,然后会自动进行初始化以及配置连接池相关的Bean,无需手动创建数据库的实例,也无需手动指定连接池的参数,一切都会自动完成,告诉SpringBoot一个规则,就完成了自动装配;
比如说想要在Spring中使用ObjectMapper,首先在pom.xml里面添加JSON框架,然后如果想要在其他程序中通过DI的方式通过依赖注入的方式将ObjectMapper注入进来,需要在Springconfig.xml文件中配置一个Bean,配置名字和Class属性,但是SpringBoot的时候,直接将ObjectMapper放到IOC容器里面,直接注入即可,自动实现装配;
类似的自动装配还有:
1)RedisTemplate添加Redis驱动以后就可以自动地获取到自动装配的RedisTemplate对象
2)JDBCTemplate是一个JDBC基本操作的核心类,通过自动装配可以获取到一个已经配置好的JdbcTemplate实例,用于执行SQL查询和相关操作;
3)RestTemplate通过自动装配可以快速地获取到HTTP请求的核心类,用于发送HTTP请求和HTTP响应;
28)SpringBoot自动装配的流程:
1)启动应用程序:当你在进行启动SpringBoot应用程序的时候,SpringBoot会自动的扫描启动类路径下面的各种依赖和组件;
2)自动配置类扫描:SpringBoot会自动地扫描类路径下面的META/spring.factories文件,该文件定义了各种配置类的类名,里面有加载的类路径,进行系统类的加载;
3)条件化装配:在扫描到自动配置类以后,SpringBoot会自动根据根据条件判断是否需要进行自动装配,每一个配置类都会有一组条件,比如说是否存在某一个特定的类,是否在配置文件里面声明,是否能找到第三方jar包,如果满足配置条件,那么该类就即将被装配
配合@ConditionalClass看看是否有这个类判断是否运行可以进行加载,返回true自动装配;
4)自动配置Bean创建:一旦条件满足SpringBoot就会创建相应的Bean,包括数据库连接池等
5)覆盖默认配置:在自动装配完成以后,可以通过在应用的配置文件中自定义配置属性来覆盖默认的自动配置,这是根据需求来进行定制;
6)启动应用程序上下文:自动装配的Bean和手动装配的Bean都会加载到上下文中
7)应用程序运行:可以装配Bean
就比如说举个例子ObjectMapper是如何进行加载的?
1)自动配置类扫描,SpringBoot会自动扫描类路径下面的META-INF/spring.factories文件,该文件中定义了各种自动配置类的类名
2)条件化装配:判断是否在类路径上面存在Jackon相关的依赖
3)启动应用程序上下文:将Bean对象加载到IOC里面
1)springboot的自动装配实际上就是为了从spring.factories文件中获取到对应的需要进行自动装配的类,并生成相应的Bean对象,然后将它们交给spring容器来进行管理
2)SpringBoot再进行自动配置的时候,尤其是我再进行Bean加载的时候,启动的时候,根据@SpringBootApplication注解来进行启动的,是一个组合注解
3)@SpringBootApplication注解由@SpringBootConfiguration,@EnableAutoConfiguration,@ComponentScan三个注解组成,三个注解共同完成自动装配
1)@SpringBootConfiguration,注解标记启动类为配置类
2)@ComponentScan,注解实现启动时扫描启动类所在的包以及子包下所有标记为bean的类由IOC容器注册为bean,也就是包扫描;3)@EnableAutoConfiguration是开启自动导入装配
3.1)通过@Import(AutoConigurationPackages.Registrar.class)来实现自动装配配置包下面的所有Bean定义和注册
3.2)其中这里面的@import注解,他会把所有满足条件的Bean全部加载到SpringBoot里面,什么是满足条件的Bean呢?
在SpringBoot里面有一个jar包,也就是在@Import(AutoConfigurationImportSelector.class)注解下面叫做:1里面有一个MEAT-INF目录,里面有一个文件叫做spring.factories,里面就记录了所有可以加载和支配启动加载的Bean,里面有PropertySourceLoader,配置信息;
29)SpringAOP使用的是JDK proxy还是CGLIB?
在Spring时代默认使用的是JDK动态代理,没有使用接口的类,默认使用的是CGLIB
但是在SpringBoot中,默认使用的是CGLIB,使用final修饰的类,项目都启动不起来,注入会直接报错,也就是说无论一个类是否实现了接口,SpringBoot采用的都是CGLIB来做动态代理的;
SpringBoot最近几年默认的去除JDK,去JAVA化;
#是否启用AOP spring.aop.auto=true #代理方式:设置为true表示强制使用CGLIB代理 spring.aop.proxy-target-class=fals
但是如果你进行了设置这个配置,如果你的被代理类没有实现接口,那么默认还是会使用CGLIB
三十)跨域问题的本质是什么?
跨域问题的本质就是浏览器为了保证用户的访问安全,防止网站恶意窃取数据,所制定的一套规则策略,而跨域问题本质上就是告诉浏览器,这是一个安全的请求,这是自己人,他的实现不过是在响应头中添加了一个Access-Control-Allow-Origin为*的响应头,告诉浏览器是自己人
31)SpringBoot如何解决跨域问题?
1)通过注解来实现跨域:使用@CrossOrigin注解可以轻松的实现跨域,该注解即可以修饰类也是可以修饰方法的,当修饰类的时候,表明该类下面的所有接口都是可以跨域的,修饰方法的时候表明该方法可以跨域,要添加一个origins等于"*"的属性;
这种方式的写法虽然实现跨域比较简单,但是使用此方式只能实现局部跨域,当一个项目中存在多个类的时候,这种方式就需要在所有类上都加上注解,使用非常麻烦;
2)通过配置文件来实现跨域:
2.1)新创建一个配置文件
2.2)添加@Configuration注解,实现WebMVCConfigurer接口
2.3)重写addCorsMappings方法,设置允许跨域的代码,通过CorsRegistry对象的addMapping方法,allowedOrginsPatterns等方法来实现
@Configuration public class UserController implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**")//所有接口都进行释放 .allowCredentials(true)//是否支持发送cookie .allowedOriginPatterns("*")//支持域 .allowedMethods(new String[]{"GET","POST"}) .allowedHeaders("*") .exposedHeaders("*"); } }
3)通过Response对象设置Header头来实现跨域文章来源:https://www.toymoban.com/news/detail-739060.html
response.setHeader("Access-Control-Allow-Origin","*");
4)通过实现ResponseBodyAdvice接口,重写supports方法,重写beforeBodyWrite方法来通过response对象设置Header头,这种方式实现的是全局跨域对于该项目中的所有配置都生效文章来源地址https://www.toymoban.com/news/detail-739060.html
到了这里,关于JAVA面经整理(9)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!