AOP面向切面编程
如何理解面向切面编程
面向切面编程(Aspect-Oriented Programming,简称AOP)是一种编程范式,它允许开发者将横切关注点(如日志记录、事务管理等)与业务逻辑分离,从而提高代码的模块化和可维护性。AOP 通过引入“切面”的概念来实现这一点,切面可以看作是封装了横切关注点的模块。
举例理解:
面向切面编程(AOP)的一个经典例子是日志记录。在一个应用程序中,你可能有多个服务层方法需要记录每次调用的日志。如果使用传统的面向对象编程(OOP),你需要在每个服务层方法中手动添加日志代码,这会导致代码重复和难以维护。
通过AOP,你可以定义一个日志切面,它将日志记录逻辑封装在一个单独的模块中。这个切面可以配置为在特定方法调用的前后自动执行日志记录,而不需要修改这些方法的代码。下面是一个简化的示例来说明这个过程:
- 定义一个日志切面:首先,你创建一个切面类,使用
@Aspect
注解标记它。
@Aspect
public class LoggingAspect {
@Before("execution(* com.example.service.*.*(..))")
public void logBefore(JoinPoint joinPoint) {
// 获取被调用的方法名和参数
String methodName = joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();
// 打印日志
System.out.println("Before method: " + methodName + ", with args: " + Arrays.toString(args));
}
@AfterReturning(pointcut = "execution(* com.example.service.*.*(..))", returning = "result")
public void logAfterReturning(JoinPoint joinPoint, Object result) {
// 打印返回结果
System.out.println("After method returned: " + result);
}
}
- 配置Spring AOP:在你的Spring配置中,启用@AspectJ自动代理,以便Spring可以识别上面定义的切面,并自动将日志逻辑织入到应用程序中。
<aop:aspectj-autoproxy/>
- 业务逻辑实现:现在,你可以在服务层实现业务逻辑,而不需要关心日志记录的代码。
@Service
public class UserService {
public User findUserById(Long id) {
// 业务逻辑
return userRepository.findById(id);
}
}
在这个例子中,无论何时调用UserService
的findUserById
方法,LoggingAspect
中的日志记录逻辑都会自动执行,无需在UserService
中添加任何日志代码。这样,日志记录就作为一个横切关注点被模块化了,符合AOP的原则。
核心概念:
- 切面(Aspect):封装了横切关注点的模块,可以是事务管理、日志记录等。
- 连接点(Joinpoint):程序执行过程中的特定点,如方法调用或异常处理。
- 通知(Advice):在切面的连接点上执行的动作,包括前置(Before)、后置(After returning)、异常(After throwing)、最终(After finally)和环绕(Around)通知。
- 切入点(Pointcut):用来匹配连接点的表达式,决定通知何时执行。
- 引入(Introduction):允许为类添加新的方法或属性。
- 目标对象(Target Object):被一个或多个切面所通知的对象。
- AOP代理(AOP Proxy):AOP框架创建的对象,用于实现切面契约。
- 织入(Weaving):将切面应用到目标对象上,创建被通知的对象。
Spring AOP
Spring框架中的AOP是一个重要的组成部分,它提供了声明式企业服务和自定义切面的支持。Spring AOP使用纯Java实现,不需要特殊的编译过程,适用于J2EE web容器或应用服务器。
两种配置风格
Spring AOP支持两种配置风格:基于注解的@AspectJ风格和基于XML的schema风格。@AspectJ风格使用Java 5的注解,可以声明切面为普通的Java类,而XML风格则使用aop命名空间来定义切面和通知。
基于注解的@AspectJ
风格
首先,需要引入必要的Spring依赖,确保项目中有Spring AOP相关的库。
- 依赖配置(Maven)
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.3.9</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.9</version>
</dependency>
</dependencies>
- 定义切面类
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LoggingAspect {
// 定义一个切入点,匹配service包下的所有方法
@Before("execution(* com.example.service.*.*(..))")
public void logBefore() {
System.out.println("A method in the service package is being called.");
}
}
- 配置Spring上下文
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan(basePackages = "com.example")
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class AppConfig {
}
- 使用注解配置的Spring AOP
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Main {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
MyService service = context.getBean(MyService.class);
service.doSomething();
}
}
@Component
public class MyService {
public void doSomething() {
System.out.println("Executing service method...");
}
}
基于XML的Schema风格
- Spring AOP配置(XML)
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 定义切面 -->
<aop:config>
<aop:aspect ref="loggingAspect">
<aop:pointcut id="serviceMethods" expression="execution(* com.example.service.*.*(..))"/>
<aop:before pointcut-ref="serviceMethods" method="logBefore"/>
</aop:aspect>
</aop:config>
<!-- 切面bean -->
<bean id="loggingAspect" class="com.example.aspect.LoggingAspect"/>
<!-- 其他bean定义 -->
<bean id="myService" class="com.example.service.MyService"/>
</beans>
- 使用XML配置的Spring AOP
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
MyService service = context.getBean(MyService.class);
service.doSomething();
}
}
代理机制
Spring AOP的代理机制可以是基于JDK动态代理或CGLIB代理。如果目标对象实现了至少一个接口,则使用JDK动态代理;如果没有实现任何接口,则使用CGLIB代理。
JDK动态代理
使用JDK动态代理(假设MyService实现了一个接口)
import org.springframework.aop.framework.ProxyFactory;
public class Main {
public static void main(String[] args) {
ProxyFactory factory = new ProxyFactory(new MyServiceImpl());
factory.addAdvice(new LoggingAdvice());
MyService proxy = (MyService) factory.getProxy();
proxy.doSomething();
}
}
CGLIB代理
使用CGLIB代理(MyService不实现任何接口)
import org.springframework.aop.framework.ProxyFactory;
public class Main {
public static void main(String[] args) {
ProxyFactory factory = new ProxyFactory(new MyService());
factory.setProxyTargetClass(true); // 强制使用CGLIB代理
factory.addAdvice(new LoggingAdvice());
MyService proxy = (MyService) factory.getProxy();
proxy.doSomething();
}
}
编程方式创建代理
此外,Spring AOP还支持以编程方式创建代理,可以使用AspectJProxyFactory类来为一个或多个@AspectJ切面通知的目标对象创建代理。
- 使用
AspectJProxyFactory
import org.springframework.aop.aspectj.annotation.AspectJProxyFactory;
public class Main {
public static void main(String[] args) {
MyService target = new MyService();
AspectJProxyFactory factory = new AspectJProxyFactory(target);
factory.addAspect(LoggingAspect.class);
MyService proxy = factory.getProxy();
proxy.doSomething();
}
}
配置AspectJ加载时织入
最后,Spring框架还支持使用AspectJ的加载时织入(LTW),这允许在虚拟机载入字节码文件时动态织入AspectJ切面,提供了更细粒度的控制。
在META-INF/aop.xml
中配置:
<aspectj>
<weaver>
<include within="com.example..*"/>
</weaver>
<aspects>
<aspect name="com.example.aspect.LoggingAspect"/>
</aspects>
</aspectj>
在Spring配置中启用LTW:
<beans>
<context:load-time-weaver/>
</beans>
还在学习中,内容有误请联系作者,本内容仅供各位大佬了解与讨论。
标签:Spring,编程,MyService,切面,AOP,org,public From: https://blog.csdn.net/weixin_63405049/article/details/141681571