AOP原理 动静态代理 CGLIB源代码分析

AOP原理

所谓AOP就是面向切面编程,与之相对的是OOP(面向对象编程)。

OOP中,如果要把有公共特性的对象集合加上同样的逻辑,常见的方法是给他们创建一个模板模式的父类,在父类中修改逻辑。这种方式对子类侵入性强,容易出错,同时公共特性数量一多,父类数量也会变多,维护难度很大。

SpringAOP封装了JDKCGLIB的动态代理实现,同时引入了AspectJ的编程方式和注解。使得可以使用标准的AOP编程规范来编写代码外,还提供了多种代理方式选择。

AOP应用场景

  • 权限
  • 缓存
  • 上下文传递
  • 异常处理
  • 事务
  • 持久化
  • 日志

AOP 代理分类

AOP实现代理的方式有几种,大的分类是静态代理和动态代理两种。这两种的区别是静态代理的Proxy类在字节码中已经生成,动态代理的Proxy类则是在代码运行过程中,动态生成这个Proxy类。

比较好理解的说法是:静态代理的Proxy类是提前写好的、或是编译时生成的,而动态代理则是运行时动态生成的。

静态代理

静态代理本身又分几种方式,最常见的就是提前写好Proxy类,这种方法需要自己写代理类,如果类的数量很大,维护成本就会很高,应该很少用了。

参考 设计模式 GOF23 结构型 # 代理模式

//为保证真实类和代理类的表现一致,需要同一个基类
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条件稍有区别)如下:

javaagent

我的效果是,显示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;
    
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值