Spring原理笔记;突击面试专用;

什么是IOC?

  • 控制反转:IOC——Inversion of Control,翻转资源获取方向。之前需要我们手动创建对象才能使用,现在把创建对象的权利交给Spring的IOC容器,由它来管理对象的生命。我们只需要从IOC容器中获取对象使用即可。
  • Spring 底层默认通过反射技术调用组件类的无参构造器来创建组件对象,加入IOC容器。
  • 获取bean一般通过bean类型获取,当然也可以通过bean的id获取。

什么是DI?

  • DI:Dependency Injection,翻译过来是依赖注入。IOC容器通调用新组件的setter等方法来给新组件注入 一些资源,比如依赖对象等。

Bean的作用域?

  • 在应用作用域下:通过@Scope注解配置bean的作用域;singleton表示bean是单例,每次获取都是同一个;prototype表示bean是多例,每次获取都会创建一个新的。
  • 此外还有session作用域,在一个会话范围内有效;request作用域,在一个请求范围内有效。

Bean的生命周期?

  • bean对象创建(调用无参构造器)
  • 给bean对象设置属性,依赖注入
  • bean对象初始化之前操作(由bean的后置处理器负责)
  • bean对象初始化,调用指定init方法(需在配置bean时指定初始化方法)
  • bean对象初始化之后操作(由bean的后置处理器负责)
  • bean对象就绪可以使用
  • bean对象销毁(需在配置bean时指定销毁方法)
  • IOC容器关闭

Bean基于注解的自动装配?

  • 在成员变量上直接标记@Autowired注解即可完成自动装配。
  • @Autowired注解也可以标记在构造器和set方法上
  • @Autowired工作流程
    • 先通过byType,再通过byName的方式查找装配,都找不着就抛异常
  • 发现NoSuchBeanDefinitionException, 没有匹配的bean错误。直接看对应了类是否加上了注解。如加上了注解,再看是不是注解扫描是不是没有扫描上。

Spring如何配置包扫描路径?

  • 使用xml配置

    <context:component-scan base-package="com.atguigu">
    </context:component-scan>
    
  • 使用@ComponentScan()注解标识扫描路径

  • SpringBoot项目会自动扫描SpringApplication启动类的同级目录下及其递归目录里的所有组件。

总结Spring的最大特性之一就是:注解+扫描+自动装配+IOC自动管理bean

什么是AOP?

  • 面向切面编程:AOP——Aspect Oriented Programming,在不修改源代码的基础上增强代码功能。常用于抽取重复使用的代码注入原代码,增强原代码功能而不改变原代码。常用于日志等操作。

为什么需要AOP?

  • 简化代码:我们希望只把核心业务功能代码留在方法内部,而将日志等这些重复的代码抽取出来,实现业务代码与日志代码的解耦。通过继承的方式不能解决这个问题。AOP基于代理模式实现。
  • 影响隔离:将日志记录,事务处理,异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离独立出来,我们修改这些行为的代码的时候不影响业务逻辑的代码。
  • 代码增强:把特定的功能封装到切面类中,看哪里有需要,就往上套,被套用了切面逻辑的方法就被切面给增强了。

什么是代理模式?

  • 所谓代理模式就是通过提供一个代理类,让我们在调用目标方法的时候,不再是直接对目标方法进行调用,而是通过代理类间接调用。代理类在调用目标方法之前、之后都可以做很多事情,来增强目标方法的功能。
  • 这样,目标方法就可以专注于自己的核心业务功能代码,而日志这些代码就可以通过代理类附加到目标方法之前、之后。 实现 非目标方法的核心代码 从 目标方法 中剥离出来——即解耦
  • 静态代理:代理哪个对象已经写死了,代理对象使用的代理方法也写死了。需要为每一个被代理的对象都创建一个代理类。
  • 动态代理:代理哪个对象可以动态改变,代理类可以动态生成,不需要开发⼈员⾃⼰实现代理类,可以通过反射生成动态代理类的实例。
    这样,每个委托类都可以动态生成代理类的实例,而不用修改动态代理类的代码。

什么是动态代理?

  • 静态代理:由程序员创建或工具生成代理类的源码,再编译代理类。所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。

  • 动态代理:动态代理类的源码是在程序运行期间由JVM根据反射等机制动态的生成,所以不存在代理类的字节码文件。代理类和委托类的关系是在程序运行时确定。

  • jdk动态代理:要求必须有接口,最终生成的代理类和目标类实现相同的接口

  • cglib动态代理:最终生成的代理类会继承目标类,并且和目标类在相同的包下

AOP的相关概念

  • 横切关注点:插入新代码的点
  • 通知:每一个横切关注点上要做的事情都需要写一个方法来实现,这样的方法就叫通知方法。包含以下通知:
    • 前置通知:在被代理的目标方法执行
    • 返回通知:在被代理的目标方法成功结束后执行(寿终正寝
    • 异常通知:在被代理的目标方法异常结束后执行(死于非命
    • 后置通知:在被代理的目标方法最终结束后执行(盖棺定论
  • 切面:封装通知方法的类。(AOP最重要的地方)
    切面类

AOP相关注解有什么?

  • @Aspect:作用是把当前类标识为一个切面类
  • @Pointcut:切入点定义表达式。比如@Pointcut(“@annotation(com.yuming.blog.annotation.OptLog)”)定义切入点为使用自定义注解@OptLog的地方。而@Pointcut修饰的方法可以当做是切入点表达式的别名,该方法体内为空即可。为了方便重用切入点表达式,因为切入点表达式一般很长
  • @AfterReturning:返回通知,方法正常退出时执行
  • @After: 后置通知,不管是抛出异常或者正常退出都会执行
  • @Before:标识一个前置通知方法,在被代理的目标方法执行
  • @AfterThrowing:异常抛出通知,在被代理的目标方法异常结束后执行
  • @Around:环绕通知,使用try…catch…finally结构围绕整个被代理的目标方法,包括上面四种通知对应的所有位置
  • 执行顺序: 前置通知 --》目标操作 -》返回通知/异常通知 -》后置通知

切入方法可以获取目标方法的哪些信息?

通过切入点获取:JoinPoint joinPoint,可获取的信息有:

  • 连接点的方法名,String methodName = joinPoint.getSignature().getName();
  • 连接点方法的参数列表,String args = Arrays.toString(joinPoint.getArgs());
  • 连接点方法的返回值,returning = “result”,放在通知方法的参数列表,如:public void afterReturningMethod(JoinPoint joinPoint, Object result)
  • 连接点方法的异常,throwing = “ex”,放在通知方法的参数列表。

环绕通知与其他通知有什么不同?

  • 环绕通知需要通过切入点ProceedingJoinPoint手动调用目标方式执行,类似于动态代理。

  • @Around(value = "optLogPointCut()")
    public Object aroundMethod(ProceedingJoinPoint joinPoint){
        String methodName = joinPoint.getSignature().getName();
        String args = Arrays.toString(joinPoint.getArgs());
        Object result = null;
        try {
            System.out.println("环绕通知-->目标对象方法执行之前");
            //目标方法的执行,目标方法的返回值一定要返回给外界调用者
            //手动调用目标方法
            result = joinPoint.proceed(); 
            
            System.out.println("环绕通知-->目标对象方法返回值之后");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            System.out.println("环绕通知-->目标对象方法出现异常时");
        } finally {
            System.out.println("环绕通知-->目标对象方法执行完毕");
        }
        return result;
    }
    

目标方法存在多个切面,执行顺序是怎么样的?

  • 使用@Order注解可以控制切面的优先级:@Order(较小的数):优先级高;@Order注解放在切面类头上。也就是跟@Aspect放一块。
  • 先执行优先级高的切面(外层),再执行优先级低的切面(内层)。

什么是编程式事务、声明式事务?

  • 编程式事务: 事务功能的相关操作比如连接数据库、开启事务、sql操作、提交事务、异常回滚事务、释放数据库连接 都需要我们手动实现,每次都要写一遍,很繁琐。
  • 声明式事务: 把连接数据库、开启事务、提交事务、异常回滚事务、释放数据库连接这部分重复的代码抽取出来,通过AOP实现事务操作。只要使用了@Transactional注解,就只需要把核心sql操作放在方法里即可,不用再考虑手动实现事务操作。

@Transactional使用有什么要注意的?

  • @Transactional标识在方法上,则只会影响该方法;@Transactional标识的类上,则会影响类中所有的方法
  • @Transactional(readOnly = true) 把事务设置成只读,可以提高查询的效率。前提是事务里确实只有查询操作。(修改操作会报错)
  • @Transactional(timeout = 3) 设置事务最长执行时间。超过该时间就回滚。防止长时间占用数据库连接资源。
  • 事务的回滚策略:声明式事务默认只针对运行时异常回滚,可以通过@Transactional(RollbackFor = ArithmeticException.class) 设置特定的异常才回滚,这里设置出现了数学运算异常才回滚。

Spring的事务隔离级别有哪几种?

隔离级别一共有四种:

  • 读未提交:READ UNCOMMITTED
    允许Transaction01读取Transaction02未提交的修改。
    存在脏读、不可重复读、幻读问题

  • 读已提交:READ COMMITTED

    要求Transaction01只能读取Transaction02已提交的修改。
    存在不可重复读、幻读问题

  • 可重复读:REPEATABLE READ
    确保Transaction01可以多次从一个字段中读取到相同的值,即Transaction01执行期间禁止其它事务对这个字段进行更新。
    存在幻读问题

  • 串行化:SERIALIZABLE
    确保Transaction01可以多次从一个表中读取到相同的行,在Transaction01执行期间,禁止其它事务对这个表进行添加、更新、删除操作。可以避免任何并发问题,但性能十分低下。
    脏读、不可重复读、幻读问题都解决了

mysql中 可重复读:REPEATABLE READ 时采取了一些特殊机制,不会出现幻读。

一般情况下,只读,超时,回滚策略,事务隔离级别,事务传播行为 五个属性都不用配置,用默认值就行。直接在事务上面配置注解@Transaction就行。

怎么指定Spring的事务隔离级别?

@Transactional(isolation = Isolation.DEFAULT)//使用数据库默认的隔离级别(不指定也是默认)
@Transactional(isolation = Isolation.READ_UNCOMMITTED)//读未提交
@Transactional(isolation = Isolation.READ_COMMITTED)//读已提交
@Transactional(isolation = Isolation.REPEATABLE_READ)//可重复读
@Transactional(isolation = Isolation.SERIALIZABLE)//串行化

什么是事务传播行为?

  • 其实就是事务的嵌套调用,在一个事务A方法里 调用事务B、C、D的方法,该怎么处理?B,C,D是在A事务上执行,还是创建一个新事务?出错了又怎么回滚?
  • 默认的事务传播行为是@Transactional(propagation = Propagation.REQUIRED),表示如果当前线程上有已经开启的事务可用,那么就在这个事务中运行。则在A事务中若有某一件事务出错了,则整个事务A都回滚。
  • @Transactional(propagation = Propagation.REQUIRES_NEW),表示不管当前线程上是否有已经开启的事务,都要开启新事务。回滚只回滚当前出错的事务。

7种事务传播行为?

1、@Transactional(propagation=Propagation.REQUIRED)
如果有事务, 那么加入事务, 没有的话新建一个(默认情况下)

2、@Transactional(propagation=Propagation.NOT_SUPPORTED)
容器不为这个方法开启事务

3、@Transactional(propagation=Propagation.REQUIRES_NEW)
重新创建一个新的事务,如果当前存在事务,延缓当前的事务。这个延缓,或者说挂起

4、@Transactional(propagation=Propagation.MANDATORY)
必须在一个已有的事务中执行,否则抛出异常

5、@Transactional(propagation=Propagation.NEVER)
必须在一个没有的事务中执行,否则抛出异常(与Propagation.MANDATORY相反)

6、@Transactional(propagation=Propagation.SUPPORTS)
如果其他bean调用这个方法,在其他bean中声明事务,那就用事务.如果其他bean没有声明事务,那就不用事务.

7、@Transactional(propagation=Propagation.NESTED)
如果没有,就新建一个事务;如果有,就在当前事务中嵌套其他事务。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值