一、老杜讲解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循环依赖及解决方式-CSDN博客
spring循环依赖及解决方法_spring循环依赖及解决方式-CSDN博客
为什么需要二级缓存?(来源于上面链接的文章)
如果出现循环依赖+aop时,多个地方注入这个动态代理对象需要保证都是同一个对象,而三级缓存中的取出来的动态代理对象每次都是新对象,地址值不一样。
为什么需要第三级缓存?(来源于上面链接的文章)
解决如果出现循环依赖,判断是否需要提前进行aop等操作。
12. 代码层面如何解决Spring的循环依赖问题?
首先,可以避免这样的设计,在上面的链接文章中也有提到,更多内容可查看上面文章。
如何理解Spring的循环依赖问题?以及解决方案?(提示:AI自动生成,仅供参考)
Spring 的循环依赖问题通常发生在以下情况:
-
构造函数注入造成的循环依赖:Spring 无法解析循环依赖的对象,因为每个Bean在创建时都依赖另外的Bean。
-
Setter方法注入引起的循环依赖:Spring 可以解决大多数的循环依赖,因为它可以先提前暴露一个代理对象,然后再注入依赖。
解决方案:
-
构造函数注入的循环依赖:尽量避免在构造函数中直接使用@Autowired注解,改用@Autowired与@Qualifier注解结合使用,或者使用Java配置类(@Configuration)中的@Bean方法来明确指定依赖关系。
-
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事务介绍-CSDN博客
5)事务的隔离级别?
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
这两个注解在不同的框架中起作用:
-
@Repository
- 它是Spring框架的一部分,主要用于标记 DAO(数据访问对象)层的类。
- 当Spring遇到这个注解的类时,它会自动处理异常并创建bean,使得你可以使用
@Autowired
注入这个bean到其他依赖它的组件中。 @Repository
有助于异常透明化,意味着DAO层抛出的数据库异常会被转换为Spring的数据访问异常。
-
@Mapper
- 这是MyBatis框架中的注解,它用于标记接口,表示这个接口是一个映射器接口。
- MyBatis-Spring桥接库允许你在接口上使用
@Mapper
,这样Spring就能找到对应的Mapper接口实例。 - 如果没有
@Mapper
注解,但有@Repository
,并且接口继承了MyBatis的Mapper接口,通常也能够工作,因为Spring会把它当作一个bean处理。
区别:
@Repository
是Spring框架的注解,而@Mapper
属于MyBatis。@Repository
用于标记整个DAO类,而@Mapper
用于标记映射器接口。- 使用
@Repository
不需要额外配置,即可与Spring一起工作,而@Mapper
需要配合@MapperScan
在Spring Boot的启动类上,或者配置XML来扫描和启用。 - 当
@Repository
和@Autowired
一起使用时,Spring会自动创建bean,避免了编译时的警告或错误。 @Mapper
接口下的方法可以直接写SQL片段,而@Repository
通常配合的是具体实现类,SQL可能写在XML文件中。