Spring面试题(二)

目录

一、老杜讲解Spring6整理

5. 工厂模式有几种?工厂方法模式的角色有哪些?好处是什么?

8. 你知道Bean的生命周期吗?7步中的那个类叫什么?

9. Spring如何管理Bean的生命周期?

11. Spring的循环依赖如何解决?

12. 代码层面如何解决Spring的循环依赖问题?

14. AOP你了解多少?

15. Spring事务

        1)Spring实现事务有哪些方式?

        2)事务的接口、实现类是什么?

        3)事务属性有哪些?

        4)事务的传播行为具体是什么?

        5)事务的隔离级别?

二、图灵B站面试视频

三、其他★★★★


一、老杜讲解Spring6整理

1. Spring有哪几大模块?

2. IoC控制反转的实现是依赖注入,set依赖注入的原理是什么?

3. 自动装配有哪几种?它的原理是基于构造注入还是基于set注入?

4. Spring容器中的Bean是单例还是多例?

  • Spring的IoC容器中,默认情况下,Bean对象是单例的。默认情况下,Bean对象的创建是在初始化Spring上下文的时候就完成的。
  • 可以在bean标签中指定scope属性的值为:prototype,这样Spring会在每一次执行getBean()方法的时候创建Bean对象,调用几次则创建几次。
  • scope属性的值不止两个,它一共包括8个选项。

5. 工厂模式有几种?工厂方法模式的角色有哪些?好处是什么?

6. Spring中Bean的实例化方式?

7. BeanFactory和FactoryBean的区别?

8. 你知道Bean的生命周期吗?7步中的那个类叫什么?

9. Spring如何管理Bean的生命周期?

Java对象的实例化和初始化的区别:

  • 实例化:创建一个对象的过程,就是使用new关键字调用类的构造函数来生成一个新的实例。
  • 初始化:给对象的成员变量赋予初始值的过程,包括字段的默认初始化、显式初始化,及构造函数中的初始化操作。

10. 自己new的对象Spring可以管理吗?

可以。自己创建默认可列表Bean工厂对象(DefaultListableBeanFactory),并在其中注册。

11. Spring的循环依赖如何解决?

这个视频说的超级明白:【Java面试】Spring的缓存有什么用? 能不能拿掉二级缓存?_哔哩哔哩_bilibili

结论:Spring只能解决setter方法注入的单例bean之间的循环依赖。Spring解决循环依赖的机理是三级缓存,代码存在于DefaultSingletonBeanRegistry类中。三级缓存,本质上是三个Map集合。

  • singletonObjects:一级缓存,单例对象的缓存,key存储bean名称,value存储已经初始化完成的Bean对象;已经完成了bean的所有流程,即实例化、注入、初始化完成的bean实例,并放进了单列池中了。
  • earlySingletonObjects:二级缓存,早期单例对象的缓存,key存储bean名称,value存储早期的Bean对象;保存所有早期创建的Bean对象,这个Bean还没有完成依赖注入,但已经提前完成了AOP增强。
  • singletonFactories:三级缓存,单例工厂缓存,key存储bean名称,value存储该Bean对应的ObjectFactory对象。它是singletonBean的生产工厂,即创建单列bean的工厂。

Spring为什么可以解决set + singleton模式下循环依赖?

根本的原因在于:这种方式可以做到将“实例化Bean”和“给Bean属性赋值”这两个动作分开去完成。

实例化Bean的时候:调用无参数构造方法来完成。此时可以先不给属性赋值,可以提前将该Bean对象“曝光”给外界。

给Bean属性赋值的时候:调用setter方法来完成。

两个步骤是完全可以分离开去完成的,并且这两步不要求在同一个时间点上完成。

也就是说,Bean都是单例的,我们可以先把所有的单例Bean实例化出来,放到一个集合当中(我们可以称之为缓存),所有的单例Bean全部实例化完成之后,以后我们再慢慢的调用setter方法给属性赋值。这样就解决了循环依赖的问题。

Spring 循环依赖解决方案_spring循环依赖及解决方式-优快云博客

spring循环依赖及解决方法_spring循环依赖及解决方式-优快云博客

为什么需要二级缓存?(来源于上面链接的文章)

如果出现循环依赖+aop时,多个地方注入这个动态代理对象需要保证都是同一个对象,而三级缓存中的取出来的动态代理对象每次都是新对象,地址值不一样。

为什么需要第三级缓存?(来源于上面链接的文章)

解决如果出现循环依赖,判断是否需要提前进行aop等操作。

12. 代码层面如何解决Spring的循环依赖问题?

首先,可以避免这样的设计,在上面的链接文章中也有提到,更多内容可查看上面文章。

如何理解Spring的循环依赖问题?以及解决方案?(提示:AI自动生成,仅供参考)

Spring 的循环依赖问题通常发生在以下情况:

  1. 构造函数注入造成的循环依赖:Spring 无法解析循环依赖的对象,因为每个Bean在创建时都依赖另外的Bean。

  2. Setter方法注入引起的循环依赖:Spring 可以解决大多数的循环依赖,因为它可以先提前暴露一个代理对象,然后再注入依赖。

解决方案:

  1. 构造函数注入的循环依赖:尽量避免在构造函数中直接使用@Autowired注解,改用@Autowired与@Qualifier注解结合使用,或者使用Java配置类(@Configuration)中的@Bean方法来明确指定依赖关系。

  2. Setter方法注入的循环依赖:确保你的Bean有一个无参构造函数,并且所有依赖的Bean都标注了@Autowired。

示例代码:

// 使用Java配置类解决循环依赖
@Configuration
public class AppConfig {

    @Bean
    public A aBean() {
        return new A(bBean()); // A依赖B,但B在此之前已经创建
    }

    @Bean
    public B bBean() {
        return new B(aBean()); // B依赖A,但A在此之前已经创建
    }
}

// 使用Setter方法解决循环依赖
public class A {
    private B b;
    public A() {}

    @Autowired
    public void setB(B b) {
        this.b = b;
    }
}

public class B {
    private A a;
    public B() {}

    @Autowired
    public void setA(A a) {
        this.a = a;
    }
}

在配置类中,通过@Bean方法间接创建Bean,Spring能够处理好这种间接依赖,从而避免直接循环依赖。在普通的Bean类中,通过@Autowired注解的Setter方法提供依赖注入,Spring能够处理好这种依赖注入,从而避免直接循环依赖。

13. 动态代理技术了解吗?项目中有用到吗?CGLIB动态代理和jdk动态代理的区别?

  • JDK动态代理:只能代理接口,即目标类是至少一个接口的实现类时,才能用;
  • CGLIB动态代理:既可以代理接口,又可以代理类。底层采用继承的方式实现。所以被代理的目标类不能使用final修饰。

14. AOP你了解多少?

  • 底层是动态代理。Spring的AOP使用的动态代理是:JDK动态代理 + CGLIB动态代理技术。Spring在这两种动态代理中灵活切换,如果是代理接口,会默认使用JDK动态代理,如果要代理某个类,这个类没有实现接口,就会切换使用CGLIB。当然,你也可以强制通过一些配置让Spring只使用CGLIB。
  • 面向切面编程的7大术语是什么?
  • 通知有哪几种类型?
  • 切点表达式的语法格式?

切点表达式如何和注解配合使用?

切点表达式可以与注解配合使用来更精确地定位需要拦截的代码点。下面是一个简化的例子:

        1) 首先,创建一个自定义注解,比如@Loggable,用于标记需要记录日志的方法:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Loggable {
}

        2)然后,在需要记录日志的方法上使用这个注解:

public class SomeService {

    @Loggable
    public void doSomethingImportant() {
        // 执行重要操作
    }
}

        3)接下来,编写一个切面(Aspect),使用@Around注解和切点表达式来匹配标注了@Loggable的方法:

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LoggingAspect {

    @Around("@annotation(Loggable)")
    public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        Object result = joinPoint.proceed(); // 执行原始方法
        long elapsedTime = System.currentTimeMillis() - start;

        String className = joinPoint.getSignature().getDeclaringTypeName();
        String methodName = joinPoint.getSignature().getName();

        System.out.println(className + "." + methodName + " executed in " + elapsedTime + "ms");

        return result;
    }
}

在这个例子中,@Around("@annotation(Loggable)")切点表达式表示匹配所有带有@Loggable注解的方法。当这些方法被调用时,logExecutionTime方法将会在实际方法执行前后进行日志记录。

多个切点还可以这样写“||” ,如下:

package com.powernode.spring6.biz;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class SecurityAspect {

    @Pointcut("execution(* com.powernode.spring6.biz..save*(..))")
    public void savePointcut(){}

    @Pointcut("execution(* com.powernode.spring6.biz..delete*(..))")
    public void deletePointcut(){}

    @Pointcut("execution(* com.powernode.spring6.biz..modify*(..))")
    public void modifyPointcut(){}

    @Before("savePointcut() || deletePointcut() || modifyPointcut()")
    public void beforeAdivce(JoinPoint joinpoint){
        System.out.println("XXX操作员正在操作"+joinpoint.getSignature().getName()+"方法");
    }
}

15. Spring事务

        1)Spring实现事务有哪些方式?

        2)事务的接口、实现类是什么?

        3)事务属性有哪些?

        4)事务的传播行为具体是什么?

        我自己写的:Spring事务介绍-优快云博客

        5)事务的隔离级别?

二、图灵B站面试视频

Spring是如何创建一个Bean对象的?_哔哩哔哩_bilibili

三、其他★★★★

2. Spring中如何配置事务管理器?

在Spring中配置事务管理器通常涉及以下几个步骤:

1. 引入依赖:确保你的项目包含了Spring的事务管理相关的库,比如对于Maven项目,在pom.xml文件中添加以下依赖(以Spring Boot为例):

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

对于没有使用Spring Boot的项目,你需要手动添加相应的Spring JDBC、Hibernate等依赖。

2. 配置数据源:在Spring Boot中,通过application.properties或application.yml配置数据库连接信息,例如:

# application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/testdb
spring.datasource.username=root
spring.datasource.password=root
spring.jpa.hibernate.ddl-auto=update

如果是XML配置方式,你需要在context.xml或类似配置文件中添加<bean>定义数据源。

3. 选择并配置事务管理器:基于不同的持久化技术,选择合适的事务管理器。如果使用JPA/Hibernate,Spring Boot会自动配置PlatformTransactionManager;若使用JDBC,可以手动配置DataSourceTransactionManager

@Configuration
@EnableTransactionManagement
public class TransactionConfig {

    @Autowired
    private DataSource dataSource;

    @Bean
    public PlatformTransactionManager transactionManager() {
        return new DataSourceTransactionManager(dataSource);
    }
}

在XML配置中,你会创建一个<tx:annotation-driven/>标签来启用基于注解的事务管理。

4. 标注事务方法:在需要进行事务管理的Service层或DAO层方法上使用@Transactional注解。

完成以上步骤后,Spring就会为你提供事务管理功能了。

@Repository和@Mapper注解的区别

@Repository@Mapper这两个注解在不同的框架中起作用:

  1. @Repository

    • 它是Spring框架的一部分,主要用于标记 DAO(数据访问对象)层的类。
    • 当Spring遇到这个注解的类时,它会自动处理异常并创建bean,使得你可以使用@Autowired注入这个bean到其他依赖它的组件中。
    • @Repository有助于异常透明化,意味着DAO层抛出的数据库异常会被转换为Spring的数据访问异常。
  2. @Mapper

    • 这是MyBatis框架中的注解,它用于标记接口,表示这个接口是一个映射器接口。
    • MyBatis-Spring桥接库允许你在接口上使用@Mapper,这样Spring就能找到对应的Mapper接口实例。
    • 如果没有@Mapper注解,但有@Repository,并且接口继承了MyBatis的Mapper接口,通常也能够工作,因为Spring会把它当作一个bean处理。

区别:

  1. @Repository是Spring框架的注解,而@Mapper属于MyBatis。
  2. @Repository用于标记整个DAO类,而@Mapper用于标记映射器接口。
  3. 使用@Repository不需要额外配置,即可与Spring一起工作,而@Mapper需要配合@MapperScan在Spring Boot的启动类上,或者配置XML来扫描和启用。
  4. @Repository@Autowired一起使用时,Spring会自动创建bean,避免了编译时的警告或错误。
  5. @Mapper接口下的方法可以直接写SQL片段,而@Repository通常配合的是具体实现类,SQL可能写在XML文件中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值