1、spring是什么
Spring:是一个轻量级的IOC和AOP的java开发框架,为了简化企业级开发而生。核心就是控制反转和面向切面编程。
IOC:控制反转(Inverse of Control),以前项目都是在哪儿用到对象 在哪儿new,把生成对象的权利反转给spring,让spring把对象管理起来,在哪用在哪注入。
AOP :面向切面编程。可以对业务逻辑和非业务逻辑进行隔离,将程序中的一些非业务代码进行提取,然后让代理对象去调用公共的方法,就可以做到在不需要修改原来代码的情况下,为程序添加额外的功能,从而使得各部分之间的耦合度降低,提高程序的可重用性。好处就是:减少重复,专注业务;
底层实现:使用的是动态代理模式。是通过一个代理对象来实现对非业务代码进行调用的。告诉代理对象,调用哪个方法时,让代理对象去帮助我们调用哪个方法。
AOP思想不是spring框架特有的,只是spring框架引入使用了这一思想。
非业务代码使用案例:
- 事务处理:开启事务,关闭事务,出现异常后回滚事务
- 验证权限:在执行方法前,判断是否具有权限
- 打印日志:在执行前进行日志处
- 统一异常处理
2、spring事务管理
jdbc自动事务提交,mybatis事务默认不自动提交,需要我们在程序中手动提交 sqlsession.commit();spring框架把提交事务的功能帮助我们管理起来了,封装好了。
保障一个事务中的多条sql,要么都执行,要么都不执行,只要有发生异常就回退到事务开始未进行操作的状态,(例如转账)@Transactional
@Transactional标签的用法:
- 一般把事务管理的注解标签在service的方法中来进行控制,需要把这多个sql,放在同一个事务管理中进行
- 添加在service层中类上,也可以添加在方法上
事务管理,在以下情况会失效:(重点)
- 修饰非public的方法,底层权限只针对public修饰的方法
- 数据库引擎不支持事务。常用的两个引擎:innodb(支持事务功能),myisam(不支持事务)
- 方法中的异常被catch捕获处理了
- 出现编译期异常,事务不生效。默认情况下,事务只对运行期异常进行生效@Transactional(rollbackFor = RuntimeException.class)。我们可以把其修改为**@Transactional(rollbackFor = Exception.class),这样就可以处理任意的异常**
- @Transactional事务传播行为设置错误
- 同一个类中,使用非代理对象调用一个有事务的方法。在一个非事务方法中使用this(原始的对象==自己new出来的对象)
3、事务传播行为?
即然是传播,那么至少有两个方法才可以发生传播。
事务传播行为:A事务方法调用B事务方法时,B事务是一个独立的事务呢(独立的,表示B出现了问题,不影响A)?还是B事务合并到A事务中呢(B影响A,A影响B)?这就是由B的事务传播行为决定的。
是Spring框架独有的事务增强特性,不属于事务实际提供者mysql。
Spring 定义了七种传播行为:
事务传播行为类型 | 说明 |
---|---|
PROPAGATION_REQUIRED | 如果A有事务, B加入到A事务中;如果A没有事务,B就新建一个事务,与A没有关系。(常见) |
PROPAGATION_REQUIRES_NEW | 无论A是否有事务,B都会创建新的事务(独立的) |
PROPAGATION_SUPPORTS | 支持A事务;如果A没有事务,B就以非事务方式执行 |
PROPAGATION_NEVER | B以非事务方式执行;如果A存在事务,则抛出异常。 |
PROPAGATION_MANDATORY | 使用A事务,如果A没有事务,就抛出异常。 |
PROPAGATION_NOT_SUPPORTED | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起 |
PROPAGATION_NESTED | 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务, 则执行与 PROPAGATION_REQUIRED 类似的操作。 |
注意:事务传播行为不能是同一个类中的方法相互调用,必须是一个类中的方法调用另一个类中的方法
4、SpringWeb运行流程
- 用户发起请求,所有的请求都可以到达DispatcherServlet(servlet),DispatcherServlet收到请求调用 HandlerMapping
- HandlerMapping映射处理器,用来解析请求中的地址,有没有对应的处理器类,将生成处理器对象返回给 DispatcherServlet。如果本次有对应的拦截器,会执行相应的拦截器。
- HandlerAdapter适配器,封装请求中的参数**。**
- Handler到达自己创建的处理器,接收参数、处理、响应。
5、spring中用到的设计模式
单例模式、工厂模式、原型模式、代理模式。自己补充其他的几种
6、BeanFactory和ApplicationContext
BeanFactory和ApplicationContext都是spring中定义的上层的接口,ClassPathXmlApplicationContext类是spring框架的底层的实现。
BeanFactory接口是spring中最顶层的接口,定义bean管理的基本方法;ApplicationContext接口继承了BeanFactory,在此基础上进行功能的扩展。
区别:
- BeanFactory是面向spring框架内部的,不支持aop、springweb等功能。ApplicationContext是面向应用程序的,功能强大。
- 实现了BeanFactory的类,都是在使用时才进行加载创建。实现ApplicationContext的类,都是在程序启动就加载创建对象。
7、Spring中常用容器
平常说的Ioc容器、spring容器都是比较宏观的,对spring管理对象的称呼。Spring中常用容器,指的是具体实现IOC功能的类。
现在使用的是实现了ApplicationContext接口的具体实现类,下面有两个具体的实现类:
- ClassPathXmlApplicationContext ,从类路径ClassPath下读取XML配置文件,并完成ApplicationContext的实例化工作
- FileSystemXmlApplicationContext ,从指定的文件系统路径下读取XML配置文件,并完成ApplicationContext的实例化工作
WebApplicationContext是ApplicationContext的子接口,主要是用于Web应用。
AnnotationConfigApplicationContext中是不需要配置 XML 配置文件。
8、BeanFactory与FactoryBean有什么区别
BeanFactory是整个Spring IOC容器的核心,是定义如何管理bean。
FactoryBean也是一个接口,用来为具体创建对象的工厂进行功能定义。
9、SpringBean的生命周期
把交给spring容器管理的对象称为bean对象,与自己的new的对象有所不同。
spring管理的bean生命周期问题:spring管理的bean,从诞生到销毁的整个过程。可以分为5个阶段:
- 对象实例化(创建一个原始的普通的对象,类似于new对象)
- 为属性赋值
- 初始化(指的是spring框架对对象进行初始化)。初始化是spring对bean进行管理的最重要的一个环节,在此时可以根据类的配置,进行各种功能的装配。例如类中的方法是否要添加事务,统一异常处理.....还可以自定义初始化的方法,以及销毁对象时调用的方法。
- 将bean对象放入到容器中,去使用它。
- 销毁Destruction。
10、Spring中的bean是线程安全的吗?
回顾一下servlet,经常还有问到servlet是线程安全的吗?
java
class XXXServlet{
int count = 0; //本意是每次请求中都可以使用一个自己的count
//但是在serlvet中不可以做到,因为serlvet对象是单实例
doGet(){
}
}
Spring的bean作用域(scope)类型:
- singleton:单例,默认作用域。
- prototype:原型,每次创建一个新对象。
如果spring中的bean(xxxController..)是原型bean,那么每次请求到来时都会创建一个新的对象,不会存在数据共享,就是线程安全的。
如果spring中的bean是单例的,所有线程都共享一个对象,就会存在线程安全问题。但也不是绝对的,分情况。bean又分为
- 有状态,可以存储数据,线程不安全
- 无状态,不会存储数据,是线程安全的
java
public class UserService {
int num = 0;
User user;//可以用来为每次请求存储数据 那么就是有状态的, 就会出现线程安全问题
UserDao userDao;//只是用来负责调用对象中的方法,不存储数据 也是无状态的 是线程安全的
}
如何解决有状态bean的线程安全问题?
- 把单例bean改为原型bean,每次请求都会创建一个新的对象。
- 使用ThreadLocal为变量在每次请求线程中进行复制。
11、Bean循环依赖
没有使用spring依赖注入,是不会有问题的。只是简单的创建A和B的对象,A类中b属性和B类中a属性都为null。
java
class A{
B b;
}
class B{
A a;
}
一旦使用了spring框架的自动注入,就会出现循环依赖问题。A创建时-->需要B----B去创建--->需要 A,从而产生了循环。
java
class A{
@Autowired //自动注入注解, 当创建A类对象时,就需要为b属性注入值.
B b;
}
class B{
@Autowired //自动注入注解, 当创建B类对象时,就需要为a属性注入值.
A a;
}
spring内部有三级缓存:三级缓存其实就是3种map,分别用来存储不同的对象。
3个map分别用来存储:
- 一级缓存 singletonObjects:用来存储初始化完成的对象,完整的bean。
- 二级缓存 earlySingletonObjects:用来存储实例化完成的bean(半成品)。提前暴露出来,提供给循环依赖的对方使用。
- 三级缓存 singletonFactories:用来创建对象的工厂,因为bean还没有初始化完成。
将处于不同阶段的bean对象进行分别的存储,以达到先将一个对象初始化完成,再初始化另一个对象。
Spring解决循环依赖问题:首先采用实例化,属性注入分离的策略。
12、Servlet的过滤器与Spring拦截器区别
过滤器是javaweb(servlet)中的规范,它是在请求进入到servlet之前进行拦截,可以拦截所有进入到web后端的请求。
拦截器是spring框架中封装的,只能拦截进入到controller中的请求。
13、spring常用注解
1.声明bean的注解
@Component :泛指各种组件。@Controller、@Service、@Repository都可以称为@Component
@RestController,组合的注解,里面包含两个注解:
- @Controller
- @ResponseBody
2.Bean的生命周期属性
@Scope设置类型包括:设置 Spring 容器如何新建 Bean 实例
- singleton:单例,一个Spring容器中只有一个bean实例,默认模式
- protetype:每次调用新建一个bean
- request:web项目中,给每个http request新建一个bean
- session:web项目中,给每个http session新建一个bean
- globalSession:给每一个global http session新建一个Bean实例
3.注入 bean 的注解
@Autowired:由Spring提供
@Qualifier:当有多个同一类型的 Bean 时,可以用@Qualifier("name")来指定。与@Autowired 配合使用。
@Resource:由 java 提供
4.切面(AOP)相关注解
Spring支持AspectJ的注解式切面编程
@Aspect:声明一个切面
@After:在方法执行之后执行(方法上)
@Before:在方法执行之前执行(方法上)
@Around:在方法执行之前与之后执行(方法上)
@PointCut:声明切点
@EnableAspectJAutoProxy:开启Spring对AspectJ代理的支持
5.SpringMVC常用注解
@RestController:该注解为一个组合注解,相 当于@Controller和@ResponseBody的组合,注解在类上,意味着,该Controller的所有方法都默认加上了@ResponseBody。
@RequestMapping:用于映射 web 请求,包括访问路径和参数
@ResponseBody:支持将返回值放到 response 内,而不是一个页面,通常用户返回json数据
@RequestBody:允许request的参数在request体中,而不是在直接连接的地址后面。
@Transactional:注解放在类级别时,表示所有该类的公共方法都配置相同的事务属性信息。
6.SpringBoot注解
@SpringBootApplication里面包含三个注解标签:
- @SpringBootConfiguration
- @EnableAutoConfiguration
- @ComponentScan:自动扫描启动类所在包下的所有类
@Confiquration用于配置类上面
@Bean在配置类的方法上,表示此方法会返回一个bean,交给spring框架,替代xml中的方式。
@RestControllerAdvice,@ExceptionHandler 用于统一异常处理,捕获指定的异常.
14、SpringBoot自动装配原理
sprinaboot是对spring框架的搭建进行简化,去掉xml配置文件,内置tomcat(容器)。
起步依赖(可以将相关的jar包自动依赖进来)
自动配置(自动装配):在springboot项目中,只需要添加一个组件的依赖(jar),springboot就会自动将这一组件进行装配,可以让我们在程序中直接使用。(例如redis,只需要添加依赖,可以注入使用Redistemplate)。
springboot是基于约定大于配置的思想。
自动装配实现
注解、以及底层类对pom.xml进程读取,读取完后找到组件对应配置类。
1.在启动类上有一个@SprinqBootApplication注解,它是一个复合注解标签,里面包含三个注解标签:
- @SpringBootConfiguration
- @EnableAutoConfiguration
- @ComponentScan:自动扫描启动类所在包下的所有类
2.@EnableAutoConfiquration注解标签中包含@Import({AutoConfigurationImportSelector.class)注解,其中包含一个类AutoConfigurationImportSelector,此类是根据pom.xml文件中配置来进行组件的加载(例如配置redis、mail等依赖)。
3.根据pom.xml文件中的依赖的配置,去jar包的spring.factories文件中,找到组件对应配置类,对配置类进行加载。
4.在程序中可以直接注入使用。
总结:首先依赖启动类的注解标签,注解标签底层有具体的实现类,对pom.xml文件进行读取,找到对应组件的配置类,再在applcation.yml文件读取组件需要的配置信息,最终完成装载。