AOP面向切面编程
AOP:面向切面的程序设计(Aspect-oriented programming)是计算机科学中的一种程序设计思想,旨在将横切关注点与业务主体进行进一步分离,以提高程序代码的模块化程度。
通过在现有代码基础上增加额外的通知(Advice)机制,能够对被声明为“切点(Pointcut)”的代码块进行统一管理与装饰,如“对所有方法名以‘set*’开头的方法添加后台日志”。该思想使得开发人员能够将与代码核心业务逻辑关系不那么密切的功能(如日志功能)添加至程序中,同时又不降低业务代码的可读性。面向切面的程序设计思想也是面向切面软件开发的基础。
AOP和OOP一样,只是一种编程范式,具体的实现可以有多种。
实现
这里有一个DataService接口
public interface DataService { String a(int i); String b(int i); }
他有一个实现类,返回一个随机UUID。
import java.util.UUID; public class DataServiceImpl implements DataService { public String a(int i) { return UUID.randomUUID().toString(); } public String b(int i) { return UUID.randomUUID().toString(); } }
我们想尽量在不改变已有类实现一个日志记录的功能。
使用装饰器模式
import com.chen.DataService; public class LogDecorator implements DataService { private DataService delegate; public LogDecorator(DataService delegate) { this.delegate = delegate; } public String a(int i) { System.out.println("a is called,parameter is:"+i); String result = delegate.a(i); System.out.println("a is finished,result is:"+result); return result; } public String b(int i) { System.out.println("b is called,parameter is:"+i); String result = delegate.b(i); System.out.println("b is finished,result is:"+result); return result; } }
我们新建了一个类,它也继承了DataService,在它内部有一个成员变量delegate,我们把真正地任务委托给它做,这个类在它执行的前后进行其他的工作。
在使用时,我们使用这个类,并将真的实现类传递给它。
DataService dataService =new LogDecorator(new DataServiceImpl()); System.out.println(dataService.a(1)); System.out.println(dataService.b(2));
这样我们可以做到AOP的功能。
JDK动态代理
装饰器模式可以部分实现AOP的功能,但是每次都要手动新建一个代理对象,使用JDK提供的动态代理可一个简化这个步骤。
使用动态代理时,首先需要一个中介类实现InvocationHandler,它定义了我们对所代理对象的操作。
import com.chen.DataService; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.util.Arrays; public class LogProxy implements InvocationHandler { private DataService delegate; public LogProxy(DataService delegate) { this.delegate = delegate; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println(method.getName()+" is invoked and args is "+ Arrays.toString(args)); Object returnValue = method.invoke(delegate, args); System.out.println(method.getName()+" is finished:"+ returnValue); return returnValue; } }
可以看到,中介类持有一个委托类对象引用,在invoke方法中调用了委托类对象的相应方法,和之前的有点类似。
动态生成代理对象如下:
DataService service = new DataServiceImpl(); DataService proxyInstance = (DataService) Proxy.newProxyInstance(service.getClass().getClassLoader(), new Class[]{DataService.class}, new LogProxy(service));
这样就创建了一个代理的对象。
使用Cglib动态生成代理对象
Spring AOP就是基于动态代理的,如果要代理的对象,实现了某个接口,那么Spring AOP会使用JDK Proxy,去创建代理对象,而对于没有实现接口的对象,就无法使用JDK Proxy去进行代理了,这时候Spring AOP会使用Cglib,生成一个被代理对象的子类,来作为代理。
和动态代理类似,首先定义一个中介类表示对增强的对象进行的操作,继承了MethodInterceptor。
public static class LogInterceptor implements MethodInterceptor { private DataServiceImpl dataService; public LogInterceptor() { } public LogInterceptor(DataServiceImpl dataService) { this.dataService = dataService; } public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println(method.getName() + " is invoked:" + Arrays.toString(objects)); Object result = method.invoke(dataService, objects); System.out.println("invoke finished:" + result); return result; } }
在使用时对类进行增强
DataServiceImpl serviceImpl = new DataServiceImpl(); Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(DataServiceImpl.class); enhancer.setCallback(new LogInterceptor(serviceImpl)); DataServiceImpl enhancedService = (DataServiceImpl) enhancer.create();
这样就得到了一个增强后的类。
Spring中使用AOP
Spring提供了极为方便的AOP支持,这里以SpringBoot为例简单介绍一下。
首先引入AOP相关的包
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
新建切面类,定义切点和通知
import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; @Aspect @Component public class LogAspect { @Before("execution(* com.example.blog.controller.*.*(..))") public void log() { System.out.println("log "); } }
这就完成了,非常的方便。