1.Spring两大核心技术是什么?IOC和AOP是什么?简单描述下你对AOP和IOC的理解?
Spring两大核心技术分别是IoC和AOP。
IoC即控制反转,指的是将对象的管理控制权交给容器,例如由Spring框架来创建、管理对象。依赖注入DI,指的是为对象的属性或方法的参数注入值,例如Controller中依赖了Service,依赖注入就表现为为Controller中的Service注入值。
AOP指的是面向切面编程,用于统一管理程序的横向关注点,常见的场景:安全、日志、事务等。例如,基于Spring JDBC的事务管理,还有Spring Security中通过@PreAuthority实现的授权访问,本质上都是通过AOP实现的。
2.AOP是如何实现的?
是通过动态代理实现的。
3.简述Spring AOP中常用的几种Advice注解
@Before(execution):在方法执行前
@AfterReturning(execution):在方法正常return后
@AfterThrowing(execution):在方法抛出异常后
@After(execution) :在方法结束后,无论正常结束还是异常结束
@Around(execution):环绕,唯一可以使ProceedingJoinPoint参数来控制流程的Advice,由于是自行通过调用参数对象来执行连接点方法,所以,可以实现其它所有Advice的效果
4.@Autowired和@Resource注解的区别?
@Resource是Java的注解,位于javax.annotation包下,Spring支持此注解,它首选按byName自动装配,如果匹配失败,再按照类型byType匹配注入。
@Autowired是Spring框架,会先查询匹配类型的对象的数量,如果数量为1,则直接装配,且装配成功,如果数量为0,则取决于@Autowired的required属性,属性值为true时,装配失败,在加载Spring的ApplicationContext时就会报错,属性值为false时,放弃装配,这种情况下可能导致NPE问题,如果数量超过1个,则尝试根据名称来匹配,如果存在名称匹配的Bean,则装配成功,如果不存在名称匹配的Bean,则装配失败,在加载Spring的ApplicationContext时就会报错。
@Resource可以添加在属性上,也可以添加在Setter方法上,用于实现自动装配,而@Autowired除了添加在属性上、添加在Setter方法上,还可以添加在构造方法上。
在解决同类型对象有多个时,@Autowired可以配合@Qualifier一起使用,通过@Qualifier配置所需要装配的Bean的名称,并且,@Qualifier既可以添加在属性上,还可以添加在方法的参数上,使得@Autowired的使用更加灵活。
另外,在使用IntelliJ IDEA时,有时使用@Autowired时会报错,但实际运行是没有问题的,而改为@Resource却不会报错,这并不表示它建议使用@Resource取代@Autowired,只是它不对@Resource的执行效果进行检查而已。
5.Spring如何解决循环依赖问题?
Spring通过三级缓存解决了循环依赖问题。三级缓存包括singletonObjects、earlySingletonObjects和singletonFactories。
在Spring容器启动过程中,当创建一个Bean时,Spring首先将其放入singletonFactories中,并标记为正在创建,然后继续递归创建该Bean依赖的其他 Bean。
当递归创建到依赖另一个正在创建的Bean时,Spring会从singletonFactories中获取正在创建的Bean的代理对象,并返回给依赖它的Bean。
当依赖的Bean创建完成后,Spring会将其放入earlySingletonObjects中,并从singletonFactories中移除,接着,Spring会注入依赖该Bean的其他Bean,完成依赖注入。
最后,当所有Bean创建完成后,Spring会将这些Bean放入singletonObjects中,以供其他 Bean 使用。
通过三级缓存,Spring实现了循环依赖的自动解决。但是,由于Spring采用了代理对象解决循环依赖,因此如果循环依赖的Bean的方法内部需要调用自己,会出现代理对象无法调用到实际对象的情况,需要额外注意。
6.事务注解以及作用?
基于Spring JDBC的事务管理中,是声明式的事务管理,通过@Transactional注解进行标记。
如果将@Transactional添加在接口上,则此接口的实现类中所有重写的方法都是事务性的,如果添加在接口中的抽象方法上,则实现类中重写的此方法是事务性,也可以添加在实现类或实现类中重写的方法,需要注意的是:此注解对于实现类中扩展的方法是无效的。
当方法被标记为事务性的,则具有事务的特征,即符合ACID特性,具体表现为一系列的写操作要么全部成功,要么全部失败。
这种事务管理机制,默认为基于RuntimeException进行回滚。
所以,在开发实践中,只要某个业务中涉及超过1次的增、删、改操作,就必须使得这个业务是事务性的,并且,在执行每个增、删、改操作后,应该判断返回的结果,如果不符合预期,必须抛出RuntimeException或其子孙类异常,以保证失败时能够回滚。
7.Spring常用注解有哪些?都有什么用?
@ComponentScan:添加在配置类上,开启组件扫描。如果没有配置包名,则扫描当前配置类所在的包,如果配置了包名,则扫描所配置的包及其子孙包
@Component:添加在类上,标记当前类是组件类,可以通过参数配置Spring Bean名称
@Controller:添加在类上,标记当前类是控制器组件类,用法同@Component
@Service:添加在类上,标记当前类是业务逻辑组件类,用法同@Component
@Repository:添加在类上,标记当前类是数据访问组件类,用法同@Component
@Configuration:添加在类上,仅添加此注解的类才被视为配置类,通常不配置注解参数
@Bean:添加在方法上,标记此方法将返回某个类型的对象,且Spring会自动调用此方法,并将对象保存在Spring容器中
@Autowired:添加在属性上,使得Spring自动装配此属性的值,添加在构造方法上,使得Spring自动调用此构造方法,添加在Setter方法上,使得Spring自动调用此方法
@Qualifier:添加在属性上,或添加在方法的参数上,配合自动装配机制,用于指定需要装配的Spring Bean的名称
@Scope:添加在组件类上,或添加在已经添加了@Bean注解的方法上,用于指定作用域,注解参数为singleton(默认)时为“单例”,注解参数为prototype时为“非单例”
@Lazy:添加在组件类上,或添加在已经添加了@Bean注解的方法上,用于指定作用域,当Spring Bean是单例时,注解参数为true(默认)时为“懒加载”,注解参数为false时为“预加载”
@Value:添加在属性上,或添加在被Spring调用的方法的参数上,用于读取Environment中的属性值,为对象的属性或方法的参数注入值
@Resource:此注解是javax包中的注解,添加在属性上,使得Spring自动装配此属性的值
8.Spring MVC常用注解有哪些?都有什么用?
@ResponseBody:添加在方法上,标记此方法是“响应正文”的,添加在类上,标记此类中所有方法都是“响应正文”的
@RestController:添加在类上,标记此类是一个“响应正文”的控制器类
@RequestMapping:添加在类上,也可以添加在处理请求的方法上,通常用于配置请求路径
@GetMapping:添加在方法上,是将请求方式限制为GET的@RequestMapping
@PostMapping:添加在方法上,是将请求方式限制为POST的@RequestMapping
@DeleteMapping:添加在方法上,是将请求方式限制为DELETE的@RequestMapping
@PutMapping:添加在方法上,是将请求方式限制为PUT的@RequestMapping
@RequestParam:添加在请求参数上,可以:1. 指定请求参数名称;2. 要求必须提交此参数;3. 指定请求参数的默认值
@PathVariable:添加在请求参数上,用于标记此参数的值来自URL中的占位符,如果URL中的占位符名称与方法的参数名称不同,需要配置此注解参数来指定URL中的占位符名称
@RequestBody:添加在请求参数上,用于标记此参数必须是对象格式的参数,如果未添加此注解,参数必须是FormData格式的
@ExceptionHandler:添加在方法上,标记此方法是处理异常的方法,可以通过配置注解参数来指定需要处理的异常类型,如果没有配置注解参数,所处理的异常类型取决于方法的参数列表中的异常类型
@ControllerAdvice:添加在类上,标记此类中特定的方法将作用于每次处理请求的过程中
@RestControllerAdvice:添加在类上,是@ControllerAdvice和@ResponseBody的组合注解
9.Spring Boot常用注解有哪些?都有什么用?
@SpringBootApplication:添加在类上,用于标记此类是Spring Boot的启动类,每个Spring Boot项目应该只有1个类添加了此注解,这个注解的典型元注解包括@SpringBootConfiguration,所以Spring Boot项目的启动类本身也是配置类,并且,元注解中还包括@EnableAutoConfiguration,所以Spring Boot项目默认开启了自动配置,元注解中还包括@ComponentScan,但没有显式的配置根包,所以Spring Boot项目默认从启动类所在的包开始执行组件扫描
@SpringBootConfiguration:通常不需要显式的使用,它是@SpringBootApplication的元注解之一
@SpringBootTest:添加在类上,用于标记此类是加载Spring环境的测试类
10.依赖注入的三种方式?
1. 属性注入
2. Setter注解
3. 构造方法注入
11.Spring Bean的作用域?
Spring Bean常见作用域有:singleton、prototype、request、session和global session,其中,singleton是默认的作用域,是“单例的”,prototype表示“原型”,是“非单例的”,其它作用域仅在Web环境下有效,而且,并不常见,在开发实践中,绝大部分Spring Bean的作用域都是默认的
12.Spring MVC、Spring Cloud、Spring Boot的区别是什么?
Spring是一个轻量级的控制反转(IoC)和支持面向切面编程(AOP)的容器框架,是Spring全家桶的基石。
Spring MVC是建立在Spring基础之上的,是一个Web框架,使得开发Web应用变得很容易。
Spring Boot主要解决了依赖管理与自动配置相关问题,其思想是约定大于配置,极大的简化了项目中的配置,做到开箱即用。
Spring Cloud构建于Spring Boot之上,是一个微服务解决方案,包含了微服务相关问题的解决方案的框架,主流的Spring Cloud体系有Netflix的,其典型代表是使用Eureka处理服务发现,使用Zuul处理网关路由,使用Ribbon处理服务间通信,另一套主流的Spring Cloud体系是Alibaba的,其典型代码是使用Nacos处理服务发现并作为配置中心,使用Dubbo处理服务间通信。
14.Spring Boot自动配置原理
@EnableAutoConfiguration会读取工厂配置,即/META-INF/spring.factories,然后找到标注@Configuration的配置类,配置类中使用@Conditional???系列注解,进行条件配置,根据条件初始化Bean组件,典型的注解有:@ConditionalOnClass(当某个类存在时)、@ConditionalOnBean(当某个对象存在时)、@ConditionalOnMissingBean(当不存在某个对象时)、@ConditionalOnProperty(当存在某个属性的配置时)等
15.MVC的设计理念
MVC 是一种使用 MVC(Model View Controller 模型-视图-控制器)设计创建 Web 应用程序的模式;
Model(数据模型)表示应用程序核心, 处理应用程序数据。
View(视图)显示数据。
Controller(控制器)处理输入与交互流程
16.Spring MVC的处理流程?
1. 客户端发送请求至前端控制器DispatcherServlet
2. DispatcherServlet收到请求后,调用处理器映射器HandlerMapping
3. HandlerMapping根据请求URL找到具体的Controller。
4. Controller处理请求,并返回ModelAndView,其中的View只是视图名,并不指向具体的视图组件
5. DispatcherServlet通过ViewReslover(视图解析器)确定负责显示数据的具体View
6. DispatcherServlet对View进行渲染视图(即将Model填充至视图组件中),并将完整的视图响应到客户端
17.MyBatis中#{}和${}的区别
Mybatis中的#{}格式的占位符是预编译的,即SQL语句会先经过词法分析、语义分析,再编译,当编译完成后,再将值代入到编译结果中一并执行!由于是预编译的,此前已经完成语义分析,语义是已经确定的,占位符的位置一直是某个值,不可能是字段名,所以,所有值都不需要使用单引号框住!并且,由于是预编译的,所以不存在SQL注入的问题!使用这种占位符时,占位符位置只能表示某个值!
Mybatis中的${}格式的占位符不是预编译的,即先将值代入到SQL语句中,然后,并代入后的结果再执行词法分析、语义分析、编译,最终执行!由于不是预编译的,而是先将值代入到SQL语句中,对于非数值、非布尔值的其它值而言,Mybatis自带2级缓存机制,即Mybatis在满足特定的情况下,会将查询结果暂时保存起来,如果下次需要查询的与此前保存的结果是相同的,则下次并不会真正的查询,而是直接将此前缓存的结果返回,以提高查询效率!
18.MyBatis的一级缓存二级缓存?
Mybatis的一级缓存是基于SqlSession的,也称之为“会话缓存”,默认是开启的(不可控),基本上人为不可控,当使用同一个SqlSession,同一个Mapper,执行同样的SQL,并且SQL语句中需要传入的参数是相同的,则一级缓存是生效的!
一级缓存会在以下情况失效:
- 不满足使用条件:不是同一个SqlSession,或不是同一个Mapper,或不是同样的SQL,或不是同样的参数
- 手动调用方法清除缓存:调用SqlSession对象的关闭或clearCache()方法
- 当前Mapper执行了任何写操作,即使这个写操作与当前需要查询的数据无关,甚至这个写操作执行完毕后受影响的行数为0,也会清除一级缓存数据
Mybatis的二级缓存是基于namespace的,在Spring Boot项目中,默认是全局开启的(可控),但各namespace并未开启,二级缓存只需要同一个namespace、执行同样的SQL、传入同样的参数,就是有效的!如果是通过XML文件来配置SQL语句,需要在XML文件中添加<cache/>标签,以开启当前namespace的二级缓存!当在namespace中开启二级缓存后,还可以在各查询的<select>标签上配置useCache属性,如果取值为false,将对此查询不启用二级缓存!需要注意:启用二级缓存后,查询的返回结果类型必须实现Serializable接口!二级缓存也会因为当前namespace中执行了任何写操作而销毁!
Mybatis在执行查询时,会先从二级缓存中查找缓存数据,如果命中,则返回,如果未命中,则会从一级缓存中查找缓存数据,如果命中,则返回,如果仍未命中,则会执行真正的查询。不加单引号,就会被误以为是字段名,所以,所有非数值、非布尔值的其它值都必须使用一对单引号框住!并且,由于不是预编译的,向SQL语句中传入的值有可能改变SQL的语义,是存在SQL注入风险的!使用这种占位符时,占位符位置可以表示SQL语句的任何片段!
19.简述MyBatis的单个参数、多个参数如何声明?
单个参数直接定义即可;使用@Param注解传递多个参数;通过Java Bean传递多个参数;使用Map封装多个参数
20.MyBatis 如何完成批量操作?
在数据量不是特别大的情况下(不超过1000条,且每条数据都不是特别长),可以在XML拼接SQL,主要通过<foreach>标签对参数进行遍历。
当数据量很大(通常超过500条甚至更多),并且非常关注执行效率的情况下,应该配置Mybatis的ExecutorType为BATCH来执行批处理操作
21.写过原生的分页吗?用过什么插件吗?分页插件的原理?
原生分页就是通过SQL语句中的limit实现分页,使用插件的话,就是通过PageHelper实现分页,PageHeapler 通过MyBatis拦截器实现的分页,核心类是PageInterceptor,使用时,需要注意,设置分页与执行查询应该是连续的2条语句,不可以在中间添加if等代码,否则可能导致线程安全问题
22.你知道MyBatis延迟加载吗?
mybatis的延迟加载就是按需查询,在需要的时候进行查询。
在MyBatis的属性中配置:将lazyLoadingEnabled设置为true表示开启延迟加载,默认为false.将aggressiveLazyLoading设置为false表示按需加载,默认为true
23.JDBC与Mybatis的区别,JDBC为什么效率比MyBatis快
JDBC是Java提供的操作数据库的API,JDBC的主要问题在于:编码工作量相对繁琐,模板化的代码太多。
Mybatis是基于JDBC实现的,能够通过反射将JDBC与Java对象映射,基本不用写JDBC代码。
由于采用反射进行映射,性能比原生JDBC API略慢。