原文作者:弥诺R
原文地址:http://www.minuor.com/1524750034/article
转载声明:转载请注明原文地址,注意版权维护,谢谢!
提前说说
项目中涉及到的代码我都会上传到码云(gitee)或者github上,提供给大家下载参考,文中就以最简单的方式说明执行过程。源码的地址在文末哦!
代理模式
代理模式分为静态代理和动态代理两种方式,静态代理是在开发的时候就写好代理的过程,并且代理类要和目标类实现同一个接口。而动态代理是代理类通过实现InvocationHandler接口完成,在运行期间动态的构建代理对象,在动态代理的实现过程中还有另一个更为重要的类Proxy,准确的来说,Proxy负责生成代理对象,而InvocationHandler是根据生成的代理对象执行增强内容和目标方法或类。
静态代理
####要点:
1、代理类需要和目标类需要实现同一个接口
2、在代理类中会内置目标类对象
####代码分析
创建一个接口Transformers(变形金刚)
public interface Transformers {
void trans2man();//变形->人
void trans2car();//变形->车
}
Transformers的实现类TransformersImpl,可以理解为擎天柱
擎天柱实现了变形金刚接口,拥有两个功能分别是变形成人、变形成车。
public class TransformersImpl implements Transformers {
@Override
public void trans2man() {
System.out.println("---->transform to man");
}
@Override
public void trans2car() {
System.out.println("---->transform to car");
}
}
代理类TransformersProxy,和TransformersImpl一样都要实现Transformers接口
public class TransformersProxy implements Transformers {
public Transformers transformers;
public void init(Object transformers) { //初始化
this.transformers = (Transformers) transformers;
}
@Override
public void trans2man() {
System.out.println("---->transform to man before");
transformers.trans2man();
}
@Override
public void trans2car() {
System.out.println("---->transform to car before");
transformers.trans2car();
}
}
运行测试方法,测试代理过程
//静态代理方式
@Test
public void staticProxyTest() {
System.out.println("=========static proxy test start=========");
TransformersProxy proxy = new TransformersProxy();
proxy.init(new TransformersImpl());
proxy.trans2man();
System.out.println("~~~~~~~~~华丽分隔线~~~~~~~~~~");
proxy.trans2car();
System.out.println("=========static proxy test end=========");
}
执行结果:
=========static proxy test start=========
---->transform to man before
---->transform to man
~~~~~~~~~华丽分隔线~~~~~~~~~~
---->transform to car before
---->transform to car
=========static proxy test end=========
先执行代理类中的逻辑,再执行目标方法,这样就完成了代理的过程。如果现在又有一个变形金刚大黄蜂实现了Transformers类,它的增强内容和擎天柱是相同,这个时候代理类中的增强代码就可复用,只要在初始化代理对象的时候,传入大黄蜂实现类对象即可。
缺点:
随着项目的迭代升级,代理的目标增多,代理的增强内容变多(可能不同的实现类需要增强的内容不同),代理类也会越来越庞大,对整个维护过程也会变得复杂。
动态代理
动态代理用的很是广泛,如面试必问、项目必用的AOP,心中一阵绞痛,想当年因为这问题也在面试中被虐的体无完肤。具体的AOP源码抛开暂时不看,我们所知道的就是它的重要一个实现机制就是动态代理,那就从最基础的了解动态代理的实现。
代码分析
创建变形金刚接口TransformersDynamic
public interface TransformersDynamic {
void trans2man();//变形->人
void trans2car();//变形->车
}
创建擎天柱TransformersDynamicImpl,实现变形金刚接口
public class TransformersDynamicImpl implements TransformersDynamic {
@Override
public void trans2man() {
System.out.println("---->transform to man");
}
@Override
public void trans2car() {
System.out.println("---->transform to car");
}
}
创建代理类TransformersDynamicProxy,实现InvocationHandler接口
public class TransformersDynamicProxy implements InvocationHandler {
private Object proxyObject;
public TransformersDynamicProxy(Object proxyObject){
this.proxyObject = proxyObject;
}
/**
* 获取代理对象
*/
public Object newProxyInstance() {
return Proxy.newProxyInstance(proxyObject.getClass().getClassLoader(),
proxyObject.getClass().getInterfaces(),
this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("---->dynamic invoke before");
method.invoke(proxyObject, args); //执行目标方法
System.out.println("---->dynamic invoke after");
return null;
}
}
说明:实现InvocationHandler接口后实现invoke接口,这个接口中就定义了这一个接口,源码:
/**
* @author Peter Jones
* @see Proxy
* @since 1.3
*/
public interface InvocationHandler {
/**
* 此处省略100000行注释……
* @see UndeclaredThrowableException
*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
proxy:指我们所代理的真实对象
method:指的是我们所要调用真实对象的某个方法Method对象
args:指调用真实对象某个方法时接受的参数
在newProxyInstance方法内,通过Proxy类生成代理对象,第二个参数Interfaces,是获取当前目标类实现的所有接口。
运行测试方法,测试代理过程
//动态代理方式
@Test
public void dynamicProxyTest() {
System.out.println("=========dynamic proxy test start=========");
TransformersDynamicProxy dynamicProxy = new TransformersDynamicProxy(new TransformersDynamicImpl());
TransformersDynamic proxy = (TransformersDynamic) dynamicProxy.newProxyInstance();
System.out.println("----->" + proxy.getClass());
proxy.trans2man();
System.out.println("~~~~~~~~~华丽分隔线~~~~~~~~~~");
proxy.trans2car();
System.out.println("=========dynamic proxy test end=========");
}
执行结果:
=========dynamic proxy test start=========
----->class com.sun.proxy.$Proxy11
---->dynamic invoke before
---->transform to man
---->dynamic invoke after
~~~~~~~~~华丽分隔线~~~~~~~~~~
---->dynamic invoke before
---->transform to car
---->dynamic invoke after
=========dynamic proxy test end=========
打印的proxy可以看出,这个对象并不是在创建时传入的TransformersDynamicImpl对象,而是通过Proxy生成的动态代理对象。
到这里静态代理和动态代理的最基本原理已经说完了。但是这里还是需要说点其他的。
动态代理的坑
上面动态代理可以看出来,trans2man和trans2car都会目标方法,在执行的时候都会执行before和after,但是下面这个演示,你将看到不一样的结果。
代码的改动
1、将dynamicProxyTest方法改成如下内容:
//动态代理方式
@Test
public void dynamicProxyTest() {
System.out.println("=========dynamic proxy test start=========");
TransformersDynamicProxy dynamicProxy = new TransformersDynamicProxy(new TransformersDynamicImpl());
TransformersDynamic proxy = (TransformersDynamic) dynamicProxy.newProxyInstance();
System.out.println("----->" + proxy.getClass());
proxy.trans2man();
System.out.println("=========dynamic proxy test end=========");
}
2、在TransformersDynamicImpl类的trans2man方法中调用trans2car方法:
@Override
public void trans2man() {
System.out.println("---->transform to man");
trans2car();
}
执行结果:
=========dynamic proxy test start=========
----->class com.sun.proxy.$Proxy11
---->dynamic invoke before
---->transform to man
---->transform to car
---->dynamic invoke after
=========dynamic proxy test end=========
从结果分析可以看出来,在输出transform to car前后少了一对before和after,也就意味着这个时候trans2car没有被增强,为什么呢,trans2car是被增强的啊。
这里需要理解的是,在trans2man中调用trans2car方法前面还隐含着一个调用对象,补全就是this.trans2car(),也就是当前对象调用的trans2car方法,并不是代理对象调用,那就肯定没有增强逻辑的执行了。代理被绕过,没有生效。在这里有这个问题,那么对于Spring AOP的动态代理有没有问题呢?
源码提供
具体的代码是在com.minuor.staticProxy包(静态代理示例)、com.minuor.dynamicProxy包(动态代理示例),测试类在test下com.minuor.MinuorJunitService。
gitHub:https://github.com/minuor/proxy
码云:https://gitee.com/minuor/proxy
文章推荐
项目发布成功、失败,Java项目来实现邮件通知相关人员,实时反馈提醒信息
Spring AOP+反射实现自定义动态配置校验规则,让校验规则飞起来
项目上线,旧数据需要修改,写SQL太麻烦,看Excel配合简单SQL的强大功能
来源:51CTO
作者:Joker_Yang丶
链接:https://blog.51cto.com/13707464/2108329