Spring 基础
什么是 Spring 框架?
开源的 轻量级的 Java 开发框架,旨在提高开发效率和系统可维护性。
一般我们说 Spring 都是指 Spring FrameWork,它是很多模块的集合,使用这些模块可以很方便地帮助我们进行 Java 开发。例如 Spring 中集合了 IoC、AOP,我们通过 Spring 可以很方便地访问数据库、方便集成第三方组件、对单元测试支持比较好。
Spring 最核心的思想就是不用重新造轮子,开箱即用,提高效率。
Spring 的核心功能是 IoC 和 AOP。
Spring 包含的模块有哪些?
各个模块之间的依赖:
Core Container
Spring 框架的核心模块,也可以说是基本模块,主要提供 IoC 依赖注入功能的支持。Spring 其他所有的模块基本都要依赖该模块,也就是上图依赖关系。
spring-core:Spring 框架基本的核心工具类
spring-context:提供对国际化、事件传播(?)、资源加载等功能支持
spring-beans:提供对 bean 的操作
spring-expression:提供对表达式语言(Spring Expression Language)SpEL 的支持,只依赖于 core 模块,可以单独使用
AOP
Data Access / Integration
Spring Web
Messaging
Spring Test
Spring,Spring MVC,Spring Boot 之间是什么关系?
Spring 包含了多个功能框架,最重要的是 Spring-Core(IoC)模块,Spring 中的其他模块基本都需要依赖 Spring-Core 模块。
Spring MVC 是 Spring 中的一个重要模块,它赋予 Spring 快速构建 MVC 架构的 Web 程序的能力。MVC 中 M 是 model(模型)、V 是 view(视图)、C 是 controller(控制器)。其核心思想是通过将业务逻辑、数据、显示分离来组织代码。
Spring Boot 的产生是由于使用 Spring 进行开发各种配置过于麻烦,Spring Boot 可以简化 Spring 的开发。(简化框架)如果你需要构建 MVC 架构的 Web 程序,你还是需要使用 Spring MVC 作为 MVC 框架,只是说 Spring Boot 帮你简化了 Spring MVC 的各种配置,真正做到开箱即用。
Spring IoC
谈谈你对 Spring IoC 的理解
IoC,全称控制反转(Inverse of Control),它是一种设计思想,而不是一个具体的技术实现。IoC 的思想就是将原来在程序中控制的各类对象,交给 Spring 框架进行管理。不过 IoC 并非 Spring 特有,在其他语言也有应用。
为什么叫控制反转?
控制:对象创建的权利
反转:将控制权交给外部环境(Spring、IoC 容器)
我们将对象之间的依赖关系交给 IoC 容器来管理,并且由 IoC 容器来完成对象的注入。这样可以很大程度上简化开发,把应用从复杂的依赖关系中解放出来。当我们需要创建一个对象的时候,只需要配置好文件/注解即可,不需要考虑对象是如何被创建的。
在 Spring 中,IoC 容器是 Spring 用来实现 IoC 的载体,IoC 容器实际上就是个 Map,Map 中存放的是各种对象。
什么是 Spring Bean?
Bean 就是 IoC 代为管理的对象。
我们需要告诉 IoC 容器帮助我们管理那些对象,这个对象是通过配置元数据来定义的。
将一个类声明为 Bean 的注解有哪些?
@Component:通用注解。
@Repository:对应持久层即 DAO 层,用于数据库相关操作。
@Service:对应服务层,主要涉及复杂的逻辑,需要用到 DAO 层。
@Controller:对应 Spring MVC 控制层,主要是接受用户请求并调用 Service 层返回数据给前端页面。
@Component 和 @Bean 有什么区别?
@Component 注解作用与类;@Bean 注解作用于方法。
@Component 通常通过类路径扫描来自动侦测以及自动装配到 Spring 容器中;@Bean 通常是我们在标有该注解的方法中定义产生这个 bean,@Bean 告诉 Spring 这是某个类的实例,当我需要它的时候还给我。
@Bean 自定义性更强,我们引用第三方库中的类需要装配到 Spring 容器时,只能通过 @Bean 实现。
注入 Bean 的注解有哪些?
Spring 内置的 @Autowired 以及 JDK 内置的 @Resource 和 @Inject 都可以用于注入 Bean。
@Autowired 和 @Resource 的区别是什么?
@Autowired 属于 Spring 提供的注解。它的默认注入方法是 byType,也就是通过接口的类型进行匹配注入 Bean。如果一个接口存在多个实现类,不能通过 byType 方式进行注入,此时就会转变为 byName 方式进行注入(类名)。
我们建议通过@Qualifier 注解来指定名称而不是依赖变量的名称。
@Resource 是 JDK 提供的注解,默认注入方式为 byName。如果无法通过名称进行匹配的话,注入方式会变成 byTpye。
@Resource 有两个比较重要且日常开发常用的属性:name、type。如果仅指定 name 则注入方式为 byName,如果仅指定 type 则注入方式为 byType,如果同时制定(不建议),则注入方式为 byType + byName。
Bean 的作用域有哪些?
1、singleton:单例
2、prototype:每次获取得到一个新的 bean 实例
3、request:每次 HTTP 请求都会产生一个新的 bean
4、session:每次来自新 session 的 HTTP 请求都会产生一个新的 bean
5、application/global-session:每个 Web 应用在启动的时候创建一个 bean
6、websocket:每一次 WebSocket 会话产生一个新的 bean
如何配置 bean 的作用域呢?
注解方式:
单例 Bean 的线程安全问题了解吗?
单例 Bean 存在线程安全问题,主要是因为 多线程操作同一个对象存在的资源竞争。
常见的解决办法:
1、Bean 中避免定义可变的成员变量
2、在类中定义 ThreadLocal 的成员变量,将需要的可变成员保存在 ThreadLocal 中。
大部分 Bean 实际都是无状态(无实例变量)的,此时单例 Bean 是线程安全的。
Bean 的生命周期
这里讲解的是 ApplicaitonContext 中 Bean 的生命周期。而实际上 BeanFactory 也是差不多的,只不过处理器需要手动注册。
Spring AOP
谈谈自己对 AOP 的了解
AOP,全称面向切面编程(Aspect-Oriented Programming),可以将与业务逻辑无关,但是却为业务模块所共同调用的逻辑或责任(日志、事务处理、权限控制)等封装起来,以减少代码的重复性,降低模块之间的耦合程度,有利于未来的可拓展性和可维护性。
Spring AOP 是基于动态代理的,如果需要代理的对象实现了某个接口,那么Spring AOP 会使用 JDK Proxy 去创建代理对象;而对于没有实现接口的对象,Spring AOP 会使用 Cglib 生成一个被代理对象的子类来作为代理,如下所示:
我们也可以使用 AspectJ,现在 Spring AOP 中已经集成了 AspectJ,它是 Java 生态中最完整的 AOP 框架。
Spring AOP 和 AspectJ AOP 有什么区别?
Spring AOP 属于运行时增强,AspectJ 属于编译时增强。Spring AOP 基于代理(proxying),而AspectJ 基于字节码操作。
如果我们的切面比较少,那么两者的性能差异并不大,但是当切面很多的时候,最好选择 AspectJ,它比 Spring AOP 快很多。
AspectJ 定义的通知类型有哪些?
1、Before
2、After
3、AfterReturning:目标对象的方法调用完成,在返回结果前触发
4、AfterThrowing
5、Around:环绕通知,它是所有通知类型中可操作范围最大的一种,因为它可以直接拿到目标对象以及要执行的方法,它可以任意在目标对象前后进行操作。
多个切面的执行顺序如何控制?
1、使用 @Order 注解直接定义切面顺序(值越小优先级越高)
2、实现 Ordered 接口重写 getOrder 方法
Spring MVC
说说自己对 Spring MVC 的了解?
MVC 是 Model、View、Controller 的简写,它的思想是通过将业务逻辑、数据、显示进行分离来组织代码。
Spring MVC 框架是当前最优秀的 MVC 框架。它可以帮助我们进行更简洁的 Web 开发,并且它天生与 Spring 框架集成。Spring MVC 下我们一般把后端项目分为 Service 层、Dao 层、Entity 层、Controller 层。
Spring MVC 的核心组件有哪些?
DispatcherServlet:核心的中央处理器
HandlerMapping:处理器映射器
HandlerAdapter:处理器适配器
Handler:请求处理器
ViewResolver:视图解析器
流程:
1、客户端发送请求,DispatcherServlet 拦截请求
2、DispatcherServlet 根据请求信息调用 HandlerMapping。HandlerMapping 根据 url 去匹配查找能够处理该请求的 Handler(也就是我们平时说的 Controller 控制器),并会将请求涉及到的拦截器和 Handler 一起封装。
3、DispatcherServlet 调用 HandlerAdapter 适配执行 Handler
4、Handler 完成对用户的请求处理后,返回一个 ModelAndView 对象给 DispatcherServlet,ModelAndView 顾名思义包含了数据模型和视图信息。
5、ViewResolver 根据逻辑 View 查找实际的 View
6、DispatcherServlet 把返回的 Model 传给 View(视图渲染)
7、把 View 的结果返回给客户端。
统一异常处理怎么做?
推荐使用注解的方式进行统一异常处理,具体使用 @ControllerAdvice + @ExceptionHandler 这两个注解。
@ControllerAdvice
@ResponseBody
public class GlobalExceptionHandler {
@ExceptionHandler(BaseException.class)
public ResponseEntity<?> handleAppException(BaseException ex,
HttpServletRequest request) {
//......
}
@ExceptionHandler(value = ResourceNotFoundException.class)
public ResponseEntity<ErrorReponse>
handleResourceNotFoundException(ResourceNotFoundException ex, HttpServletRequest
request) {
//......
}
}
这种异常处理方式下,会给所有或者指定的 Controller 织入异常处理的逻辑(AOP),当 Controller 中的方法抛出异常的时候,由被 @ExceptionHandler 注解修饰的方法进行处理。
Spring 框架中用到了哪些设计模式?
Spring 事务
Spring 管理事务的方式有几种?
1、编程式事务:通过 TransactionTemplate 或者 TransactionManager 手动管理事务,但在实际中很少进行使用,因为它对代码的侵入性过强,但对我们理解 Spring 事务管理的原理是有帮助的。
2、声明式事务:在 XML 配置文件中配置或者直接基于 @Transcational 注解(推荐):实际山是通过 AOP 进行实现的。
使用 TransactionTemplate:
@Autowired
private TransactionTemplate transactionTemplate;
public void testTransaction() {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
try {
// .... 业务代码
} catch (Exception e){
//回滚
transactionStatus.setRollbackOnly();
}
}
});
}
使用注解:
@Transactional(propagation = Propagation.REQUIRED)
public void aMethod {
//do something
B b = new B();
C c = new C();
b.bMethod();
c.cMethod();
}
一般在业务中我们都用的是声明式事务
Spring 事务中有哪几种事务传播行为?
在 Spring 的事务管理中,有三个最重要的接口:
1、PlatformTransactionManager:事务管理器,核心接口
2、TransactionDefinition:事务定义信息(传播行为、隔离等级、超时、只读、回滚策略)
3、TransactionStatus:事务运行状态
我们可以把 PlatformTransactionManager 看成是上层的管理者,而 TransactionDefinition 和 TransactionStatus 都是对事物的描述。
事务传播行为是在 TransactionDefinition 中进行定义的,它的存在是为了解决业务层方法相互调用而产生的。
下面举一个例子:
@Service
Class A {
@Autowired
B b;
@Transactional(propagation = Propagation.xxx)
public void aMethod {
//do something
b.bMethod();
}
}
@Service
Class B {
@Transactional(propagation = Propagation.xxx)
public void bMethod {
//do something
}
}
这里方法a会去调用方法b,且方法a、b都被 @Transactional 进行注解,如果方法a或者方法b回滚的时候,会对另外一个方法造成怎么样的影响?这就是事务传播行为需要干的事情。
在 TransactionDefinition 中定义了如下常量:
public interface TransactionDefinition {
int PROPAGATION_REQUIRED = 0;
int PROPAGATION_SUPPORTS = 1;
int PROPAGATION_MANDATORY = 2;
int PROPAGATION_REQUIRES_NEW = 3;
int PROPAGATION_NOT_SUPPORTED = 4;
int PROPAGATION_NEVER = 5;
int PROPAGATION_NESTED = 6;
......
}
1、TransactionDefinition.PROPAGATION_REQUIRED
这是默认的事务传播行为,也是使用的最多的一个事务传播行为。如果当前存在事务,则加入该事务;如果不存在,则新建一个事务。
在此例中相当于,如果我们上面的aMethod()
和bMethod()
使用的都是PROPAGATION_REQUIRED
传播行为的话,两者使用的就是同一个事务,只要其中一个方法回滚,整个事务均回滚。
2、TransactionDefinition.PROPAGATION_REQUIRES_NEW
创建一个新的事务,如果当前存在事务,则把当前的事务挂起。
如果我们上面的bMethod()
使用PROPAGATION_REQUIRES_NEW
事务传播行为修饰,aMethod
还是用PROPAGATION_REQUIRED
修饰的话。如果aMethod()
发生异常回滚,bMethod()
不会跟着回滚,因为 bMethod()
开启了独立的事务。但是,如果 bMethod()
抛出了未被捕获的异常并且这个异常满足事务回滚规则的话,aMethod()
同样也会回滚,因为这个异常被 aMethod()
的事务管理机制检测到了。
3、TransactionDefinition.PROPAGATION_NESTED
如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;
如果方法a回滚,则方法b也会回滚;如果方法b回滚,方法a不会回滚。
4、TransactionDefinition.PROPAGATION_MANDATORY
使用的很少。如果当前存在事务,则加入该事务;如果没事务,则抛出异常。
Spring 事务中的隔离级别有哪几种?
一共五种
public interface TransactionDefinition {
......
int ISOLATION_DEFAULT = -1;
int ISOLATION_READ_UNCOMMITTED = 1;
int ISOLATION_READ_COMMITTED = 2;
int ISOLATION_REPEATABLE_READ = 4;
int ISOLATION_SERIALIZABLE = 8;
......
}
1、default:使用后端数据库默认的隔离级别
2、读未提交
3、读已提交
4、可重复读
5、可串行化
@Transactional(rollbackFor = Exception.class)注解了解吗?
类:类中所有 public 方法
方法:仅限 public 方法
rollbackFor 属性:不加则只有遇到 RuntimeException 才会回滚;加了之后可以让事务在遇到 非运行时异常时也可以回滚
Spring Data JPA
JPA 重要的是实战,对小部分知识点进行总结
如何使用 JPA 在数据库中非持久化一个字段?
不想让某个字段被持久化,也就是不被数据库存储
一般使用后面两种方式是比较多的
JPA 的审计功能是做什么的?有什么用?
审计功能帮助我们记录数据库操作的具体行为:某条记录是谁创建的、什么时候创建的、最后修改人是谁、最后修改时间等等。
@Data
@AllArgsConstructor
@NoArgsConstructor
@MappedSuperclass
@EntityListeners(value = AuditingEntityListener.class)
public abstract class AbstractAuditBase {
@CreatedDate
@Column(updatable = false)
@JsonIgnore
private Instant createdAt;
@LastModifiedDate
@JsonIgnore
private Instant updatedAt;
@CreatedBy
@Column(updatable = false)
@JsonIgnore
private String createdBy;
@LastModifiedBy
@JsonIgnore
private String updatedBy;
}
实体之间的关联关系注解有哪些?
1、@OneToOne
2、@ManyToMany
3、@OneToMany
4、@ManyToOne
Spring Security
Spring Security 重要的也是实战,这里仅对小部分知识点进行总结。
有哪些控制请求访问权限的方法?
hasRole 和 hasAuthority 有区别吗?
单纯从源码上看,它们的区别就在于 hasAuthority 不会加前缀;而 hasRole 会自动加上 ROLE_ 前缀。
为什么要这么做?
这是一个设计上的思想,authority 可以说是一个具体的权限,针对某一个功能(例如针对某一个数据的修改或删除);而对于 role 而言,它是很多个 authority 的集合。在项目中,我们可以将用户和角色关联,角色和权限关联,权限和资源关联。
如何对密码进行加密?
如果需要保存密码这类敏感信息到数据库的话,需要先加密再进行保存。
Spring Security 提供了多种加密算法的实现,开箱即用,非常方便。这些加密算法的父类是 PasswordEncoder,如果我们想要自己实现一个加密算法,也需要继承它。
一共三个必须实现的方法:
public interface PasswordEncoder {
// 加密也就是对原始密码进⾏编码
String encode(CharSequence var1);
// ⽐对原始密码和数据库中保存的密码
boolean matches(CharSequence var1, String var2);
// 判断加密密码是否需要再次进⾏加密,默认返回 false
default boolean upgradeEncoding(String encodedPassword) {
return false;
}
}
官方推荐使用基于 bcrypt 强哈希函数的加密算法实现类。
如何更换系统中使用的加密算法?
推荐做法是通过 DelegatingPasswordEncoder 兼容多种不同的密码加密方案,以适应不同的业务需求。