说一下IOC和AOP?
IOC是依赖反转,通过依赖注入DI实现IOC。主要思想是面向抽象编程而不是面向具体实现编程。反转的意思是从依赖具体
实现变为依赖抽象。
AOP是面向切面编程,通过代理对代码无侵入的添加功能,实现切面编程。比如事务、日志等模块。
代理机制主要有jdk提供的动态代理和cglib提供的字节码动态代码。
★Spring中Bean的生命周期。
- 首先这个bean在进行开始实例化的时候会先进行调用该类的构造函数,默认是单例的
- 然后去注入属性中的bean
- 如果bean实现了BeanNameAware接口,spring将bean的id传给setBeanName()方法;
- 如果bean实现了BeanFactoryAware接口,spring将调用setBeanFactory方法,将BeanFactory实例传进来;使bean获得访问Spring容器的能力。
- 如果bean实现了ApplicationContextAware接口,它的setApplicationContext()方法将被调用,将应用上下文的引用传入到bean中;使得bean获取访问对应上下文的能力。
- 如果bean实现了BeanPostProcessor接口,它的postProcessBeforeInitialization方法将被调用;如AutowiredAnnotationBeanPostProcessor
- 如果bean实现了InitializingBean接口,spring将调用它的afterPropertiesSet接口方法,类似的如果bean使用了init-method属性声明了初始化方法,该方法也会被调用;
- 如果bean实现了BeanPostProcessor接口,它的postProcessAfterInitialization接口方法将被调用;
- 此时bean已经准备就绪,可以被应用程序使用了,他们将一直驻留在应用上下文中,直到该应用上下文被销毁;
- 若bean实现了DisposableBean接口,spring将调用它的distroy()接口方法。同样的,如果bean使用了destroy-method属性声明了销毁方法,则该方法被调用;
Spring中注解Autowired和Resource的区别?
共同点:
两者都可以写在字段和setter方法上。两者如果都写在字段上,那么就不需要再写setter方法。
不同点 :
(1)@Autowired 为Spring提供的注解,需要导入包org.springframework.beans.factory.annotation.Autowired;
只按照byType注入。 @Autowired注解是按照类型(byType)装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,
可以设置它的required属性为false。如果我们想使用按照名称(byName)来装配,可以结合@Qualifier注解一起使用。
(2)@Resource 默认按照ByName自动注入,由J2EE提供,需要导入包javax.annotation.Resource。@Resource有两个重要的属性:
name和type,而Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。所以,如果使用name属性,
则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略。如果既不制定name也不制定type属性,这时将通过反射
机制使用byName自动注入策略。如果按name找不到对应的bean的话回退到ByType的自动注入策略。
@Controller和@RestController的区别?
@Controller
方法的返回值。默认是视图页面的跳转路径。
如果想返回json对象,必须在方法的上面加@ResponseBody
@RestController
方法返回值,默认是json对象,也就是相当于@Controller里面的方法上添加了@ResponseBody
如果方法返回值需要跳转视图页面的话,那么方法的返回类型必须是View 或者ModelAndView.
5.依赖注入的方式有哪几种?
- set方法注入
- 构造器注入
- 注解注入
★Spring中IOC的原理?
第一个过程是Resource定位过程。这个Resource定位指的是BeanDefinition的资源定位,它由ResourceLoader通过统一的Resource接口来完成,这个Resource对各种形式的BeanDefinition的使用都提供了统一接口。对于这些BeanDefinition的存在形式,相信大家都不会感到陌生。比如,在文件系统中的Bean定义信息可以使用FileSystemResource来进行抽象;在类路径中的Bean定义信息可以使用前面提到的ClassPathResource来使用,等等。这个定位过程类似于容器寻找数据的过程,就像用水桶装水先要把水找到一样。
第二个过程是BeanDefinition的载入。这个载入过程是把用户定义好的Bean表示成IoC容器内部的数据结构,而这个容器内部的数据结构就是BeanDefinition。下面介绍这个数据结构的详细定义。具体来说,这个BeanDefinition实际上就是POJO对象在IoC容器中的抽象,通过这个BeanDefinition定义的数据结构,使IoC容器能够方便地对POJO对象也就是Bean进行管理。在下面的章节中,我们会对这个载入的过程进行详细的分析,使大家对整个过程有比较清楚的了解。
第三个过程是向IoC容器注册这些BeanDefinition的过程。这个过程是通过调用BeanDefinitionRegistry接口的实现来完成的。这个注册过程把载入过程中解析得到的BeanDefinition向IoC容器进行注册。通过分析,我们可以看到,在IoC容器内部将BeanDefinition注入到一个HashMap中去,IoC容器就是通过这个HashMap来持有这些BeanDefinition数据的。值得注意的是,这里谈的是IoC容器初始化过程,在这个过程中,一般不包含Bean依赖注入的实现。在Spring IoC的设计中,Bean定义的载入和依赖注入是两个独立的过程。依赖注入一般发生在应用第一次通过getBean向容器索取Bean的时候。但有一个例外值得注意,在使用IoC容器时有一个预实例化的配置,通过这个预实例化的配置(具体来说,可以通过为Bean定义信息中的lazyinit属性),用户可以对容器初始化过程作一个微小的控制,从而改变这个被设置了lazyinit属性的Bean的依赖注入过程。
Spring容器如何创建?
在web.xml文件中配置ServletContextListener为spring的环境启动监听器。
然后启动的时候,读取配置好的xml文件,解析这个xml文件,根据xml文件中的bean定义,生成实例,填入到Bean容器中。之后就是注入了。
SpringMVC容器和Spring容器的区别?
Spring是web.xml中注册启动监听器时,读取spring相关的xml去生成bean,放入容器。
SringMVC是web.xml中注册的一个Servlet,一般来说所有请求都经过这个servlet,这个serlvet在初始化的时候
读取springmvc.xml中的数据,扫描指定的包,注册好所有Controller,解析请求对应的方法,放入一个Map中,当
有请求进来的时候就从这个map中取对应的Controller。
SpringMVC的控制器是不是单例模式,如果是,有什么问题,怎么解决?
是单例的。如何保证并发安全:1.不使用controller中的实例变量或类变量。2.或者在controller注解上加@Scope("prototype"),使之成为非单例的。3.使用ThreadLocal变量。
★Spring中BeanFactory和ApplicationContext的区别?
-
BeanFactory和ApplicationContext都是接口,并且ApplicationContext是BeanFactory的子接口。
-
BeanFactory是Spring中最底层的接口,提供了最简单的容器的功能,只提供了实例化对象和拿对象的功能。
-
而ApplicationContext是Spring的一个更高级的容器,提供了更多的有用的功能。提供更多面向应用的功能。
-
ApplicationContext提供的额外的功能:国际化的功能、消息发送、响应机制、统一加载资源的功能、强大的事件机制、对Web应用的支持等等。
-
事件传播 :载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层,更易于创建实际应用。
-
加载方式的区别:BeanFactory采用的是延迟加载的形式来注入Bean;ApplicationContext则相反的,它是在Ioc启动时就一次性创建所有的Bean,好处是可以马上发现Spring配置文件中的错误,坏处是启动时间会比较耗时。
什么是IoC和DI?DI是如何实现的?
- IOC是控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,遵循了依赖倒置的原则。可以用来减低计算机代码之间的耦合度。控制指的是对实现类的控制,反转指的是这种控制权从调用类中移除,交给第三方(容器)决定。
- 而控制反转其中最常见实现方式叫做依赖注入(Dependency Injection,简称DI)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体,将其所依赖的对象的引用传递(注入)给它。
- 还有一种ioc的实现方式叫“依赖查找”(Dependency Lookup),但Martin Fowler认为DI的实现方式更灵活解耦。
★请问Spring中Bean的作用域有哪些?
- singleton为默认值,IOC容器中仅存在一个Bean实例,Bean都以单例模式存在
-
prototype,在每次请求获取Bean的时候,都会创建一个新的实例,它在容器初始化的时候不会创建实例,采用的是延迟加载的形式注入
Bean,当你使用的时候,才会进行实例化,每次实例化获取的对象都不是同一个 就像BeanFactory的实例化模式 实例不唯一
- request,在每一次http请求时会创建一个实例,该实例仅在当前http request有效
- session,在每一次http请求时会创建一个实例,该实例仅在当前http session有效
- globalSession,全局Session,仅供基于Porlet的web环境进行使用。(很少使用到)
★Spring容器对Bean组件是如何管理的?
- Bean对象创建的时机:懒加载或饥饿加载
- Bean对象创建的个数:原型或者单例
- Bean对象初始化和销毁:可配置初始化或者销毁调用的方法。
- 实例化Bean地方时:构造器、静态工厂、实例工厂
请谈一谈Spring中自动装配的方式有哪些?
- no:不进行自动装配,手动设置Bean的依赖关系。
- byName:根据Bean的名字进行自动装配。
- byType:根据Bean的类型进行自动装配。
- constructor:类似于byType,不过是应用于构造器的参数,如果正好有一个Bean与构造器的参数类型相同则可以自动装配,否则会导致错误。
- autodetect:如果有默认的构造器,则通过constructor的方式进行自动装配,否则使用byType的方式进行自动装配
aop的应用场景?
- 事务
- 权限
- 日志
- 缓存
- 性能统计
Spring AOP的配置
SpringAOP,XML配置aop:config,切面aop:aspect切点aop:pointcut,连接切点和通知方法aop:before和aop:after等,注解可以直接使@before执行方法@after ,@before(“pointcut()”) ,@after("pointcut"), @Aroud("excutete()),@AfteReturning,@AfterThrowing,可作日志事务,权限等待,AOP即通过把具体的类创建对应的 代理类,从代理类来对具体进行操作。
目标实现了接口,默认采用JDK实现AOP,也可以强制使用CGlib来实现AOP,目标没有实现接口的话,则必须采用CGlib,Spring自动在JDK和CGlib切换。如果要求spring强制使用CGlib实现AOP,则可以配置,添加Cglib库。。。jar, Spring配置文件中加入<aop:aspecj-autoproxy proxy-target-Class=true>
AOP的原理?
Spring AOP中的代理使用的默认策略是:
如果目标对象实现了接口,则默认采用JDK动态代理
如果目标对象没有实现接口,则采用CgLib进行动态代理
如果目标对象实现了接口,且强制CgLib代理,则采用CgLib进行动态代理
★你如何理解AOP中的连接点(Joinpoint)、切点(Pointcut)、增强(Advice)、引介(Introduction)、织入(Weaving)、切面(Aspect)这些概念?
- 连接点(Joinpoint):程序执行的某个特定位置(如:某个方法调用前、调用后,方法抛出异常后)。一个类或一段程序代码拥有一些具有边界性质的特定点,这些代码中的特定点就是连接点。Spring仅支持方法的连接点。
- 切点(Pointcut):如果连接点相当于数据中的记录,那么切点相当于查询条件,一个切点可以匹配多个连接点。Spring AOP的规则解析引擎负责解析切点所设定的查询条件,找到对应的连接点。
-
增强(Advice):增强是织入到目标类连接点上的一段程序代码。Spring提供的增强接口都是带方位名的,如:BeforeAdvice、AfterReturningAdvice、ThrowsAdvice等。很多资料上将增强译为“通知”,这明显是个词不达意的翻译,让很多程序员困惑了许久。
说明: Advice在国内的很多书面资料中都被翻译成”通知”,但是很显然这个翻译无法表达其本质,有少量的读物上将这个词翻译为”增强”,这个翻译是对Advice较为准确的诠释,我们通过AOP将横切关注功能加到原有的业务逻辑上,这就是对原有业务逻辑的一种增强,这种增强可以是前置增强、后置增强、返回后增强、抛异常时增强和包围型增强。
- 引介(Introduction):引介是一种特殊的增强,它为类添加一些属性和方法。这样,即使一个业务类原本没有实现某个接口,通过引介功能,可以动态的为该业务类添加接口的实现逻辑,让业务类成为这个接口的实现类。
- 织入(Weaving):织入是将增强添加到目标类具体连接点上的过程,AOP有三种织入方式:1.编译期织入:需要特殊的Java编译期(例如AspectJ的ajc);2.装载期织入:要求使用特殊的类加载器,在装载类的时候对类进行增强;3.运行时织入:在运行时为目标类生成代理实现增强。Spring采用了动态代理的方式实现了运行时织入,而AspectJ采用了编译期织入和装载期织入的方式。
- 切面(Aspect):切面是由切点和增强(引介)组成的,它包括了对横切关注功能的定义,也包括了对连接点的定义。
请问Spring支持的事务管理类型有哪些?
-
声明式事务
本质使用AOP,将业务和事务管理分离,降低耦合度和提高事务的复用能力。声明式事务可以通过注解@Transactional和配置来管理事务,操作简单。
-
编程式事务
编程式事务是在代码中使用TransactionTemplate进行硬编码,与业务的耦合度高,难以复用。但控制事务的范围比较灵活。
Spring事务的传播特性?
1.PROPAGATION_REQUIRED 如果存在一个事务,则支持当前事务。如果没有事务则开启一个新的事务。
2.PROPAGATION_SUPPORTS 如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行。但是对于事务同步的事务管理器,PROPAGATION_SUPPORTS与不使用事务有少许不同。
3.PROPAGATION_MANDATORY 如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常。
4.PROPAGATION_REQUIRES_NEW 总是开启一个新的事务。如果一个事务已经存在,则将这个存在的事务挂起。
5.PROPAGATION_NOT_SUPPORTED 总是非事务地执行,并挂起任何存在的事务。
6.PROPAGATION_NEVER 总是非事务地执行,如果存在一个活动事务,则抛出异常
7.PROPAGATION_NESTED如果一个活动的事务存在,则运行在一个嵌套的事务中. 如果没有活动事务, 则按TransactionDefinition.PROPAGATION_REQUIRED 属性执行
Spring事务的隔离级别?
ISOLATION_READ_UNCOMMITTED 读未提交
ISOLATION_READ_COMMITTED 读已提交
ISOLATION_REPEATABLE_READ 可重复读
ISOLATION_SERIALIZABLE 串行读
Spring的通知类型有哪些?
前置通知、正常返回通知、异常返回通知、返回通知、环绕通知
Spring的优点?
- 降低了组件之间的耦合性,实现了软件各层之间的解耦
- 可以使用容易提供的众多服务,如事务管理,消息服务等
- 容器提供单例模式支持
- 容器提供了 AOP 技术,利用它很容易实现如权限拦截,运行期监控等功能
- 容器提供了众多的辅助类,能加快应用的开发
- spring 对于主流的应用框架提供了集成支持,如 hibernate,JPA,Struts 等
- spring 属于低侵入式设计,代码的污染极低
- 独立于各种应用服务器
- spring 的 DI 机制降低了业务对象替换的复杂性
- Spring 的高度开放性,并不强制应用完全依赖于 Spring,开发者可以自由选择 spring 的部分或全部
请阐述一下Hibernate实体对象的三种状态是什么?以及对应的转换关系是什么??
瞬时态,持久态和游离态。
瞬时态没有id,对象刚new出来的时候处于瞬时态。
持久态有id(与数据库中的主键关联),通过get()或者load()方法获取的对象处于持久态。
游离态:持久态的对象脱离了session的管理变成游离态,处于游离态的对象如果不被其它对象引用则会被垃圾回收机制回收。
转化:
瞬时转持久:Session对象的save()或saveOrUpdate()方法保存对象
持久转瞬时,调用delete()方法
持久转游离:调用Session对象的clear()/close()/evict()
游离转瞬时:调用delete()方法
游离转持久: 调用update()或者saveOrUpdate()
Hibernate中SessionFactory是线程安全的吗?Session是线程安全的吗(两个线程能够共享同一个Session吗)?
SessionFactory对应Hibernate的一个数据存储的概念,它是线程安全的,可以被多个线程并发访问。
SessionFactory一般只会在启动的时候构建。对于应用程序,最好将SessionFactory通过单例模式进行封装以便于访问。
Session是一个轻量级非线程安全的对象(线程间不能共享session),它表示与数据库进行交互的一个工作单元。
Session是由SessionFactory创建的,在任务完成之后它会被关闭。Session是持久层服务对外提供的主要接口。
Session会延迟获取数据库连接(也就是在需要的时候才会获取)。为了避免创建太多的session,可以使用ThreadLocal
将session和当前线程绑定在一起,这样可以让同一个线程获得的总是同一个session。Hibernate 3中SessionFactory的
getCurrentSession()方法就可以做到。
Hibernate中Session的load和get方法的区别是什么?
- 如果没有找到符合条件的记录,get方法返回null,load方法抛出异常。
- get方法直接返回实体类对象,load方法返回实体类对象的代理(如果lazy='true' ,就使用延迟加载,返回的是代理,不是真实对象)。
- 在Hibernate 3之前,get方法只在一级缓存中进行数据查找,如果没有找到对应的数据则越过二级缓存,直接发出SQL语句完成数据读取;load方法则可以从二级缓存中获取数据;从Hibernate 3开始,get方法不再是对二级缓存只写不读,它也是可以访问二级缓存的。
请问Query接口的list方法和iterate方法有什么区别?
- list()方法返回的每个对象都是完整的,而iterator()方法所返回的对象中仅包含了主键值(标识符)。
- 只有当你对iterator中的对象进行操作时,Hibernate才会向数据库再次发送SQL语句来获取该对象的属性值。相当于延迟加载。
如何理解Hibernate的延迟加载机制?在实际应用中,延迟加载与Session关闭的矛盾是如何处理的?
延迟加载就是并不是在读取的时候就把数据加载进来,而是等到使用时再加载。Hibernate使用了虚拟代理机制实现
延迟加载,我们使用Session的load()方法加载数据或者一对多关联映射在使用延迟加载的情况下从一的一方加载多
的一方,得到的都是虚拟代理,简单的说返回给用户的并不是实体本身,而是实体对象的代理。代理对象在用户调用
getter方法时才会去数据库加载数据。但加载数据就需要数据库连接。而当我们把会话关闭时,数据库连接就同时关
闭了。
延迟加载与session关闭的矛盾一般可以这样处理:
1. 关闭延迟加载特性。这种方式操作起来比较简单,因为Hibernate的延迟加载特性是可以通过映射文件或者注解进行配置的,
但这种解决方案存在明显的缺陷。首先,出现"no session or session was closed"通常说明系统中已经存在主外键关联,
如果去掉延迟加载的话,每次查询的开销都会变得很大。
2. 在session关闭之前先获取需要查询的数据,可以使用工具方法Hibernate.isInitialized()判断对象是否被加载,如果没
有被加载则可以使用Hibernate.initialize()方法加载对象。
3. 使用拦截器或过滤器延长Session的生命周期直到视图获得数据。Spring整合Hibernate提供的OpenSessionInViewFilter和
OpenSessionInViewInterceptor就是这种做法。
hibernate和ibatis的区别
- Hibernate是完全自动的,利用配置好的对象关系模型,不用写任何sql语句就能实现数据库的操作。
- 而Mybatis是半自动的,仅自动映射字段,但基本的sql是需要自己编写。
- Hibernate使用完全自动的映射操作数据库以及hql语句使得数据库移植性更强。
- Mybatis在sql的直接优化上优于Hibernate。
解释一下MyBatis中命名空间(namespace)的作用。
在大型项目中,可能存在大量的SQL语句,这时候为每个SQL语句起一个唯一的标识(ID)就变得并不容易了。
为了解决这个问题,在MyBatis中,可以为每个映射文件起一个唯一的命名空间,这样定义在这个映射文件中
的每个SQL语句就成了定义在这个命名空间中的一个ID。只要我们能够保证每个命名空间中这个ID是唯一的,
即使在不同映射文件中的语句ID相同,也不会再产生冲突了。
MyBatis中的动态SQL是什么意思?
MyBatis的动态SQL是基于OGNL表达式的,它可以帮助我们方便的在SQL语句中实现某些逻辑。
MyBatis中#和$的区别?
$通过直接拼接进sql中,#是预编译再填入参数。所以$不安全,#较安全。
MyBatis一级缓存原理以及失效情况?
一级缓存的作用域在session级别,在一个事务中,两次查询用的是一个sqlsession,非事务中,两次查询用的是不同的sqlsession。配置正确的情况下,失效的原因一般都是没有在同一个session中。
MyBatis二级缓存的使用?
二级缓存是针对Mapper的namespace的,同namespace的Mapper中不同sqlsession可以获取到缓存,但发生更新操作的时候,会清除缓存。在查询操作远远多于增删改操作的情况下可以使用二级缓存。因为任何增删改操作都将刷新二级缓存,对二级缓存的频繁刷新将降低系统性能。最好在单表上使用二级缓存。
springmvc和spring-boot区别?
SpringMVC是MVC框架,Spring Boot是快速开发基于Spring的应用.Spring Boot 通过stater的方式简化
整个Spring生态配置, SpringMVC, 对应的starter就是spring-boot-starter-web, 开发体验上来说,
没有很多xml配置,并内置了tomcat.
★看过MyBatis源码吗,请说说它的工作流程?
1.加载配置文件。
2.创建会话工厂。
3.创建会话。
4.创建执行器。(可能会执行拦截器)
5.输入映射封装到sql,去数据库查询,返回结果集封装给应用。
★MyBatis拦截器原理?
- mybatis给Executor、StatementHandler、ResultSetHandler、ParameterHandler提供了拦截器功能,
- Executor提供了增删改查的接口.
- StatementHandler负责处理Mybatis与JDBC之间Statement的交互.
- ResultSetHandler负责处理Statement执行后产生的结果集,生成结果列表.
- ParameterHandler是Mybatis实现Sql入参设置的对象。
- 拦截器采用了责任链模式,把请求发送者和请求处理者分开,各司其职。
★Spring MVC完整工作流程
- 用户发起请求到前端控制器(DispatcherServlet),该控制器会过滤出哪些请求可以访问Servlet、哪些不能访问。就是url-pattern的作用,并且会加载springmvc.xml配置文件。
- 前端控制器会找到处理器映射器(HandlerMapping),通过HandlerMapping完成url到controller映射的组件,简单来说,就是将在springmvc.xml中配置的或者注解的url与对应的处理类找到并进行存储,用map<url,handler>这样的方式来存储。
- HandlerMapping有了映射关系,并且找到url对应的处理器,HandlerMapping就会将其处理器(Handler)返回,在返回前,会加上很多拦截器。
- DispatcherServlet拿到Handler后,找到HandlerAdapter(处理器适配器),通过它来访问处理器,并执行处理器。
- 执行处理器
- 处理器会返回一个ModelAndView对象给HandlerAdapter
- 通过HandlerAdapter将ModelAndView对象返回给前端控制器(DispatcherServlet)
- 前端控制器请求视图解析器(ViewResolver)去进行视图解析,根据逻辑视图名解析成真正的视图(jsp),其实就是将ModelAndView对象中存放视图的名称进行查找,找到对应的页面形成视图对象
- 返回视图对象到前端控制器。
- 视图渲染,就是将ModelAndView对象中的数据放到request域中,用来让页面加载数据的。
- 通过第8步,通过名称找到了对应的页面,通过第10步,request域中有了所需要的数据,那么就能够进行视图渲染了。最后将其返回即可。
SpringMVC如何处理JSON数据?
在xml中配置转换Json的类,运行时就会调用这个类进行转换。
SpringMVC常见注解有哪些?
@Controller 、@RequestMapping、@Autowired、@Resource、@PathVariable、@RequestParam、
@ModelAttribute、@SessionAttribute
Hibernate继承映射策略
继承关系的映射策略有三种:
- 每个继承结构一张表(table per class hierarchy),不管多少个子类都用一张表。
- 每个子类一张表(table per subclass),公共信息放一张表,特有信息放单独的表。
- 每个具体类一张表(table per concrete class),有多少个子类就有多少张表。
SpringMVC拦截器原理,如何自定义拦截器?
跟web.xml的过滤器链差不多。通过统一的Servlet入口进入,针对的是Controller,在Controller前后做一些增强性的操作。
SpringMVC如何将请求映射定位到方法上面?结合源码阐述?
SpringMVC根据请求的路径,在一个类似于Map的结构中,以路径为键值找到对应的Handler。
★请说说SpringBoot自动装配原理?
自动装配的过程:
- 通过各种注解+继承,引入包含自动装配核心方法的类
- SpringApplication.run(Application.class, args)在运行时,调用自动装配方法
- 自动装配方法会读取spring-boot-autoconfigure.jar里面的spring.factories配置文件,配置文件中有所有自动装配类的配置类的类名
- 生成对应功能的Configuration类,这些功能配置类要生效的话,会去classpath中找是否有该类的依赖类(也就是pom.xml必须有对应功能的jar包才行)
- 配置类里再通过判断生成最后的功能类,并且配置类里面注入了默认属性值类,功能类可以引用并赋默认值。生成功能类的原则是自定义优先,没有自定义时才会使用自动装配类。
综上所述,要想自动装配一个类需要满足2个条件:
-
spring.factories里面有这个类的配置类(一个配置类可以创建多个围绕该功能的依赖类)
2 .pom.xml里面需要有对应的jar包
自动装配的结果:
- 根据各种判断和依赖,最终生成了业务需要的类并且注入到IOC容器当中了
- 自动装配生成的类赋予了一些默认的属性值
SpringBoot的优点?
快速构建项目,极大的提高了开发、部署效率。
对主流开发框架的无配置集成。
项目可独立运行,无须外部依赖Servlet容器。
提供运行时的应用监控。
★Spring MVC和 Spring IOC的生命周期
SpirngMVC的生命周期 :
-
A,-》DispatcherSerlvet(前端控制器)
-
B,-》HandlerMapping(处理器映射器),根据xml注解查找对应的Hander -》 返回Handler
-
C,-》处理器适配器去执行Handler
-
D,-》Handler执行完成后给处理器适配器返回ModelAndView
-
E,-》前端控制器请求视图解析器去执行视图解析,根据逻辑视图名解析成真正的视图JSP,向前端控制器返回view
-
F,-》前端控制器进行视图渲染,将模型数据放到request-》返回给用户
SpringBean的生命周期:
Instance实例化-》设置属性值-》调用BeanNameAware的setBeanName方法-》调用BeanPostProsessor的预初始化方法-》调用InitializationBean的afterPropertiesSet()的方法-》调用定制的初始化方法callCustom的init-method-》调用BeanPostProsessor的后初始化方法-》Bean可以使用了 -》 容器关闭-》 调用DisposableBean的destroy方法-》调用定制的销毁方法CallCustom的destroy-method。
★Spring+MyBatis实现读写分离简述?
方案一:通过MyBatis配置文件创建读写分离两个DataSource,每个SqlSessionFactoryBean对象的mapperLocations属性制定两个读写数据源的配置文件。将所有读的操作配置在读文件中,所有写的操作配置在写文件中。
方案二:通过Spring AOP在业务层实现读写分离,在DAO层调用前定义切面,利用Spring的AbstractRoutingDataSource解决多数据源的问题,实现动态选择数据源
方案三:通过Mybatis的Plugin在业务层实现数据库读写分离,在MyBatis创建Statement对象前通过拦截器选择真正的数据源,在拦截器中根据方法名称不同(select、update、insert、delete)选择数据源。
方案四:通过spring的AbstractRoutingDataSource和mybatis Plugin拦截器实现非常友好的读写分离,原有代码不需要任何改变。推荐第四种方案
Quartz实现原理?
A、scheduler是一个计划调度器容器(总部),容器里面可以盛放众多的JobDetail和trigger,当容器启动后,里面的每个JobDetail都会根据trigger按部就班自动去执行。
B、JobDetail是一个可执行的工作,它本身可能是有状态的。
C、Trigger代表一个调度参数的配置,什么时候去调。
D、当JobDetail和Trigger在scheduler容器上注册后,形成了装配好的作业(JobDetail和Trigger所组成的一对儿),就可以伴随容器启动而调度执行了。
E、scheduler是个容器,容器中有一个线程池,用来并行调度执行每个作业,这样可以提高容器效率。
springmvc用到的注解,作用是什么?
- @Controller或@RestController标记该类是一个控制器
- @RequestMapping通过这个注解可以定义不同的处理器映射规则,即为控制器指定可以处理哪些URL请求。
- @RequestBody用于读取http请求的内容(字符串),通过springMVC提供的HttpMessageConverter接口将读取到的内容转换为json、xml等格式的数据,再转换为java对象绑定到Controller类方法的参数上。
- @ResponseBody 注解表示该方法的返回的结果直接写入 HTTP 响应正文(ResponseBody)中,一般在异步获取数据时使用,通常是在使用 @RequestMapping 后,返回值通常解析为跳转路径,加上 @Responsebody 后返回结果不会被解析为跳转路径,而是直接写入HTTP 响应正文中。
- @ModelAndAttribute 绑定请求参数到指定对象/暴露表单引用对象为模型数据 /暴露@RequestMapping方法返回值为模型数据
- @RequestParam 处理简单类型的绑定
- @PathVariable 绑定URL占位符到入参。
- @ExceptionHandler 注解到方法上, 出现异常时会执行该方法。
- @ControllerAdvice 使一个Controller成为全局的异常处理类, 类中用ExceptinHandler方法注解的方法可以处理所有Controller发生的异常。
- @Autowired 它可以对类成员变量、方法以及构造函数进行标注,完成自动装配的工作。自动装配的意思就是让Spring从应用上下文中找到对应的bean的引用,并将它们注入到指定的bean。
★springboot启动机制。
- 如果我们使用的是SpringApplication的静态run方法,那么,这个方法里面首先要创建一个SpringApplication对象实例,然后调用这个创建好的SpringApplication的实例方法。在SpringApplication实例初始化的时候,它会提前做几件事情:根据classpath里面是否存在某个特征类(org.springframework.web.context.ConfigurableWebApplicationContext)来决定是否应该创建一个为Web应用使用的ApplicationContext类型。使用SpringFactoriesLoader在应用的classpath中查找并加载所有可用的ApplicationContextInitializer。使用SpringFactoriesLoader在应用的classpath中查找并加载所有可用的ApplicationListener。推断并设置main方法的定义类。
- SpringApplication实例初始化完成并且完成设置后,就开始执行run方法的逻辑了,方法执行伊始,首先遍历执行所有通过SpringFactoriesLoader可以查找到并加载的SpringApplicationRunListener。调用它们的started()方法,告诉这些SpringApplicationRunListener,“嘿,SpringBoot应用要开始执行咯!”。
- 创建并配置当前Spring Boot应用将要使用的Environment(包括配置要使用的PropertySource以及Profile)。
- 遍历调用所有SpringApplicationRunListener的environmentPrepared()的方法,告诉他们:“当前SpringBoot应用使用的Environment准备好了咯!”。
- 如果SpringApplication的showBanner属性被设置为true,则打印banner。
- 根据用户是否明确设置了applicationContextClass类型以及初始化阶段的推断结果,决定该为当前SpringBoot应用创建什么类型的ApplicationContext并创建完成,然后根据条件决定是否添加ShutdownHook,决定是否使用自定义的BeanNameGenerator,决定是否使用自定义的ResourceLoader,当然,最重要的,将之前准备好的Environment设置给创建好的ApplicationContext使用。
- ApplicationContext创建好之后,SpringApplication会再次借助Spring-FactoriesLoader,查找并加载classpath中所有可用的ApplicationContext-Initializer,然后遍历调用这些ApplicationContextInitializer的initialize(applicationContext)方法来对已经创建好的ApplicationContext进行进一步的处理。
- 遍历调用所有SpringApplicationRunListener的contextPrepared()方法。
- 最核心的一步,将之前通过@EnableAutoConfiguration获取的所有配置以及其他形式的IoC容器配置加载到已经准备完毕的ApplicationContext。
- 遍历调用所有SpringApplicationRunListener的contextLoaded()方法。
- 调用ApplicationContext的refresh()方法,完成IoC容器可用的最后一道工序。
- 查找当前ApplicationContext中是否注册有CommandLineRunner,如果有,则遍历执行它们。
- 正常情况下,遍历执行SpringApplicationRunListener的finished()方法、(如果整个过程出现异常,则依然调用所有SpringApplicationRunListener的finished()方法,只不过这种情况下会将异常信息一并传入处理)
★srpingMVC启动原理
-
解析 键值对
- 创建一个application对象即ServletContext,servlet上下文,用于全局共享
-
将 键值对放入ServletContext中,web应用全局共享
-
读取 标签,创建监听器,一般使用ContextLoaderListener,如果使用了ContextLoaderListener,Spring就会创建一个WebApplicationContext对象,这个就是IOC容器, ContextLoaderListener创建的IOC容器是全局共享的,并将其放在ServletContext中, 键名为WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, 读取web.xml文件里的contextConfigLocation配置中的xml文件来创建bean
- listener创建完毕后如果有Filter会去创建Filter
- 初始化Servlet,一般使用DispatchServlet类
- DispatchServlet的父类FrameworkServlet会重写其父类的initServletBean方法,并调用initWebApplicationContext()以及onRefresh()方法
- initWebApplicationContext()方法会创建一个当前servlet的一个IOC子容器,如果存在上述的全局WebApplicationContext则将其设置为父容器,如果不存在上述全局的则父容器为null。
-
读取
标签的 配置的xml文件并加载相关Bean
- onRefresh()方法创建Web应用相关组件
★AOP的原理详解
AOP实现中,可以看到三个主要的步骤,一个是代理对象的生成,然后是切面的织入,然后是执行拦截器链。
- Spring AOP代理对象的生成:
Spring提供了两种方式来生成代理对象: JDKProxy和Cglib,具体使用哪种方式生成由AopProxyFactory根据AdvisedSupport对象的配置来决定。默认的策略是如果目标类是接口,则使用JDK动态代理技术,否则使用Cglib来生成代理。Spring使用JDK来生成代理对象,具体的生成代码放在JdkDynamicAopProxy这个类中。
2. 切面的织入:
获取可以应用到此方法上的通知链,Advised中配置能够应用到连接点或者目标类的Advisor全部被转化成了MethodInterceptor.
3. 执行拦截器链:
如果得到的拦截器链为空,则直接反射调用目标方法,否则创建MethodInvocation,调用其proceed方法,触发拦截器链的执行,
注意:
- CGLib不能对声明为final的方法进行代理,因为CGLib原理是动态生成被代理类的子类。
- JDK代理三要素Proxy.newProxyInstance(类加载器,目标接口,InvocationHandler实现),JDKDynaimicAopProxy实现了InvocationHandler接口,并使用了AdvisedSupport中存在的目标接口,及设置的类加载器,进行JDK的动态代理。
- CGLIB创建代理主要是创建Enhancer enhancer,并通过AdvisedSupport设置相应的属性,比如目标类rootClass,如果由接口合并接口给代理类,最主要的是设置Callback集合和CallbackFilter,使用CallBackFilter可以根据方法的不同使用不同的Callback进行拦截和增强方法。其中最主要的使用于AOP的Callback是DynamicAdvisedInterceptor。
★spring中循环注入的方式
- 能成功的循环注入方式都是因为能先创建出实例放入到临时创建Bean池当中。
- 构造器循环依赖(失败)
- setter方法循环注入(成功)
- setter方法注入 单例模式(scope=singleton)(成功)
- setter方法注入 非单例模式(scope=prototype)(失败)原型bean的话,不会放入到临时创建Bean池当中。
★Spring的BeanFactory和FactoryBean的区别
BeanFactory接口用来生产Bean,它处理生产bean的接口体系的最顶层,它为其他具体的IOC容器提供了最基本的规范,例如DefaultListableBeanFactory,XmlBeanFactory,ApplicationContext 等具体的容器都是实现了BeanFactory,再在其基础之上附加了其他的功能。
FactoryBean接口用来定制Bean的生产过程,getObject方法中可以实现自定义过程。MyBatis的SqlSessionFactoryBean中,Spring会调用SqlSessionFactoryBean这个实现了FactoryBean的工厂Bean 同时加载dataSource,Mapper文件的路径,对sqlSessionFactory进行初始化。
FactoryBean比BeanFactory在生产Bean的时候灵活,还能修饰对象,带有工厂模式和装饰模式的设计思想在里面,不过它的存在还是以Bean的形式存在。BeanFactory因为是核心接口,编写复杂逻辑很容易接触到其他不必要的接口,不好实现。
spring boot特性,优势,适用场景等
- 约定优于配置思想
- 专注与业务逻辑之间思维切换
- 基于Spring的开发提供更快入门体验
- 开箱即用,没有代码生成,无需XML配置。
- 支持修改默认配置满足特定需求
-
提供大型项目中常见的非功能性特性,如嵌入Tomcat服务器、安全、指标、健康检测、外部配置等
很契合微服务场景。
★Mybatis的底层实现原理
-
加载mybatis全局配置文件(数据源、mapper映射文件等),解析配置文件,MyBatis基于XML配置文件生成Configuration,和一个个MappedStatement(包括了参数映射配置、动态SQL语句、结果映射配置),其对应着<select | update | delete | insert>标签项。
-
SqlSessionFactoryBuilder通过Configuration对象生成SqlSessionFactory,用来开启SqlSession。
-
SqlSession对象完成和数据库的交互:
a、用户程序调用mybatis接口层api(即Mapper接口中的方法)
b、SqlSession通过调用api的Statement ID找到对应的MappedStatement对象
c、通过Executor(负责动态SQL的生成和查询缓存的维护)将MappedStatement对象进行解析,
sql参数转化、动态sql拼接,生成jdbc Statement对象
d、JDBC执行sql。
e、借助MappedStatement中的结果映射关系,将返回结果转化成HashMap、JavaBean等存储结构并返回。
quartz和timer对比
Quartz:
-
非常灵活,可以使用多种形式的定时方式
-
非常轻量级,而且只需要很少的设置/配置
-
是容错的,在系统重启后记住以前的记录
-
可以参与事务
Timer:
-
定时器没有持久性机制
-
创建方便简单,不用另外引入jar包
★动态代理的几种方式
JDK原生动态代理:
- Proxy:Proxy是所有动态代理的父类,它提供了一个静态方法来创建动态代理的class对象和实例。
- InvovationHandler:每个动态代理实例都有一个关联的InvocationHandler。在代理实例上调用方法时,方法调用将被转发到InvocationHandler的invoke方法。
- JDK原生动态代理是Java原生支持的,不需要任何外部依赖,但是它只能基于接口进行代理。
Cglib代理:是一个基于ASM的字节码生成库,它允许我们在运行时对字节码进行修改和动态生成。Cglib通过继承方式进行代理。
- Enhancer:来指定要代理的目标对象、实际处理代理逻辑的对象,最终通过调用create()方法得到代理对象,对这个对象所有非final方法的调用都会转发给MethodInterceptor;
- MethodInterceptor:动态代理对象的方法调用都会转发到intercept方法进行增强。
Cglib通过继承的方式进行代理,无论目标对象有没有实现接口都可以进行代理,但是无法处理final的情况。
tomcat如何调优,涉及哪些参数 。
一般调线程参数:
- maxThreads :Tomcat 使用线程来处理接收的每个请求,这个值表示 Tomcat 可创建的最大的线程数,默认值是 200
- minSpareThreads:最小空闲线程数,Tomcat 启动时的初始化的线程数,表示即使没有人使用也开这么多空线程等待,默认值是 10。
- maxSpareThreads:最大备用线程数,一旦创建的线程超过这个值,Tomcat 就会关闭不再需要的 socket 线程。上边配置的参数,最大线程 500(一般服务器足以),要根据自己的实际情况合理设置,设置越大会耗费内存和 CPU,因为 CPU 疲于线程上下文切换,没有精力提供请求服务了,最小空闲线程数 20,线程最大空闲时间 60 秒,当然允许的最大线程连接数还受制于操作系统的内核参数设置,设置多大要根据自己的需求与环境。当然线程可以配置在“tomcatThreadPool”中,也可以直接配置在“Connector”中,但不可以重复配置。
- URIEncoding:指定 Tomcat 容器的 URL 编码格式,语言编码格式这块倒不如其它 WEB 服务器软件配置方便,需要分别指定。
- connnectionTimeout: 网络连接超时,单位:毫秒,设置为 0 表示永不超时,这样设置有隐患的。通常可设置为 30000 毫秒,可根据检测实际情况,适当修改。
- enableLookups: 是否反查域名,以返回远程主机的主机名,取值为:true 或 false,如果设置为false,则直接返回IP地址,为了提高处理能力,应设置为 false。
- disableUploadTimeout:上传时是否使用超时机制。
- connectionUploadTimeout:上传超时时间,毕竟文件上传可能需要消耗更多的时间,这个根据你自己的业务需要自己调,以使Servlet有较长的时间来完成它的执行,需要与上一个参数一起配合使用才会生效。
- acceptCount:指定当所有可以使用的处理请求的线程数都被使用时,可传入连接请求的最大队列长度,超过这个数的请求将不予处理,默认为100个。
- keepAliveTimeout:长连接最大保持时间(毫秒),表示在下次请求过来之前,Tomcat 保持该连接多久,默认是使用 connectionTimeout 时间,-1 为不限制超时。
- maxKeepAliveRequests:表示在服务器关闭之前,该连接最大支持的请求数。超过该请求数的连接也将被关闭,1表示禁用,-1表示不限制个数,默认100个,一般设置在100~200之间。
- compression:是否对响应的数据进行 GZIP 压缩,off:表示禁止压缩;on:表示允许压缩(文本将被压缩)、force:表示所有情况下都进行压缩,默认值为off,压缩数据后可以有效的减少页面的大小,一般可以减小1/3左右,节省带宽。
- compressionMinSize:表示压缩响应的最小值,只有当响应报文大小大于这个值的时候才会对报文进行压缩,如果开启了压缩功能,默认值就是2048。
- compressableMimeType:压缩类型,指定对哪些类型的文件进行数据压缩。noCompressionUserAgents="gozilla, traviata": 对于以下的浏览器,不启用压缩。如果已经对代码进行了动静分离,静态页面和图片等数据就不需要 Tomcat 处理了,那么也就不需要配置在 Tomcat 中配置压缩了。
★Springmvc 中DispatcherServlet初始化过程。
DispatcherServlet主要的继承体系如下,具体的初始化过程,使用了模板方法模式,将初始化各个步骤依次从顶级父类开始执行如下:
- HttpServletBean:
主要做一些初始化的工作,将web.xml中配置的参数设置到Servlet中。比如servlet标签的子标签init-param标签中配置的参数。
2. FrameworkServlet:
将Servlet与Spring容器上下文关联。其实也就是初始化FrameworkServlet的属性webApplicationContext,这个属性代表SpringMVC上下文,它有个父类上下文,既web.xml中配置的ContextLoaderListener监听器初始化的容器上下文。
3. DispatcherServlet:
初始化各个功能的实现类。比如异常处理、视图处理、请求映射处理等。
转自我的github
技术讨论群QQ:1398880
http://vc2x.com/articles/2020/03/09/1583688157859.html