在企业级Java开发中,Spring框架无疑是最重要的开发工具之一。而Spring AOP(面向切面编程)作为Spring框架的核心功能之一,为开发者提供了强大的代码增强和功能扩展能力。本文将深入解析Spring AOP的核心概念、底层原理以及实战应用,帮助读者全面掌握这一强大工具。
一、AOP概念的引入
在传统的面向对象编程(OOP)中,我们通常按照模块化的方式进行功能开发,但会遇到一些横切关注点(如日志记录、权限验证、事务管理等)的问题,这些功能需要重复编写代码,增加了代码的耦合度和维护成本。而AOP(Aspect Oriented Programming)正是为了解决这类问题而生。
AOP通过横向抽取机制,将这些横切关注点从业务逻辑中分离出来,形成一个可复用的模块,这个模块被称为切面(Aspect)。在运行时,AOP框架会将这些切面应用到指定的业务逻辑中,从而实现功能的增强。
举个例子,假设我们有一个用户登录功能,现在需要添加权限校验。如果不使用AOP,我们可能需要在登录方法中添加权限校验的代码。而使用AOP,我们可以在不修改登录方法的情况下,通过切面的方式在登录方法执行前进行权限校验。
二、AOP相关的概念
1. AOP的概述
AOP(Aspect Oriented Programming),即面向切面编程,是一种编程范式,它通过预编译方式或者运行期动态代理实现程序功能的统一维护。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容。
2. AOP的优势
-
减少重复代码:将横切关注点抽取出来,避免在多个地方重复编写相同代码。
-
提高开发效率:专注于业务逻辑的开发,而不必关心横切关注点的实现。
-
维护方便:修改横切关注点时,只需修改对应的切面,无需修改业务逻辑代码。
3. AOP的底层原理
Spring AOP的底层实现主要依赖于JDK的动态代理技术和CGLIB代理技术。
-
JDK动态代理:为接口创建代理类的字节码文件,使用ClassLoader将字节码文件加载到JVM,创建代理类实例对象,执行对象的目标方法。适用于实现了接口的类。
-
CGLIB代理:为类生成代理对象,被代理类有没有接口都无所谓,底层是生成子类,继承被代理类。适用于没有实现接口的类。
三、Spring的AOP技术
1. AOP相关的术语
-
Joinpoint(连接点):类里面有哪些方法可以增强,这些方法称为连接点。
-
Pointcut(切入点):所谓切入点是指我们要对哪些Joinpoint进行拦截的定义。
-
Advice(通知/增强):所谓通知是指拦截到Joinpoint之后所要做的事情就是通知。通知分为前置通知、后置通知、异常通知、最终通知、环绕通知。
-
Aspect(切面):是切入点+通知的结合,以后咱们自己来编写和配置的。
2. Spring的AOP技术-配置文件方式
基本准备工作
创建Maven项目,导入相关依赖:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!--AOP联盟-->
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<!--Spring Aspects-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!--aspectj-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.3</version>
</dependency>
</dependencies>
AOP配置文件方式的入门
创建被增强的类:
public class User {
public void add() {
System.out.println("add......");
}
public void update() {
System.out.println("update......");
}
}
将目标类配置到Spring中:
<bean id="user" class="com.aopImpl.User"></bean>
定义切面类:
public class UserProxy {
public void before() {
System.out.println("before.............");
}
}
在配置文件中定义切面类:
<bean id="userProxy" class="com.aopImpl.UserProxy"></bean>
在配置文件中完成aop的配置:
<aop:config>
<aop:aspect ref="userProxy">
<aop:before method="before" pointcut="execution(public void com.aopImpl.User.add())"/>
</aop:aspect>
</aop:config>
完成测试:
public class DemoTest {
@Test
public void aopTest1() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
User user = (User) applicationContext.getBean("user");
user.add();
}
}
3. 切入点的表达式
在配置切入点的时候,需要定义表达式,具体格式如下:
execution([修饰符] [返回值类型] [类全路径] [方法名 ( [参数] )])
-
修饰符可以省略不写。
-
返回值类型不能省略不写,可以使用
*
代替。 -
包名、类名、方法名、参数的规则:
-
包名、类名、方法名不能省略不写,但可以使用
*
代替。 -
中间的包名可以使用
*
号代替。 -
类名也可以使用
*
号代替。 -
方法也可以使用
*
号代替。 -
参数如果是一个参数可以使用
*
号代替,如果想代表任意参数使用..
。
-
比较通用的表达式:
execution(* com.qcby.*.ServiceImpl.save(..))
4. AOP的通知类型
-
前置通知:目标方法执行前,进行增强。
-
环绕通知:目标方法执行前后,都可以进行增强。目标对象的方法需要手动执行。
-
最终通知:目标方法执行成功或者失败,进行增强。
-
后置通知:目标方法执行成功后,进行增强。
-
异常通知:目标方法执行失败后,进行增强。
5. Spring的AOP技术-注解方式
AOP注解方式入门程序
创建Maven工程,导入坐标,编写接口,完成IOC的操作。
编写切面类,给切面类添加注解@Aspect
,编写增强的方法,使用通知类型注解声明:
@Component
@Aspect
public class UserProxy {
@Before(value = "execution(* com.*.User.add(..))")
public void before() {
System.out.println("before.............");
}
@Around(value = "execution(* com.*.User.add(..))")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("before.............");
proceedingJoinPoint.proceed();
System.out.println("after.............");
}
@After(value = "execution(* com.*.User.add(..))")
public void after() {
System.out.println("after.............");
}
@AfterThrowing(value = "execution(* com.*.User.add(..))")
public void afterThrowing() {
System.out.println("afterThrowing.............");
}
@AfterReturning(value = "execution(* com.*.User.add(..))")
public void afterReturning() {
System.out.println("afterReturning.............");
}
}
在配置文件中开启自动代理:
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
测试类:
@Test
public void aopTest1() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
User user = (User) applicationContext.getBean("user");
user.add();
}
四、Spring AOP的实战应用
1. 日志记录
在实际开发中,日志记录是一个常见的横切关注点。我们可以通过AOP来实现日志记录功能,而无需在每个方法中手动添加日志代码。
@Aspect
@Component
public class LogAspect {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Before("execution(* com.example.service.*.*(..))")
public void before(JoinPoint joinPoint) {
logger.info("方法开始:" + joinPoint.getSignature().getName());
}
@AfterReturning("execution(* com.example.service.*.*(..))")
public void afterReturning(JoinPoint joinPoint) {
logger.info("方法结束:" + joinPoint.getSignature().getName());
}
}
2. 权限验证
在用户登录后,需要对用户进行权限验证。通过AOP可以在方法执行前进行权限验证,确保用户有权限执行该操作。
@Aspect
@Component
public class AuthAspect {
@Before("execution(* com.example.controller.*.*(..))")
public void before(JoinPoint joinPoint) {
// 获取用户信息
User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
// 验证用户权限
if (!user.hasPermission("permission")) {
throw new AccessDeniedException("权限不足");
}
}
}
3. 性能监控
在系统性能优化过程中,需要对方法的执行时间进行监控。通过AOP可以在方法执行前后记录时间,计算方法的执行时间。
@Aspect
@Component
public class PerformanceAspect {
@Around("execution(* com.example.service.*.*(..))")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
Object result = joinPoint.proceed();
long end = System.currentTimeMillis();
System.out.println("方法执行时间:" + (end - start) + "ms");
return result;
}
}
五、总结
Spring AOP是Spring框架中的一个重要功能,它通过面向切面编程的方式,将横切关注点从业务逻辑中分离出来,实现了代码的复用和功能的增强。本文深入解析了Spring AOP的核心概念、底层原理以及实战应用,帮助读者全面掌握这一强大工具。在实际开发中,我们可以通过Spring AOP实现日志记录、权限验证、性能监控等功能,提高开发效率和代码质量。