Java动态代理初次体验

简介

动态代理,见名思议动态的添加代理,而不是事先写好代理。何为动态,即运行时!

首先我们大概的来了解下Java代码是怎么运行的?

Java代码之所以可以运行要经过编码Java文件、编译成Class文件、加载到JVM、再编译机器码被操作系统识别!

相对于动态代理,还有一种代理实现方式叫静态代理. 在了解动态之前先来看下静态代理。

现在我们实现一个抢占高地的规则、现代战争模式都是先用炮火先往敌方阵地先打个几万发、再用坦克和步兵实施具体的占领!这样能减少更多的人员损失!

静态代理

先定义一个攻击接口

public interface Attack {

    /**
     * 定义一个射击的方法、具体怎么射 那是实现类的工作
     */
    void attackMethod();
}

先模拟下早期攻击方式、只是单纯的人海战术

public class HugeCrowdAttack implements Attack {

    /**
     * 人海战术的精华,就是一起上~~
     */
    public void attackMethod() {
        System.out.println("一起冲~~~~~");
    }
}

这种攻击方式太简单了,人海战术对于现代战争已经不适用了,那么改变下吧、在发起冲的时候,先用炮火打一会!gmd在解放战争的时候经常干的事情!修改代码要保持OOP原则,那么我们就新加个代理类吧,代理HugeCrowAttack.这样可以不破坏原来的实现。

public class GunfireAttach implements Attack {

    private HugeCrowdAttack attack;

    public GunfireAttach(HugeCrowdAttack attack) {
        this.attack = attack;
    }


    @Override
    public void attachMethod() {
        //先发一会炮弹
        gunFire();

        //再冲上去
        attack.attachMethod();
    }


    private void gunFire() {
        System.out.println("发射1万枚喀秋沙~~~~~");
    }
}

Test

@Test
public void test_static_proxy_huge_crow_attach() {
    //代理目标
    HugeCrowdAttack attack = new HugeCrowdAttack();
    //代理
    Attack proxyAttach = new GunfireProxyAttach(attack);

    proxyAttach.attachMethod();
}

相对缺点

这样一个就简单实现了一个静态代理,在原来的基础上添加了新的功能,也就增强方法而已,经过上面的实现方法可以看出来有一些缺点的:

  • 有更多的代理的话,就是写更多的代理类
  • 接口添加方法,所有的代理类也要实现

由此而生出动态代理

动态代理

目前Java动态代理实现大方向分为两大派系、JDK自动的和CGlib实现方式,至于两者有啥不同的,网上太多了,说太多就没意思了!至于谁好谁坏呢?我也不好评价!在Spring源码里创建Bean实例用的倒是CGlib方式!(这里是不是暗示我们呢??)


public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
		implements AutowireCapableBeanFactory {
    //Cglib的策略来生成Bean实例!!!
    private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();
    
    //.....其他代码
    
    protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
    	try {
    		Object beanInstance;
    		final BeanFactory parent = this;
    		if (System.getSecurityManager() != null) {
    			beanInstance = AccessController.doPrivileged((PrivilegedAction<Object>) () ->
    					getInstantiationStrategy().instantiate(mbd, beanName, parent),
    					getAccessControlContext());
    		}
    		else {
    		    //调用
    			beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
    		}
    		BeanWrapper bw = new BeanWrapperImpl(beanInstance);
    		initBeanWrapper(bw);
    		return bw;
    	}
    	catch (Throwable ex) {
    		throw new BeanCreationException(
    				mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);
    	}
    }
    
     //.....其他代码
}

好吧、上次说到静态代理,那么现在说下动态代理!

JDK

使用JDK实现动态代理,之前呢,它有几个小要求!

  • 被代理的类呢,它必须要实现接口!
  • 实代理的类呢方法,他肯定不能private!!!(实现接口,自然也不会是private~~~)
  • 要实现处理器InvocationHandler

来吧!

创建InvocationHandler实现类

public class JDKHugeCrowdAttachInvocationHandler implements InvocationHandler {


    private Attack attack;


    public JDKHugeCrowdAttachInvocationHandler(Attack attack) {
        this.attack = attack;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //先发射
        gunFire();
        
        return method.invoke(this.attack, args);
    }

    private void gunFire() {
        System.out.println("发射1万枚喀秋沙~~~~~");
    }
}

Test

@Test
public void test_JDK_proxy() {
    HugeCrowdAttack crowdAttack = new HugeCrowdAttack();
    JDKHugeCrowdAttachInvocationHandler invocationHandler = new JDKHugeCrowdAttachInvocationHandler(crowdAttack);

     Attack proxyInstance = (Attack)Proxy.newProxyInstance(
            crowdAttack.getClass().getClassLoader(),
            crowdAttack.getClass().getInterfaces(),
            invocationHandler
    );

    proxyInstance.attachMethod();
}

JDK反射包下为我们提供一个创建代理类的Api: Proxy#newProxyInstance

  • 类加载器 classLoader
  • 实现的接口 目标类实现的 interfaces
  • 具体的处理器实现类 hander

从这三个参数大致可以猜测下:
使用反射生成一个代理类,它实现指定的接口方法、实现接口方法、实现为Hander,因为它是在运行时生成的一个字节码文件,所以用ClassLoader加载到内存,从而运行!

CGlib

上一回说到JDK动态代理,经过一个小小的实验,发现它并不是很完美的,为啥呢?
必须实现接口!就这一个理由就很夠吃一壶了!哪有那么多接口的??所以出现了个CGLIB,它可以不用代理那些不用实现接口的类!也不是说Cglib也是完美的,它也有几个小要求!

  • 不能实现被final修饰的类~~或者方法!
  • 要实现一个MethodInterceptor方法拦截器!是不是很类型JDK的invocationHandler接口呢?
  • 引入Cglib依赖!!!
public class CglibHugeCrowdAttachMethodInterceptor implements MethodInterceptor {


    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {

        gunFire();

        return methodProxy.invokeSuper(o, objects);
    }

    private void gunFire() {
        System.out.println("发射1万枚喀秋沙~~~~~");
    }

}

介绍下几个参数:

  • 被代理的对象 o
  • 被代理的方法 method
  • 参数 objects
  • 新生成的代理对应的代理方法

有关Java动态代理就就记录到这里吧、更深入的讨论期待下次吧!

代码在Github

posted @ 2021-09-06 17:52  乌托拉赛文  阅读(56)  评论(0编辑  收藏  举报