深度解析java动态代理

何为代理模式(Proxy Pattern)

程序设计中的一种设计模式
角色:抽象角色;代理角色;真实角色
优点:中介隔离;职责清晰;高可扩展;开闭原则
分类:静态代理(Static Proxy);动态代理(Dynamic Proxy)

静态代理(Static Proxy)

优点:简单
缺点:需要创建太多代理类,工作量太大,接口变化,代理类也要修改。
类图
Subject

package com.zlj.test.proxy.statics;

public interface Subject {

    boolean takeCar();
}

RealSubject

package com.zlj.test.proxy.statics;

import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;

public class RealSubject implements Subject {

    @Override
    public boolean takeCar() {
        try {
            TimeUnit.MILLISECONDS.sleep(ThreadLocalRandom.current().nextLong(100l, 200l));
            if (ThreadLocalRandom.current().nextBoolean()) {
                System.out.println("take a fast car");
                return true;
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return false;
    }
}

ProxySubject

package com.zlj.test.proxy.statics;

import java.time.LocalDateTime;

public class ProxySubject implements Subject {

    private Subject subject;

    public ProxySubject(Subject subject) {
        this.subject = subject;
    }

    @Override
    public boolean takeCar() {
        LocalDateTime start = LocalDateTime.now();
        System.out.println(String.format("take car start:%s", start));
        boolean result = subject.takeCar();
        LocalDateTime end = LocalDateTime.now();
        System.out.println(String.format("take car end: %s, result: %b ", end, result));
        return result;
    }
}

Client

package com.zlj.test.proxy.statics;

public class Client {
    public static void main(String[] args) {
        Subject real = new RealSubject();
        Subject proxy = new ProxySubject(real);
        proxy.takeCar();
    }
}

JDK 动态代理(JDK Dynamic Proxy)

JDK动态代理基于Java反射机制实现,主要涉及一下两个类:
java.lang.reflect.InvocationHandler
java.lang.reflect.Proxy

InvocationHandler是一个接口,通过实现这个接口定义一个横切的逻辑!然后通过反射机制调用目标类的方法,这样就能动态的把非业务逻辑和业务逻辑动态的拼接在一起!
Proxy则利用InvocationHandler创建代理实例,来间接的调用代理的方法!
注:JDK 动态代理需要基于接口实现,被代理的对象必须有接口。

package com.zlj.test.proxy.jdk;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class JDKDynamicProxy implements InvocationHandler {

    private Object delegate;

    public JDKDynamicProxy(Object delegate) {
        this.delegate = delegate;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("proxy start");
        Object result = method.invoke(delegate, args);
        System.out.println("proxy end");
        return result;
    }

    public Object proxy(Class clazz) {
        return Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, this);
    }
}

Proxy.newProxyInstance JDK1.8内部实现
在这里插入图片描述
@CallSensitive 注解,参考https://blog.csdn.net/HEL_WOR/article/details/50199797

  1. SecurityManager
    每个Java应用都可以有自己的安全管理器,它是防范恶意攻击的主要安全卫士。
    自己写应用,一般不会开启,此处校验不会执行。
    https://nicky-chen.github.io/2018/07/13/java-securitymanager/
    checkProxyAccess 检查创建一个proxy class 需要的权限。
    Reflection.getCallerClass()返回调用者的class
    判断 CallerClass 的ClassLoader 和 interface的ClassLoader
    判断interface 的package 权限
  2. Class<?> cl = getProxyClass0(loader, intfs) 获取代理类
    private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        ......
        return proxyClassCache.get(loader, interfaces);
    }
    public V get(K key, P parameter) {
        Objects.requireNonNull(parameter);
        expungeStaleEntries();
        Object cacheKey = CacheKey.valueOf(key, refQueue);
        // lazily install the 2nd level valuesMap for the particular cacheKey
        ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
        ......
        Supplier<V> supplier = valuesMap.get(subKey);
        Factory factory = null;
        while (true) {
            if (supplier != null) {
                // supplier might be a Factory or a CacheValue<V> instance
                V value = supplier.get();
                if (value != null) {
                    return value;
                }
            }
            if (factory == null) {
                factory = new Factory(key, parameter, subKey, valuesMap);
            }
            ......
             supplier = factory;
            ......
        }
    }
     /**
    * Proxy 类
    * a cache of proxy classes
    */
    private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
        proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
    
    private static final class ProxyClassFactory
        implements BiFunction<ClassLoader, Class<?>[], Class<?>>
    {
        // prefix for all proxy class names
        private static final String proxyClassNamePrefix = "$Proxy";
    
        // next number to use for generation of unique proxy class names
        private static final AtomicLong nextUniqueNumber = new AtomicLong();
    
        @Override
        public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
           ......
            String proxyPkg = null;     // package to define proxy class in
            int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
            for (Class<?> intf : interfaces) {
                int flags = intf.getModifiers();
                if (!Modifier.isPublic(flags)) {
                    accessFlags = Modifier.FINAL;
                    String name = intf.getName();
                    int n = name.lastIndexOf('.');
                    String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                    if (proxyPkg == null) {
                        proxyPkg = pkg;
                    } else if (!pkg.equals(proxyPkg)) {
                        throw new IllegalArgumentException(
                            "non-public interfaces from different packages");
                    }
                }
            }
            if (proxyPkg == null) {
                // if no non-public proxy interfaces, use com.sun.proxy package
                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
            }
            long num = nextUniqueNumber.getAndIncrement();
            String proxyName = proxyPkg + proxyClassNamePrefix + num;
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
            try {
                return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);
            } catch (ClassFormatError e) {
                throw new IllegalArgumentException(e.toString());
            }
        }
    }
    
    先从缓存中获取,缓存中没有则通过ProxyClassFactory生成并缓存。
    代理类的前缀 private static final String proxyClassNamePrefix = “$Proxy”;
    代理类序号 private static final AtomicLong nextUniqueNumber = new AtomicLong();
    生成代理类class文件 class ProxyGenerator.generateProxyClass
    加载并返回代理类 private static native Class<?> defineClass0(…)
  3. checkNewProxyPermission(Reflection.getCallerClass(), cl);
    如果开启SecurityManager 需要验证生成的proxy class 权限。
  4. 生成代理类的构造器,参数为InvocationHandler
    如果代理类的修饰符非public时需要设置 accessible 为 true
    值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。
    值为 false 则指示反射的对象应该实施 Java 语言访问检查。
    实际上setAccessible是启用和禁用访问安全检查的开关,并不是为true就能访问为false就不能访问 。
    由于JDK的安全检查耗时较多.所以通过setAccessible(true)的方式关闭安全检查就可以达到提升反射速度的目的。
    扩展 AccessController https://www.jianshu.com/p/81985bc2bfa3
  5. 根据构造器生成代理类对象
    return cons.newInstance(new Object[]{h});
    h为自己实现的InvocationHandler子类的实例。

CGLIB(Code Generator Library )

CGLIB基于继承来实现代理,他的原理是对代理的目标类生成一个子类,并覆盖其中方法实现增强,因为底层是基于创建被代理类的一个子类,所以它避免了JDK动态代理类的缺陷。
但因为采用的是继承,所以不能对final修饰的类进行代理。final修饰的类不可继承。
CGLIB底层使用了ASM(一个短小精悍的字节码操作框架)来操作字节码生成新的类。
maven 依赖

<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>

主要涉及的类:
net.sf.cglib.proxy.MethodInterceptor
net.sf.cglib.proxy.Enhancer

package com.zlj.test.proxy.cglib;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;

public class CGLIBMain {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Hello.class);
        enhancer.setCallback((MethodInterceptor) (o, method, objects, methodProxy) -> {
            System.out.println("cglib proxy start");
            Object result = methodProxy.invokeSuper(o, objects);
            System.out.println("cglib proxy end");
            return result;
        });
        Hello hello = (Hello) enhancer.create();
        hello.say("zlj");
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值