您的位置:首页 > 新闻 > 热点要闻 > Spring Aop

Spring Aop

2024/12/21 22:07:37 来源:https://blog.csdn.net/qq_41848006/article/details/141724673  浏览:    关键词:Spring Aop

AOP代理

1、开启AOP代理

1.1、Spring MVC中的AOP配置

依赖

<!--aop依赖1:aspectjrt -->
<dependency><groupId>org.aspectj</groupId><artifactId>aspectjrt</artifactId><version>1.9.5</version>
</dependency><!--aop依赖2: aspectjweaver -->
<dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.5</version>
</dependency>

在Spring MVC中,使用XML配置时,如果希望使用基于注解的AspectJ AOP,需要在applicationContext.xml或相应的配置文件中加入:

<aop:aspectj-autoproxy />

这行配置的作用是启用自动的AspectJ代理处理,允许Spring扫描带有@Aspect@Before@After等注解的类,并根据定义的切入点执行相应的横切逻辑。

1.2、Spring Boot中的AOP配置

在Spring Boot中,由于其自动配置的特性,许多功能都可以通过简单的添加依赖来实现,而不需要手动进行复杂的XML配置。Spring Boot中使用AOP时,只需要引入相关的依赖,Spring Boot会自动配置AOP所需的组件。

例如,如果你在Spring Boot应用中添加了spring-boot-starter-aop依赖:

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

上面的start包含了aop和aspectjweaver

image-20240830181116410

这样,Spring Boot会自动启用AspectJ代理,允许你直接使用@Aspect和相关的AOP注解,而无需手动配置<aop:aspectj-autoproxy>

1.3、总结

  • Spring MVC:需要在XML配置中手动添加<aop:aspectj-autoproxy>来启用AOP。
  • Spring Boot:通过引入spring-boot-starter-aop依赖,启用AOP功能,简化了配置过程。

这种设计让Spring Boot提供了一种更为简便和现代的开发体验,减轻了开发者的负担。

2、spring代理自身

在Spring框架中,如果你希望在一个服务类内部调用另一个事务性的方法,并确保事务能够正常工作,不会因为直接调用导致失效(即不使用代理),你可以利用Spring的自我注入形式。

问题背景

在Spring中,事务是通过AOP代理实现的。当你在一个service内部直接调用另一个同一个类的方法时,实际上是通过方法调用的形式,而不是通过代理形式,这样就不会触发事务管理,因此事务可能会失效。

解决方案

1. 使用 ApplicationContext 获取代理对象

可以从Spring的ApplicationContext中手动获取该Service的代理实例:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;@Service
public class MyServiceImpl implements MyService {@Autowiredprivate ApplicationContext applicationContext;@Override@Transactionalpublic void methodA() {// 这个方法是事务控制的methodB();}@Transactionalpublic void methodB() {// 这个方法也需要事务控制}private void methodB() {// 通过ApplicationContext手动获取代理MyService proxy = applicationContext.getBean(MyService.class);proxy.methodB(); // 通过代理的方式调用,保证事务有效。}
}

注意:如果方法 methodB() 在外部调用时已经标记为 @Transactional,在自我注入时可直接使用。

2. 使用 @Lazy 注解结合自我注入

当然,这种方式需要在 @Autowired 的时候被标记为 @Lazy。这是因为Spring需要通过代理来解析循环依赖的影响。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;@Service
public class MyServiceImpl implements MyService {@Autowired@Lazyprivate MyService myService; // 使用 @Lazy 注解进行自我注入@Override@Transactionalpublic void methodA() {// 这个方法是事务控制的myService.methodB(); // 通过代理调用,能够保持事务}@Transactionalpublic void methodB() {// 这个方法的事务控制同样有效}
}

除了上述提到的通过 ApplicationContext 手动获取代理对象和使用 @Lazy 注解进行自我注入之外,实际上还有一些其他方法可以用来确保在同一 Service 内部调用其他方法时,事务能够正常工作。

3. 拆分 Service

如果业务逻辑允许,把相关的方法拆分到不同的 Service 中,可以有效避免自调用的问题。例如:

@Service
public class MyServiceA {@Autowiredprivate MyServiceB myServiceB;@Transactionalpublic void methodA() {myServiceB.methodB(); // 调用另一个 Service 的方法}
}@Service
public class MyServiceB {@Transactionalpublic void methodB() {// 事务逻辑}
}

这种方式是最为推荐的,因为它符合单一职责原则,也让代码结构更加清晰。

4. 使用 AopProxyUtils

Spring 提供了一些工具类,可以用来获取当前对象的代理,通过这些工具类也可以借助 Spring AOP 机制来实现自我调用。AopProxyUtils 是其中之一,但通常在实际使用中不常见。

import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;@Service
public class MyServiceImpl {@Autowiredprivate MyServiceImpl self; // self 是 Spring 的代理对象@Transactionalpublic void methodA() {self.methodB(); // 通过代理自己调用}@Transactionalpublic void methodB() {// 事务逻辑}
}
5. 使用 AspectJ

如果项目中已经使用了 AspectJ,则可以考虑将业务逻辑提取到独立的 Aspect 中,然后通过 AOP 处理事务。这也是一种较为复杂但灵活的做法。

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.After;
import org.springframework.stereotype.Service;@Aspect
@Service
public class BusinessAspect {@After("execution(* MyServiceImpl.methodA(..))")public void afterMethodA() {// 处理逻辑}
}

注意:这需要额外的配置和理解 AspectJ 的工作原理。

6. 事件驱动方式

如果业务逻辑相对复杂,可以考虑使用 Spring 事件机制来触发事务。虽然这种方法不直接在同一个 Service 中调用方法,但可以实现某些业务的解耦。

import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;@Service
public class MyService {private final ApplicationEventPublisher eventPublisher;public MyService(ApplicationEventPublisher eventPublisher) {this.eventPublisher = eventPublisher;}@Transactionalpublic void methodA() {// 事务逻辑eventPublisher.publishEvent(new MyEvent(this)); // 触发事件}
}// 事件监听器
@Service
public class EventListenerService {@EventListener@Transactionalpublic void handleMyEvent(MyEvent event) {// 事务逻辑}
}

总结

每种方案都有其适用的场景和优劣所在。大部分情况下,将方法拆分到不同的 Service 中是最佳实践,它能够保持代码的清晰性和可维护性。在复杂的应用中,可以根据具体的需求考虑其他方法,但在选择方案时要注意事务管理的影响。

3、AspectJ、JDK代理和CGLIB代理

都是Java中实现AOP(面向切面编程)和动态代理的技术,它们有不同的特点和使用场景。下面是它们之间的关系和主要区别:

1. AspectJ

AspectJ是一个功能强大的AOP框架,它可以在编译时、类加载时、运行时进行切面编程。通过AspectJ,开发者可以定义切面(Aspect)、连接点(Join Point)、通知(Advice)等概念来实现横切关注点的模块化。
AspectJ允许定义更复杂的切入点表达式,可以应用于不同的连接点,例如方法调用、构造函数、字段访问等。

2. JDK动态代理

JDK动态代理是Java内置的动态代理机制,要求被代理的类实现一个或多个接口。通过创建Proxy类的实例和实现InvocationHandler接口,可以在运行时创建代理对象。
JDK动态代理只能代理实现了接口的类,无法直接代理类本身。

3. CGLIB代理

**CGLIB(Code Generation Library)**是一个强大的、高性能的字节码生成库,可以在运行时动态创建一个类的子类。CGLIB可以用于类代理,甚至可以代理没有实现任何接口的类。
CGLIB通过继承方式进行代理,因此不能代理final类和final方法。

4、总结

  • 使用场景:

    • AspectJ:在需要复杂AOP,而不仅仅是简单的代理时,可以使用AspectJ。需要在项目中引入AspectJ的支持。
    • JDK代理:适用于接口代理,但仅限于实现了接口的类。
    • CGLIB代理:适合需要对类进行代理的场景,尤其是没有接口可供代理的情况。
  • 优缺点:

    • AspectJ:功能丰富,但配置复杂,学习曲线较陡。
    • JDK动态代理:相对简单,但只能代理接口。
    • CGLIB:强大且灵活,但生成的子类可能会对性能产生一定影响,且不支持final类和方法的代理。
  • 关系
    这三者都可以用于AOP,但实现方式不同,选择合适的代理方式取决于具体的需求和场景。在实际应用中,Spring框架通常使用JDK动态代理和CGLIB代理,对于简单的接口代理使用JDK,对于没有接口的类或需要更复杂的功能时使用CGLIB。AspectJ则可以与Spring整合使用提升AOP的能力。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com