目录
3.BeanFactory和ApplicationContext区别
5.spring中的bean是线程安全的嘛,bean的作用域
1.Ioc有那些容器?
beanfactory:是基础类型的ioc容器,提高完整的ioc服务支持,默认采用延迟初始化策略,只有当在使用某个bean的时候,才会去对bean进行初始化和依赖注入等。spring容器启动的时候前期事比较多,资源有限,可以用beanfactorty
applicationcontext:是在beanfactory基础上构建的,拥有beanfactory的所有功能,还比他高级,可以实现其他的拓展功能,比如说事件发布等。在资源充足的时候,用applicationcontext好一点。
2.spring是如何管理bean的
通过ioc容器来管理bean,可以通过注解和xml来配置。用注解管理bean时的常用注解:@componentscan,扫描策略,扫描那些包,@componet,@repository,@service,@controller都用于声明这是一个bean。
autowired,qualifier注入bean,autowire是按类型注入bean,如果这个类型有多个bean,则根据qualifier指定bean的名称来注入。
scope声明bean的作用域,默认是单例的,可以改为多例的。
postconstruct,predestory声明bean的声明周期,postconstruct在初始化之后调用的方法,predestory在销毁之前执行的方法。
3.BeanFactory和ApplicationContext区别
BeanFactory是spring框架最底层的接口,定义如何创建,获得bean,主要是面向spring框架本身,不支持AOP和web功能,在获取bean时,才会去创建bean。
ApplicationContext继承BeanFacoory接口,对他的功能进行拓展,支持AOP,web等组件,面向开发层面,可以在框架启动时就可以创建bean。
spring中实际实现classpathxmlApplicationcontext这个类,具体实现bean创建和管理
4.spring中bean的生命周期
spring中的bean特指的是spring框架所管理的对象,和自己创建的对象完全不同。
spring管理bean的过程:
1.实例化bean,以反射的方式创建一个对象。
2.注入属性,可以用xml注入或者用注解注入。@Autowire(spring,type)@Resource(name,type),可能涉及到循环依赖(三级缓存解决)。
3.调用aware接口相关的方法,invokeawaremethed,完成例如beanname,beanfactory,beancalssloader属性的设置。
4.执行初始化之前执行的方法,调用beanpostprocess中的前置处理方法,比如说设置applicationcontext,envrionment等。
5.执行init-methed方法为bean进行初始化
6.执行初始化之后执行的方法,调用beanpostprocess中的后置方法,增强一些东西,spring中的aop就是在此实现的。
7.获取到完整的对象,使用
8.销毁
5.spring中的bean是线程安全的嘛,bean的作用域
不一定是线程安全的,不同情况下结果不同。
线程安全问题指的是在单例bean情况下,对象只创建了一个,如果拥有成员变量,所有的请求是共享的,所以线程不安全
可以使用Threadlocal为每一个请求来存储一个变量副本。将单例bean改为原型bean。
单例bean也有可能是线程安全的
单例bean中,如果包含的是有状态bean(可以存储数据,例如a=10)那就是线程不安全的,如果包含的是无状态bean(例如controller中包含service)那就是线程安全的bean。
spring中bean的作用域scope:
1.单例的singleton,是默认的:自始至终为一个类只创建一个对象,和servlet差不多
2.原型的prototype:每次使用就创建一个新的对象
3.request,每次http请求都会创建一个对象,session等
6.spring Bean的循环依赖
spring中依赖注入的方式
-
构造器注入
-
Set方法注入
-
注解注入
在Spring中的循环依赖主要包括两种,构造器循环依赖和setter循环依赖
什么是循环依赖?为什么出现这样的问题?
A类依赖B类,B类依赖A类,本身是没有问题的,创建A对象,B属性为null,但是在spring中有自动注入功能,
那么问题就产生了,创建A时,就要注入B,但是B也是一样的,创建时需要注入A。
spring是如何解决问题的
使用三级缓存来解决循环依赖。过程是a创建时需要b,于是a将自己放到三级缓存里面去实例化b,b实例化的时候发现需要a,于是去缓存里面找,在三级缓存里面找到了,然后把三级缓存的a放入二级缓存,删除三级缓存里的a,b初始化完成,将自己放入一级缓存里,此时b里面的a依然是创建中的状态,然后继续创建a,此时b已经创建完毕,直接从一级缓存拿到b,然后完成创建a,并把a也放入一级缓存。
针对于属性注入的,构造方法注入,无法解决循环依赖。
7.spring中的缓存
把每一级缓存理解为3个存储不同对象的Map
一级缓存保存实例化,属性赋值初始化完成的bean。
二级缓存保存实例化完了之后的bean(半成品bean)
三级缓存:保存bean工厂,便于添加其他功能,例如事务管理中的对象代理。
8.Servlet过滤器于spring拦截器不同
实现方式不同:
过滤器是当请求进入servlet前,进入过滤器是纯粹javaweb规范实现的
拦截器是spring框架自己的实现
实现原理不同:
过滤器:使用函数回调方式实现
拦截器:使用反射机制(纯java实现)
使用范围不同:
过滤器依赖于服务器只适用于web项目
拦截器可以适用所有jva程序
触发机制不同:
拦截器只能拦截controller中的
过滤器在进入服务器到达servlet前触发
拦截的请求范围不同:
过滤器:可以拦截所有进入web程序中的请求
拦截器只能拦截进入控制器的请求。
9.讲一讲ioc
ioc是控制反转的意思,原来的对象是我们自己想创建就创建的,控制反转就是讲对象的创建权交给spring容器,让他来帮助我们创建对象。 di是依赖注入,将对应的属性注入到具体的对象中,通过autowire和resource注解可以注入。populationbean方法来完成属性注入。
autowire和resource注解的区别:
-
@Resource注解是Java自身的注解,@Autowired注解是Spring的注解.
-
resource可以通过byname或者bytype注入,如果不指定name或者type会利用反射机制使用byname来注入依赖。autowire只根据类型来注入。
容器,用来存储对象的,使用map结构在存储对象,在spring中缓存对象一般有3级singletonobjects存放完整对象,earlysingletonobjects存放半成品对象,singletonfactory用来存放lambda表达式和对象名称的映射。整个bean的生命周期从创建到销毁,各个环节都是由容器来帮我们控制的。 bean的生命周期 1.实例化bean,通过反射的方式来实现,有一个createbeaninstance是专门来生成对象的。 2.bean创建完了时候,bean的属性都是默认值,我们要为bean的属性设置值,通过populationbean方法来完成对bea属性的填充,这里会有依赖循环的问题 。 3.在这里,执行初始化之前执行的方法,调用bean的初始化方法可以在xml中设置init-method,执行初始化之后执行的方法。 4.获取创建bean的实例对象 5.使用和销毁 循环依赖的产生,解决。 循环依赖就是a依赖b,b依赖a,给a注入值的时候要注入b,给注入值的时候要注入a。导致创建的时候,创建了b,创建b的时候又创建了a,导致形成了一个闭环。停不下来,整坏cpu。 解决。 使用三级缓存来解决循环依赖。过程是a创建时需要b,于是a将自己放到三级缓存里面去实例化b,b实例化的时候发现需要a,于是去缓存里面找,在三级缓存里面找到了,然后把三级缓存的a放入二级缓存,删除三级缓存里的a,b初始化完成,将自己放入一级缓存里,此时b里面的a依然是创建中的状态,然后继续创建a,此时b已经创建完毕,直接从一级缓存拿到b,然后完成创建a,并把a也放入一级缓存。 spring中的bean都是通过反射来生成的 。不知道有没有错误,指点一下
ioc解决了什么问题
ioc主要是解决了对象之间的耦合问题,例如当service调用dao时,传统方式下我们需要在service里面newdao,当我们需要改变的时候,service也要改变,就造成了service和dao的强耦合,ioc只需要关注dao层的接口,在service中声明接口属性,具体的实现类在ioc容器中切换,不会出现强耦合的现象。
ioc和DI的区别
ioc是控制反转,是把对象的创建的控制权交给了spring容器,而di是依赖注入。ioc是站在对象的角度,di是站在容器的角度,两者其实干的都是对象实例化和依赖关系维护这同一件事情。
注解注入和xml注入有什么区别
xml优点:xml注入的时候,修改时不需要重新编译和部署,而注解需要。xml的优先级高于注解,更具有拓展性。缺点:文件庞大,格式复杂,不易维护。
注解优:实现简单,比xml安全一点,xml在运行时才能发现错误, 注解是类型安全的。
缺:修改后需要重新编译,注解分散在各个类中,不宜维护,灵活性不太强。
factorybean和beanfactory
factorybean是工厂的bean,是一个bean,作用是产生其他bean实例。
而beanfactory是一个工厂,ico容器的最顶层接口就是beanfactory,作用是管理bean,实例化,定位,配置应用程序中的对象,以及建立依赖等,。
spring容器的启动过程
首先是实例化beanfactory工厂,用于生成bean,
然后实例化beandefinationreader注解配置扫描器,用于扫描特定的注解,service,compoent,等,转化为beandefination对象,beandefination描述的是bean的信息,比如说是否单例,是否懒加载等。
实例化路径扫描器,扫描目录的所有bean。
然后把beandefination注册到容器中,
调用refresh刷新容器,获取创建的beanfactory工厂,完成一些bean的创建,创建非懒加载的单例bean,放入单例池中,下一次使用即可直接使用。非单例bean在启动的时候不去创建,在使用的时候才回去创建,最后发布一个容器刷新完成事件完成容器启动。
10.讲一下aop
aop是ioc的一个扩展功能。
aop意思是面相切面编程,把与业务代码无关的代码抽取出来,然后横切进我们的代码之中。他是为了接耦而生。
有一些术语:
连接点(Joinpoint):类中可以被增强的方法,这个方法就被称为连接点
切入点(pointcut):类中有很多方法可以被增强,但实际中只有add 和update被增了,那么 add 和 update 方法就被称为切入点(实际实现的连接点)
通知(Advice): 通知是指一个切面在特定的连接点要做的事情(增强的功能)。通知分为方法执行前通知,方法执行后通知,环绕通知等.
切面(Aspect):把通知添加到切入点的过程叫切面.
目标(Target): 代理的目标对象(要增强的类) 代理(Proxy): 向目标对象应用通知之后创建的代理对象
bean创建过程中,有一个步骤可以对bean进行拓展实现,aop本身就是一个拓展功能,在beanpostprocess后置方法中,来实现aop。先创建代理对象,找到在切点呀,在切面呀,给那个方法拓展呀等,创建代理对象。
aop有两种实现方式,jdk动态代理和gclib动态代理。 jdk动态代理是java提供的动态代理技术,可以在运行时创建接口的动态实例,spring中的aop默认使用的是jdk动态代理,在接口的代理实例中织入代码。
gclib代理,采用底层的字节码技术,在运行时创建子类代理的实例,当目标对象不存在接口时,spring aop就会采用这种方式在子类实例中织入代码 。在执行调用的方法的时候,会调用生成的字节码文件,直接从dynamicadviserintercept类中的intercept方法来执行。从拦截器链中依次获得每一个通知,并执行。 根据之前定义好的通知来生成拦截器链。 aop的应用场景,统一异常处理,日志,事务处理等
通知的类型有哪些 前置,before 环绕,around 后置,after 异常,afterthrowing 最终,afterreturning五种
11.说一下spring的事务
事务是逻辑上的一组操作,要么都执行,要么都不执行。
spring中的事务管理有两种:编程式事务,生命式事务。
编程式事务:通过transationTemplate手动管理事务,然后在我们代码中需要提交事务或回滚事务时自己写代码实现。
声明式事务:代码入侵性小,是通过aop来实现的。本质是对方法前后进行拦截,所以声明式事务是方法级别的。声明式事务的实现方式有两种:一种是通过xml文件配置,还有一种是通过注解配置。通过transational注解来生命事务,通常把事务加在业务处理层。也可以给某个方法加事务。
事务存在传播行为,就是一个方法有事务,另一个方法也有事务,第一个方法调用第二个方法,事务该如何执行。
一共有7种事务传播行为,PROPAGATION_REQUIRED,指定的方法必须在事务内执行,若当前存在事务,加入到当前事务中,若没有,则新建一个事务,这种是spring中默认使用的。
2.PROPAGATION_SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行
3.PROPAGATION_REQUIRES_NEW,总是新建一个事务,如果当前存在事务,把当前事务挂起,直到新建的事务结束.
事务也有失效的情况,比如说transational注解用在非public方法上,异常被catch捕获导致事务失效。
12.spring中的核心注解
@bean,用在方法上,告诉Spring容器,你可以从下面这个方法中拿到一个Bean。
声明为bean的注解
@component,@Repository,@service,@controller
@configuration 声明是一个配置类,会通过gclib生成代理对象,他没有接口。
aop中的注解
@ConfigurationProperties,把配置文件注册为bean
@before,方法执行前做增强
@after,方法执行后的通知
@aspect,定义那些类是需要增强的,
@AfterThrowing,处理程序中没有处理的异常
@afterreturning,执行之后的通知,有返回值
@arount,环绕通知
13.springboot核心注解
@springbootapplication:核心注解,用于开启自动配置,准确的说是通过改注解内组合的enableautoconfiguration开启了自动配置。
@enableautoconfiguration:启动是spring应用程序上下文时进行自动配置,自动配置是基于classpath中引入的类和以及定义的bean的来实现的。
@mapperscan,指定要变成实现类的接口所在的包,然后包下面的所有接口在编译后都会生成对应的实现类。
14.spring中的设计模式
单例模式,bean默认都是单例的
工厂模式:beanfactory工厂来创建对象
代理模式:aop创建代理对象
还有一些记不清了
15.aop和aspectj的区别
aop是面向切面编程,spring本身是通过动态代理的方式,通过创建一共代理对象来支持aop,比如说源码里定义了一些接口,pontcut,adviser等,还包括propriety factory类,这个类就是创建冬天代理对象的工具类,spring就是通过这一套机制去实现aop的。
而aspectj也是去实现类似于aop的功能,底层是通过编译期的时候去增强我们的字节码,比如说他可能有自己的编译器,例如after注解,before注解,around注解等,都是aspectj里面定义的,他在利用他的编译器去编译某一个java文件的时候,他会去看看文件里是否有这些注解,有的话,去进一步解析,切面在哪里,切点在哪里,那个方法需要增强等,根据这些信息,完成对字节码的增强,相当于直接把逻辑加到字节码里,然后生成的东西就直接被增强了。
springaop只是利用了aspectj里面的那几个注解,把他们的源码拿来,自己去解析那些注解,还是用动态代理的方式去实现的。
16.springmvc的执行流程
1.客户端发送一个http请求,服务器接受到请求后,在dispatcherservlet的请求映射里面找,如果有,则交给他处理。
2.dispatcherservlet接收请求后,根据信息找到对应的handler处理器。
3.得到handler后,根据handleradpter对hanler进行封装,再以统一的适配器handleradpter接口调用handler
4.处理完业务逻辑后,返回一个modelandview给dispatcherservlet,modelandview包含视图逻辑名和数据。
5.dispatcherservlet借助viewreslover完成逻辑视图名到真实视图名的解析转换。
6.得到视图名后,dispathcerservlet根据view对modelandview进行视图渲染。
7.最终客户端得到的响应可能是一个静态页面或者是json数据,或者其他形式的数据。
18.springboot自动装配怎么理解
自动配置是springboot中的重要功能,如果我们不用springboot,而是用原生spring,比如说要去用消息队列呀,要用spring事物呀,mybaits,缓存等,我们要去加入对应的jar包,还要去配置很多的bean,例如mybaits中配置各种mapper factorybean等。自动配置就是自动帮我们配置这些bean,帮我们提前给配置好了,当然这些bean也不是一定会生效 ,springboot中有很多条件注解,就是这些bean已经配好了,你这里需要啥比如说需要事物,他就会吧事物条件开启,就会自动有bean事物了。这些都是springboot提前帮我们做好的。想那些端口呀什么的配置,大部分还是我们自己来做的。springboot也会去提供默认的端口,tomcat8080,reids6379。最重要的就是他提前把bean给配置好了
19.mybaits缓存
mybaits的缓存分为一级缓存和二级缓存
一级缓存也叫本地缓存,默认启用,不能关闭,一级缓存再sqlsession声明周期中,再同一个sqlsession中查询时,会把执行的方法和参数转换成缓存的键和值存入一个map中,下一次来如果map里面有对应的,则直接用map里面的东西。
二级缓存是sqlsessionfactory级别的缓存,开启二级缓存通过cacheenable来配置。
二级缓存作用于全局,对于一些相当消耗性能的,时效性不敏感的查询可以使用二级缓存。
开启了二级缓存后,查询顺序是二级缓存--》一级缓存--》数据库
20.cookie,session
cookie存放于客户端,session放于服务器端
cookie存放容量<=4kb,session无线
cookie对客户端是可见的,session不可见,比cookie安全一点。
cookie浏览器管理后可以不失效,session浏览器管理了就失效了。
cookie支持跨域,sesion不支持。
session存在服务器端,每个用户都有一个,并发量大的情况下,会有消耗。
21.token
token身份验证过程:
1.用户通过用户名和密码发送请求
2.程序验证
3.程序返回一个签名的token给客户端
4.客户端存储token,并且每次用于发送请求,把token存入http头部发送,保证http请求无状态。
5.服务器端验证token并返回数据。
token优势:无状态,可拓展,安全。可拓展。多平台跨域。
token和session区别:
session是空间换时间,token是时间换空间。session在多个服务器之间就不好用了,session存在一个服务器里面,去访问另一个服务器的话,他没有就会又生成一个,多个就冲突了。而jwttoken则还可以用。
jwt token 和token的区别
Token需要查库验证token 是否有效,而JWT不用查库,直接在服务端进行校验,因为用户的信息及加密信息,和过期时间,都在JWT里,只要在服务端进行校验就行,并且校验也是JWT自己实现的。
jwttoken加密过程
jwttoken分为3部分:header头部,payload数据,signature签名
转换成base64编码
把前两部分的编码串通过.来连接,然后使用加密算法来加密,得到第三部分的数据,三部分组合在一起就是jwttoken令牌。
加密算法可以有:对称加密和非对称加密
对称加密:对称加密采用对称的编码思想,特点是文件加密和解密使用同样的密钥加密。也就是密钥也可以用来解密。快捷简单,密钥较短,破译困难,常见的有:des,idea
非对称加密:非对称加密需要两个密钥,公开密钥和私密密钥
公开密钥和私有密钥是一对,如果用公开密钥来加密则用私有密钥解密,反之依然,加密和解密使用不同的密钥,所以交非对称加密。
过程大概是:甲生成一对密钥并把其中的一个作为公钥向其他方公开,得到公钥的乙使用该密钥加密后的数据发送给甲,甲在用私钥去解密。典型应用就是数字签名。
常见的有:rsa,ecc
hash算法:Hash算法特别的地方在于它是一种单向算法,用户可以通过hash算法对目标信息生成一段特定长度的唯一hash值,却不能通过这个hash值重新获得目标信息。因此Hash算法常用在不可还原的密码存储、信息完整性校验等。
常见的Hash算法有MD2、MD4、MD5、HAVAL、SHA
22.#和$的区别
#是预编译处理,是占位符。$是字符串替换,是拼接符 mybaits在处理#号的时候,会吧sql中的#替换为?,调用preparestatement来赋值。 在处理$的时候,会吧$替换为字符串的值,调用statement来赋值。 #可以防止sql注入,提高安全性。
23.springboot启动类注解
启动类上面的注解是springbootapplication,是springboot的核心注解,组合包含3个,1个是springbootconfiguration注解代替了configuration注解,实现了配置文件的功能,2是enableautoconfiguration是打开自动配置的功能,也可以关闭某个自动配置的选项,例如关闭数据源自动配置功能, springbootapplication(exclude={datasourceautoconfigurationclass}) 3是componentsacn是spring的组件扫描
24.controller类的作用
controller是控制层,负责接受前段发来的参数,然后把参数发送给业务层 返回数据给前段。最核心而功能就是前后端交互。 @controller注解的作用,声明该类是控制层的bean,让他被spring容器管理。 @responsebody表示该类的所有方法都可以直接提交而不经过识图解析器 ,返回值是json格式的数据。 controller和restcontroller的区别 restcontroller是controller和responsebody的结合,支持快捷的返回一个json对象。controller的返回类型是string类型的。 requestmapping 控制器指定可以处理哪些URL请求,并且可以指定处理请求的类型(POST/GET),如果@RequestMapping没有指定请求的方式,那么代表这个方法可以同时处理GET/POST请求。 getmapping 用于将HTTP get请求映射到特定处理程序的方法注解 getmapping是requestmapping(method=requestmethod.get)的缩写 postmapping 用于将HTTP post请求映射到特定处理程序的方法注解 pathvaribale获取url中的数据,例如传id的时候get/{id}可以获得拿到id。
25.怎么理解mybaits
mybaits是一个半ORM的框架,它内部封装了JDBC,开发只需关注SQL语句本身,不需要花费时间去处理加载驱动,创建连接,创建statement对象等繁琐过程。直接编写原生态SQL,执行。
可以使用xml或者注解来配置和映射原生信息,将POJO映射成数据库中的记录,避免了几乎所有的JDBC代码和手动设置参数以及获取结果集。
是一个持久层框架
mybatis的工作流程 通过建造者模式创建一个工厂类,定位,加载,解析配置文件的就是在这一步完成的,包括mybatis-config.xml 和Mapper 适配器文件。 通过SqlSessionFactory 创建一个SqlSession。 获得Mapper 对象。 调用接口方法(insert,delete,update,select)。
26.mybaits分页
1.sql分页 limit
2.使用pagehelper插件
27.mvc核心组件
1:前端控制器(DispatcherServlet)
本质上是一个Servlet,相当于一个中转站,所有的访问都会走到这个Servlet中,再根据配置进行中转到相应的Handler中进行处理,获取到数据和视图后,在使用相应视图做出响应。
2:处理器映射器(HandlerMapping)
本质上就是一段映射关系,将访问路径和对应的Handler存储为映射关系,在需要时供前端控制器查阅。
3:处理器适配器(HandlerAdapter)
本质上是一个适配器,可以根据要求找到对应的Handler来运行。前端控制器通过处理器映射器找到对应的Handler信息之后,将请求响应和对应的Handler信息交由处理器适配器处理,处理器适配器找到真正handler执行后,将结果即model和view返回给前端控制器
4:视图解析器(ViewResolver)
本质上也是一种映射关系,可以将视图名称映射到真正的视图地址。前端控制器调用处理器适配完成后得到model和view,将view信息传给视图解析器得到真正的view。
5:视图渲染(View)
本质上就是将handler处理器中返回的model数据嵌入到视图解析器解析后得到的jsp页面中,向客户端做出响应。