温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

web中动态代理模式是什么

发布时间:2021-11-17 10:31:54 来源:亿速云 阅读:103 作者:iii 栏目:大数据

本篇内容介绍了“web中动态代理模式是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

一、什么是代理模式

代理模式的定义:代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是我们生活中常见的中介。

web中动态代理模式是什么

代理模式的分类:代理模式分为静态代理和动态代理

二、静态代理

以简单的事务处理为例

interface
public interface UserDao{
     void save();
}
实现类
public class UserDaoImpl implements UserDao{
      public void save(){//保存}
}
事务处理代理类
public class TransactionHandler implements UserDao{
     private UserDaoImpl userDao;//目标代理对象
     public TransactionHandler(UserDao userDao){
         this.useDao = userDao;
     }
    
     public void save(){
          //开启事务
          userDao.save();
         //结束事务
     }
}

静态代理很简单,但是有一个缺点,如果要对很多类、方法进行代理只能一个一个写多个代理类,无法做到代码重用的目的

三、动态代理

以JDK动态代理实现为例

//事务处理代理类
public class TransactionHandler implements InvocationHandler{
     private Object target;//目标代理对象
     public TransactionHandler(Object target){
         this.target= target;
     }
     
     @Override
     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
         //开启事务
         Object result = method.invoke(target,args);
         //结束事务
         return result
     }
}
//调用方法
public class Main {
    public static void main(String[] args) {
        Object target = new UserDaoImpl();
        TransactionHandler handler = new TransactionHandler(target);
        UserDao userDao = (UserDao)Proxy.newProxyInstance(
           target.getClass().getClassLoader(),
           target.getClass().getInterfaces(),
           handler);
        userDao.save();
    }

在这里有两个疑问,我们在后面慢慢解开它们的面纱

1.如何生成代理类?2.如何执行代理方法?

四、JDK动态代理原理

我们先看Proxy.newProxyInstance方法原码

public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,
                                                               InvocationHandler h) throws IllegalArgumentException {
.....
Class<?> cl = getProxyClass0(loader, intfs);//获取或生成代理类
....
final Constructor<?> cons = cl.getConstructor(constructorParams);//得到参数类型是InvocationHandler.class构造函数
....
return cons.newInstance(new Object[]{h});//生成代理实例
....
}

这个方法主要就做三件事

1.获取或生成代理类

2.得到参数类型是InvocationHandler.class构造函数

3.生成代理实例

我们现在来看看它是如何生成代理类的。看getProxyClass0源码

private static Class<?> getProxyClass0(ClassLoader loader,Class<?>... interfaces) {
....
   return proxyClassCache.get(loader, interfaces);//proxyClassCache->WeakCache
}
public V get(K key, P parameter) {
....
    Object cacheKey = CacheKey.valueOf(key, refQueue);//将ClassLoader包装成CacheKey, 作为一级缓存的key
    ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);//获得classload缓存
    if (valuesMap == null) {
         ConcurrentMap<Object, Supplier<V>> oldValuesMap = map.putIfAbsent(cacheKey, 
                 valuesMap = new ConcurrentHashMap<>());//以CAS方式放入, 如果不存在则put,否则返回旧值
         //问题:为什么要用二级缓存?为什么要map.putIfAbsent?
    }
    Supplier<V> supplier = valuesMap.get(subKey);
    while (true) {
        if (supplier != null) {
            V value = supplier.get();//得到代理类
            if (value != null) {
                 return value;//取到了直接返回
            }
        }
        //如果为null,生成代理类工厂
        if (factory == null) {
            factory = new Factory(key, parameter, subKey, valuesMap);
        }
        if (supplier == null) {
            supplier = valuesMap.putIfAbsent(subKey, factory);
            if (supplier == null) {
                supplier = factory;//如果没有旧值,将新值赋给引用对象,循环退出
            }//如果替换失败,说明已经有值,重新循环
        }else{//其它线程修改了值, 那么就将原先的值替换
             if (valuesMap.replace(subKey, supplier, factory)) {
                   supplier = factory;
             } else {
                   supplier = valuesMap.get(subKey);//替换失败, 继续使用原先的值
             }
        } 
    }
}

这个方法主要就是看缓存里是否存在代理工厂类,如果存在直接调用get()返回,这里缓存用的是WeakCache,新生代回收时就会被回收,不会占用内存

如果缓存中没有就通过new Factory生成一个代理工厂。这里有一些线程安全方面的处理。

这里返回的是Supperlier.get(),现在看这个方法中做了些什么事情

private final class Factory implements Supplier<V> {
....
    public synchronized V get() {
        Supplier<V> supplier = valuesMap.get(subKey);
        if (supplier != this) {//在这里验证supplier是否是Factory实例本身,因为可能被其它线程修改了
            return null;
        }
        V value = null;
        try{
            value = Objects.requireNonNull(valueFactory.apply(key, parameter));
            //交给ProxyClassFactory去生成代理类
        }finally{
            if (value == null) {//如果生成代理类失败, 就将这个二级缓存删除
                 valuesMap.remove(subKey, this);
            }
        }
       ....
       return value;
    }
}

这个方法比较简单主要是交给valueFactory.apply生成返回,valueFactory是ProxyClassFactory类

我们再看看这个方法里在做什么

public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
.... 
    int accessFlags = Modifier.PUBLIC | Modifier.FINAL;//生成代理类默认是public final
    String proxyName = proxyPkg + proxyClassNamePrefix + num;//包名+前缀+序号
    //用ProxyGenerator来生成字节码
    byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName,interfaces, accessFlags);
    try{
        //根据字节码生成代理类
        return defineClass0(loader, proxyName, proxyClassFile,0, proxyClassFile.length);
    } catch (ClassFormatError e) {....}
}

主要是一些规范定义,然后根据ProxyGenerator.generateProxyClass生成字节码

再这个方法里做了些什么

public static byte[] generateProxyClass(String var0, Class<?>[] var1) {
   return generateProxyClass(var0, var1, 49);
}

public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {
   ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
   final byte[] var4 = var3.generateClassFile();
   ....
   return var4;
}

private byte[] generateClassFile() {
    //首先为代理类生成toString, hashCode, equals等代理方法
    addProxyMethod(hashCodeMethod, Object.class);
    addProxyMethod(equalsMethod, Object.class);
    addProxyMethod(toStringMethod, Object.class);
    //遍历每一个接口的每一个方法, 并且为其生成ProxyMethod对象
    for (int i = 0; i < interfaces.length; i++) {
        Method[] methods = interfaces[i].getMethods();
        for (int j = 0; j < methods.length; j++) {
             addProxyMethod(methods[j], interfaces[i]);
        }
     }
     ....//将类的属性写入bout流中
     return bout.toByteArray();
}

可以看出主要是分多步将被代理类的方法属性等写到字节流中

生成的代理类就如下

public class Proxy0 extends Proxy implements UserDao {
    //第一步, 生成构造器
    protected Proxy0(InvocationHandler h) {
        super(h);
    }
    //第二步, 生成静态域
    private static Method m1;   //hashCode方法
    private static Method m2;   //equals方法
    private static Method m3;   //toString方法
    private static Method m4;   //...
    
    //第三步, 生成代理方法
    @Override
    public int hashCode() {
        try {
            return (int) h.invoke(this, m1, null);
        } catch (Throwable e) {
            throw new UndeclaredThrowableException(e);
        }
    }
    
    @Override
    public boolean equals(Object obj) {
    }
    
    @Override
    public String toString() {
    }

    @Override
    public void save(User user) {
        try {
            //构造参数数组, 如果有多个参数往后面添加就行了
            Object[] args = new Object[] {user};
            h.invoke(this, m4, args);//代理类增强方法
        } catch (Throwable e) {
            throw new UndeclaredThrowableException(e);
        }
    }
    
    //第四步, 生成静态初始化方法
    static {
        try {
            Class c1 = Class.forName(Object.class.getName());
            Class c2 = Class.forName(UserDao.class.getName());    
            m1 = c1.getMethod("hashCode", null);
            m2 = c1.getMethod("equals", new Class[]{Object.class});
            m3 = c1.getMethod("toString", null);
            m4 = c2.getMethod("save", new Class[]{User.class});
            //...
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

五、CGLIB动态代理

先看纯CGLIB是如何使用

//拦截类
public class Interceptor implements MethodInterceptor {
    public Object intercept(Object obj, Method method, Object[] args,    MethodProxy proxy) throws Throwable {
        //invokeSuper调用原方法,invoke对原类实例生效,调用代理方法
        //前置处理
        Object retVal =  proxy.invokeSuper(obj, args);
        //后置处理
        return retVal;
    }
}

public static void main(String[] args) {
    //实例化一个增强器,也就是cglib中的一个class generator
    Enhancer eh = new Enhancer();
    //设置目标类
    eh.setSuperclass(Target.class);
    // 设置拦截对象
    eh.setCallback(new Interceptor());
    // 生成代理类并返回一个实例
    Target t = (Target) eh.create();
    t.method();
}

具体如何生成代理类的源码就不展开了,大体思路和JDK一致,最后生成代理类的方式有所不同。

先看生成的代理类

public class Target$$EnhancerByCGLIB$$788444a0 extends Target implements Factory
{
    ....//equal tostring就不写了,拿一个典型    
    final void CGLIB$g$0()//invokeSuper调用这个方法
    {
      super.g();
    }
    
    public final void g()//invoke调用这个方法
    {
      MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
      if (tmp4_1 == null)
      {
          CGLIB$BIND_CALLBACKS(this);
          tmp4_1 = this.CGLIB$CALLBACK_0;
      }
      if (this.CGLIB$CALLBACK_0 != null) {
          tmp4_1.intercept(this, CGLIB$g$0$Method, CGLIB$emptyArgs, CGLIB$g$0$Proxy);
      }
      else{
          super.g();
      }
    }
}

可以看出和JDK不同的是JDK是implements 原接口,CGLIB是extends原类,而且会生成两个对应的方法,供不同的调用。

CGLIB与JDK动态代理的区别 1、CGLIB代理类不需要实现接口,但是也不能是final型 2、CGLIB回调方法不再通过method.invoke反射来处理,采用效率更高的FastClass机制     FastClass机制就是对一个类的方法建立索引,通过索引来直接调用相应的方法

例如:

//先通过获得方法索引
    public int getIndex(String signature){
        switch(signature.hashCode()){
        case 3078479:
            return 1;
        case 3108270:
            return 2;
        }
        return -1;
    }

    //然后通过索引得到方法
    public Object invoke(int index, Object o, Object[] ol){
        Test t = (Test) o;
        switch(index){
        case 1:
            t.f();
            return null;
        case 2:
            t.g();
            return null;
        }
        return null;
    }

六、SPRING AOP

Spring Aop默认使用JDK动态代理,除非指定使用CGLIB动态代理,但对接口类只能使用JDK动态代理 具体代码在DefaultAopProxyFactory

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
    if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
        Class targetClass = config.getTargetClass();
        if (targetClass == null) {
            throw new AopConfigException("TargetSource cannot determine target class: " +
                    "Either an interface or a target is required for proxy creation.");
        }
        if (targetClass.isInterface()) {
            return new JdkDynamicAopProxy(config);
        }
        if (!cglibAvailable) {
            throw new AopConfigException(
                    "Cannot proxy target class because CGLIB2 is not available. " +
                    "Add CGLIB to the class path or specify proxy interfaces.");
        }
        return CglibProxyFactory.createCglibProxy(config);
    }
    else {
        return new JdkDynamicAopProxy(config);
    }
}

以JdkDynamicAopProxy为例我们来看spring是如何对方法进行有选择性的增强的

1、生成代理类

@Override
public Object getProxy(ClassLoader classLoader) {
	if (logger.isDebugEnabled()) {
		logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
	}
	Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised);
	findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
	return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);//hanler传入的是自己
}

可以看出增强的方法不是我们自己定义的逻辑,而是JdkDynamicAopProxy 那么JdkDynamicAopProxy 必然有一个invoke方法,再看这个方法怎么处理

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
.....
     //获取拦截链
     List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
     if (chain.isEmpty()) {
         //直接调原方法返回
         retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);
    }else{
        invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
        //执行拦截链
        retVal = invocation.proceed();
    }
....
}

“web中动态代理模式是什么”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注亿速云网站,小编将为大家输出更多高质量的实用文章!

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

web
AI