AOP原理
所谓AOP
就是面向切面编程,与之相对的是OOP
(面向对象编程)。
在OOP
中,如果要把有公共特性的对象集合加上同样的逻辑,常见的方法是给他们创建一个模板模式
的父类,在父类中修改逻辑。这种方式对子类侵入性强,容易出错,同时公共特性数量一多,父类数量也会变多,维护难度很大。
SpringAOP
封装了JDK
和CGLIB
的动态代理实现,同时引入了AspectJ
的编程方式和注解。使得可以使用标准的AOP编程规范来编写代码外,还提供了多种代理方式选择。
AOP应用场景
- 权限
- 缓存
- 上下文传递
- 异常处理
- 事务
- 持久化
- 日志
AOP 代理分类
AOP
实现代理的方式有几种,大的分类是静态代理和动态代理两种。这两种的区别是静态代理的Proxy类在字节码中已经生成,动态代理的Proxy类则是在代码运行过程中,动态生成这个Proxy类。
比较好理解的说法是:静态代理的Proxy类是提前写好的、或是编译时生成的,而动态代理则是运行时动态生成的。
静态代理
静态代理本身又分几种方式,最常见的就是提前写好Proxy类,这种方法需要自己写代理类,如果类的数量很大,维护成本就会很高,应该很少用了。
//为保证真实类和代理类的表现一致,需要同一个基类
public interface BaseNet{
public void visit();}
public class OutNet implements BaseNet {
@override
public void visit(){
System.out.println("net is ok"); }
}
//通过网络代理,可以访问外网
public class NetProxy implements BaseNet{
public OutNet outnet;
@override
public void visit(){
//通过真实对象,实现访问
if (outnet==null){
outnet = new OutNet();
}
outnet.visit();
}
}
字节码织入
这里再介绍一种通过字节码织入实现的静态代理。原理就是在代码编译或载入时,直接修改字节码,把自己的逻辑织入
进去。
他的好处就是在代码编译或载入时,目标类已经被修改成了增强类,在运行时被调用的话,无需再进行增强操作,效率上高于动态代理。
我们写一个简单的LoadTime Weaver
的实现,即在载入类前,将类的二进制码进行修改:
package com.it_laowu;
import java.lang.instrument.ClassFileTransformer;
......
public class MyTransformer implements ClassFileTransformer {
@Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
try {
if (className.contains("OrderController")){
Utils.writeInColor(className);
}
return null;
} catch (Exception ex) {
Utils.writeInColor(ex.getMessage());
}
}
}
上面这个转换器,通过在程序preMain
阶段,使用javaagent
载入:
public class MyAgent {
/**
* args ;-javaagent启动参数的jarpath的值会通过这个参数传递进来
*/
public static void premain(String args, Instrumentation instrumentation) {
Utils.writeInColor("begin premain***************************************************");
ClassFileTransformer transformer = new MyTransformer(args);
// 插入一个转换器,所有转换器都会被调用
instrumentation.addTransformer(transformer);
}
}
打包时,修改MetaInfo
即可。src/main/resources/META-INF/MANIFEST.MF
Manifest-Version: 1.0
Main-Class: com.it_laowu.MyAgent
Premain-Class: com.it_laowu.MyAgent
Agent-Class: com.it_laowu.MyAgent
VSCode
的debug配置,
{
"version": "0.2.0",
"configurations": [
{
"type": "java",
"name": "Debug-Agent",
"request": "launch",
"mainClass": "com.it_laowu.springbootstudy.springbootstudydemo.DemoApplication",
"projectName": "springboot-study-demo",
"vmArgs": "-javaagent:debug_agent-1.0-SNAPSHOT.jar"
}
]
}
运行效果(我的MyTransformer条件稍有区别)如下:
我的效果是,显示CGlib加强后的类(后面动态代理会讲到)。
动态代理
动态代理分为JDK动态代理
和CGLib动态代理
。
JDK动态代理
JDK动态代理基本原理就是:创建一个目标类接口的对象,该对象的接口调用通过封装目标类的反射方法
实现。
代理的方式和前面讲到的静态代理是一样的,都是设计模式中标准的代理。但不需要自己写代理类,而是通过反射实现。
核心方法:
// 核心方法,java.lang.reflect.proxy;
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
public class MyInvokeHandler implements InvocationHandler {
}
动态代理,主要功能就是生成一个实现目标类的接口
的类,同时在这个类中调用目标类的方法
//调用以上方法
public class DynamicProxy{
private Object t;
public DynamicProxy(Object _t) {
t = _t;