思维导图
Spring、SpringBoot
备注: 下图是我之前总结的,与文章不同
SpringCloud
常见知识点
一、Spring
1.Spring基础
1.1 Spring框架?
Spring Framework,开源的轻量级 Java 开发框架,旨在提高开发效率以及系统的可维护性。
- 支持IoC、AOP,方便的访问数据库,有效的集成第三方组件(电子邮件,任务,调度,缓存等)、友好支持RESTful Java应用程序开发及单元测试。
1.2 Spring模块?
模块图:
模块依赖图:
1.2.1 Core Container
核心、基础模块,主要提供IoC依赖注入功能支持。
- spring-core :Spring 框架基本的核心工具类。
- spring-beans :提供对 bean 的创建、配置和管理等功能的支持。
- spring-context :提供对国际化、事件传播、资源加载等功能的支持。
- spring-expression :提供对表达式语言的支持,只依赖于 core 模块,不依赖于其他模块,可以单独使用。
1.2.2 AOP
- spring-aspects :该模块为与 AspectJ 的集成提供支持。
- spring-aop :提供了面向切面的编程实现。
- spring-instrument :提供了为 JVM 添加代理(agent)的功能。
它为 Tomcat 提供了一个织入代理,能够为 Tomcat 传递类文 件,就像这些文件是被类加载器加载的一样。
1.2.3 Data Access/Integration
- spring-jdbc:提供了对数据库访问的抽象 JDBC。
- spring-tx :提供对事务的支持。
- spring-orm : 提供对 Hibernate、JPA 、iBatis 等 ORM 框架的支持。
- spring-oxm :提供一个抽象层支撑 OXM(Object-to-XML-Mapping)
例如:JAXB、Castor、XMLBeans、JiBX 和 XStream 等。 - spring-jms : 消息服务。提供对 spring-messaging 模块的继承。
1.2.4 Spring Web
- spring-web :对 Web 功能的实现提供一些最基础的支持。
- spring-webmvc : 提供对 Spring MVC 的实现。
- spring-websocket : 提供了对 WebSocket 的支持,WebSocket 可以让客户端和服务端进行双向通信。
- spring-webflux :提供对 WebFlux 的支持。
与 Spring MVC 不同,它不需要 Servlet API,是完全异步。
1.2.5 Messaging
为 Spring 框架集成一些基础的报文传送应用。
1.2.6 Spring Test
- Spring 团队提倡测试驱动开发(TDD)。
有了控制反转 (IoC)的帮助,单元测试和集成测试变得更简单。 - Spring 的测试模块对 JUnit(单元测试框架)、TestNG(类似 JUnit)、Mockito(主要用来 Mock 对象)、PowerMock(解决 Mockito 的问题比如无法模拟 final, static, private 方法)等等常用的测试框架支持的都比较好。
1.3 Spring、SpringMVC、SpringBoot关系?
- Spring 包含了多个功能模块,其中最重要的是 Spring-Core模块, Spring 中的其他模块包含 Spring MVC的功能实现基本都需要依赖于该模块。
- Spring MVC 是 Spring 中的一个很重要的模块,主要赋予 Spring 快速构建 MVC 架构的 Web 程序的能力。
MVC 是模型(Model)、视图(View)、控制器(Controller)的简写,其核心思想是通过将业务逻辑、数据、显示分离来组织代码。
- 使用 Spring 进行开发各种配置过于麻烦,需要用 XML 或 Java 进行显式配置。Spring Boot可以减少配置文件,开箱即用。
- 如果你需要构建 MVC 架构的 Web 程序,你还是需要使用 Spring MVC 作为 MVC 框架,只是说 SpringBoot 帮你简化了 Spring MVC 的很多配置。
2.Spring IoC
2.1 Spring IoC是什么 ?
IoC(Inversion of Control:控制反转) 是一种设计思想,而不是一个具体的技术实现。
2.1.1 控制反转
- 控制 :指的是对象创建(实例化、管理)的权力
- 反转 :控制权交给外部环境(Spring 框架、IoC 容器)
2.2 Spring Bean是什么 ?
那些被 IoC 容器所管理的对象。
- 通过配置元数据来告诉 IoC 容器帮助我们管理哪些对象
<bean id="..." class="...">
<constructor-arg value="..."/>
</bean>
2.3 将一个类声明为 Bean 的注解有哪些?
- @Component :通用的注解,可标注任意类为 Spring 组件。如果一个 Bean 不知道属于哪个层,可以使用@Component 注解标注。
- @Repository : 对应持久层即 Dao 层,主要用于数据库相关操作。
- @Service : 对应服务层,主要涉及一些复杂的逻辑,需要用到 Dao 层。
- @Controller : 对应 Spring MVC 控制层,主要用户接受用户请求并调用 Service 层返回数据给前端页面。
2.4 @Component 和 @Bean 的区别是什么?
- @Component 注解作用于类,而@Bean注解作用于方法。
- @Component通常是通过类路径扫描来自动侦测以及自动装配到 Spring 容器中(我们可以使用 @ComponentScan 注解定义要扫描的路径从中找出标识了需要装配的类自动装配到 Spring 的 bean 容器中)。
@Bean 注解通常是我们在标有该注解的方法中定义产生这个 bean,@Bean告诉了 Spring 这是某个类的实例,当我需要用它的时候还给我。 - @Bean 注解比 @Component 注解的自定义性更强,而且很多地方我们只能通过 @Bean 注解来注册 bean。
比如当我们引用第三方库中的类需要装配到 Spring容器时,则只能通过 @Bean来实现。
@Bean注解使用示例:
@Configuration
public class AppConfig {
@Bean
public TransferService transferService() {
return new TransferServiceImpl();
}
}
上面的代码相当于下面的 xml 配置:
<beans>
<bean id="transferService" class="com.acme.TransferServiceImpl"/>
</beans>
下面这个例子是无法通过 @Component 实现的:
@Bean
public OneService getService(status) {
case (status) {
when 1:
return new serviceImpl1();
when 2:
return new serviceImpl2();
when 3:
return new serviceImpl3();
}
}
2.5 注入 Bean 的注解有哪些?
Spring 内置的 @Autowired 以及 JDK 内置的 @Resource 和 @Inject 都可以用于注入 Bean。
2.6 @Autowired 和 @Resource 的区别是什么?
2.6.1 @Autowired
- Autowired 属于 Spring 内置的注解,默认的注入方式为byType(根据类型进行匹配),也就是说会优先根据接口类型去匹配并注入 Bean (接口的实现类)。
- 当一个接口存在多个实现类的话,byType就不知道选择哪一个。注入方式就会变为byName(根据名称进行匹配),这个名称通常就是类名(首字母小写)。
- 举个例:
SmsService 接口有两个实现类: SmsServiceImpl1和 SmsServiceImpl2,且它们都已经被 Spring 容器所管理。
// 报错,byName 和 byType 都无法匹配到 bean
@Autowired
private SmsService smsService;
// 正确注入 SmsServiceImpl1 对象对应的 bean
@Autowired
private SmsService smsServiceImpl1;
// 正确注入 SmsServiceImpl1 对象对应的 bean
// smsServiceImpl1 就是我们上面所说的名称
@Autowired
@Qualifier(value = "smsServiceImpl1")
private SmsService smsService;
备注: 建议通过 @Qualifier 注解来显式指定名称而不是依赖变量的名称。
2.6.2 @Resource
- @Resource属于 JDK 提供的注解,默认注入方式为 byName。如果无法通过名称匹配到对应的 Bean 的话,注入方式会变为byType。@Resource 有两个比较重要且日常开发常用的属性:name(名称)、type(类型)。
public @interface Resource {
String name() default "";
Class<?> type() default Object.class;
}
- 如果仅指定 name 属性则注入方式为byName,如果仅指定type属性则注入方式为byType,如果同时指定name 和type属性(不建议这么做)则注入方式为byType+byName。
// 报错,byName 和 byType 都无法匹配到 bean
@Resource
private SmsService smsService;
// 正确注入 SmsServiceImpl1 对象对应的 bean
@Resource
private SmsService smsServiceImpl1;
// 正确注入 SmsServiceImpl1 对象对应的 bean(比较推荐这种方式)
@Resource(name = "smsServiceImpl1")
private SmsService smsService;
// 正确注入 type为SmsServiceImpl1 对象对应的 bean
@Resource(type= "smsServiceImpl1.class")
private SmsService smsService;
2.7 Bean 的作用域有哪些?
2.7.1 分类
- singleton : IoC 容器中只有唯一的 bean 实例。
Spring 中的 bean 默认都是单例的,是对单例设计模式的应用。 - prototype : 每次获取都会创建一个新的 bean 实例。
连续 getBean() 两次,得到的是不同的 Bean 实例。 - request (仅 Web 应用–Spring ApplicationContext可用)
每一次 HTTP 请求都会产生一个新的 bean(请求 bean),该 bean 仅在当前 HTTP request 内有效。 - session (仅 Web 应用–Spring ApplicationContext可用) :
每一次来自新 session 的 HTTP 请求都会产生一个新的 bean(会话 bean)该 bean 仅在当前 HTTP session 内有效。 - application/global-session (仅 Web 应用–portlet context可用)
每个 Web 应用在启动时创建一个 Bean(应用 Bean),该 bean 仅在当前应用启动时间内有效。 - websocket (仅 Web 应用可用):每一次 WebSocket 会话产生一个新的 bean。
2.7.2 配置
- xml 方式:
<bean id="..." class="..." scope="singleton"></bean>
- 注解方式:
@Bean
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Person personPrototype() {
return new Person();
}
2.8 单例 Bean 的线程安全吗?
- 大部分 Bean 实际都是无状态(没有实例变量)的(比如 Dao、Service),这种情况下, Bean 是线程安全的。
- 但单例 Bean 还是存在线程问题,因为当多个线程操作同一个对象的时候是存在资源竞争的。
2种解决办法如下:
- 在 Bean 中避免定义可变的成员变量。
- 在类中定义一个 ThreadLocal 成员变量,将需要的可变成员变量保存在 ThreadLocal 中(推荐的一种方式)。
2.9 Bean 的生命周期?
- Bean 容器找到配置文件中 Spring Bean 的定义。
- Bean 容器利用 Java Reflection API 创建一个 Bean 的实例。
- 如果涉及到一些属性值 利用 set()方法设置一些属性值。
- 实现 Aware接口并调用相应方法。
4.1 如果 Bean 实现了 BeanNameAware 接口,调用 setBeanName()方法,传入 Bean 的名字。
4.2 如果 Bean 实现了 BeanClassLoaderAware 接口,调用 setBeanClassLoader()方法,传入 ClassLoader对象的实例。
4.3 如果 Bean 实现了 BeanFactoryAware 接口,调用 setBeanFactory()方法,传入 BeanFactory对象的实例。
4.4 与上面的类似,如果实现了其他 *.Aware接口,就调用相应的方法。 - 如果有和加载这个 Bean 的 Spring 容器相关的 BeanPostProcessor 对象,执行postProcessBeforeInitialization() 方法。
- 如果 Bean 实现了InitializingBean接口,执行afterPropertiesSet()方法。
- 如果 Bean 在配置文件中的定义包含 init-method 属性,执行指定的方法。
- 如果有和加载这个 Bean 的 Spring 容器相关的 BeanPostProcessor 对象,执行postProcessAfterInitialization() 方法。
- 销毁Bean 。
9.1 当要销毁 Bean 的时候,如果 Bean 实现了 DisposableBean 接口,执行 destroy() 方法。
9.2 当要销毁 Bean 的时候,如果 Bean 在配置文件中的定义包含 destroy-method 属性,执行指定的方法。
3.Spring AoP
3.1 Spring AoP是什么?
3.1.1 AOP(Aspect-Oriented Programming:面向切面编程)
能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。
3.1.2 实现方式
- Spring AOP 就是基于动态代理的,如果要代理的对象,实现了某个接口,那么 Spring AOP 会使用 JDK Proxy,去创建代理对象
- 对于没有实现接口的对象,就无法使用 JDK Proxy 去进行代理了,这时候 Spring AOP 会使用 Cglib 生成一个被代理对象的子类来作为代理
- 也可以使用 AspectJ !Spring AOP 已经集成了 AspectJ ,AspectJ 应该算的上是 Java 生态系统中最完整的 AOP 框架了。
3.1.3 AOP专业术语
![在这里插入图片描述]()
3.2 Spring AoP和AspectJ AOP 有什么区别?
- Spring AOP 属于运行时增强,而 AspectJ 是编译时增强。
Spring AOP 基于代理(Proxying),而 AspectJ 基于字节码操作(Bytecode Manipulation)。 - AspectJ 相比于 Spring AOP 功能更加强大,但是 Spring AOP 相对来说更简单。
- 当切面太多的话,最好选择 AspectJ ,它比 Spring AOP 快很多。
3.3 AspectJ 定义的通知类型有哪些?
- Before(前置通知):目标对象的方法调用之前触发
- After (后置通知):目标对象的方法调用之后触发
- AfterReturning(返回通知):目标对象的方法调用完成,在返回结果值之后触发
- AfterThrowing(异常通知) :目标对象的方法运行中抛出 / 触发异常后触发。
AfterReturning 和 AfterThrowing 两者互斥。
如果方法调用成功无异常,则会有返回值;
如果方法抛出了异常,则不会有返回值。 - Around (环绕通知):编程式控制目标对象的方法调用。
环绕通知是所有通知类型中可操作范围最大的一种,因为它可以直接拿到目标对象,以及要执行的方法,所以环绕通知可以任意的在目标对象的方法调用前后搞事,甚至不调用目标对象的方法。
3.4 多个切面的执行顺序如何控制?
3.4.1 通常使用@Order 注解直接定义切面顺序
// 值越小优先级越高
@Order(3)
@Component
@Aspect
public class LoggingAspect implements Ordered {
3.4.2 实现Ordered 接口重写 getOrder 方法。
@Component
@Aspect
public class LoggingAspect implements Ordered {
// ....
@Override
public int getOrder() {
// 返回值越小优先级越高
return 1;
}
}