框架---面经

news2024/11/26 18:41:33

Spring

循环依赖

概念

多个实体之间相互依赖并形成闭环的情况就叫做"循环依赖”,也叫做”循环引用。

三级缓存解决循环依赖的原理

循环依赖的解决方案--- Feild注入单例(@AutoWired)

直接在类的成员变量上使用@Autowired注解,让Spring容器自动将依赖注入到相应的成员变量中。这种方式适用于只有少量依赖关系的情况。

package com.example.tmp;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
 
@Component
public class A {
    @Autowired
    private B b;
 
    private String name = "Tony";
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public String getTest() {
        return b.getAge().toString() + name;
    }
}

package com.example.tmp;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
 
@Component
public class B {
    @Autowired
    private A a;
 
    private Integer age = 20;
 
    public Integer getAge() {
        return age;
    }
 
    public void setAge(Integer age) {
        this.age = age;
    }
 
}

循环依赖的解决方案---构造器注入+@Lazy

构造器注入是通过类的构造函数进行依赖注入,而@Lazy注解可以延迟Bean的初始化。这种方式适用于需要在应用程序启动时延迟初始化某个单例Bean的场景,特别是当该单例Bean的初始化过程涉及到其他依赖关系时。

package com.example.tmp;
 
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
 
@Component
public class A {
    private B b;
 
    public A(@Lazy B b) {
        this.b = b;
    }
 
    private String name = "Tony";
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public String getTest() {
        return b.getAge().toString() + name;
    }
}


package com.example.tmp;
 
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
 
@Component
public class B {
    private A a;
 
    public B(@Lazy A a) {
        this.a = a;
    }
 
    private Integer age = 20;
 
    public Integer getAge() {
        return age;
    }
 
    public void setAge(Integer age) {
        this.age = age;
    }
 
}

循环依赖的解决方案---  Setter/Field注入单例

setter注入通过setter方法来注入依赖关系,而Field注入则是直接将依赖关系注入到成员变量上。这种方式适用于类中有多个依赖关系,并且可以根据需要选择合适的注入方式,更为灵活。

package com.example.tmp;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
 
@Component
public class A {
    private B b;
 
    private String name = "Tony";
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public String getTest() {
        return b.getAge().toString() + name;
    }
 
    public B getB() {
        return b;
    }
 
    @Autowired
    public void setB(B b) {
        this.b = b;
    }
}



package com.example.tmp;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
 
@Component
public class B {
    private A a;
 
    private Integer age = 20;
 
    public Integer getAge() {
        return age;
    }
 
    public void setAge(Integer age) {
        this.age = age;
    }
 
    public A getA() {
        return a;
    }
 
    @Autowired
    public void setA(A a) {
        this.a = a;
    }
}

循环依赖的解决方案--- @PostConstruct

@PostConstruct注解用于标记一个方法,在Bean初始化完成后执行该方法。适用场景包括需要在Bean初始化完成后进行一些额外的初始化工作,例如数据加载、资源初始化等。

package com.example.tmp;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
 
import javax.annotation.PostConstruct;
 
@Component
public class A {
    @Autowired
    private B b;
 
    @PostConstruct
    public void init() {
        b.setA(this);
    }
 
    private String name = "Tony";
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public String getTest() {
        return b.getAge().toString() + name;
    }
}


package com.example.tmp;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
 
@Component
public class B {
    @Autowired
    private A a;
 
    private Integer age = 20;
 
    public Integer getAge() {
        return age;
    }
 
    public void setAge(Integer age) {
        this.age = age;
    }
 
    public A getA() {
        return a;
    }
 
    public void setA(A a) {
        this.a = a;
    }
}

循环依赖的解决方案--- 实现ApplicationContextAware与InitializingBean

实现ApplicationContextAware接口可以让Bean获取到Spring容器的上下文,从而可以在需要时访问容器的功能和资源。InitializingBean接口定义了一个方法,Bean在初始化完成后会自动调用该方法,适用于需要在Bean初始化完成后进行一些额外的初始化逻辑的场景。

package com.example.tmp;
 
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
 
@Component
public class A implements ApplicationContextAware, InitializingBean {
    private B b;
 
    private ApplicationContext context;
 
    private String name = "Tony";
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public String getTest() {
        return b.getAge().toString() + name;
    }
 
    @Override
    public void afterPropertiesSet() throws Exception {
        this.b = context.getBean(B.class);
    }
 
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.context = applicationContext;
    }
}



package com.example.tmp;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
 
@Component
public class B {
    @Autowired
    private A a;
 
    private Integer age = 20;
 
    public Integer getAge() {
        return age;
    }
 
    public void setAge(Integer age) {
        this.age = age;
    }
}

AOP

概念

AOP,Aspect Oriented Programming,面向切面编程,是对面向对象编程OOP的升华。而AOP是横向的对不同事物的抽象,属性与属性、方法与方法、对象与对象都可以组成一个切面,而用这种思维去设计编程的方式叫做面向切面编程。AOP通过预编译方式和运行期动态代理方式实现,在不修改源代码的情况下,给程序动态添加额外功能的一种技术。

AOP思想的实现方案

项目中的使用场景

权限管理、异常处理、操作日志、事务控制。

权限管理

情景1:控制用户的功能权限
方案详述:在@ControllerAdvice里边,处理全局请求,控制权限。
权限管理的其他方案:(除了AOP之外的方案)
在过滤器或者拦截器中处理


异常处理

情景1:在@ControllerAdvice里边,处理全局异常
 

操作日志

情景:按产品的需求,有的接口需要记录操作日志

自定义的AopBeanPostProcessor类,实现了BeanPostProcessor接口和ApplicationContextAware接口。它的作用是对目标中的目标方法进行增强。

返回代理类的代码思路如下:

  1. 实现BeanPostProcessor接口和ApplicationContextAware接口,这样可以在Bean初始化后进行后置处理,并且获取ApplicationContext对象。

  2. 在后置处理方法中,通过判断bean对象所属的包名是否正确,来确定是否对该类进行增强。

  3. 如果是对应包下的类,就创建一个代理对象(beanProxy)来替代原始的bean对象。代理对象将执行增强逻辑。

  4. 代理对象的创建使用Proxy.newProxyInstance()方法,传入目标对象的类加载器、目标对象实现的接口数组和一个InvocationHandler接口的实现。

  5. 用Lambda表达式实现InvocationHandler接口。在Lambda表达式中,首先从applicationContext中获取增强对象,然后执行目标方法,最后调用增强对象的日志方法。

  6. 返回代理对象(beanProxy),完成增强。

  7. 如果不需要对该类进行增强,直接返回原始的bean对象。

返回代理类的代码如下:

public class MockAopBeanPostProcessor implements BeanPostProcessor, ApplicationContextAware {

    private ApplicationContext applicationContext;

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {

        //目的:对UserServiceImpl中的show1和show2方法进行增强,增强方法存在与MyAdvice中
        //问题1:筛选service.impl包下的所有的类的所有方法都可以进行增强,解决方案if-else
        //问题2:MyAdvice怎么获取到?解决方案:从Spring容器中获得MyAdvice

        if(bean.getClass().getPackage().getName().equals("com.itheima.service.impl")){
            //生成当前Bean的Proxy对象
            Object beanProxy = Proxy.newProxyInstance(
                    bean.getClass().getClassLoader(),
                    bean.getClass().getInterfaces(),
                    (Object proxy, Method method, Object[] args) -> {
                        MyAdvice myAdvice = applicationContext.getBean(MyAdvice.class);
                        //执行增强对象的before方法
                        myAdvice.beforeAdvice();
                        //执行目标对象的目标方法
                        Object result = method.invoke(bean, args);
                        //执行增强对象的after方法
                        myAdvice.afterAdvice();
                        return result;
                    }
            );

            return beanProxy;
        }

        return bean;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

测试代码思路如下(不用在面试中将,但要知道):

  1. 创建一个ApplicationContext对象,使用ClassPathXmlApplicationContext类实现。该对象是Spring框架的核心容器,负责加载和管理应用程序的组件。
  2. 通过构造函数传递一个指向XML配置文件的路径,这里的路径是"applicationContext3.xml"。这个配置文件包含了定义和配置应用程序中的组件。
  3. 通过调用getBean()方法从容器中获取一个UserService的实例。getBean()方法的参数是UserService.class,表示要获取UserService接口的实现类的实例。
  4. 调用bean对象的show2()方法,执行相应的业务逻辑。

测试代码如下:

        ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext3.xml");
        UserService bean = app.getBean(UserService.class);
        bean.show2();

事务控制

情景:使用Spring的@Transactional

注解配置声明式事务控制

首先在xml文件中启用基于注解的事务管理的配置项:<tx:annotation-driven/>。

在业务方法上加Transactional,配置isolation="READ COMMITTED" 和propagation="REQUIRED"。

isolation:

  1. READ UNCOMMITTED(读未提交):最低的隔离级别,一个事务可以读取到其他事务未提交的数据。

  2. READ COMMITTED(读已提交):事务只能读取到已经提交的数据,而不能读取到其他事务未提交的数据。

  3. REPEATABLE READ(可重复读):事务执行期间,多次读取同一数据会得到一致的结果,即使其他事务对该数据进行了修改。

  4. SERIALIZABLE(串行化):最高的隔离级别,事务按顺序串行执行。但并发性能较差,一般情况下很少使用。

propagation:

  1. REQUIRED(默认):如果当前已存在事务,则加入到当前事务中执行;如果当前没有事务,则创建一个新的事务并执行。这是最常用的传播行为,它确保一组相关操作要么都成功地执行,要么都回滚。

  2. REQUIRES_NEW:每次都创建一个新的事务,暂停当前事务(如果存在),并在新的事务中执行方法。该传播行为会将当前事务挂起,并在新事务执行完毕后恢复。

  3. SUPPORTS:如果当前已存在事务,则加入到当前事务中执行;如果当前没有事务,则以非事务方式执行。该传播行为适用于不需要强制事务的方法,可以根据调用上下文决定是否参与事务。

  4. NOT_SUPPORTED:以非事务方式执行方法,如果当前存在事务,则挂起该事务并执行方法。该传播行为适用于不需要事务支持的方法,即使当前存在事务,也会暂时挂起。

事务失效的原因及解决

1、方法内的自调用:Spring事务是基于AOP的,只要使用代理对象调用某个方法时,Spring事务才能生效,而在一个方法中调用使用this.xx0调用方法时,会导致注解失效,因为this并不是代理对象。
解放办法1,把调用方法拆分到另外一个Bean中,再通过Autowired注入。
解决办法2,通过Autowired注入当前类的对象,然后调用方法。

2、事务方法是private,Spring事务会基于CGLIB来进行AOP,而CGLB会基于父子类来失效,子类是代理类,父类是被代理类,如果父类中的某个方法是private的,那么子类就没有办法重写它,也就没有办法额外增加Spring事务的逻辑。


3、事务方法是final, 原因和private是一样的,也是由于子类不能重写父类中的final的方法。

4、单独的线程调用方法:当Myatis或JdbcTemplaten执行SQL时,会从Threadlocal中去获取数据库连接对象,如果开启事务的线程和执行SQL的线程是同一个,那么就能拿到数据库连接对象,如果不是同一个线程,那就拿到不到数据库连接对象,这样,Mybats或JdbcTemplaten就会自己去新建一个数据库连接用来执行SQL,该数据库连接的autocommit为true,那么执行完SQL就会提交,后续再抛异常也就不能再回滚之前已经提交了的SQL了。

5、没加@Configuration注解,如果用SpringBoot基本没有这问题,但是如果用的spring,那么可能会有这个问题,这个问题的原因其实也是由于Mybatis或dbcIemplate会从Threadlocal中去获取数据库连接,但是Threadlocal中存储的是MAP,MAP的key为DataSource对象,value为连接对象,而如果我们没有在AppConig上添加@Configuration注解,会导致MAP中存的Datasoure对象和MyBatis或JdbcTemplate中的Datasource对象不相等,从而也拿不到数据库连接,导致自己去创建数据库连接了。

6、异常被吃掉:如果Spring事务没有捕获到异常,那么也就不会回滚了,默认情况下sprin会捕获RuntimeException和Error。

7、类没有被Spring管理,例如类没有加Component,没有被加入Bean容器。

8、数据库不支持事务。

MyBatis--#与$的区别

1、#0是预编译处理、是占位符, 50是字符串替换、是拼接符

2、Mybatis 在处理#0时,会将 sql 中的#0替换为?号,调用 PreparedStatement 来赋值

3、Mybatis 在处理S0时,就是把S0替换成变量的值,调用 Statement 来赋值

4、使用#n可以有效的防止SQL注入,提高系统安全性

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/651274.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SM2椭圆曲线公钥密码算法

国家密码管理局于2010年12月17日发布了SM2椭圆曲线公钥密码算法&#xff0c;并要求为对现有基于RSA算法的电子认证系统、密钥管理系统、应用系统进行升级改造。关于算法标准&#xff0c;请参见《国家密码管理局公告&#xff08;第 21 号&#xff09;》&#xff0c;网址为​​ht…

JavaWeb【总结】——(请求和响应)浏览器发送请求的方式 服务器响应的方式

本文目录 引出JavaWeb相关知识1.网页状态码web相关背景知识如何在idea中建tomcat web项目2.Web的请求request&#xff1a;get和post&#xff0c;响应response3.同步请求和异步请求Ajax&#xff0c;以及异步的Json响应4.同步jsp和异步ajax的axios下&#xff0c;转发或重定向思考…

【图片轮播】Vue如何实现移动端图片轮播效果,支持左右滑动(附图文及代码)

【写在前面】 日常生活中&#xff0c;其实我们是离不开手机查看相册的&#xff0c;尤其是图片的轮播展示&#xff0c;最近我就接到我家老大给出的需求&#xff0c;首先是从网上下载下来之前她做的图片&#xff0c;然后她就希望能够在自己手机上能随时查看&#xff0c;这不没办法…

Java使用OpenCV进行图像操作

OpenCV图像操作 OpenCV概述下载与安装目录说明项目集成验证 Mat类创建Mat对象其他操作 常见图像API读取与输出图像显示图像图像压缩和解压缩图像转换图像缩放亮度调整图像锐化图像梯度图像二值化边缘检测图像高斯模糊图像反色 OpenCV 概述 OpenCV&#xff08;开源计算机视觉库…

2023互联网高级测试工程师至少具备的能力

业务熟悉 熟悉本系统 测试人员参与测试的系统的各种业务场景&#xff0c;必须做到精熟 。一旦需求有改动&#xff0c;可以清楚快速的知道上下文。同时可以清楚的知道哪些点是需要重点测试的。 熟悉跟本系统有通讯的上下游系统业务 跟本系统有通讯的上下游系统也要非常熟悉。这…

Exception in thread “main“ java.lang.UnsupportedClassVersionError 50报错处理

之间正常走jenkinsdocker自动化部署的项目&#xff0c;今天改了一个文件&#xff0c;点了一下&#xff0c;竟然没有部署上去&#xff0c;提示如上&#xff0c;如下 Exception in thread "main" java.lang.UnsupportedClassVersionError: com/coocaa/tsp/sys/user/Use…

Web 自动化测试Selenium 之PO 模型

目录 1. po 模型介绍 2. PageObject 设计模式 3. PO 的核心要素 4. 非PO 实现 5. PO 实现 6. 总结 7. PO 模式的特点 总结&#xff1a; 1. po 模型介绍 在自动化中&#xff0c;Selenium 自动化测试中有一个名字经常被提及 PageObject (思想与面向对象的特征相同)&#x…

【改进算法】混合鲸鱼WOA和BAT算法(Matlab代码实现)​

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

Python键盘监听:实现快捷操作和自动化脚本(监听组合键

前言 本文主要介绍一下使用Python进行事件监听功能&#xff0c;以实现一些特有的操作。本文旨在介绍如何实现这一功能。 笔者根据一些需求写了一个小工具&#xff0c;流程和功能如下图所示&#xff08;实际功能有更多&#xff09;&#xff1a; 该工具主要实现了 键盘监听 和 鼠…

从Linux源码看TIME_WAIT状态的持续时间

前言 笔者一直以为在Linux下TIME_WAIT状态的Socket持续状态是60s左右。线上实际却存在TIME_WAIT超过100s的Socket。由于这牵涉到最近出现的一个复杂Bug的分析。所以&#xff0c;笔者就去Linux源码里面&#xff0c;一探究竟。 首先介绍下Linux环境 TIME_WAIT这个参数通常和五…

C++入门,一些C++基本概念介绍

文章目录 目录 前言 1.C关键字 1.1命名空间 1.2命名空间定义 1.3命名空间的使用 2.C输入&输出 3.缺省参数 3.1缺省参数的概念 3.2缺省参数分类 4.函数重载 4.1函数重载的概念 5.引用 5.1 引用特性 5.2 常引用 5.3引用的使用场景 5.4引用和指针 6.内联函数…

【Java基础】注解——自定义注解

什么是注解? Java 注解(Annotation)又称 Java 标注&#xff0c;是 JDK5.0 引入的一种注释机制。 比如我们常见的Override和Deprecated都是注解&#xff0c;注解可以加在类、方法、成员变量等上面&#xff0c;类似于给他们“打标签"。 注解怎么定义? public interface…

华为OD机试真题 JavaScript 实现【百钱买百鸡问题】【牛客练习题】

一、题目描述 公元五世纪&#xff0c;我国古代数学家张丘建在《算经》一书中提出了“百鸡问题”&#xff1a;鸡翁一值钱五&#xff0c;鸡母一值钱三&#xff0c;鸡雏三值钱一。百钱买百鸡&#xff0c;问鸡翁、鸡母、鸡雏各几何&#xff1f; 现要求你打印出所有花一百元买一百…

AM@空间直角坐标系@数量积和向量积@向量的外积在物理学中的相关概念

文章目录 空间直角坐标系坐标面分向量坐标分解式余弦定理数量积的坐标表示公式 向量积向量积的坐标表示公式 向量的外积在物理学中的相关概念物理量ref 角速度和向量积量纲Base unit (measurement)Background&#x1f388;International System of Units&#x1f388;附 表达方…

【ROS】ROS1导航(了解)

1、简述 ROS1导航模块navigation&#xff0c;它从里程计、传感器流和目标姿势中获取信息&#xff0c;并将速度、角速度控制命令发送至差速控制单元。 因为是ROS1&#xff0c;所以下面的内容只是一带而过&#xff0c;没有深入学习总结。详细内容可参考官网&#xff1a;http://…

Spring高手之路5——彻底掌握Bean的生命周期

文章目录 1. 理解Bean的生命周期1.1 生命周期的各个阶段 2. 理解init-method和destroy-method2.1 从XML配置创建Bean看生命周期2.2 从配置类注解配置创建Bean看生命周期2.3 初始化和销毁方法的特性2.4 探究Bean的初始化流程顺序 3. PostConstruct和PreDestroy3.1 示例&#xff…

Scala入门

第1章 Scala入门 1.1 概述 Scala将面向对象和函数式编程结合成一种简洁的高级语言。 语言特点如下&#xff1a; &#xff08;1&#xff09;Scala和Java一样属于JVM语言&#xff0c;使用时都需要先编译为class字节码文件&#xff0c;并且Scala能够直接调用Java的类库。 &#…

Linux进程信号 | 信号处理

前面的文章中我们讲述了信号的产生与信号的保存这两个知识点&#xff0c;在本文中我们将继续讲述与信号处理有关的信息。 信号处理 之前我们说过在收到一个信号的时候&#xff0c;这个信号不是立即处理的&#xff0c;而是要得到的一定的时间。从信号的保存中我们可以知道如果…

CSP-J组初赛历年真题讲解第1篇

一、二进制基础 1.二进制数 00100100 和 00010100 的和是( )。 A.00101000 B.01100111 C.01000100 D.00111000 来源&#xff1a;模拟试题正确答案&#xff1a;D 讲解&#xff1a; 2.在二进制下&#xff0c;1011001()11001101011001( )1100110 A. 1011 B. 1101 C. 1010…

仓库Vuex

1. 搭建vuex仓库 1.1 安装 npm install vuexnext 1.2 引入 创建store文件夹&#xff0c;里面创建index.js&#xff0c;该js文件中写&#xff1a; import { createStore } from vuex // 引入子仓库 import model1 from "./model1.js" import model2 from "…