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
本文来自博客园,作者:乌托拉赛文,转载请注明原文链接:https://www.cnblogs.com/m78-seven/articles/15234905.html