AOP技术利⽤"横切"技术,剖解开封装的对象内部,并将那些影响了多个类的公共⾏为封装到⼀个可重⽤模块,并将其命名为"Aspect",即切⾯。所谓"切⾯",简单说就是那些与业务⽆关,却为业务模块共同调⽤的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。
使⽤"横切"技术,AOP把软件系统分为两个部分:核⼼关注点和横切关注点。业务处理的主要流程是核⼼关注点,与之关系不⼤的部分是横切关注点。横切关注点的⼀个特点是,他们经常发⽣在核⼼关注点的多处,⽽各处基本相似,⽐如权限认证、⽇志、事物。AOP的作⽤在于分离系统中的各种关注点,将 核⼼关注点和横切关注点分离开来。
AOP的常见概念
1、横切关注点 对哪些⽅法进⾏拦截,拦截后怎么处理,这些关注点称之为横切关注点
2、切⾯就是对横切关注点的抽象
3、连接点(joinpoint) 被拦截到的点,因为Spring只⽀持⽅法类型的连接点,所以在Spring中连接点指的就是被截到的⽅法,实际上连接点还可以是字段或者构造器
4、切⼊点(pointcut) 对连接点进⾏拦截的定义
5、通知(advice) 指的就是指拦截到连接点之后要执⾏的代码,通知分为前置、后置、异常、最终、环绕通知五类
// 业务接口
public interface HelloService {
void sayHello(String name);
}
// 业务实现类
public class HelloServiceImpl implements HelloService {
@Override
public void sayHello(String name) {
System.out.println("Hello, " + name + "!");
}
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class LoggingInvocationHandler implements InvocationHandler {
private final Object target; // 目标对象,即被代理的对象
public LoggingInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 前置增强:在目标方法执行前记录日志
System.out.println("The method " + method.getName() + " begins with arguments: " + Arrays.toString(args));
// 执行目标方法
Object result = method.invoke(target, args);
// 后置增强:在目标方法执行后记录日志
System.out.println("The method " + method.getName() + " ends with result: " + result);
return result;
}
}
//创建动态代理使用它
import java.lang.reflect.Proxy;
import java.util.Arrays;
public class AOPExample {
public static void main(String[] args) {
// 创建被代理的实际对象
HelloService realHelloService = new HelloServiceImpl();
// 创建一个代理实例
HelloService helloServiceProxy = (HelloService) Proxy.newProxyInstance(
HelloService.class.getClassLoader(), // 类加载器
new Class<?>[] {HelloService.class}, // 代理需要实现的接口
new LoggingInvocationHandler(realHelloService) // InvocationHandler
);
// 使用代理实例调用方法
helloServiceProxy.sayHello("Alice");
}
}
在这个例子中,当我们调用helloServiceProxy.sayHello(“Alice”)时,实际上会调用LoggingInvocationHandler的invoke方法。invoke方法内部会先打印日志,然后调用实际对象的sayHello方法,并在方法执行后再次打印日志。