🚩Spring

IOC 控制反转 || Bean 的生命周期

使用对象的时候,不再通过 new 产生对象,而是将对象交给外部的IoC容器(ApplicationContext,接口)来管理,这个容器是 map 结构,存在三级缓存。

一、实例化 bean 对象

  1. 通过 createBeanFactory 创建出一个 bean 工厂 DefaultListableBeanFactory
  2. 循环创建对象,因为容器中的 bean 默认是单例的,所以优先尝试使用 getBean、doGetBean 获取 bean
  3. 找不到的话,通过 createBean、doCreateBean 以反射的方式创建对象,一般情况采用的是无参构造方法 clazz.getDeclaredConstructor().newInstance() ,可以灵活访问到私有的属性。
Q:BeanFactory 和 FactoryBean 的区别?

它们都是用来创建对象的,区别在于:

  • BeanFactory 由 spring 来管理生命周期,它的实现类包括ApplicationContextXmlBeanFactory
  • FactoryBean 对象创建过程由用户自己控制,更加灵活。
    • getObject():返回由工厂创建的对象实例。
    • getObjectType():返回由工厂创建的对象的类型。
    • isSingleton():指示由工厂创建的对象是否是单例。
五个作用域

其中最基础的有下面两种:

  1. Singleton,这是 Spring 的默认作用域,也就是为每个 IOC 容器创建唯一的一个 Bean 实例。【适合无状态】
  2. Prototype,针对每个 getBean 请求,容器都会单独创建一个 Bean 实例。【适合有状态】

如果是 Web 容器:

  1. Request,为每个 HTTP 请求创建单独的 Bean 实例。
  2. Session,很显然 Bean 实例的作用域是 Session 范围。
  3. GlobalSession,用于 Portlet 容器,因为每个 Portlet 有单独的 Session,GlobalSession 提供一个全局性的 HTTP Session。

二、属性赋值(Population)

依赖注入(Dependency Injection)或属性赋值 populateBean()

  1. setter注入(建议):更加灵活,可以在对象创建以后动态更改依赖关系。set方法上添加@Autowired注解,类定义上添加@Component注解。
  2. 构造方法注入:一旦创建完成,依赖关系就不能再改变
🌟Q1:循环依赖如何解决?三级缓存 or @lazy
  1. 初始化过程中的循环依赖

    1. 一级 singletonObjects:单例池,Map 结构,存放完整的 bean
      • 生成完整对象之后放到一级缓存,删除 二、三级缓存:addSingleton()
    2. 二级 earlySingletonObjects:Map 结构,存放生命周期未走完的 bean
      • 第一次从三级缓存确定是代理对象还是普通对象的时候放入,同时删除三级缓存 getSingleton()
    3. 三级 singletonFactories:是一个函数式接口,保证在整个容器运行过程中,同名的 bean 只有一个
      • createBeanInstance 之后:addSingletonFactory()
  2. 构造方法中的循环依赖:采用 @lazy 进行懒加载

Q2:如果对象要被代理,是否需要优先生成一个普通对象?

需要。在实际调用对象过程中,先判断对象是否需要被代理,如果需要被代理,调用 getEarlyBeanReference() 用代理对象来覆盖普通对象。因此,所有 bean 对象在创建的时候都要优先放到三级缓存中,再根据是否被代理来返回代理对象或普通对象。

三、调用 Aware 相关接口的方法

invokeAwareMethod 进行 BeanName、BeanFactory、BeanClassLoader 的属性设置。

可以拿到容器的其他对象。

四、BeanPostProcessor 前置处理

  • ApplicationContextPostProcessor
  • ApplicationContext
  • Environment
  • ResourceLoader

五、执行 init-method 方法

  • InitializingBean 的 afterPropertiesSet()
  • 自定义的初始化方法

六、BeanPostProcessor 后置处理

AOP 是 IOC 的一个扩展功能,是在 bean 生命周期中的一个扩展点。

Spring 的 AOP 在此处通过 AbstractAutoProxyCreator 实现,注册 Destruction 相关的回调接口

return 一、2.

七、销毁

  • @PreDestroy 注解

  • 实现 DisposableBean 接口

🌟AOP

Aspect Oriented Programming,面向切面编程

作用:无侵入式增强。

流程

  1. 创建代理对象(advice、切面、切点)
  2. 通过 jdk 或 cglib 生成代理对象
  3. 执行方法调用:找到 DynamicAdvicoredInterceptor 类中的 intercept() 方法,开始执行
  4. 根据定义好的通知来生成拦截器链
  5. 利用 CglibMethodInvocation 对象从 -1 的位置开始遍历通知

AOP 基本概念

在 pom.xml 中导入 aspectjweaver ,配置 @EnableAspectJAutoProxy

  • 连接点(JoinPoint):方法【可利用的机会】
  • 切入点(PointCut):是连接点的子集,要追加功能的方法 【解决了切面编程中的 Where 问题,让程序可以知道哪些机会点可以应用某个切面动作】
    • @Pointcut(“execution(void Pointcut() com.dao.xxxDao.update())”) 修饰的方法 pt()
  • 通知(Advice):有共性的功能 【明确了切面编程中的 What,也就是做什么;同时通过指定 Before、After 或者 Around,定义了 When,也就是什么时候做。】
    • @Before(“pt()”)
      • 形参:JoinPoint
    • @After(“pt()”)
    • @Around (“pt()”) public void around(ProceedingJoinPoint pjp) throws Throwable{ pjp.proceed();} (重点,常用)
      • 形参:ProceedingJoinPoint(必须放在参数的第一位)
      • 要注意返回值和原始方法一致。
      • 如果不执行pjp.proceed(),可以实现对原始方法的隔离(权限校验)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
@Component
@Aspect
public class ProjectAdvice{
  @Pointcut("execution(* com.edu.zjut.service.*Service.*(..))")
  private void servicePt(){}
  
  @Around("ProjectAdvice.servicePt()")
  public void runSpeed(ProceedingJoinPoint pjp) throws Throwable{
    Signature signature = pjp.getSignature();
    String className = signature.getDeclaringTypeNmae();
    String methodName = signature.getName();
    long start = System.currentTimeMillis();
    for(int i=0;i<10000;i++){
      pjp.proceed();
    }
    long end = System.currentTimeMillis();
    System.out.println("业务层接口万次执行"+ className + "." + methodName + "------>" + (end-start)+"ms");
  }
}
  • AfterReturning(“pt()”) 只有不抛异常才运行
  • AfterThrowing(“pt()")只有抛异常才运行
  • 切面(Aspect):通知和切入点的联系(多对多)
  • 通知类:定义通知的类
    • @Aspect

AOP 工作流程

AOP的本质是代理模式

  1. Spring容器启动
  2. 读取切面中配置的切入点
  3. 初始化bean,若类中方法匹配到切入点,则创建代理对象
  4. 获取bean执行方法

切入点表达式

标准格式:execution(返回值 包.类.接口(推荐)/实现类.方法()) 通配符:*匹配任意符号,..匹配多个连续的任意符号,如:execution(_ _..Service+.(..))`匹配业务层所有接口 书写技巧:

  1. 所有类命名需规范,否则以下失效
  2. 描述切入点通常描述接口,而不描述实现类
  3. 增删改类使用精准类型加速匹配,查询类使用*通配快速描述
  4. 方法名以动词进行精准匹配
  5. 包名书写尽量不使用 .. 匹配,效率太低,常用 * 做单个包描述匹配
  6. 不使用异常作为匹配规则

静态代理和动态代理

  • 静态代理:在编译时就将接口、实现类、代理类这些都变成了一个个实际的 class 文件。接口一旦增加方法,目标和代理对象都要进行修改。
  • 动态代理:在运行时生成类字节码,并加载到 JVM 中。可以用来包装 RPC 调用、面向切面的编程(AOP)
    • JDK :只能代理实现了接口的类,运用反射的原理。Spring AOP将使用JDK动态代理来生成代理对象。代理对象实现了与目标对象相同的接口,并在方法调用前后织入切面逻辑。JDK动态代理是通过java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口实现的。
    • CGLIB:可以代理未实现任何接口的类,通过修改字节码,生成子类实现,然后在子类中织入切面逻辑。
      • 基于 ASM,可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。

Spring AOP 基于动态代理方式实现;AspectJ 基于静态代理方式实现。

Spring 事务

流程

Spring 事务通过 AOP 实现,生成具体的代理对象。

事务是通过 TransactionInterceptor 来实现的,然后调用 invoke 来实现具体的逻辑。

  1. 解析事务相关的属性,来判断是否开启新事务
  2. 当需要开启的时候,获取数据库连接,关闭自动提交功能,开启事务
  3. 执行 SQL 逻辑操作
  4. 如果执行失败,通过 completeTransactionAfterThrowing 进行事务的回滚,回滚的具体逻辑是通过 doRollBack() 方法获取 jdbc 连接来实现的
  5. 通过 doCommit() 方法获取连接来提交
  6. 当事务执行完毕后清除相关事务信息:cleanupTransactionInfo

分类

  • 编程式:TransactionTemplate 对业务有侵入性,少用。
  • 声明式:@EnableTransactionManagement + @TranSactional
    • 通过 AOP 将事务处理的功能编织到拦截的方法中(方法前开启事务,方法后酌情回滚)

事务失效场景

  1. 异常捕获 => 在 catch 中 throw 异常
  2. 抛出检查异常 => 指定 rollbackfor,因为默认只回滚 unchecked 异常(继承自 RuntimeException 或 Error)
  3. 修饰的方法不是 public
  4. 数据库引擎 MyISAM 不支持事务
  5. propagation 属性设置为 SUPPORTS、NOT_SUPPORTED、NEVER 时

3 类事务传播特性

指的是不同方法的嵌套调用过程中,事务应该延用还是新建,当出现异常的时候是回滚还是提交。

  • 支持当前事务
  • 不支持当前事务
  • 嵌套事务
    • 异常统一放在外层方法处理
    • 内层方法能影响外层,但是外层不会影响内层

🌟7 种事务传播参数

  • REQUIRED(默认):事务管理员有事务就加入,没事务就新建

  • REQUIRED_NEW:无论如何都新建事务

    • Q:如何保证无论转账是否成功都记录日志留痕?

      A:日志对应的事务不要加入转账的事务中,在插入日志的方法上加上 @Transactional(propagation = Propagation.REQUIRES_NEW)

  • SUPPORTS:事务管理员有事务就加入,没有就算了(@Transactional 失效)

  • NOT_SUPPORTS:无乱如何都不支持事务(@Transaction al 失效)

  • MANDATORY:事务管理员有事务就加入,没有就报错

  • NEVER:事务管理员没事务没关系,有事务就报错(@Transactional 失效)

  • NESTED:延用外层方法的事务,设置 savePoint

Spring Cloud 是如何调用的?

  1. RestTemplate方式调用
  2. 注入 RestTemplate 的bean,添加@Bean注解
  3. 调用 .getForObject().postForObject() 方法
  4. Feign方式调用
  5. 添加 pom 依赖
  6. 启动类添加 @EnableFeignClients 注解
  7. 写接口:
1
2
3
4
5
@FeignClient("userservice")
public interface UserClient{
  @GetMapping("/user/{id}")
  User findById(@PathVariable("id") Long id);
}
  1. 使用 FeignClient 中定义的方法取代 RestTemplate(并集成了 ribbon,实现了负载均衡) IRule 接口决定负载均衡策略,并选择某个服务: 1. ZoneAvoidanceRule(默认):对Zone区域内多个服务轮询,zone值可配置 2. RoundRobinRule:轮询 3. WeightedResponseTimeRule:服务响应时间越长,服务器权重就越小,概率就越低 4. RandomRule:随机

SpringMVC 执行流程?

与 DispatcherServlet(前端控制器) 依次交互的有:

  • HandlerMapping (处理器映射器):找到类名#方法名
  • HandlerAdaptor(适配器):处理参数、返回值
  • ViewResolver(视图解析器,用于 JSP):逻辑视图 -> 视图

对于前后端分离项目,在方法上添加 @ResponseBody,通过 HttpMessageConverter 响应 JSON 数据 @RestController = @Controller + @ResponseBody

JWT

JWT 数据结构?

  • head
  • payload
    • 用户信息
    • 加密信息
    • 过期时间
  • signature

JWT 和 token 的区别?

token 需要查数据库,JWT 不需要。

security和jwt怎么融合?

  • 认证:主要 jwt 的 token 实现
  • 鉴权:springSecurity实现,RBAC = Role-Based Access Control

从cookie到jwt解决了什么?

  1. 可以转化为 json 串,可读性强;
  2. RESTful 设计原则,避免在服务端保存会话状态,降低了服务器端的负担;
  3. 签名机制,安全性强。

token 泄露怎么处理?

采用非对称加密:给前端一个公钥,加密数据传到后台,用私钥解密后处理数据。

Springboot的自动装配原理?

@SpringbootApplication

  • @SpringBootConfiguration 声明当前类为配置类
  • @ComponentScan 组件扫描
  • @EnableAutoConfiguration 自动化配置
    • @Import 读取了项目和项目引用的 jar 包,位于 classpath 路径下 META-INF/spring.factories 文件
    • @ConditionalOnClass 有则加载

注入方式

  • 对于引用类型:

    1. 按类型 byType(推荐):@Autowired使用的是暴力反射,不需要 setter方法

    2. 按名称 byName(变量名与配置耦合,不推荐)

      1. @Autowired+ @Qualifier("beanName ")

      2. @Resource("名称")

    3. 按构造方法(不常用)

    4. setter

  • 对于简单属性:使用 @Value(“xxx”)注入,可以来自外部 property,写法:@Value("${name}") 加载外部properties 使用 @PropertySource(“classpath:jdbc.properties”),不可以使用星号*

如何对静态成员注入?

static 对象不能使用 Autowired 注入,只能通过set 方法注入。

1
2
3
4
5
6
7
8
@Component  
public class GlobalValue {  
    public static String DATABASE;  
    @Value("${mongodb.db}")  
    public void setDatabase(String db) {  
        DATABASE = db;  
    } 
} 

拦截器(Interceptor)与过滤器(Filter)的区别?

  • 归属不同:Filter属于Servlet技术,Interceptor属于SpringMVC技术(基于 AOP)
  • 拦截内容不同:Filter对所有访问进行增强,Interceptor仅针对SpringMVC的访问进行增强,可以精细到方法

执行流程

  1. preHandle(return true 才走下一步,false结束)
  2. controller
  3. postHandle
  4. AfterCompletion

拦截器(Interceptor)

是一种动态拦截方法调用的机制,作用:

  1. 在指定的方法调用前后执行预先设定的代码
  2. 组织原始方法的执行
1
2
3
4
// preHandle 方法中可以根据返回值决定是否继续执行方法
// 并且可以通过反射拿到拦截方法,进行更多操作
HandlerMethod hm = (HandlerMethod) handler;
hm.getMethod();

拦截器链

preHandle在前的拦截器,postHandle 和 afterCompletion 在后。

  • 123 都为 true:pre 123 -> controller -> post 321 -> after 321
  • 12 true 3 false:pre12 -> after 21
  • 13 true 2 false: pre1 -> after 1
  • 23 true 1 false:都不执行

全局异常处理

表现层通过AOP处理

1
2
3
4
5
6
7
@RestControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(value = Exception.class)
    public CommonResult handleUnknownException(Exception e) {
        return CommonResult.failed(ResultCode.UNKN0OWN_FAILED,"系统繁忙,请稍后再试!");
    }
}
  • 业务异常:提醒用户规范操作
  • 系统异常:安抚用户,通知运维,记录日志

REST风格(Representational State Transfer)表现形式状态转换

资源名称采用复数,如users、books

  • GET:查询
  • POST:新增
  • PUT:修改
  • DELETE:删除

Q:如何避免Spring加载SpringMVC已加载的Controller?

A:加载Spring控制的bean的时候,排除掉SpringMVC控制的bean。

  1. @ComponentScan(value = "edu.zjut",excludeFilters=@ComponentScan.Filter(type=FilterType.ANNOTATION,classes=Controller.class))按照注解排除
  2. @ComponentScan({"edu.zjut.service","edu.zjut.dao"})
  3. 不区分Spring和SpringMVC的环境,都加载

参数传递

  • @RequestParam("xxx"):请求参数和形参不同时需配置,不写的话默认取相同。

  • @RequestParam 修饰 List 就可以实现集合传参。

  • 参数对象中有对象时采用 address.city=beijing 这种形式。

  • @RequestBody:设置controller方法返回值为响应体。传字符串(json)用

    • HttpMessageConverter
  • 日期参数:接收可以用 Date 接,加上 @DateTimeFormat(pattern = "yyyy-MM-dd")

  • @PathVariable 从路径中取值

SpringMVC

基于 Java 实现 MVC 模型的轻量级 web 框架。

后端服务器:

  • 表现层:SpringMVC(取代 Servlet),封装数据(code、msg、data),用postman测试

  • 业务层:Spring,用JUnit测试

    • @RunWith(SpringJUnit4ClassRunner.class)
    • @ContextConfiguration(classes = SpringConfig.class)
  • 数据层:Mybatis

返回 json 数据,需要加 @ResponseBody注解。

0%