Spring

Spring AOP

Spring AOP(面向切面编程)是 Spring 的一个重要功能,它允许我们将横向关注点(诸如日志记录、事务管理等)与核心业务逻辑进行解耦,使得系统更加模块化。Spring AOP 通过动态代理原理实现,它可以在运行时基于配置或注解为 Spring Bean 自动生成代理,拦截方法调用并在调用前、调用后添加用户自定义逻辑。

以下是 Spring AOP 的一些核心概念:
切面(Aspect):一个模块化的程序功能,可以跨多个对象实施。例如,日志记录、安全检查等通常可视为一个切面。一个切面由切点和增强组成。
切点(Pointcut):指定了在哪些方法中引入切面功能。Spring 支持使用表达式语言定义切点,例如:“execution(* com.example.service…(…))”,表明匹配 com.example.service 包下所有类的所有方法。
增强(Advice):实际应用在匹配切点方法的代码逻辑。根据增强在方法何处执行,可分为前置(Before)、后置(After)、环绕(Around)、返回(AfterReturning)和异常(AfterThrowing)五类。
介绍(Introduction):一种特殊类型的增强,允许在目标类中添加新的方法或属性。
目标对象(Target object):应用了一个或多个切面的被代理对象。
代理(Proxy):根据目标对象和配置自动生成的拦截方法调用的对象。Spring AOP 使用 JDK 动态代理或 CGLIB 代理实现。
织入(Weaving):将切面功能插入目标对象并创建代理对象的过程。在 Spring AOP 中,织入是在运行时完成的。

使用 Spring AOP 的步骤:
1、配置 AOP 相关依赖(如 spring-aop 和 aspectjweaver 依赖)。
2、定义切面。在 Spring 中,您可以使用类似 @Aspect 注解来标记一个类作为切面。
3、在切面类中,定义切点和增强。使用 @Pointcut 注解定义切点表达式,使用 @Before、@After、@Around、@AfterReturning 和 @AfterThrowing 注解定义对应类型的增强(通常为切面类的方法)。
4、在 Spring 配置中,开启 AOP 功能。对于 Java 配置,可以使用 @EnableAspectJAutoProxy 注解;对于 XML 配置,可以使用 aop:aspectj-autoproxy 元素。
5、根据需要,将切面应用于目标对象。这可以通过将切面作为 Spring Bean 定义实现。Spring 会在运行时自动识别切面并为匹配的目标对象创建代理。

spring 事务传播行为

1、REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。
2、REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。
3、SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。
4、NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
5、MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常。
6、NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
7、NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,就执行REQUIRED行为

spring事务失效原因

1、方法没有被public修饰:在Spring中,@Transactional注解只有在public方法上才会生效。如果方法不是public的,那么Spring的事务管理将不会起作用。
2、类没有被Spring托管:如果事务方法所在的类没有加载到Spring IoC容器中,即没有被Spring管理,那么Spring就无法实现对该类的方法进行代理,从而导致事务失效。
3、不正确的异常捕获:如果在事务方法中抛出的异常被捕获并处理了,而没有重新抛出,那么@Transactional注解可能无法触发事务的回滚,从而导致事务失效。
4、同一类中方法调用:如果在同一个类中的一个方法(没有@Transactional注解)调用了另一个有@Transactional注解的方法,那么被调用的方法的事务可能会失效。这是因为Spring的事务管理是基于AOP实现的,而AOP是通过动态代理实现的。在类内部的方法调用中,调用方和被调用方都位于同一个对象实例内,因此不会经过Spring的代理类,从而无法触发事务管理。
5、propagation事务传播行为配置错误:如果事务方法的传播行为配置错误,例如配置为不支持事务的传播类型,那么该方法的事务在Spring中将会失效。
6、rollbackFor参数设置错误:在@Transactional注解中,如果rollbackFor参数标注了错误的异常类型,那么当该类型的异常发生时,Spring事务的回滚机制可能无法识别并触发回滚,从而导致事务失效。
7、没有配置事务管理器:如果在项目中没有正确配置Spring的事务管理器,那么即使代码中使用了@Transactional注解,事务也不会生效。
8、数据库本身不支持事务:Spring事务生效的前提是所连接的数据库要支持事务。如果底层的数据库不支持事务(例如,MySQL的MyISAM存储引擎不支持事务),那么Spring的事务肯定会失效。

依赖注入的方式

Spring框架中的依赖注入主要有以下几种方式:
1、属性注入(Setter方法注入):
它通过Bean的setter方法进行注入。
Spring首先会调用Bean的默认构造函数实例化对象,然后通过反射调用setter方法注入属性值。
这种方式要求Bean提供一个默认的构造函数,并且为需要注入的属性提供setter方法。
2、构造函数注入:
构造函数注入是通过Bean的构造函数来完成依赖注入的。在Bean的构造函数中声明依赖对象的参数列表,Spring的IoC容器会检查构造方法并根据所需参数进行注入。这种方式适用于那些必须在创建对象时就确定其依赖关系的场景。
3、工厂方法注入:
工厂方法被定义为一个返回Bean实例的静态方法,Spring通过配置来调用这个方法创建和初始化Bean。
4、基于注解的注入:
通过在类或者字段上使用特定的注解(如@Autowired),Spring会自动将匹配的Bean注入到相应的属性或构造函数参数中。

spring的bean是线程安全的吗

Spring管理的bean默认是单例的,这意味着在整个Spring IoC容器中,对于每个bean定义,只会创建一个实例。但是,单例bean本身并不自动保证线程安全。
1、Bean的状态:单例bean分为无状态bean和有状态bean,多线程状态下对bean只进行查询操作,不修改成员变量的值,这种叫做无状态的bean,不存在线程安全问题的,对bean中的成员变量进行数据更新,这时候的bean是有状态的bean,存在线程安全问题。
2、同步机制:对于有状态的bean,如果需要在多线程环境中安全地使用,通常需要使用同步机制(如synchronized关键字、Lock接口等)来确保线程安全。
3、作用域:虽然Spring的bean默认是单例的,但Spring也支持其他作用域,如prototype多例(每次请求都创建一个新实例)、request(每个HTTP请求一个实例)、session(每个HTTP会话一个实例)等。使用prototype作用域可以避免单例bean的线程安全问题,因为每个线程都会获得自己的bean实例。
4、不可变对象:如果bean的所有字段都是不可变的(final),并且没有提供修改这些字段的方法,那么这个bean是线程安全的,因为不可变对象本身就是线程安全的。
5、服务层的线程安全设计:在设计服务层时,应该避免在类的字段中存储线程不安全的状态。相反,应该使用局部变量或方法参数来传递状态。此外,应该避免在服务层中使用静态变量来存储状态,因为这会导致所有线程共享该状态。
6、使用ThreadLocal:在某些情况下,可以使用ThreadLocal来为每个线程提供其自己的变量副本,从而避免线程安全问题。但请注意,ThreadLocal需要谨慎使用,以避免内存泄漏和其他问题。

spring bean的生命周期

1、实例化:当Spring IOC容器接收到对某个Bean的请求时,IOC容器会先通过反射机制实例化该Bean,给bean分配内存空间。
2、属性赋值:Spring IOC容器在创建Bean的过程中,会将Bean在XML文件中配置的属性值和BeanDefinition中的属性值设置到Bean中。
3、初始化:初始化是Bean接口中的一种方法,如果Bean在容器中配置了init-method,那么这个方法将会被调用,完成Bean的初始化操作,然后就可以使用bean了。
5、销毁:最后是,容器关闭时,记录释放所有的资源,包括所有的对象。

详解:

1.首先容器启动后,会对scope为singleton且非懒加载的bean进行实例化,
2.按照Bean定义信息配置信息,注入所有的属性,
3.如果Bean实现了BeanNameAware接口,会回调该接口的setBeanName()方法,传入该Bean的id,此时该Bean就获得了自己在配置文件中的id,
4.如果Bean实现了BeanFactoryAware接口,会回调该接口的setBeanFactory()方法,传入该Bean的BeanFactory,这样该Bean就获得了自己所在的BeanFactory,
5.如果Bean实现了ApplicationContextAware接口,会回调该接口的setApplicationContext()方法,传入该Bean的ApplicationContext,这样该Bean就获得了自己所在的ApplicationContext,
6.如果有Bean实现了BeanPostProcessor接口,则会回调该接口的postProcessBeforeInitialzation()方法,
7.如果Bean实现了InitializingBean接口,则会回调该接口的afterPropertiesSet()方法,
8.如果Bean配置了init-method方法,则会执行init-method配置的方法,
9.如果有Bean实现了BeanPostProcessor接口,则会回调该接口的postProcessAfterInitialization()方法,
10.经过上一步操作之后,就可以正式使用该Bean了,对于scope为singleton的Bean,Spring的ioc容器中会缓存一份该bean的实例,而对于scope为prototype的Bean,每次被调用都会new一个新的对象,期生命周期就交给调用方管理了,不再是Spring容器进行管理了
11.容器关闭后,如果Bean实现了DisposableBean接口,则会回调该接口的destroy()方法,
12.如果Bean配置了destroy-method方法,则会执行destroy-method配置的方法,Bean的生命周期结束

spring如何解决循环依赖的问题

1、使用@Lazy注解:
这样,当Spring IoC容器在初始化这个bean时,不会立即创建它的实例,而是在首次使用时才进行创建。这种方式可以有效地解决循环依赖的问题。
例如,在相互依赖的类A和类B上,可以选择在其中一个类(如类B)上使用@Lazy注解,这样当类A需要注入类B时,Spring不会立即实例化类B,而是在类A实例化完成并首次调用类B时才进行实例化。
2、使用setter方法注入:
当两个bean相互依赖时,Spring会先实例化其中一个bean(如BeanA),然后通过setter方法将另一个bean(BeanB)注入到已经实例化的bean中。由于setter方法是在对象创建之后调用的,因此这可以避免构造器循环依赖的问题。
需要注意的是,这种方式要求依赖的bean必须是单例的,因为对于原型bean,每次请求都会创建一个新的实例,这样无法通过setter方法注入来解决循环依赖。
3、使用三级缓存解决单例模式下的setter循环依赖:
Spring在解决单例模式下的setter循环依赖时,采用了三级缓存机制。在bean的实例化过程中,Spring会将其提前曝光到一个ObjectFactory中(即三级缓存singletonFactories),当其他bean尝试注入这个未完成初始化的bean时,会通过ObjectFactory来获取一个未完成初始化的bean的代理,从而打破循环依赖。

Spring中的三级缓存包括:
singletonObjects(一级缓存):也称为“单例池”,用于存储已经完全初始化好的单例bean实例。
earlySingletonObjects(二级缓存):用于存储尚未完全初始化但已经创建好的单例bean实例。这些bean可能只经过了实例化,但属性还未完全填充。
singletonFactories(三级缓存):也称为“单例工厂池”,存储的是ObjectFactory对象,这些ObjectFactory用于创建单例bean。

解决循环依赖的过程
1、当Spring IoC容器发现需要创建一个bean时,它首先会检查singletonObjects缓存中是否已经存在该bean的实例。如果存在,则直接返回;如果不存在,则开始bean的创建过程。
2、如果在bean的实例化过程中完成了对象的实例化(但还未进行属性填充和初始化),Spring会将这个bean实例通过ObjectFactory提前放到三级缓存中。这个ObjectFactory的作用是当其他bean尝试注入这个未完成初始化的bean时,提供一个代理对象,从而打破循环依赖。
处理循环依赖:如果在初始化当前bean的过程中,发现它需要注入一个正在创建中的bean也就是发生了循环依赖,那么Spring会从三级缓存中获取对应的ObjectFactory,并使用它来创建一个代理对象,然后将这个代理对象注入到当前bean中。这样,即使依赖的bean还没有完全初始化,当前bean也能继续其初始化过程,从而解决了循环依赖的问题。
完成初始化:当bean完成所有属性的填充和初始化后,它会被从三级缓存和二级缓存中移除,并放入一级缓存中,表示该bean已经完全初始化并可供使用。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值