Spring

谈谈你对Spring的理解


1.Spring框架帮我们实现一些复杂的重复的逻辑 让我更有精力进行业务的开发 Spring是一个轻量级框架 内部集成了很多模块 最核心的IOC和AOP
2.其2004年发布Spring 1.0版本,2013年发布4.0版本,4.0版本提供对JAVA8的支持,包括 lambda 表达式、Stream API 等。

 

什么是IOC


1.控制反转(Inversion of control),核心思想是由框架创建和管理对象,而不是由程序直接控制,实现程序的解耦、灵活性、扩展性

1.解耦:对象的创建交给框架管理,不需要程序直接控制
2.灵活性:依赖注入的灵活性:构造注入、Setter注入、字段注入:@Autowired
3.扩展性:通过BeanPostProcessor等扩展点,可以在Bean初始化前后插入自定义逻辑

2.IOC其实就是一个map,key是对象名字,value是对象的实例,当我们容器启动的时候会去扫描配置文件,或者注解,然后根据反射生成对象实例放入到map中,当进行依赖注入的时候直接从map中获取对象实例

3.实现一个简单的IOC容器:https://www.cnblogs.com/sunnycc/p/19166917


1.容器初始的时候加载BeanFactor
2.bean工厂初始化的时候创建BeanDefinitionRegistry生成singletonObjects(单例缓存对象)对象和加载文件资源生成beanDefinitionMap,将配置文件的数据维护进BeanDefinition
3.spring容器默认bean对象是单例的,单例默认下容器启动时会调用getBean()方法,在实例化bean的时候会先去singletonObjects缓存中获取,如果缓存中不存在则使用反射创建根据类的Class对象实例化对象,多例情况下使用对象时才会创建对象
备注: 1.beanDefinitionRegistry管理和存储Bean的定义信息 2.BeanDefinition中维护bean的类型、作用域、依赖项和初始化参数等信息

 

 

什么是DI


1.依赖注入(Dependency Injection),是实现IOC和核心技术,在容器运行时动态的将对象注入到组件中。
2.三种实现方式:构造函数注入(通过对象的构造函数来注入依赖关系)、Setter注入(通过Set方法对属性进行赋值)、注解注入(通过反射机制为属性赋值)

构造方法注入

public CatDaoImpl(String message){
this. message = message;
}

<bean id="CatDaoImpl" class="com.CatDaoImpl">
<constructor-arg value=" message "></constructor-arg>
</bean>

 

Setter注入

public class Id {
private int id;

public int getId() { return id; }

public void setId(int id) { this.id = id; }
}


<bean id="id" class="com.id ">
<property name="id" value="123"></property>
</bean>

 

注解注入

public class MyAopTransaction {

@Autowired
private MyTransactionManager myTransactionManager;

}


 


什么是AOP


1.面向切面编程,基于动态代理实现可以在不修改源码的情况下为程序添加统一维护的功能
2.AOP的核心构成:

切点(Pointcut):提供一组规则来告诉程序对那些方法进行增强。
切面(Aspect):是被@Aspect注解标注的类,由切点+通知组成。
通知(Advice):定义切面具体的行为,就是具体要做的工作,包括前置、后置、返回、异常等五种类型。
连接点(JoinPoint):满足切点表达式的具体方法,也就是被AOP控制的方法

 


3.动态代理:JDK动态代理(针对实现了接口的类,通过反射调用目标类)、CGLIB代理(针对没有实现接口的类,通过生成子类在子类的方法调用拦截父类方法进行增强)
4.应用场景:日志记录、事务管理:https://www.cnblogs.com/sunnycc/p/13549995.html、限流、AB实验。

AB实验:需要根据页面不同的展示样式观察用户的点击率,要求服务端根据用户信息确定具体走实验A还是实验B,这样的场景有很多。
1.可以结合AOP+自定义注解@AbExperiment的能力实验统一管理。
2.切点配置@Pointcut("@annotation(com.fox.annotation.AbExperiment)"),使用环绕通知先判断的Response是否成功,成功则进行AB实验,不成功则原结果返回。
3.接口响应的返回值使用统一的AbResultResponse,不同接口有不同响应值视图泛型T接收
4.当Controller接口上使用@AbExperiment注解,就自动进行了AB实验


@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AbExperiment {
String value() default "";
}

 

@Data
public class AbResultResponse<T> {
/**
* 是否命中实验版本
*/
private Boolean abResult;
/**
* 接口响应结果
*/
private T resultDate;
}

@Aspect
@Component
@Slf4j
public class AbTestAspect {

//切点 提供一组规则告诉程序需要对aop.AbTestController下的任意方法进行增强
@Pointcut("execution(* cn.itcast.hotel.aop.AbTestController.*(..))")
private void pointcut() {
}

//环绕通知
@Around("pointcut()")
private Object after(ProceedingJoinPoint joinPoint) throws Throwable {
Object result = joinPoint.proceed();

if (result instanceof AbResultResponse) {
log.info("当前时机:{}", System.currentTimeMillis());
Boolean num = System.currentTimeMillis() % 2l == 0;
((AbResultResponse) result).setAbResult(num);
return result;
}
return result;
}

}


@RestController
@RequestMapping("/abTest")
@RequiredArgsConstructor
@Slf4j
public class AbTestController {

//连接点 满足切点表达式规则的方法,就是连接点
@RequestMapping(value = "/method1",method = RequestMethod.GET)
public AbResultResponse<String> method1(){
log.info("我是AbTest测试的第一个方法");
AbResultResponse<String> resultResponse = new AbResultResponse<>();
resultResponse.setResultDate("第一个方法");
return resultResponse;
}

}


 

什么是动态代理,JDK 动态代理和 CGLIB 代理 区别是什么

 

1.Spring框架中的动态代理是实现AOP(面向切面编程)的一种关键技术。代理就是通过代理类访问目标类之前,对目标类做增加(前置、后置处理),如日志记录、事务管理、安全检查等横切关注点。
2.JDK 动态代理需要依赖接口实现,代理处理类也要实现InvocationHandler接口,然后通过反射调用方法
3. CGLIB 代理可以直接对目标类进行增强,代理类要实现MethodInterceptor接口,需要传入目标类的class文件,通过字节码创建一个子类,在子类中拦截所有父类方法调用并进行增强

 

Spring Framework中常用的注解有哪些


Spring框架中常用的注解有:

1. @Autowired:自动装配,将需要的依赖注入到类中。通过使用不同的方式注入(如构造器注入、Setter注入、字段注入等)来指定要注入的实例对象。
2. @Component:声明一个组件,将会由Spring框架进行扫描,并将其实例化作为一个Bean纳入Spring容器管理。
3. @Controller:声明一个MVC控制器,标记该类为Spring的控制器,处理Web请求。
4. @Service:声明一个服务类,标记该类为Spring的服务类,用于处理业务逻辑。
5. @Repository:声明一个数据访问类,标记该类为Spring的数据访问类,用于进行数据库操作。
6. @Configuration:声明一个Java配置类,其内部包含了若干个@Bean注解用于声明Bean对象。
7. @Bean:声明一个Bean,用于在Java配置类中定义需要注入IOC容器中的Bean实例对象。
8. @RequestMapping:用于将HTTP请求映射到对应的控制器中的处理方法上。
9. @Value:用于将配置文件中的属性值注入到Spring Bean中的字段属性中。


Spring中常见的设计模式


1.工厂模式:解耦对象的创建与使用,IOC容器就是利用工厂模式实现
2.代理模式:在程序运行期间对逻辑进行拦截,动态的增强逻辑,AOP使用动态代理实现。
3.单例模式:确保一类只有一个实例确保机器性能和资源使用率,IOC生成对象实例默认就是单例的。
4.策略模式:动态的逻辑算法,营销弹窗接口的实现。
5.模板模式:核心作用是‌定义算法骨架‌,将可变部分延迟到子类实现,从而避免代码重复并提高扩展性,如事务管理和JdbcTemplate
6.责任链模式:使多个对象都有处理请求的计划,直到请求被某个逻辑拦截或执行到末端,Spring的拦截器和过滤器使用的是责任链模式。

 

 

spring事务传播机制

 

spring事务的七种传播机制决定了在多个事务方法相互调用时,事务应该如何进行。以下是它们的详细说明:


‌required(必需)‌:默认的传播行为。如果当前存在事务,就加入该事务;如果当前没有事务,则创建一个新事务。
‌supports(支持)‌:支持当前事务。如果当前存在事务,就加入该事务;如果当前没有事务,就以非事务方式执行。
‌mandatory(强制)‌:强制要求必须在事务中运行。如果当前存在事务,则加入;如果当前没有事务,则抛出异常。
‌requires_new(需要新事务)‌:总是会启动一个新的事务。如果当前存在事务,就把当前事务挂起。
‌not_supported(不支持)‌:以非事务方式执行操作。如果当前存在事务,就把当前事务挂起。
‌never(从不)‌:绝不在事务中执行。如果当前存在事务,则抛出异常。
‌nested(嵌套)‌:如果当前存在事务,则在嵌套事务内执行;如果当前没有事务,则行为与required一样。

 

 

 

@Transactional失效的场景有那些


spring代理不生效

1.作用在static或final上
2.类方法内部调用
3.作用在接口方法上
4.类没有被spring管理,

 

框架或底层不支持

1.未开启事务
2.作用在非public方法上
3.多线程调用
4.数据库本身不支持

 

错误使用@Transactional 

1.rollbackFor属性设置错误(默认仅支持RuntimeExprice异常)
2.异常被catch
3.事务传播机制异常(不支持PROPAGATION_SUPPORTS,PROPAGATION_NOT_SUPPORTED,PROPAGATION_NEVER),
4.嵌套事务
5.锁的作用域异常,需要锁的作用域包含事务的作用域。

 

 

@Transactional类方法内部调用失效原因

 

Spring 通过 AOP 代理实现事务管理,但‌同一个类内部调用‌不会触发代理对象,导致事务失效。具体机制如下:

‌代理对象调用‌:外部调用通过代理对象(如 serviceA.methodB())触发事务拦截器。
‌内部调用问题‌:this.methodB() 直接调用原始对象方法,跳过代理逻辑。

 

@Transactional作用在非public方法上失效的原因

 

1. ‌AOP代理机制限制‌
Spring AOP通过动态代理实现事务管理,但仅对public方法生效。非public方法(如private、protected)无法被代理对象拦截,导致事务失效。

‌原理‌:
Spring通过JDK动态代理或CGLIB生成代理对象,代理逻辑仅作用于public方法。非public方法调用直接绕过代理,事务管理器无法感知。



2.访问控制限制‌
Spring AOP底层通过反射机制实现事务拦截,public方法允许外部代理对象调用,而非public方法仅限类内调用,无法触发代理逻辑。

‌关键点‌:
public方法:通过代理对象调用,事务生效。
非public方法:类内直接调用(如this.update()),跳过代理,事务失效。

 

posted @ 2025-10-26 10:54  爵士灬  阅读(8)  评论(0)    收藏  举报