1.什么是Spring?
Spring是个轻量级的框架,通过IOC达到松耦合的目的,通过AOP可以分离应用业务逻辑和系统服务分开,不过配置各种组件时比较繁琐,所以后面才出选了SpringBoot的框架。
2.Spring用了哪些设计模式
BeanFactory用了工厂模式,AOP用了动态代理模式,Bean 用了单例模式,RestTemplate用了模板方法模式,监听器用了观察者模式,SpringMVC中HandlerAdapter用了适配器模式
3.IOC是什么?
IOC:IOC控制反转,是一种思想,把对象的创建和调用从程序员手中交由IOC容器管理,降低对象之间的依赖关系。Spring通过DI(依赖注入)实现了IOC。
依赖注入:DI依赖注入,是IOC的实现,在Spring创建对象的过程中,把对象依赖的属性注入到对象中。
依赖注入有三种方式:构造器注入、set注入、接口注入
4.将类声明为Bean的注解有哪些?
-
@Component
:通用的注解,可标注任意类为Spring
组件。 -
@Repository
: 对应持久层。 -
@Service
: 对应服务层。 -
@Controller
: 对应SpringMVC控制层。
5.注入Bean有哪些方式?
-
@Configuration + @Bean:
@Configuration用来声明一个配置类,然后使用 @Bean 注解,用于声明一个Bean,将其加入到Spring容器中。
-
通过包扫描特定注解的方式:
@ComponentScan放置在我们的配置类上,然后可以指定一个路径,进行扫描带有特定注解的Bean,然后加至容器中。
特定注解包括@Controller、@Service、@Repository、@Component
-
@Import注解导入:
@Import注解平时开发用的不多,经常搭配自定义注解进行使用,然后往容器中导入一个配置文件。
6.Spring自动装配有哪些方式?
-
byName:根据Bean名称注入对象依赖项。@Resource默认按byName自动注入。
-
byType:根据类类型注入对象依赖项。@AutoWried默认按byType自动注入。
-
constructor :存在单个实例则优先按类型进行参数匹配;当存在多个相同类型的实例时,优先按名称匹配。
7.@Component和@Bean的区别是什么?
-
@Component
注解作用于类,而@Bean
注解作用于方法(@Bean需要配合@Configuration使用)。 -
@Bean
注解比@Component
注解更加灵活,当需要将第三方类装配到 Spring 容器中,因为没办法源代码上添加@Component注解,只能使用@Bean 注解的方式。
8.@Autowired和@Resource的区别?
-
@Autowired
是 Spring 提供的注解,@Resource
是 JDK 提供的注解。 -
Autowired
默认的注入方式为byType
(根据类型进行匹配),@Resource
默认注入方式为byName
(根据名称进行匹配)。 -
当一个接口存在多个实现类的情况下,
@Autowired
和@Resource
都需要通过名称才能正确匹配到对应的 Bean。Autowired
可以通过@Qualifier
注解来显式指定名称,@Resource
可以通过name
属性来显式指定名称。
9.Bean的作用域
(1)singleton:单例,Spring中的Bean默认都是单例的。
(2)prototype:每次请求都会创建一个新的Bean实例。
(3)request:每一次HTTP请求都会产生一个新的Bean,该Bean仅在当前HTTP request内有效。
(4)session:每一次HTTP请求都会产生一个新的Bean,该Bean仅在当前HTTP session内有效。
(5)application:全局session作用域。
10.Bean生命周期
-
调用Bean的构造方法创建Bean
-
通过反射调用setter方法进行属性的依赖注入
-
如果Bean实现了
BeanNameAware
接口,Spring将调用setBeanName
(),设置Bean
的name(xml文件中Bean标签的id) -
如果Bean实现了
BeanFactoryAware
接口,Spring将调用setBeanFactory()
把Bean factory设置给Bean -
如果存在
BeanPostProcessor
,Spring将调用它们的postProcessBeforeInitialization
(预初始化)方法,在Bean初始化前对其进行处理 -
如果Bean实现了
InitializingBean
接口,Spring将调用它的afterPropertiesSet
方法,然后调用xml定义的 init-method 方法,两个方法作用类似,都是在初始化 Bean 的时候执行 -
如果存在
BeanPostProcessor
,Spring将调用它们的postProcessAfterInitialization
(后初始化)方法,在Bean初始化后对其进行处理 -
Bean初始化完成,供应用使用,这里分两种情况:
- 8.1 如果Bean为单例的话,那么容器会返回Bean给用户,并存入缓存池。如果Bean实现了
DisposableBean
接口,Spring将调用它的destory
方法,然后调用在xml中定义的destory-method
方法,这两个方法作用类似,都是在Bean实例销毁前执行。 - 8.2 如果Bean是多例的话,容器将Bean返回给用户,剩下的生命周期由用户控制。
11.AOP是什么?
AOP是面向切面编程,能够将那些与业务无关,却为业务模块所共同调用的逻辑(如事务处理、日志、权限等)封装起来,以减少系统的重复代码,降低模块间的耦合度,并有利于未来的维护和扩展。
12.SpringAOP实现原理
SpringAOP是基于动态代理实现的,动态代理是有两种,一种是jdk动态代理,一种是cglib动态代理;
-
jdk动态代理是原理是利用反射来实现的,需要调用反射包下的Proxy类的newProxyInstance方法来返回代理对象,这个方法中有三个参数,分别是用于加载代理类的类加载器,被代理类实现的接口的class数组和一个用于增强方法的InvocationHandler实现类。
-
cglib动态代理原理是利用asm开源包来实现的,是把被代理类的class文件加载进来,通过修改它的字节码生成子类来处理
13.jdk动态代理和cglib动态代理的区别?
jdk动态代理要求被代理类必须有实现的接口,生成的动态代理类会和代理类实现同样的接口;cglib动态代理则不同,生成的动态代理类会继承被代理类。Spring默认使用jdk动态代理,当被代理的类没有接口时就使用cglib动态代理
14.AOP相关术语
目标(Target):被通知的对象
代理(Proxy):向目标对象应用通知之后创建的代理对象
连接点(JoinPoint):目标对象的所属类中,定义的所有方法均为连接点
切入点(Pointcut):被切面拦截 / 增强的连接点(切入点一定是连接点,连接点不一定是切入点)
通知(Advice):增强的逻辑 / 代码,也即拦截到目标对象的连接点之后要做的事情
切面(Aspect):切入点(Pointcut)+通知(Advice)
Weaving(织入):将通知应用到目标对象,进而生成代理对象的过程动作
15.AOP通知类型有哪些?
Before(前置通知):目标对象的方法调用之前触发
After (后置通知):目标对象的方法调用之后触发
AfterReturning(返回通知):目标对象的方法调用完成,在返回结果值之后触发
AfterThrowing(异常通知):目标对象的方法运行中抛出 / 触发异常后触发。AfterReturning 和 AfterThrowing 两者互斥。如果方法调用成功无异常,则会有返回值;如果方法抛出了异常,则不会有返回值。
Around (环绕通知):编程式控制目标对象的方法调用。环绕通知是所有通知类型中可操作范围最大的一种,因为它可以直接拿到目标对象,以及要执行的方法,所以环绕通知可以任意的在目标对象的方法调用前后搞事,甚至不调用目标对象的方法
16.Spring事务原理?
Spring事务有编程式和声明式,我们一般使用声明式,在某个方法上增加@Transactional注解,这个方法中的sql会统一成功或失败。
原理:
当一个方法加上@Transactional注解,Spring会基于这个类生成一个代理对象并将这个代理对象作为Bean,当使用这个Bean中的方法时,如果存在@Transactional注解,就会将事务自动提交设为false,然后执行方法,执行过程没有异常则提交,有异常则回滚、
17.Spring事务失效场景
- 事务方法所在的类没有加载到容器中
- 业务自己捕获了异常,事务会认为程序正常秩序
- 方法使用 final 或 static关键字
- 事务方法不是public类型
- 同一类中,一个没有添加事务的方法调用另外以一个添加事务的方法,事务不生效
- Spring事务默认只回滚运行时异常,可以用rollbackfor属性设置
18.Spring事务的隔离级别
default:默认级别,使用数据库自定义的隔离级别
其它四种隔离级别与MySQL一样
19.Spring事务的传播行为
- PROPAGATION_REQUIRED:支持当前事务,如果不存在,则新启一个事务
- PROPAGATION_MANDATORY:支持当前事务,如果不存在,则抛出异常
- PROPAGATION_SUPPORTS: 支持当前事务,如果不存在,则以非事务方式执行
- PROPAGATION_REQUIRES_NEW:不支持当前事务,创建一个新事物
- PROPAGATION_NEVER: 不支持当前事务,如果已存在事务就抛异常
- PROPAGATION_NOT_SUPPORTED: 不支持当前事务,始终以非事务方式执行
- PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与
PROPAGATION_REQUIRED
类似的操作。
PROPAGATION_NESTED 与PROPAGATION_REQUIRES_NEW的区别:
使用PROPAGATION_REQUIRES_NEW
时,内层事务与外层事务是两个独立的事务。一旦内层事务进行了提交后,外层事务不能对其进行回滚。两个事务互不影响。
使用PROPAGATION_NESTED
时,外层事务的回滚可以引起内层事务的回滚。而内层事务的异常并不会导致外层事务的回滚,它是一个真正的嵌套事务。
20.单例Bean是线程安全的吗?
Spring的默认Bean作用域是单例的,单例的Bean不是线程安全的,但是开发中大部分的Bean都是无状态的,不具备存储功能,比如controller、service、dao,他们不需要保证线程安全。
如果要保证线程安全,可以将Bean的作用域改为prototype。
另外还可以采用ThreadLocal来解决线程安全问题。ThreadLocal为每个线程保存一个副本变量,每个线程只操作自己的副本变量。
21.循环依赖是什么,怎么解决的?
循环依赖就是在创建 A 实例的时候里面包含着 B 属性实例,所以这个时候就需要去创建 B 实例,而创 建 B 实例过程中也包含着 A 实例。 这样 A 实例还在创建的过程当中,所以就导致 A 和 B 实例都创建不出来。
Spring通过三级缓存来解决循环依赖:
一级缓存:缓存经过完整的生命周期的Bean
二级缓存 :缓存未经过完整的生命周期的Bean
三级缓存:缓存的是ObjectFactory
我们实例化A之后,先将 A 放入三级缓存 ,这时要创建B,B要注入A就直接去三级缓存中查找,然后将取出的A对象放入二级缓存中,因为这个时候 A 还未经 过完整的生命周期所以不能放入一级缓存。当B创建完成,放入一级缓存,A 继续执行生命周期,当A完成了属性的注入后,就可以放入一级缓存了
22.Spring用过哪些重要的注解?
@Component,@Service,@Repository,@Controller 用于服务类
@Autowired 通过类型来实现自动注入Bean
@Qualifier 配合@Autowired实现根据name注入Bean。
@Scope 用于配置 Spring Bean 的范围
@Configuration,@ComponentScan,@Bean 用于基于 java 的配置
@Aspect,@Before,@After,@Around,@Pointcut 用于切面编程
23.SpringMVC工作原理
SpringMVC工作过程围绕着前端控制器DispatcherServlet,几个重要组件有HandleMapping(处理器映射器)、HandleAdapter(处理器适配器)、ViewResolver(试图解析器)
工作流程:
(1)DispatcherServlet接收用户请求将请求发送给HandleMapping
(2)HandleMapping根据请求url找到具体的handle和拦截器,返回给DispatcherServlet
(3)DispatcherServlet调用HandleAdapter,HandleAdapter执行具体的controller,并将controller返回的ModelAndView返回给DispatchServler
(4)DispatcherServlet将ModelAndView传给ViewResolver,ViewResolver解析后返回具体view
(5)DispatcherServlet根据view进行视图渲染,返回给用户
24.如何定义一个全局异常处理类?
创建一个全局异常处理类,这个类上添加@ControllerAdvice注解,然后定义用于捕捉不同异常类型的方法,在这些方法上添加@ExceptionHandler(value = 异常类型.class)和@ResponseBody注解,方法参数是HttpServletRequest和异常类型,然后将异常消息进行处理。
25.如何自定义异常类?
创建一个自定义异常类继承一个异常接口,类属性包括final类型的连续id、错误码、错误信息,再根据需求写构造方法。
26.SpringMVC中的拦截器和Servlet中的过滤器有什么区别?
拦截器是基于Java的反射机制的,而过滤器是基于函数回调。
拦截器不依赖于servlet容器,过滤器依赖于servlet容器。
拦截器只能对部分请求起作用,而过滤器则可以对几乎所有的请求起作用。
拦截器可以访问Spring容器上下文里的对象,而过滤器不能访问。
拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次。
27.SpringMVC常见注解
- @Controller:用于定义控制器类
- @ResponseBody:表示方法的返回结果直接写入HTTP response body中
- @PathVariable:获取路径参数
- @RequestParam:用在方法的参数前面
- @RequestBody:请求的json转化为Bean去接收
- @RestController:是@Controller和@ResponseBody的合集
- @RequestMapping:提供路由信息,负责URL到Controller中的具体函数的映射
- @GetMapping:是@RequestMapping(method = RequestMethod.GET)的缩写。
- @PutMapping:是@RequestMapping(method = RequestMethod.PUT)的缩写。
- @PostMapping:是@RequestMapping(method = RequestMethod.POST)的缩写。
- @DeleteMapping:是@RequestMapping(method = RequestMethod.DELETE)的缩写。
- @ControllerAdvice:统一处理异常。
- @ExceptionHandler:用在方法上表示遇到这个异常就执行以下方法。