切面技术进阶
自定义注解是切面技术中的一项重要功能,允许开发者灵活标记需要增强的方法。/ * 自定义注解,用于标记需要记录执行时间的方法。* - @Retention(RetentionPolicy . RUNTIME) :注解在运行时有效。* - @Target(ElementType . METHOD) :注解仅能作用于方法。/ * 自定义注解,用于标记需要记录执行时间的方法。* - @Retention(
摘要
在上一阶段中,我们学习了AOP的基础知识和简单的切面实现方法。在这一阶段,我们将进一步深入,探索如何通过注解实现自定义切点,以及使用动态代理机制实现AOP功能。文章包含详细的代码示例,并为每段代码添加详细注释,帮助初学者快速理解和上手。
引言
切面编程的真正强大之处在于其灵活性,特别是在复杂应用中,它可以极大地提高代码的可维护性。本阶段主要涵盖以下两部分内容:
- 如何结合注解自定义切点,动态定义哪些方法需要增强。
- 动态代理机制的底层实现,包括JDK动态代理和CGLIB。
无论你是希望更深入理解AOP机制,还是希望将其灵活应用到项目中,这篇文章都能为你提供有力的帮助。
博主 默语带您 Go to New World.
✍ 个人主页—— 默语 的博客👦🏻 优秀内容
《java 面试题大全》
《java 专栏》
《idea技术专区》
《spring boot 技术专区》
《MyBatis从入门到精通》
《23种设计模式》
《经典算法学习》
《spring 学习》
《MYSQL从入门到精通》数据库是开发者必会基础之一~
🍩惟余辈才疏学浅,临摹之作或有不妥之处,还请读者海涵指正。☕🍭
🪁 吾期望此文有资助于尔,即使粗浅难及深广,亦备添少许微薄之助。苟未尽善尽美,敬请批评指正,以资改进。!💻⌨
默语是谁?
大家好,我是 默语,别名默语博主,擅长的技术领域包括Java、运维和人工智能。我的技术背景扎实,涵盖了从后端开发到前端框架的各个方面,特别是在Java 性能优化、多线程编程、算法优化等领域有深厚造诣。
目前,我活跃在CSDN、掘金、阿里云和 51CTO等平台,全网拥有超过10万的粉丝,总阅读量超过1400 万。统一 IP 名称为 默语 或者 默语博主。我是 CSDN 博客专家、阿里云专家博主和掘金博客专家,曾获博客专家、优秀社区主理人等多项荣誉,并在 2023 年度博客之星评选中名列前 50。我还是 Java 高级工程师、自媒体博主,北京城市开发者社区的主理人,拥有丰富的项目开发经验和产品设计能力。希望通过我的分享,帮助大家更好地了解和使用各类技术产品,在不断的学习过程中,可以帮助到更多的人,结交更多的朋友.
我的博客内容涵盖广泛,主要分享技术教程、Bug解决方案、开发工具使用、前沿科技资讯、产品评测与使用体验。我特别关注云服务产品评测、AI 产品对比、开发板性能测试以及技术报告,同时也会提供产品优缺点分析、横向对比,并分享技术沙龙与行业大会的参会体验。我的目标是为读者提供有深度、有实用价值的技术洞察与分析。
切面技术进阶
正文
1. 结合注解实现自定义切点
自定义注解是切面技术中的一项重要功能,允许开发者灵活标记需要增强的方法。
步骤 1:定义自定义注解
创建一个注解,用于标记需要增强的方法:
package com.example.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/
* 自定义注解,用于标记需要记录执行时间的方法。
* - @Retention(RetentionPolicy.RUNTIME):注解在运行时有效。
* - @Target(ElementType.METHOD):注解仅能作用于方法。
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface LogExecutionTime {
}
步骤 2:创建业务类并使用注解
在业务类的方法上添加自定义注解:
package com.example.service;
import com.example.annotation.LogExecutionTime;
import org.springframework.stereotype.Service;
/
* 产品服务类,包含商品价格计算逻辑。
*/
@Service
public class ProductService {
/
* 计算商品价格的方法,使用自定义注解标记。
*/
@LogExecutionTime
public void calculatePrice() {
System.out.println("Calculating product price...");
// 模拟耗时操作
try {
Thread.sleep(2000); // 让线程休眠2秒,模拟复杂的业务逻辑
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Price calculation completed.");
}
}
步骤 3:创建切面并处理注解
使用切面捕获注解并添加增强逻辑:
package com.example.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
/
* 切面类,用于拦截带有@LogExecutionTime注解的方法,并记录其执行时间。
*/
@Aspect
@Component
public class ExecutionTimeAspect {
/
* 定义环绕通知,拦截@LogExecutionTime注解标记的方法。
* @param joinPoint 切点,表示目标方法的上下文信息。
* @return 目标方法的返回值。
* @throws Throwable 如果目标方法抛出异常,继续向上抛出。
*/
@Around("@annotation(com.example.annotation.LogExecutionTime)")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis(); // 方法执行开始时间
Object proceed = joinPoint.proceed(); // 执行目标方法
long executionTime = System.currentTimeMillis() - start; // 计算执行时间
System.out.println(joinPoint.getSignature() + " executed in " + executionTime + "ms");
return proceed;
}
}
步骤 4:运行并验证
调用ProductService.calculatePrice()
方法时,将自动记录其执行时间。
控制台输出:
Calculating product price...
Price calculation completed.
void com.example.service.ProductService.calculatePrice() executed in 2002ms
2. 动态代理与切面编程
AOP的底层实现依赖于动态代理技术。Spring AOP默认使用JDK动态代理或CGLIB实现切面功能。
2.1 JDK动态代理
JDK动态代理仅适用于接口类型。以下是一个简单的示例:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
// 定义支付服务接口
interface PaymentService {
void processPayment();
}
// 支付服务实现类
class PaymentServiceImpl implements PaymentService {
@Override
public void processPayment() {
System.out.println("Processing payment...");
}
}
// 动态代理处理器,用于在方法调用前后添加日志逻辑
class LoggingHandler implements InvocationHandler {
private final Object target; // 目标对象
public LoggingHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("[LOG] Method " + method.getName() + " is called");
return method.invoke(target, args); // 调用目标方法
}
}
public class JDKProxyExample {
public static void main(String[] args) {
PaymentService service = new PaymentServiceImpl(); // 创建目标对象
// 创建代理对象
PaymentService proxy = (PaymentService) Proxy.newProxyInstance(
service.getClass().getClassLoader(),
new Class[]{PaymentService.class},
new LoggingHandler(service)
);
proxy.processPayment(); // 调用代理对象的方法
}
}
输出:
[LOG] Method processPayment is called
Processing payment...
2.2 CGLIB动态代理
CGLIB通过生成目标类的子类实现代理,因此适用于没有接口的类。
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
// 用户服务类
class UserService {
public void createUser() {
System.out.println("Creating user...");
}
}
public class CGLIBProxyExample {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserService.class); // 设置目标类
// 设置回调逻辑
enhancer.setCallback((MethodInterceptor) (obj, method, args1, proxy) -> {
System.out.println("[LOG] Method " + method.getName() + " is called");
return proxy.invokeSuper(obj, args1); // 调用目标方法
});
UserService proxy = (UserService) enhancer.create(); // 创建代理对象
proxy.createUser(); // 调用代理对象的方法
}
}
输出:
[LOG] Method createUser is called
Creating user...
总结
本篇文章详细介绍了如何结合注解实现自定义切点,并深入讲解了JDK动态代理和CGLIB在AOP中的底层实现。每段代码都添加了详细注释,确保初学者能够快速理解其原理和用法。
通过这些内容,你不仅可以轻松实现简单的AOP功能,还能深入理解Spring AOP的核心机制。
参考资料
添加我的微信,一起交流技术吧!
如对本文内容有任何疑问、建议或意见,请联系作者,作者将尽力回复并改进📓;(联系微信:Solitudemind )
点击下方名片,加入IT技术核心学习团队。一起探索科技的未来,共同成长。
更多推荐
所有评论(0)