1、Spring
1.1 谈谈你对Spring的理解
1.1.1 Spring是什么?
- 一个开放源代码的J2EE应用程序框架,针对bean的生命周期进行管理的轻量级容器
- 是为了解决企业级编程中开发的复杂性,实现敏捷性开发的应用型框架
- Spring是一个开源的容器框架,他集成各类型的工具,通过核心的 Bean Factory 实现了底层类的实例化和生命周期的管理,在整个框架中,各类型的功能被抽象成一个个的Bean,这样就可以实现各种功能的管理
- 让Java开发模块化,并且全面。
- Spring通过控制反转降低耦合性,一个对象的依赖通过被动注入的方式而非主动的new,还通过代理模式实现了面向切面编程
1.1.2 IoC控制反转
- 是一个概念,是一种思想
- 就是对对象的控制权进行转移,从程序代码本身反转到了外部容器。把对象的创建、初始化、销毁等工作交给Spring容器来做。由Spring控制对象的生命周期,即将new的过程交给Spring容器去处理
1.1.3 依赖注入
- 依赖注入(DI)表示程序在运行过程中,若需要调另外一个对象协助时,无需在代码中创建被调用者,而是依赖于外部容器,由外部容器创建后传递给程序
- 依赖注入是目前最优秀的解耦方式,依赖注入让Spring的bean之间以配置文件的形式组织在一起,而不是以硬编码的方式耦合在一起
1.1.4 Spring的容器有哪些
- ApplicationContext是BeanFactory的子类,它比BeanFactory提供了更加丰富的功能
- BeanFactory和ApplicationContext在Bean加载方面有什么不同呢?
- BeanFactory是延迟加载。BeanFactory初始化完成后,并不加载任何Bean,只有在第一次获取Bean时,BeanFactory才会加载该Bean
- ApplicationContext会在启动后主动加载好单例Bean,这样,从ApplicationContext中获取Bean时效率更高
- 其他子类:
- ClassPathXmlApplicationContext主要用在在类路径加载xml文件的场景
- FileSystemXmlApplicationContext主要用在在文件系统加载xml文件的场景
- XMLWebApplicationContext主要用在在Web中加载xml文件的场景
1.1.5 Spring依赖注入的3种方式
- 构造器注入
/*带参数,方便利用构造器进行注入*/
public CatDaoImpl(String message){
this. message = message;
}
<bean id="CatDaoImpl" class="com.CatDaoImpl">
<constructor-arg value=" message "></constructor-arg>
</bean>
- set方法注入
public class Id {
private int id;
public int getId() { return id; }
public void setId(int id) { this.id = id; }
}
<bean id="id" class="com.id "> <property name="id" value="123"></property> </bean>
- 实例工厂注入
public class DaoFactory { //实例工厂
public FactoryDao getFactoryDaoImpl(){
return new FactoryDaoImpl();
}
}
public class SpringAction {
private FactoryDao factoryDao; //注入对象
public void setFactoryDao(FactoryDao factoryDao) {
this.factoryDao = factoryDao;
}
}
<bean name="springAction" class="SpringAction">
<!--使用实例工厂的方法注入对象,对应下面的配置文件-->
<property name="factoryDao" ref="factoryDao"></property>
</bean>
<!--此处获取对象的方式是从工厂类中获取实例方法-->
<bean name="daoFactory" class="com.DaoFactory"></bean>
<bean name="factoryDao" factory-bean="daoFactory" factory-method="getFactoryDaoImpl">
</bean>
1.2 SpringMVC处理请求的流程
Spring MVC:Spring的模型-视图-控制器,是围绕一个DispatcherServlet来设计的,这个servlet会把请求分发给各个处理器,并支持可以配置的处理器映射、视图渲染、本地化、时区与主题渲染等,甚至还能支持文件上传
- 用户发送一个请求到服务器,服务器中的前端控制器 DispatcherServlet 接收
- 前端控制器 DispatcherServlet 用于 HTTP 请求处理程序/控制器的中央调度器,由他监控和调度整个流请求处理流程
- 前端控制器 DispatcherServlet 接收请求并发送给处理器映射器 HandlerMapping
- 处理器映射器负责将请求的 URL 映射到某个 Handler
- 处理器 Handler 处理请求
- 处理器映射器 HandlerMapping 找到对应的 Handler,返回执行链 HandlerExecutionChain 到前端控制器 DispatcherServlet
- 执行链 HandlerExecutionChain 中有一些拦截器,拦截一些请求
- 前端控制器 DispatcherServlet 告诉处理器配置器 HandlerAdapter 执行 Handler
- 处理器配置器 HandlerAdapter根据配置结果,执行 Handler
- 处理器配置器 HandlerAdapter,就是实际调用一个 Handler 类
- Handler 处理具体的业务需求返回的模型和视图对象 ModelAndView给处理器配置器 HandlerAdapter,再由处理器配置器返回给前段控制器 DispatcherServlet
- 模型和视图 ModelAndView 中保存对象和视图信息,对象的信息 key 和 value
- 视图信息就是要跳转的视图的信息
- 前段控制器 DispatcherServlet 得到 ModelAndView,就去请求视图解析器 ViewResolver
- 视图解析器 ViewResolver 负责将逻辑视图名解析为某个具体实现了的 view 接口的对象
- 视图解析器 ViewResolver 解析模型和视图 ModelAndView 得到 view
- 视图 View 负责给客户端返回应答结果,结果不仅仅可以是 HTML 页面,也可以是 pdf…
- 前端控制器 DispatcherServlet 渲染得到的 view,将 Model 中的数据填充到request作用域,响应客户端
1.3 Spring AOP解决了什么问题?怎么实现的?
1.3.1 解释一下什么是AOP?
- 面向切面编程,利用一种横切的技术,剖开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用的模块中,并将其命名为 Aspect
- 将那些与业务⽆关,却为业务模块所共同调⽤的逻辑或责任封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性
1.3.2 实现(JDK动态代理、CGLib)
- 动态代理-JDK动态代理、CGLib:基于动态代理来实现的,默认如果使用接口的,用JDK提供的动态代理来实现,如果是方法则使用cglib来实现
- JDK动态代理:主要涉及java.lang.reflect中的两个类,Proxy、InvocationHandler,InvocationHandler是一个接口,通过反射机制调用目标类的代码,动态将横切逻辑和业务逻辑编织在一起。Proxy利用InvocationHandler动态创建一个符合某一接口的实例,生成目标类的代理对象
- CGLib(Code Generation Lib):是一个强大的高性能、高质量的代码生成库,可以在运行期扩展Java类和实现Java接口,CGLib封装了ASM,可以在运行期动态生成新的class。和JDK动态代理相比较,JDK 创建代理有一个限制,就是只能为接口创建代理实例,而对于没有通过接口定义业务方法的类,则可以通过 CGLib 创建动态代理
1.4 Spring事务
1.4.1 什么是事务?
- 事务就是用户定义的一系列数据库操作,这些操作可以视为一个完整的逻辑处理工作单元,要嘛全部执行,要嘛全部不执行
- 事务是一组不可再分割的操作单元
1.4.2 事务的使用(begin transaction、commit、rollback)
- begin transaction:表示事务的开启
- commit:表示事务的提交操作,表示该事务的结束
- rollback:表示事务回滚,说明事务异常结束,此时将事务中已经执行的动作撤回
1.4.3 事务的产生
- 数据库中的数据是共享资源,因此数据库系统通常要支持多个用户的或不同应用程序的访问,并且各个访问进程都是独立执行的,这样就有可能出现并发存取数据的现象,这里有点类似Java开发中的多线程安全问题(解决共享变量安全存取问题),如果不采取一定措施会出现数据异常的情况
- DBMS系统必须对这种并发操作提供一种相应的处理机制来保证,访问彼此之间不受任何干扰,从而保证数据库的正确性不受到破坏,为了避免数据库的不一致性,这种处理机制称之为“并发控制”,其中事务就是为了保证数据的一致性而产生的一种概念和手段(事务不是唯一手段)
1.4.4 事务的特征(ACID)
为了保证数据库的正确性和一致性,事务具有四个特征
- 原子性(Atomicity):
- 事务的原子性保证事务中包含的一组更新操作是原子的,不可分割的。不可分割是事务的最小分割单位,所包含的操作被视为一个整体,执行过程中遵循要嘛全部执行,要嘛全部不执行,不存在一半执行、一半不执行的情况
- 一致性(Consistency):
- 事务的一致性要求事务必须满足数据库的完整性约束,且事务执行完毕后会将数据库由一个一致性的状态变为另一个一致性的状态。事务的一致性与原子性是密不可分的
- 要么全部操作,要么都不执行,可见事务的一致性与原子性息息相关
- 隔离性(Isolation):
- 事务的隔离性要求事务之间是彼此独立的,隔离的。即一个事务的执行不可以被其他事务干扰。具体到操作是指一个事务的操作必须在一个事务commit之后才可以进行操作
- 事务并发执行时,相当于将并发事务变成串行事务,顺序执行,如同串行调度般的执行事务。这里可以思考事务如何保证它的可串行化的呢?答案是锁
- 持续性(Durability):
- 事物的持续性也称持久性,是指一个事务一旦提交,它对数据库的改变将是永久性的,因为数据刷进了物理磁盘了,其他操作将不会对它产生任何影响。在此事务的处理思想是与多线程安全的处理思想是类似的,面临问题是一致的都是解决资源共享安全问题,解决问题的思想也是进行串行化调度
1.4.5 Spring事务的行为是什么(7种事务的传播行为)?
事务的传播行为(propagation behavior)指的是,一个事务方法被另外一个事务调用的时候,这个事务方法该如何进行
- REQUIRED(默认):支持使用当前事务,如果当前事务不存在,则创建一个新的事务
- SUPPORTS:支持使用当前事务,如果该事务不存在,则不使用事务
- MANDATORY:强制,支持使用当前事务,如果当前事务不存在,则抛出异常
- REQUIRES_NEW:创建一个新事务,如果当前事务存在,则把当前事务挂起
- NOT_SUPPORT:以⾮事务⽅式执⾏操作,如果当前事务存在,则把当前事务挂起
- NEVER:以⾮事务⽅式执⾏操作,如果当前有事务则抛出异常
- NESTED:嵌套事务,如果当前事务存在,那么就在嵌套的事务中执行,如果当前事务不存在,则与REQUIRED表现一致
1.4.6 事务的隔离级别
- ISOLATION_DEFAULT:使用数据库默认的隔离级别
- ISOLATION_READ_UNCOMMITTED:允许读取尚未提交的,可能导致脏读、幻读或不可重复读
- ISOLATION_READ_COMMITTED:允许从已经提交的并发事务读取。可防止脏读,但幻读和不可重复读仍可能会发生
- ISOLATION_REPEATABLE_READ:这种事务隔离级别可以防⽌脏读,不可重复读。但是可能出现幻读。它除了保证⼀个事务不能读取另⼀个事务未提交的数据外,还保证了避免不可重复读
- ISOLATION_SERIALIZABLE:完全服从ACID的隔离级别,确保不发生脏读、不可重复读和幻影读。这在所有隔离级别中也是最慢的,因为它通常是通过完全锁定当前事务所涉及的数据表来完成的
1.4.7 脏读、不可重复读、幻读
- 脏读:
- 脏读发生在一个事务读取了被另一个事务改写但尚未提交的数据时。如果这些改变在稍后被回滚了,那么第一个事务读取的数据就会是无效的
- 不可重复读:
- 事务1读取记录时,事务2更新了记录并提交,事务1再次读取时可以看到事务2修改后的记录
- 不可重复读重点在修改
- 是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的
- 幻读
- 事务1读取记录时事务2增加了记录并提交,事务1再次读取时可以看到事务2新增的记录
- 是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样
1.5 Spring中BeanFactory和FactoryBean有什么区别?
- BeanFactory:
- 它是一个工厂类(接口),用于管理bean的一个工厂。在Spring中,BeanFactory是IOC容器的核心接口
- 它的主要职责是:实例化、定位、配置应用程序中的对象以及建立这些对象间的依赖
- FactoryBean:是一个实现了FactoryBean接口的Bean
1.6 Spring框架中IOC的原理是什么?
- 控制反转(IOC):控制权由应⽤代码中转到了外部容器,控制权的转移是所谓反转
- 在系统运⾏中,动态的向某个对象提供它所需要的其他对象
- 依赖注入的思想是通过反射机制实现的,在实例化一个类时,他通过反射调用类中set方法将事先保存在HashMap中的类属性注入到类中,总⽽⾔之,在传统的对象创建⽅式中,通常由调⽤者来创建被调⽤者的实例,⽽在Spring中创建被调⽤者的⼯作由Spring来完成,然后注⼊调⽤者,即所谓的依赖注⼊or控制反转。 注⼊⽅式有两种:依赖注⼊和设置注⼊; IoC的优点:降低了组件之间的耦合,降低了业务对象之间替换的复杂性,使之能够灵活的管理对象
1.7 ⽤Spring如何实现⼀个切⾯?
1.7.1 通知
- 前置:before
- 后置:after-returning
- 环绕:around
- 异常抛出通知:after-throwing
- 最终通知:after
1.7.2 切面的具体实现
@Aspect
@Component
@Slf4j
public class ConsoleLogAspect {
//设置切面点(切面地址根据自己的项目填写)
@Pointcut(value = "(execution(* com.xpos.stm.modules.sys.controller.*.*(..)))")
public void webLog() {}
//指定切点前的处理方法
@Before("webLog()")
public void doBefore(JoinPoint joinPoint) throws Exception {
//获取request对象
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
StringBuilder sb = new StringBuilder();
//拼接请求内容
sb.append("\n请求路径:" + request.getRequestURL().toString() + "\n");
sb.append("请求方式:" + request.getMethod() + "\n" );
sb.append("Encoding:" + request.getCharacterEncoding() + "\n" );
sb.append("请求的资源:" + request.getRequestURI() + "\n" );
sb.append("请求的URL地址中附带的参数:" +request.getQueryString()+ "\n" );
sb.append("请求内容:"+ JSON.toJSONString(joinPoint.getArgs()));
log.info(sb.toString());
}
//指定切点前的处理方法
@AfterReturning(pointcut = "webLog()",returning = "result")
public void doAfterReturning(Object result) {
if (ObjectUtils.isEmpty(result)){
return;
}
log.info("\n返回結果:" + JSON.toJSONString(result));
}
}
1.8 Spring如何实现数据库事务?
1.8.1 实现方式
- 使⽤@Transactional注解或在配置⽂件⾥⾯配置
1.8.2 @Transactional注解-声明式事务
- 相关内容
- Spring中支持最底层的接口:PlatformTransactionManager
- 使用注解
- 第一种:配置文件配置
<!--引入公共的配置文件-->
<import resource="application-context.xml"/>
<!--Spring提供的事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<constructor-arg ref="dataSource"/>
</bean>
<!--
开启事务注解
这里有个小技巧,如果你的事务管理bean名不是transactionManager
就要给这个标签配置transaction-manager来指定
-->
<tx:annotation-driven/>
<!-- 配置spring扫描注解注入service类-->
<context:component-scan base-package="cn.lyn4ever.service"/>
- 在这个类或者方法上加注解
1.8.3 编程式事务
- 就是将事务的操作直接写在业务代码中
- 有两种方式:
- 将PlatformTransactionManager的实例注入到bean中
- 使用Spring为我们提供的TransactionTemplate
<import resource="application-context.xml"/>
<!--Spring提供的事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<constructor-arg ref="dataSource"/>
</bean>
<tx:advice id="txAdvice">
<tx:attributes>
<!-- 对单独方法配置属性-->
<tx:method name="insert*" rollback-for="java.lang.Exception"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="serviceTrans" expression="execution(* cn.lyn4ever.serviceaop.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="serviceTrans"/>
</aop:config>
<!-- 配置spring扫描注解注入service类-->
<context:component-scan base-package="cn.lyn4ever.serviceaop"/>
1.9 Spring Bean
1.9.1 什么是Spring的Bean
- 是基于用户提供的配置创建的,构成了应用程序的主干对象,是由Spring IoC容器实例化、装配和管理的
1.9.2 Spring框架中创建bean和装配bean有什么区别
- 装配是将bean的这个生命周期托管给spring容器管理,一般通过注解方式手动装配
- 创建bean就是指具体的实例化了,因为托管给spring了,创建这个动作对用户是透明的
1.9.3 Spring装配Bean的方式
- 隐式的bean扫描发现机制和自动装配
- spring从两个角度实现自动化装配:组件扫描和自动装配
- 组件扫描:自动发现应用上下文中所创建的bean
- 自动装配:自动满足bean之间的依赖
(1) 当对一个类标注@Component注解时,表明该类会作为组件类,spring将为这个类创建bean
(2) 当在应用文中引用这个bean,spring会自动扫描事先指定的包查找这个 bean。但spring默认是不启用组件扫描的,可以在XML中配置加上<context:component-scan base-package=“xx”/>。还有一种方法:在新建一个配置类,类中可以什么不用写,在配置类上加上@ComponentScan注解,spring会自动扫描改配置类所在的包,一般应该倾向xml配置
@Configuration
@ComponentScan
class Config {
}
(3) @Configuration注解表示是配置类,@ComponentScan注解能够在Spring中启用组件扫描,Spring会查找带有@Component注解的类,并会在Spring中自动为其创建一个bean
(4) @ContextConfiguration(classes=Config.class)表明了配置类的所在,这个Car类将会从Config配置类中获得扫描到的组件,然后通过@Autowired注解自动匹配,将bean注入给各个属性
interface Transportation {
void run();
void stop();
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=Config.class)
public class Car implements Transportation {
@Autowired
private Wheel wheel;
@Autowired
private Seat seat;
@Autowired
private Engine engine;
@Test
public void run() {
seat.seat();
engine.start();
wheel.roll();
System.out.println("The car has run.");
}
@Test
public void stop() {
System.out.println("Now, the car has stopped.");
}
}
(5) Autowired固然强大,但是如果有不止有一个bean能否匹配的话,就会阻碍自动装配。有两种处理方式:1. 表示首选的bean:只需要在组件类前面加一个@Primary注解即可,这样如果有多个bean匹配,就会先注入此组建类 2. 限定自动装配的bean:@Qualifier(“beanid”)注解
- 通过java代码装配bean
- 在XML中装配bean
1.9.4 Spring装配Bean的过程
- 实例化:
- 实例化一个 Bean,也就是我们常说的 new
- 设置属性值-依赖注入
- 检查 xxxAware 相关接口,比如 BeanNameAware,BeanClassLoaderAware,ApplicationContextAware( BeanFactoryAware)等等,如果有就调用相应的 setxxx 方法把所需要的xxx传入到 Bean 中
- Aware 的目的是为了让Bean获得Spring容器的服务。 实现了这类接口的 bean 会存在“意识感”,从而让容器调用 setxxx 方法把所需要的 xxx 传到 Bean 中
- 调用BeanPostProcessor,此时检查是否存在有于 Bean 关联的任何 BeanPostProcessors, 执行 postProcessBeforeInitialization() 方法(前置处理器)
- 如果 Bean 实现了InitializingBean接口(正在初始化的 Bean),执行 afterPropertiesSet() 方法
- 检查是否配置了自定义的 init-method 方法,如果有就调用
- 调⽤BeanPostProcessor的后初始化⽅法,此时检查是否存在有于 Bean 关联的任何 BeanPostProcessors, 执行postProcessAfterInitialization() 方法(后置处理器)。返回 wrapperBean(包装后的 Bean)
- Destroy 过期自动清理阶段:当 Bean 不再需要时,会经过清理阶段,如果 Bean 实现了 DisposableBean 这个接口,会调用那个其实现的 destroy()方法
- destroy-method 自配置清理:最后,如果这个 Bean 的 Spring 配置中配置了 destroy-method 属性,会自动调用其配置的销毁方法
总结:实例化 -> 设置属性值 -> BeanNameAware -> BeanFactoryAware -> ApplicationContextAware -> BeanPostProcessor(postProcessBeforeInitialization) -> InitializingBean(afterPropertiesSet) -> init-method -> BeanPostProcessor(postProcessAfterInitialization) -> Destroy 过期自动清理阶段 -> destroy-method 自配置清理
1.9.5 Spring中Bean的⽣命周期
1.10 SpringMVC,Struts2的对比
- 共同点
- 他们都是表现型框架,都是基于MVC模型编写的
- 他们的底层都离不开原始的ServletAPI
- 他们处理请求的机制都是一个核心控制器
- 区别:
- SpringMVC的入口是Servlet,Struts2的入口是Filter
- SpringMVC是基于方法设计的,而Struts2是基于类,Struts2每次执行都会创建一个动作类,所以SpringMVC稍微优于Struts2
- springMVC使用更加简洁
- Struts2的OGNL表达式使页面的开发效率相比springMVC更高些,但执行效率并没有比JSTL提升,尤其是Struts2的表单标签,远没有html执行效率高
1.11 spring事件的实现原理,写出常⽤的⼏个事件
1.12 Springmvc 有哪些组件
1.12.1 DispatcherServlet
- Spring MVC的入口函数。接收请求、响应结果,相当于转发器、中央处理器
- 减少了组件之间的耦合度
- 整个流程的控制中心,由它调用其他组件处理用户请求
1.12.2 HandlerMapping
- 根据请求的url查找handler
- spring提供了不同的映射器实现不同的映射方式:配置文件方式、注解、实现接口方式
1.12.3 处理器适配器HandlerAdapter
- 按照特定的规则(HandlerAdapter要求的规则)去执行Handler,通过HandlerAdapter对处理器进行执行
- 这是适配器模式的使用,通过扩展适配器可以对更多类型的处理器进行执行
1.13 Spring框架的特征
- 轻量-从大小与开销两方面而言Spring都是轻量的。完整的Spring框架可以在一个大小只有1MB多的JAR文件里发布。并且Spring所需的处理开销也是微不足道的。此外,Spring是非侵入式的:典型地,Spring应用中的对象不依赖于Spring的特定类
- 控制反转
- 面向切面
- 容器-Spring包含并管理应用对象的配置和生命周期
- 框架-Spring可以将简单的组件配置、组合成为复杂的应用。在Spring中,应用对象被声明式地组合,典型地是在一个XML文件里。Spring也提供了很多基础功能(事务管理、持久化框架集成等等),将应用逻辑的开发留给了你
- MVC
1.14 什么是AOP?Spring AOP 和 AspectJ的区别是什么?
1.14.1 AOP
- 主要应用于处理一些具有横切性质的系统级服务,如日志收集、事务管理、安全检查、缓存、对象池管理等
- AOP实现的关键就是在于AOP框架自动创建的AOP代理,AOP代理则可分为静态代理和动态代理两大类
- 静态代理:使用AOP框架提供的命令进行编译,从而在编译阶段就可以生成AOP代理类,因此也称为编译时增强
- 动态代理:则在运行时借助JDK动态代理、CGLIB等在内存中“临时”生成AOP动态代理,因此也被称为运行时增强
- AOP是一种编程范式,旨在通过允许横切关注点的分离,提高模块化。AOP提供切面来将跨越对象关注点模块化
- AOP要实现的是我们在写的代码的基础上进行一定的包装,如在方法执行前、或在执行后、或是在执行中出现异常后这些地方进行拦截处理或叫做增强处理
1.14.2 AOP概念
- 横切关注点:对哪些方法进行拦截,拦截后怎么处理,这些关注点称为横切关注点
- 切面(aspect):类是对物体特征的抽象,切面就是对横切关注点的抽象
- 连接点(JoinPoint):被拦截到的点,因为spring只支持方法类型的连接点,所以在spring中连接点指的就是被拦截到的方法,实际上连接点还可以是字段或者是构造器
- 切入点(pointcut):对连接点进行拦截的定义
- Advice(通知):在选取出来的JoinPoint上要执行的操作、逻辑(所谓通知就是指拦截到连接点之后要执行的代码),通知分为:前置、后置、异常、最终、环绕
- 目标对象:代理的目标对象
- 织入(weaving):将切面应用到目标对象并导致代理对象创建的过程
- 引入(introduction):在不修改代码的前提下,引入可以在运行期 为类动态的添加一些方法和字段
1.14.3 Spring AOP和Aspectj的区别
- 能力和目标
- Spring AOP旨在提供一个跨Spring IOC的简单AOP实现,他不打算做一个完整的AOP实现-他只能应用于Spring容器管理的Bean
- AspectJ是原始的AOP技术,目的是提供完整的AOP解决方案。它更健壮,但比Spring AOP复杂,AspectJ可以在所有的域对象中进行使用
- 织入(weaving)方式不同
- AspectJ编译时增强(编译时和class文件加载时织入),它不需要任何设计模式。为了编织代码的各个方面, 它引入了称为 AspectJ 编译器 (ajc) 的编译器, 通过它编译我们的程序, 然后通过提供一个小型 (100K) 运行时库来运行它
- Spring AOP运行时增强(运行时织入),JDK动态代理、CGLib
- AspectJ 使用三种不同类型的Weaving
(1) 编译时 Weaving: AspectJ 编译器作为输入我们的方面的源代码和我们的应用, 并产生一个织入类文件作为输出
(2)编译后 Weaving: 这也称为二进制织入。它是用来织入现有的类文件和 JAR 文件与我们的方面
(3)加载时间 Weaving: 这就像前二进制织入, 不同的是织入被推迟, 直到类加载程序加载类文件到 JVM
- 简单性
- Spring AOP:更简单,因为他不会在我们构建的过程中引入任何额外的编译器或织入,它使用运行时编织,因此它与我们通常的构建过程无缝集成,虽然它看起来简单,但是他只适用于由Spring管理的Bean
- 要使用AspectJ,我们需要引入AspectJ编译(ajc),并重新打包所有的库(除非我们切换到编译后或者加载时间的织入)。这比前者更加复杂,因为他引入了AspectJ Java工具(包括编译器 ajc、调试器 ajdb,文档生成器 ajdoc,程序结构浏览器 ajbrowser)
- 性能
- 编译时织入快于运行时织入
- Spring AOP是基于动态代理的框架,因此在程序启动时会创建动态代理。另外,每个方面还有一些方法的调用,这会对性能产生负面影响
- AspectJ 在应用程序执行之前将这些方面编织到主代码中, 因此没有额外的运行时开销, 与 Spring AOP 不同
- 基准表明 AspectJ 的速度几乎比 Spring AOP 快8到35倍
1.15 Spring、Spring MVC是如何配置的?
- web.xml
<!-- Spring MVC配置 -->
<!-- ====================================== -->
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 可以自定义servlet.xml配置文件的位置和名称,默认为WEB-INF目录下,名称为[<servlet-name>]-servlet.xml,如spring-servlet.xml
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring-servlet.xml</param-value> 默认
</init-param>
-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
<!-- Spring配置 -->
<!-- ====================================== -->
<listener>
<listenerclass>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<!-- 指定Spring Bean的配置文件所在目录。默认配置在WEB-INF目录下 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:config/applicationContext.xml</param-value>
</context-param>
- applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 加载配置文件 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 自动扫描web包 ,将带有注解的类纳入spring容器管理 -->
<!--Spring 容器初始化的时候,会扫描 com.web 下标有
(@Component,@Service,@Controller,@Repository) 注解的类,纳入spring容器管理-->
<context:component-scan base-package="com.web"></context:component-scan>
<!-- dataSource 配置 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<!-- 基本属性 url、user、password -->
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="${jdbcUrl}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
<!-- 配置初始化大小 -->
<property name="initialSize" value="1"/>
<!-- 连接池最小空闲 -->
<property name="minIdle" value="1"/>
<!-- 连接池最大使用连接数量 -->
<property name="maxActive" value="20"/>
<!-- 配置获取连接等待超时的时间 -->
<property name="maxWait" value="60000"/>
<!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
<property name="timeBetweenEvictionRunsMillis" value="60000"/>
<!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
<property name="minEvictableIdleTimeMillis" value="300000"/>
</bean>
<!--使用Spring+MyBatis的环境下,我们需要配值一个SqlSessionFactoryBean来充当SqlSessionFactory
在基本的MyBatis中,SqlSessionFactory可以使用SqlSessionFactoryBuilder来创建,
而在mybatis-spring中,则使用SqlSessionFactoryBean来创建。-->
<!-- mybatis文件配置,扫描所有mapper文件 -->
<bean id="sqlSessionFactory"
class="org.mybatis.spring.SqlSessionFactoryBean"
p:dataSource-ref="dataSource"
p:configLocation="classpath:mybatis-config.xml"
p:typeAliasesPackage="com.entity"
<!-- 如果 MyBatis 映射器 XML 文件在和映射器类相同的路径下不存在,那么另外一个需要配置文件的原因就是它了。 -->
p:mapperLocations="classpath*:mapper/*.xml"/>
<!-- spring与mybatis整合配置,自动扫描所有dao ,将dao接口生成代理注入到Spring-->
<!-- MapperScannerConfigurer 的作用是取代手动添加 Mapper ,自动扫描完成接口代理。
而不需要再在mybatis-config.xml里面去逐一配置mappers。 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"
p:basePackage="com.dao"
p:sqlSessionFactoryBeanName="sqlSessionFactory"/>
<!-- 对dataSource 数据源进行事务管理 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
p:dataSource-ref="dataSource"/>
<!-- 配置AOP通知 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!-- 配置事务属性 -->
<tx:attributes>
<!-- 添加事务管理的方法 -->
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="delete*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="select*" propagation="REQUIRED" read-only="true"/>
</tx:attributes>
</tx:advice>
<!-- 配置一个切面AOP -->
<aop:config>
<aop:aspect id="helloWorldAspect" ref="txAdvice">
<!-- 配置切点 -->
<aop:pointcut id="pointcut" expression="execution(* com.aop.*.*(..))"/>
<!-- 配置前置通知 -->
<aop:before pointcut-ref="pointcut" method="beforeAdvice"/>
<!-- 配置前置通知 -->
<aop:after pointcut-ref="pointcut" method="afterAdvice"/>
<!-- 配置后置返回通知 -->
<aop:after-returning pointcut-ref="pointcut" method="afterReturnAdvice" returning="result"/>
<!-- 配置环绕通知 -->
<aop:around pointcut-ref="pointcut" method="aroundAdvice"/>
<!-- 异常通知 -->
<aop:after-throwing pointcut-ref="pointcut" method="throwingAdvice" throwing="e"/>
</aop:aspect>
</aop:config>
<!-- 配置使Spring采用CGLIB代理 -->
<aop:aspectj-autoproxy proxy-target-class="true"/>
<!-- 启用对事务注解的支持 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
- spring-servlet.xml配置:spring-servlet这个名字是因为上面web.xml中标签配的值为spring(spring),再加上“-servlet”后缀而形成的spring-servlet.xml文件名,如果改为springMVC,对应的文件名则为springMVC-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/context <a href="http://www.springframework.org/schema/context/spring-context-3.0.xsd">http://www.springframework.org/schema/context/spring-context-3.0.xsd</a>">
<!-- 启用spring mvc 注解 -->
<context:annotation-config />
<!-- 设置使用注解的类所在的jar包 -->
<context:component-scan base-package="controller"></context:component-scan>
<!-- 完成请求和注解POJO的映射 -->
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" />
<!-- 对转向页面的路径解析。prefix:前缀, suffix:后缀 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" p:prefix="/jsp/" p:suffix=".jsp" />
</beans>
1.16 @Bean、@Component
- 相同点:
- 都是注册bean到Spring容器中
- 区别:
- @Component注解表明一个类将会作为组件类,并告知Spring要为这个类创建bean
- @Bean注解告诉Spring这个方法将会返回一个对象,这个对象要注册为Spring应用上下文中的bean,通常方法中包含了最终产生bean实例的逻辑
- @Component(@Controller、@Service、@Repository)通常是通过类路径扫描来自动侦测以及自动装配到Spring容器中
- @Bean注解通常是我们在标有该注解的方法中定义产生这个bean的逻辑
- @Component 作用于类,@Bean作用于方法
- @Component不能包含@Bean
- 总结:
- @Component和@Bean都是用来注册Bean并装配到Spring容器中,但是Bean比Component的自定义性更强。可以实现一些Component实现不了的自定义加载类
1.17 Spring的启动过程
Spring的启动过程其实就是IOC容器的启动过程
- 执行web.xml中的 ContextLoaderListener监听器(ContextLoaderListener是一个实现了ServletContextListener的监听器,ServletContextListener的父类是ContextLoader)
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/application_context.xml</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
- 执行ContextLoaderListener中的contextInitialized方法
public void contextInitialized(ServletContextEvent event) {
initWebApplicationContext(event.getServletContext());
}
- contextInitialized方法中调用了父类的initWebApplicationContext方法
- initWebApplicationContext中执行了三个任务
- 创建WebApplicationContext容器
- 加载context-param中配置的Spring配置文件中的bean
- 将WebApplicationContext放入ServletContext中(Java web的全局变量)
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
第4步中,代码在最后
(1)createWebApplicationContext(servletContext)方法即是完成创建WebApplicationContext工作,也就是说这个方法创建上下文对象支持用户自定义上下文对象,但必须继承ConfigurableWebApplicationContext,而Spring MVC默认使用ConfigurableWebApplicationContext作为ApplicationContext(它仅仅是一个接口)的实现
(2)configureAndRefreshWebApplicationContext就是用来加载spring配置文件中的Bean实例的。这个方法于封装ApplicationContext数据并且初始化所有相关Bean对象。它会从web.xml中读取名为 contextConfigLocation的配置,这就是spring xml数据源设置,然后放到ApplicationContext中
(3)最后调用传说中的refresh方法执行所有Java对象的创建
(4)最后完成ApplicationContext创建之后就是将其放入ServletContext中
- web容器停止之后会调用ContextLoaderListener中的contextDestoryed方法销毁context容器
if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
throw new IllegalStateException(
"Cannot initialize context because there is already a root application context present - " +
"check whether you have multiple ContextLoader* definitions in your web.xml!");
}
Log logger = LogFactory.getLog(ContextLoader.class);
servletContext.log("Initializing Spring root WebApplicationContext");
if (logger.isInfoEnabled()) {
logger.info("Root WebApplicationContext: initialization started");
}
long startTime = System.currentTimeMillis();
try {
// Store context in local instance variable, to guarantee that
// it is available on ServletContext shutdown.
if (this.context == null) {
this.context = createWebApplicationContext(servletContext);
}
if (this.context instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
if (!cwac.isActive()) {
// The context has not yet been refreshed -> provide services such as
// setting the parent context, setting the application context id, etc
if (cwac.getParent() == null) {
// The context instance was injected without an explicit parent ->
// determine parent for root web application context, if any.
ApplicationContext parent = loadParentContext(servletContext);
cwac.setParent(parent);
}
configureAndRefreshWebApplicationContext(cwac, servletContext);
}
}
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
ClassLoader ccl = Thread.currentThread().getContextClassLoader();
if (ccl == ContextLoader.class.getClassLoader()) {
currentContext = this.context;
}
else if (ccl != null) {
currentContextPerThread.put(ccl, this.context);
}
if (logger.isDebugEnabled()) {
logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" +
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");
}
if (logger.isInfoEnabled()) {
long elapsedTime = System.currentTimeMillis() - startTime;
logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");
}
return this.context;
}
catch (RuntimeException ex) {
logger.error("Context initialization failed", ex);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
throw ex;
}
catch (Error err) {
logger.error("Context initialization failed", err);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);
throw err;
}
1.18 Spring中的监听器
1.18.1 Spring中的监听器有哪些
1.19.2 Spring中的监听器的作用
- 监听器的作用就是监听一些事件的发生从而进行一些操作,比如监听ServletContext、HttpSession创建、销毁,从而执行一些初始化加载配置文件的操作
- 当web容器启动后,Spring的监听器会启动监听,监听是否创建ServletContext对象,如果发生了创建ServletContext对象这个事件(当web容器启动后一定会生成一个ServletContext对象,所以监听事件一定会发生),ContextLoaderListener类会实例化并且执行初始化方法,将spring的配置文件中配置的bean注册到Spring容器中
- ContextLoaderListener的作用就是启动Web容器后,自动装配ApplicationContext的配置信息。因为它实现了ServletContextListener这个接口,在web.xml配置这个监听器,启动容器时,就会默认执行它实现的方法
1.19 IOC的初始化
1.20 GET请求和POST请求的区别
- GET请求的数据会暴露在地址栏中,而POST请求则不会
- GET请求,请求的数据会附加在URL之后,以?分割URL和传输数据,多个参数用&连接
- POST请求会把请求的数据放置在HTTP请求包的包体中
- 传输数据的大小
- 在HTTP规范中,没有对URL的长度和传输的数据大小进行限制。但是在实际开发过程中,对于GET,特定的浏览器和服务器对URL的长度有限制。因此,在使用GET请求时,传输数据会受到URL长度的限制
- 对于POST,由于不是URL传值,理论上是不会受限制的,但是实际上各个服务器会规定对POST提交数据大小进行限制,Apache、IIS都有各自的配置
- POST的安全性比GET的高。这里的安全是指真正的安全,而不同于上面GET提到的安全方法中的安全,上面提到的安全仅仅是不修改服务器的数据
- HTTP中的GET,POST,SOAP协议都是在HTTP上运行的
2、SpringBoot
2.1 Spring Boot、Spring MVC和Spring区别
2.1.1 三者的区别
- Spring 框架就像一个家族,有众多衍生产品例如 boot、security、jpa等等。但他们的基础都是Spring 的 ioc和 aop,ioc 提供了依赖注入的容器 aop ,解决了面向横切面的编程,然后在此两者的基础上实现了其他延伸产品的高级功能
- Spring MVC是基于 Servlet 的一个 MVC 框架 主要解决 WEB 开发的问题
- 因为 Spring 的配置非常复杂,各种XML、 JavaConfig、hin处理起来比较繁琐。于是为了简化开发者的使用,从而创造性地推出了Spring boot,约定优于配置,简化了spring的配置流程
- Spring Boot实现了auto-configuration自动配置(另外三大神器actuator监控,cli命令行接口,starter依赖)
总结:Spring 最初利用“工厂模式”(DI)和“代理模式”(AOP)解耦应用组件。大家觉得挺好用,于是按照这种模式搞了一个 MVC框架(一些用Spring 解耦的组件),用开发 web 应用( SpringMVC )。然后有发现每次开发都写很多样板代码,为了简化工作流程,于是开发出了一些“懒人整合包”(starter),这套就是 Spring Boot
2.1.2 SpringBoot的特点
- 快速开发的Spring应用框架
- 内嵌tomcat和jetty容器,不需要单独安装容器,jar包直接发布一个web应用
- 简化maven配置,parent这种方式,一站式引入需要的各种依赖
- 基于注解的零配置思想
- 和各种流行框架,spring web mvc,mybatis,spring cloud无缝整合
- 自带应用监控
- 方便对外输出各种形式的服务,如 REST API、WebSocket、Web、Streaming、Tasks
2.1.3 Spring Boot和微服务
- Spring Boot不是微服务技术,只是一个用于加速开发spring应用的基础框架,简化工作,开发单块应用很适合
- 如果要直接基于spring boot做微服务,相当于需要自己开发很多微服务的基础设施,比如基于zookeeper来实现服务注册和发现
- Spring Boot才是微服务技术
2.2 Spring Boot的启动过程
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
this.configureHeadlessProperty();
SpringApplicationRunListeners listeners = this.getRunListeners(args); // 初始化监听器
listeners.starting();
Collection exceptionReporters;
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
this.configureIgnoreBeanInfo(environment);
Banner printedBanner = this.printBanner(environment);
context = this.createApplicationContext(); // 创建上下文实例
exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
this.refreshContext(context);
this.afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
listeners.started(context);
this.callRunners(context, applicationArguments);
} catch (Throwable var10) {
this.handleRunFailure(context, var10, exceptionReporters, listeners);
throw new IllegalStateException(var10);
}
try {
listeners.running(context);
return context;
} catch (Throwable var9) {
this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
throw new IllegalStateException(var9);
}
}
2.3 SpringBoot的运行机制
springboot默认扫描启动类所在的包下的主类与子类的所有组件,但并没有包括依赖包的中的类,那么依赖包中的bean是如何被发现和加载的?
- SpringBootApplication是一个复合注解包含下面三个注解
- @SpringBootConfiguration:表示一个类为配置类(它只是对Configuration注解的一个封装而已)
- @ComponentScan:Spring中用来将指定包下面需要装配的组件注册到容器里面(无非就是自动扫描并加载符合条件的Bean到容器中,这个注解会默认扫描声明类所在的包开始扫描)
- @EnableAutoConfiguration
@SpringBootApplication
public class StmApplication {
public static void main(String[] args) {
SpringApplication.run(StmApplication.class, args);
}
}
- 进入@EnableAutoConfiguration中之后,发现有两个注解
- @AutoConfigurationPackage:将主配置所在的包作为自动配置的包进行管理
- @Import:导入一个类到IOC容器中(根据META-INF下的spring.factories的配置进行导入)
总结:SpringBoot就是根据配置文件自动装配所属依赖的类,再用动态代理的方式注入到Spring容器中
2.4 Spring Boot自动配置原理是什么
在Spring程序main方法中,添加@SpringBootApplication或者@EnableAutoConfiguration会自动去maven中读取每个starter中的spring.factories文件,该文件里配置了所有需要被创建的Spring容器中的bean
2.5 Spring Boot拦截器和过滤器
- Filter是依赖于Servlet容器,属于Servlet规范的一部分,而拦截器则是独立存在的,可以在任何情况下使用
- Filter的执行通常由Servlet容器回调完成,而拦截器通常使用动态代理的方式来执行
- Filter的生命周期由Servlet容器管理,而拦截器则可以通过IoC容器来管理,因此可以通过注入等方式来获取其他Bean的实例,因此使用会更方便
2.6 Spring Boot处理一个http请求的全过程
- 前端发起请求 -> Controller -> service -> Dao
2.7 Spring Boot的核心注解是哪个?
- SpringBootApplication
2.8 什么是Spring Boot Starter?作用在什么地方?
2.8.1 什么是Spring Boot Starter?
- Spring Boot Starter的目的也是简化配置,而Spring Boot Starter解决的是依赖管理配置复杂的问题,有了它,当我需要构建一个Web应用程序时,不必再遍历所有的依赖包,一个一个地添加到项目的依赖管理中,而是只需要一个配置spring-boot-starter-web, 同理,如果想引入持久化功能,可以配置spring-boot-starter-data-jpa
2.8.2 作用在什么地方?
2.9 为什么需要spring-boot-maven-plugin
- spring-boot-maven-plugin 插件以Maven的方式为Springboot应用提供支持,能够将Springboot应用打包为可执行的jar或war文件,进行相应部署后即可启动Springboot应用
2.10 RequestMapping和GetMapping的不同之处在哪里?
- @GetMapping是一个组合注解,是@RequestMapping(method=RequestMethod.GET)的缩写。该注解将HTTP Get 映射到 特定的处理方法上
- 同理PostMapping也是一个组合注解,是@RequestMapping(method=RequestMethod.POST)的缩写
- @RequestMapping如果没有指定请求方式,将接收Get、Post、Head、Options等所有的请求方式
2.11 SpringBoot 打成jar和普通的jar有什么区别?
2.11.1 jar包和war包的区别
- war是一个web模块,其中需要包括WEB-INF,是可以直接运行的WEB模块;jar一般只是包括一些class文件,在声明了Main_class之后是可以用java命令运行的
- war包是做好一个web应用后,通常是网站,打成包部署到容器中;jar包通常是开发时要引用通用类,打成包便于存放管理
- war是Sun提出的一种Web应用程序格式,也是许多文件的一个压缩包。这个包中的文件按一定目录结构来组织;classes目录下则包含编译好的Servlet类和Jsp或Servlet所依赖的其它类(如JavaBean)可以打包成jar放到WEB-INF下的lib目录下
2.12 开启Spring Boot特性有哪几种方式?什么是Spring Data?
2.13 Spring Boot中如何解决跨域问题?
2.14 Spring Boot的核心注解是哪些?他主由哪几个注解组成的?
2.15 SpringBoot的核心配置文件有哪几个?他们的区别是什么?
2.16 SpringBoot有哪几种读取配置的方式?
2.17 Spring Boot 支持哪些日志框架?推荐和默认的日志框架是哪个?
2.18 保护SpringBoot应用有哪些方法?
2.19 三大神器actuator监控,cli命令行接口,starter依赖
2.20 什么是 CSRF 攻击?
2.21 spring boot常用的配置文件?
- properties
- yml
3、SpringCloud
3.1 SpringCloud中的组件
3.2 负载均衡自带的策略
4、Mybatis
4.1 #{ } 与 ${ } 的区别?
- ${ } 是properties文件中的变量占位符,他可以用于标签属性值和sql内部,属于 静态文本替换,比如 ${driver}会被静态替换为 com.mysql.jdbc.Driver
- #{ } 是sql的参数占位符,Mybatis会将sql中的 #{ }替换为?号,在sql执行前会使用PreparedStatement的参数设置方法,按序给sql的?号占位符设置参数值
- 比如:#{item.name}的取值方式为使用反射从参数对象中获取item对象的name属性值,相当于param.getItem().getName()
4.2 xml映射文件中,除了常见的select、insert、update、delete标签外,还有哪些标签
- resultMap、parameterMap、sql、include、selectKey
- 动态sql的9个标签:trim、where、set、foreach、if、choose、when、otherwise、bind
- 注意:
- 通过include标签引入sql片段
- selectKey为不支持自增的主键生成策略标签
4.3 通常一个xml映射文件,都会写一个dao接口与之对应,请问这个dao接口工作原理是什么?dao接口里的方法,参数不同时,方法能重载吗?
- dao接口就是mapper接口,接口的全限名,就是映射文件中的namespace的值,接口的方法名,就是映射文件中 MappedStatement的id值,接口方法内的参数,就是传递给sql的参数。Mapper接口是没有实现类的,当调用接口方法时,接口全限名+方法名拼接字符串作为key值,可唯一定位一个MappedStatement
- Dao接口里的方法是不能重载的,因为是全限名+方法名的保存和寻找策略
- Dao接口的工作原理是JDK动态代理,Mybatis运行时会使用JDK动态代理为Dao接口生成proxy对象,代理对象proxy会拦截接口方法,转而执行MappedStatement所代表的sql,然后将sql执行的结果返回
4.4 Mybatis是如何进行分页的?分页插件的原理是什么?
- Mybatis使用RowBounds对象进行分页,它是针对ResultSet结果集执行的内存分页,而非物理的分页,可以在sql内直接写带有物理分页的参数来完成物理分页的功能,也可以使用分页插件来完成物理分页
- 分页插件的基本原理是使用Mybatis提供的插件接口,实现自定义插件,在插件的拦截方法内拦截待执行的sql,然后重写sql根据dialect方言,添加对应的物理分页语句和物理分页参数
4.5 简述Mybatis的插件运行原理?
- Mybatis仅可以编写针对ParameterHandler、ResultSetHandler、StatementHandler、Executor这四种接口的插件
- Mybatis使用JDK的动态代理,为需要拦截的接口生成代理对象以实现接口方法拦截功能,每当执行这4种接口对象的的方法时,就会进入拦截方法,具体就是InvocationHandler的Invoke()方法,当然只会拦截那些你指定需要拦截的方法
4.6 Mybatis动态sql是做什么的?都有哪些动态sql?简述一下动态sql的执行原理
4.6.1 Mybatis动态sql是做什么的?
- Mybatis动态sql可以让我们在xml映射文件中,以标签的形式编写动态sql,完成逻辑判断和动态拼接sql的功能,Mybatis提供了9种动态sql标签trim、where、set、foreach、if、choose、when、otherwise、bind
4.6.2 动态sql的执行原理
- 使用OGNL从sql参数对象中计算表达式的值,根据表达式的值动态拼接sql,以此来完成动态sql的功能
4.7 Mybatis 是如何将 sql 执行结果封装为目标对象并返回的?都有哪些映射形式?
- 有了列名和属性名的映射关系后,Mybatis通过反射创建对象,同时使用反射给对象的属性逐一赋值并返回,那些找不到映射关系的属性,是没有办法赋值的
4.8 Mybatis是否支持延迟加载?如果支持,它的实现原理是什么?
4.8.1 Mybatis是否支持延迟加载
- Mybatis仅支持association关联对象和collection关联集合对象的延迟加载,association指的就是一对一,collection指的就是一对多查询
- 在Mybatis配置文件中,可以配置是否启用延迟加载:
lazyLoadingEnabled=true|false
4.8.2 延迟加载的实现原理是什么?
- MyBatis 延迟加载是通过动态代理实现的,当调用配直为延迟加载的属性方法时, 动态代理的操作会被触发,这些额外的操作就是通过 MyBatis 的 SqlSession口去执行嵌套 SQL 的 。
- 由于在和某些框架集成时, SqlSession 的生命周期交给了框架来管理,因此当对象超出SqlSession 生命周期调用时,会由于链接关闭等问题而抛出异常。 在和 Spring 集成时,要确保只能在 Service 层调用延迟加载的属性 。 当结果从 Service 层返回至 Controller 层时, 如果获取延迟加载的属性值,会因为 SqlSessio口已经关闭而抛出异常
4.8.3 什么是延迟加载?延迟加载的目的?
- 延迟加载又叫懒加载,也叫按需加载,也就是说先加载主信息,需要的时候,再去加载从信息。代码中有查询语句,当执行到查询语句时,并不是马上去DB中查询,而是根据设置的延迟策略将查询向后推迟
- 配置之后在对关联对象进行查询时使用延迟加载
- 目的
- 减轻DB服务器的压力,因为我们延迟加载只有在用到需要的数据才会执行查询操作
4.9 Mybatis的XML映射文件中,不同的XML映射文件,id是否可以重复?
- 不同的 Xml 映射文件,如果配置了 namespace,那么 id 可以重复;如果没有配置 namespace,那么 id 不能重复;毕竟 namespace 不是必须的,只是最佳实践而已
- 原因就是 namespace+id 是作为 Map<String, MappedStatement>的 key 使用的,如果没有 namespace,就剩下 id,那么,id 重复会导致数据互相覆盖。有了 namespace,自然 id 就可以重复,namespace 不同,namespace+id 自然也就不同
4.10 Mybatis都有哪些Executor执行器?他们之间的区别是什么?
Mybatis有三种基本的Executor执行器,SimpleExecutor、ReuseExecutor、BatchExecutor
- SimpleExecutor:每执行一次 update 或 select,就开启一个 Statement 对象,用完立刻关闭 Statement 对象
- ReuseExecutor:执行 update 或 select,以 sql 作为 key 查找 Statement 对象,存在就使用,不存在就创建,用完后,不关闭 Statement 对象,而是放置于 Map<String, Statement>内,供下一次使用。简言之,就是重复使用 Statement 对象
- BatchExecutor:执行 update(没有 select,JDBC 批处理不支持 select),将所有 sql 都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个 Statement 对象,每个 Statement 对象都是 addBatch()完毕后,等待逐一执行 executeBatch()批处理。与 JDBC 批处理相同
4.11 为什么说Mybatis是半自动ORM映射工具?它与全自动的区别在哪里?
- Hibernate 属于全自动 ORM 映射工具,使用 Hibernate 查询关联对象或者关联集合对象时,可以根据对象关系模型直接获取,所以它是全自动的。而 Mybatis 在查询关联对象或关联集合对象时,需要手动编写 sql 来完成,所以,称之为半自动 ORM 映射工具
4.12 Mybatis整体架构
(1)Mybatis最上面是接口层,接口层就是开发人员在Mapper或者是Dao接口中的接口定义,是查询、新增、更新还是删除操作
(2)中间层是数据处理层,主要是配置Mapper -> XML层级之间的参数配置,SQL解析,SQL执行,结果映射的过程
(3)上述两种流程都是由基础支持层来提供功能支持,基础支持层包括连接管理、事务管理、配置加载、缓存处理等
4.12.1 接口层
InputStream is = Resources.getResourceAsStream("myBatis-config.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(is);
sqlSession = factory.openSession();
- SqlSessionFactory,SqlSession是 MyBatis 接口的核心类,尤其是 SqlSession,这个接口是 MyBatis 中最重要的接口,这个接口能够让你执行命令、获取映射、管理事务
4.12.2 数据处理层
- 配置解析
- 在Mybatis初始化过程中,会加载mybatis-config.xml配置文件、映射配置文件以及Mapper接口中的注解信息,解析后的配置信息会形成相应的对象并保存到 Configration 对象中。
- 之后,根据该对象创建SqlSessionFactory对象。待Mybatis初始化完成之后,可以通过SqlSessionFactory创建SqlSession对象开始数据库操作
- SQL解析与scripting模块
- Mybatis实现的动态sql语法,几乎可以编写出所有满足需要的SQL
- Mybatis中scripting模块会根据用户传入的参数,解析映射文件中定义的动态SQL节点,形成数据库能执行的SQL语句
- SQL执行
- SQL语句的执行涉及多个组件,包括Mybatis的四大核心,他们是Executor、StatementHandler、ResultHandler、ParameterHandler
4.12.3 基础支持层
- 反射模块
- mybatis中的反射模块,对Java反射进行了良好的封装,提供了建议的API,方便上层调用,并且对反射操作进行了一系列优化
- 比如:缓存了 类的元数据(MetaCLass) 和 对象的元数据(MetaObject),提高了反射操作的性能
- 类型转换模块
- Mybatis 的别名机制,能够简化配置文件,该机制是类型转换模块的主要功能之一。
- 类型转换模块的另一个功能是实现 JDBC 类型与 Java 类型的转换。在 SQL 语句绑定参数时,会将数据由 Java 类型转换成 JDBC 类型;在映射结果集时,会将数据由 JDBC 类型转换成 Java 类型
- 日志模块
- 在 Java 中,有很多优秀的日志框架,如 Log4j、Log4j2、slf4j 等
- Mybatis 除了提供了详细的日志输出信息,还能够集成多种日志框架,其日志模块的主要功能就是集成第三方日志框架
- 资源加载模块
- 该模块主要封装了类加载器,确定了类加载器的使用顺序,并提供了加载类文件和其它资源文件的功能
- 解析器模块
- 该模块有两个主要功能:一个是封装了 XPath,为 Mybatis 初始化时解析 mybatis-config.xml配置文件以及映射配置文件提供支持;另一个为处理动态 SQL 语句中的占位符提供支持
- 数据源模块
- Mybatis 自身提供了相应的数据源实现,也提供了与第三方数据源集成的接口。数据源是开发中的常用组件之一,很多开源的数据源都提供了丰富的功能,如连接池、检测连接状态等,选择性能优秀的数据源组件,对于提供 ORM 框架以及整个应用的性能都是非常重要的
- 事务管理模块
- 一般地,Mybatis 与 Spring 框架集成,由 Spring 框架管理事务。但 Mybatis 自身对数据库事务进行了抽象,提供了相应的事务接口和简单实现
- 缓存模块
- Mybatis 中有一级缓存和二级缓存,这两级缓存都依赖于缓存模块中的实现。但是需要注意,这两级缓存与 Mybatis 以及整个应用是运行在同一个 JVM 中的,共享同一块内存,如果这两级缓存中的数据量较大,则可能影响系统中其它功能,所以需要缓存大量数据时,优先考虑使用 Redis、Memcache 等缓存产品
- Binding模块
- 在调用 SqlSession 相应方法执行数据库操作时,需要制定映射文件中定义的 SQL 节点,如果 SQL 中出现了拼写错误,那就只能在运行时才能发现。为了能尽早发现这种错误,Mybatis 通过 Binding 模块将用户自定义的 Mapper 接口与映射文件关联起来,系统可以通过调用自定义 Mapper 接口中的方法执行相应的 SQL 语句完成数据库操作,从而避免上述问题。注意,在开发中,我们只是创建了 Mapper 接口,而并没有编写实现类,这是因为 Mybatis 自动为 Mapper 接口创建了动态代理对象
4.13 Mybatis的核心组件
SQL语句的执行涉及多个组件,包括Mybatis的四大核心,他们是Executor、StatementHandler、ResultHandler、ParameterHandler
4.13.1 SqlSessionFactory
- SqlSessionFactory是Mybatis框架中的一个接口,它主要负责的是:
- Mybatis框架初始化操作
- 为开发人员提供SqlSession对象
4.13.2 SqlSessionFactory的执行流程
- SqlSessionFactory的创建
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
- 首先创建了一个SqlSessionFactoryBuilder工厂,这是一个建造者模式的设计思想,由builder建造者来创建SqlSessionFactory工厂
- 然后调用SqlSessionFactoryBuilder中的build方法传递一个inputStream输入流,inputStream输入流中就是你传过来的配置文件 mybatis-config.xml,SqlSessionFactoryBuilder根据传入的InputStream输入流和environment、properties属性创建一个XMLConfigBuilder对象。SqlSessionFactoryBuilder对象调用XMLConfigBuilder的parse()方法
- XMLConfigBuilder 会解析/configuration标签,configuration 是 MyBatis 中最重要的一个标签。
4.13.3 Configuration的配置
- properties:配置数据库的信息
- setting:可以在此标签哪设置缓存、懒加载、自动驼峰命名
4.13.4 SqlSession
- 在Mybatis初始化流程结束,也就是SqlSessionFactoryBuilder -> SqlSessionFactory的获取流程以后,我们就可以通过SqlSessionFactory对象得到SqlSession然后执行SQL语句
- SqlSession对象是Mybatis中最重要的一个对象,这个接口能让你执行命令、获取映射、管理事务。SqlSession中定义了一系列模版方法,让你能够进行简单的CRUD的操作,也可以通过getMapper获取Mapper层,执行自定义的SQL语句
- 因为SQLSession在执行SQL语句之前是需要先开启一个会话,会涉及到事务操作,所以还会有commit、rollback、close等方法
4.13.5 Executor
- 每一个SqlSession都会拥有一个Executor对象,这个对象负责增删改查的具体操作,我们可以简单将它理解为JDBC中Statement的封装版,也可以理解为SQL的执行引擎(要干活总得有一个发起人吧,可以把Executor理解为发起人的角色)
- Executor执行器,它有两个实现类,分别是BaseExecutor和CachingExecutor
- BaseExecutor是一个抽象类,这种通过抽象的实现接口方式是 适配器设计模式之接口适配 的体现,是Executor的默认实现,实现了大部分Executor接口定义的功能,降低了接口实现的难度。其子类有三个,分别是SimpleExecutor、ReuseExecutor和BathExecutor:
- SimpleExecutor:简单执行器,是Mybatis中默认使用的执行器,每执行一次update或select,就开启一个Statement对象,用完就直接关闭Statement对象(可以是Statement或者是PrepareStatement对象)
- ReuseExecutor:可重用执行器,这里的可重用指的是重复使用Statement,它会在内部使用一个 Map 把创建的 Statement 都缓存起来,每次执行 SQL 命令的时候,都会去判断是否存在基于该 SQL 的 Statement 对象,如果存在 Statement 对象并且对应的 connection 还没有关闭的情况下就继续使用之前的 Statement 对象,并将其缓存起来。因为每一个 SqlSession 都有一个新的 Executor 对象,所以我们缓存在 ReuseExecutor 上的 Statement 作用域是同一个 SqlSession
- BatchExecutor:批处理执行器,用于将多个 SQL 一次性输出到数据库
- CachingExecutor:缓存执行器,先从缓存中查询结果,如果存在就返回之前的结果;如果不存在,再委托给 Executor delegate 去数据库中取,delegate 可以是上面任何一个执行器
4.13.6 Executor的执行过程
- 当有一个查询请求访问的时候,首先会经过Executor的实现类 CachingExecutor ,先从缓存中查询 SQL 是否是第一次执行,如果是第一次执行的话,那么就直接执行 SQL 语句,并创建缓存,如果第二次访问相同的 SQL 语句的话,那么就会直接从缓存中提取
- 如果没有的话,就再重新创建 Executor 执行器执行 SQL 语句, 创建我们上面提到的三种执行器
- 到这里,执行器所做的工作就完事了,Executor 会把后续的工作交给 StatementHandler 继续执行
4.13.7 StatementHandler 的创建
- StatementHandler 是四大组件中最重要的一个对象,负责操作 Statement 对象与数据库进行交互,在工作时还会使用 ParameterHandler 和 ResultSetHandler对参数进行映射,对结果进行实体类的绑定
- StatementHandler 的继承结构和 Executor 比较相似,主要有三个实现类:
- SimpleStatementHandler: 管理 Statement 对象并向数据库中推送不需要预编译的 SQL 语句
- PreparedStatementHandler: 管理 Statement 对象并向数据中推送需要预编译的 SQL 语句
- CallableStatementHandler:管理 Statement 对象并调用数据库中的存储过程
- StatementHandler 的创建
- MyBatis 会根据 SQL 语句的类型进行对应 StatementHandler 的创建
4.14 MyBatis是什么?和Hibernate区别?
- MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Ordinary Java Object,普通的 Java对象)映射成数据库中的记录
- Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,它将POJO与数据库表建立映射关系,是一个全自动的orm框架,hibernate可以自动生成SQL语句,自动执行,使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库。 Hibernate可以应用在任何使用JDBC的场合,既可以在Java的客户端程序使用,也可以在Servlet/JSP的Web应用中使用,最具革命意义的是,Hibernate可以在应用EJB的JavaEE架构中取代CMP,完成数据持久化的重任
4.15 Mybatis执行器
- simple:默认的执行器,预编译->参数设置->执行
- Batch:批处理,将相同的Sql进行一次预编译,然后设置参数,最后一起执行
- REUSE:可重用的
5、Dubbo
5.1 什么是Dubbo?
5.1.1 什么是Dubbo
- 是一款高性能、轻量级的开源Java RPC框架,它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现