1、什么是AOP?
AOP:Aspect-Oriented Programming,即面向切面编程,旨在将横切关注点与业务逻辑分离,以提高代码的模块化和可维护性。
概念:
Aspect:切面,即一个横跨多个核心逻辑的功能,或者称之为系统关注点;
Joinpoint:连接点,即定义在应用程序流程的何处插入切面的执行;
Pointcut:切入点,即一组连接点的集合;
Advice:通知,在切面识别到某个连接点后要执行的动作。有多种类型的通知,包括前置通知(Before)、后置通知(After)、返回通知(AfterReturning)、异常通知(AfterThrowing)和环绕通知(Around);
Introduction:引介,指为一个已有的Java对象动态地增加新的接口;
Weaving:织入,指将切面整合到程序的执行流程中;
Interceptor:拦截器,是一种实现增强的方式;
Target Object:目标对象,即真正执行业务的核心逻辑对象;
AOP Proxy:AOP代理,是客户端持有的增强后的对象引用。
在Java平台上,对于AOP的织入,有3种方式:
编译期:在编译时,由编译器把切面调用编译进字节码,这种方式需要定义新的关键字并扩展编译器,AspectJ就扩展了Java编译器,使用关键字aspect来实现织入;
类加载器:在目标类被装载到JVM时,通过一个特殊的类加载器,对目标类的字节码重新“增强”;
运行期:目标对象和切面都是普通Java类,通过JVM的动态代理功能或者第三方库实现运行期动态织入。
2、AOP常用场景
日志记录(Logging):在方法调用前后记录日志信息,用于跟踪方法执行情况、性能监控或调试。
权限检查(Security/Authorization):在方法执行前验证用户是否有权限执行该操作,比如角色检查或资源访问控制。
事务管理(Transaction Management):自动管理数据库事务的开启、提交或回滚,保证数据的一致性。
异常处理(Exception Handling):集中处理特定类型的异常,比如记录异常信息或执行特定的恢复操作。
性能监控(Performance Monitoring):监控方法执行时间,帮助识别和优化性能瓶颈。
缓存(Caching):自动缓存方法的返回结果,减少不必要的数据库查询或其他耗时操作。
参数校验和转换(Parameter Validation and Conversion):在方法调用前对参数进行校验或转换,确保符合业务逻辑要求。
API调用统计(API Call Tracking):记录API的调用次数、频率等,用于分析和优化。
3、aop实践
Java代码中实现AOP的两种方式
- JDK动态代理(只能代理有接口的类)
- 使用字节码操作库,如CGLIB(使用字节码生成技术在运行时动态生成被代理类的子类。)
JDK动态代理
package aop;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @author 86175
*/
public class JDKAspectDemo {
public static void main(String[] args) {
// 创建目标对象
Service service = new ServiceImpl();
// 创建代理对象
Service proxyService = (Service) Proxy.newProxyInstance(
service.getClass().getClassLoader(),
service.getClass().getInterfaces(),
new JDKInvocationHandler(service)
);
// 通过代理对象调用方法
proxyService.doSomething();
}
}
// 定义一个接口
interface Service {
void doSomething();
}
// 需代理类
class ServiceImpl implements Service {
@Override
public void doSomething() {
System.out.println("ServiceImpl doSomething");
}
}
// 定义一个切面类,实现InvocationHandler接口
class JDKInvocationHandler implements InvocationHandler {
// 代理类
private Object target;
public JDKInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 前置增强
System.out.println("Before method: " + method.getName());
// 调用目标对象的方法
Object result = method.invoke(target, args);
// 后置增强
System.out.println("After method: " + method.getName());
return result;
}
}
输出
Before method: doSomething
ServiceImpl doSomething
After method: doSomething
CGLIB
package aop;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* @author 86175
*/
public class CglibDemo {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Service2.class);
enhancer.setCallback(new CGLIBMethodInterceptor());
Service2 service = (Service2) enhancer.create();
service.doSomething();
}
}
// 定义一个接口
class Service2{
public void doSomething(){
System.out.println("Service2 doSomething");
}
}
// 切面
class CGLIBMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("Before method" + method.getName());
Object result = methodProxy.invokeSuper(o, objects);
System.out.println("After method" + method.getName());
return result;
}
}
输出
Before methoddoSomething
Service2 doSomething
After methoddoSomething
JDK 动态代理 和 CGLIB:都是在运行时进行织入。
AspectJ:可以在编译时、加载时或运行时进行织入,提供了更多的灵活性。