详解java动态代理机制以及使用场景

详解java动态代理机制以及使用场景详解java动态代理机制以及使用场景

大家好,又见面了,我是你们的朋友全栈君。

(1)什么是代理?

大道理上讲代理是一种软件设计模式,目的地希望能做到代码重用。具体上讲,代理这种设计模式是通过不直接访问被代理对象的方式,而访问被代理对象的方法。这个就好比 商户—->明星经纪人(代理)—->明星这种模式。我们可以不通过直接与明星对话的情况下,而通过明星经纪人(代理)与其产生间接对话。

(2)什么情况下使用代理?

(1)设计模式中有一个设计原则是开闭原则,是说对修改关闭对扩展开放,我们在工作中有时会接手很多前人的代码,里面代码逻辑让人摸不着头脑(sometimes the code is really like shit),这时就很难去下手修改代码,那么这时我们就可以通过代理对类进行增强。

(2)我们在使用RPC框架的时候,框架本身并不能提前知道各个业务方要调用哪些接口的哪些方法 。那么这个时候,就可用通过动态代理的方式来建立一个中间人给客户端使用,也方便框架进行搭建逻辑,某种程度上也是客户端代码和框架松耦合的一种表现。

(3)Spring的AOP机制就是采用动态代理的机制来实现切面编程。

(3)静态代理和动态代理

动态代理静态代理的区别,代理涉及到两个关联词代理类和委托类。静态代理一个代理类针对一个委托类!动态代理一个代理类可利用反射机制代理多个委托类

我们根据加载被代理类的时机不同,将代理分为静态代理和动态代理。如果我们在代码编译时就确定了被代理的类是哪一个,那么就可以直接使用静态代理;如果不能确定,那么可以使用类的动态加载机制,在代码运行期间加载被代理的类这就是动态代理,比如RPC框架和Spring AOP机制。

(4)静态代理

我们先创建一个接口,遗憾的是java api代理机制求被代理类必须要实现某个接口,对于静态代理方式代理类也要实现和被代理类相同的接口;对于动态代理代理类则不需要显示的实现被代理类所实现的接口。

/**
 * 顶层接口
 * @author yujie.wang
 *
 */
public interface Person {
	public void sayHello(String content, int age);
	public void sayGoodBye(boolean seeAgin, double time);
}
/**
 * 需要被代理的类 实现了一个接口Person
 * @author yujie.wang
 *
 */
public class Student implements Person{

	@Override
	public void sayHello(String content, int age) {
		// TODO Auto-generated method stub
		System.out.println("student say hello" + content + " "+ age);
	}

	@Override
	public void sayGoodBye(boolean seeAgin, double time) {
		// TODO Auto-generated method stub
		System.out.println("student sayGoodBye " + time + " "+ seeAgin);
	}

}
/**
 * 静态代理,这个代理类也必须要实现和被代理类相同的Person接口
 * @author yujie.wang
 *
 */
public class ProxyTest implements Person{
	
	private Person o;
	
	public ProxyTest(Person o){
		this.o = o;
	}

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		//s为被代理的对象,某些情况下 我们不希望修改已有的代码,我们采用代理来间接访问
		Student s = new Student();
		//创建代理类对象
		ProxyTest proxy = new ProxyTest(s);
		//调用代理类对象的方法
		proxy.sayHello("welcome to java", 20);
		System.out.println("******");
		//调用代理类对象的方法
		proxy.sayGoodBye(true, 100);

	}

	@Override
	public void sayHello(String content, int age) {
		// TODO Auto-generated method stub
		System.out.println("ProxyTest sayHello begin");
		//在代理类的方法中 间接访问被代理对象的方法
		o.sayHello(content, age);
		System.out.println("ProxyTest sayHello end");
	}

	@Override
	public void sayGoodBye(boolean seeAgin, double time) {
		// TODO Auto-generated method stub
		System.out.println("ProxyTest sayHello begin");
		//在代理类的方法中 间接访问被代理对象的方法
		o.sayGoodBye(seeAgin, time);
		System.out.println("ProxyTest sayHello end");
	}

}

测试代码输出:

ProxyTest sayHello begin
student say hellowelcome to java 20
ProxyTest sayHello end
******
ProxyTest sayHello begin
student sayGoodBye 100.0 true
ProxyTest sayHello end

(5)动态代理

关于动态代理模式里面有两种实现,一种是jdk实现,一种是cglib来实现
下面来整jdk来实现动态代理的Java实例。
jdk动态代理模式里面有个拦截器的概念,在jdk中,只要实现了InvocationHandler这个接口的类就是一个拦截器类
还使用了些反射的相关概念。
拦截器的概念不了解没关系,假如写了个请求到action,经过拦截器,然后才会到action。然后继续有之后的操作。
拦截器就像一个过滤网,一层层的过滤,只要满足一定条件,才能继续向后执行。
拦截器的作用:控制目标对象的目标方法的执行。

拦截器的具体操作步骤:
1.引入类:目标类和一些扩展方法相关的类。
2.赋值:调用构造函数给相关对象赋值
3.合并逻辑处理:在invoke方法中把所有的逻辑结合在一起。最终决定目标方法是否被调用。

下面看具体的代码实例:

目标接口类:

 

package com.lxk.designPattern.proxy.dynamicProxy.jdkDynamicProxy;

/**
 * 目标接口:
 * 包含目标方法的声明
 */
public interface TargetInterface {
    /**
     * 目标方法
     */
    void business();
}

 

 

目标类:

package com.lxk.designPattern.proxy.dynamicProxy.jdkDynamicProxy;

/**
 * 被代理的类
 * 目标对象类
 * 实现目标接口.
 * 继而实现目标方法。
 */
public class TargetObject implements TargetInterface {

    /**
     * 目标方法(即目标操作)
     */
    @Override
    public void business() {
        System.out.println("business");
    }

}

拦截器:

 

package com.lxk.designPattern.proxy.dynamicProxy.jdkDynamicProxy;

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

/**
 * 动态代理-拦截器
 * <p>
 * Created by lxk on 2016/11/25
 */
public class MyInterceptor implements InvocationHandler {
    private Object target;//目标类

    public MyInterceptor(Object target) {
        this.target = target;
    }

    /**
     * args 目标方法的参数
     * method 目标方法
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("aaaaa");//切面方法a();
        //。。。
        method.invoke(this.target, args);//调用目标类的目标方法
        //。。。
        System.out.println("bbbbb");//切面方法f();
        return null;
    }
}

具体通过调用代理对象,来调用目标对象的目标方法的具体测试:

 

 

package com.lxk.designPattern.proxy.dynamicProxy.jdkDynamicProxy;

import java.lang.reflect.Proxy;

public class MainTest {
    public static void main(String[] args) {
        //目标对象
        TargetObject target = new TargetObject();
        //拦截器
        MyInterceptor myInterceptor = new MyInterceptor(target);

        /*
         *  Proxy.newProxyInstance参数:
         * 	1、目标类的类加载器
         * 	2、目标类的所有的接口
         *  3、拦截器
         */
        //代理对象,调用系统方法自动生成
        TargetInterface proxyObj = (TargetInterface) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), myInterceptor);
        proxyObj.business();
    }
}

看完代码实例,要对这个动态代理进一步理解,要考虑到以下的问题。
1、代理对象是由谁产生的?

jvm产生的,不像上次的静态代理,我们自己得new个代理对象出来。
2、代理对象实现了什么接口?
实现的接口是目标对象实现的接口。同静态代理模式中代理对象实现的接口。那个继承关系图还是相同的。代理对象和目标对象都实现一个共同的接口。就是这个接口。所以Proxy.newProxyInstance()方法返回的类型就是这个接口类型。
3、代理对象的方法体是什么?
代理对象的方法体中的内容就是拦截器中invoke方法中的内容。所有代理对象的处理逻辑,控制是否执行目标对象的目标方法。都是在这个方法里面处理的。
4、拦截器中的invoke方法中的method参数是在什么时候赋值的?
在客户端,代理对象调用目标方法的时候,此实例中为:proxyObj.business();实际上进入的是拦截器中的invoke方法,这个时候
,拦截器中的invoke方法中的method参数会被赋值。

最后,为啥这个方式叫做jdk动态代理呢?
因为这个动态代理对象时用jdk的相关代码生成的,所以这个叫jdk动态代理
后面的cglib动态代理,就是因为要用到cglib的jar包,所以才叫cglib动态代理

下面是使用cglib实现的动态代理模式的Java代码

cglib:Code Generation Library,是一个强大的,高性能,高质量的Code生成类库,也是一个开源项目

package com.lxk.designPattern.proxy.dynamicProxy.cglbDynamicProxy;

/**
 * 被代理的类
 * 目标对象类
 */
public class TargetObject {

    /**
     * 目标方法(即目标操作)
     */
    public void business() {
        System.out.println("business");
    }

}
package com.lxk.designPattern.proxy.dynamicProxy.cglbDynamicProxy;

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

import java.lang.reflect.Method;

/**
 * 动态代理-拦截器
 * <p>
 * Created by lxk on 2016/11/25
 */
public class MyInterceptor implements MethodInterceptor {
    private Object target;//目标类

    public MyInterceptor(Object target) {
        this.target = target;
    }

    /**
     * 返回代理对象
     * 具体实现,暂时先不追究。
     */
    public Object createProxy() {
        Enhancer enhancer = new Enhancer();
        enhancer.setCallback(this);//回调函数  拦截器
        //设置代理对象的父类,可以看到代理对象是目标对象的子类。所以这个接口类就可以省略了。
        enhancer.setSuperclass(this.target.getClass());
        return enhancer.create();
    }

    /**
     * args 目标方法的参数
     * method 目标方法
     */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        if(true){//不要在意这为什么是恒成立的条件语句,为的是说明一个aop的概念:切入点。
            System.out.println("aaaaa");//切面方法a();
            //。。。
            method.invoke(this.target, objects);//调用目标类的目标方法
            //。。。
            System.out.println("bbbbb");//切面方法f();
        }
        return null;
    }
}
package com.lxk.designPattern.proxy.dynamicProxy.cglbDynamicProxy;

public class MainTest {
    public static void main(String[] args) {
        //目标对象
        TargetObject target = new TargetObject();
        //拦截器
        MyInterceptor myInterceptor = new MyInterceptor(target);
        //代理对象,调用cglib系统方法自动生成
        //注意:代理类是目标类的子类。
        TargetObject proxyObj = (TargetObject) myInterceptor.createProxy();
        proxyObj.business();
    }
}

结果:

aaaaa
business
bbbbb

总结:动态代理的目标对象可以是多种对象,目标对象是以参数的方式传入到动态代理拦截器中的

jdk和cglib的2种区别:

首先从文件数上来说,cglib比jdk实现的少了个接口类。因为cglib返回的代理对象是目标对象的子类。而jdk产生的代理对象和目标对象都实现了一个公共接口。

动态代理:

 

   动态代理分为两种:
      *  jdk的动态代理
         *  代理对象和目标对象实现了共同的接口
         *  拦截器必须实现InvocationHanlder接口

      *  cglib的动态代理
         *  代理对象是目标对象的子类
         *  拦截器必须实现MethodInterceptor接口
         *  hibernate中session.load采用的是cglib实现的

转自:https://blog.csdn.net/u011784767/article/details/78281384

https://blog.csdn.net/qq_27093465/article/details/53351403

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/106063.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • html img 能显示psd吗_psd变成html

    html img 能显示psd吗_psd变成html今日小结psd是指经过Photoshop处理过保存后的图片,其格式为psd。这是清除浮动的最常用,最普遍的方法拿到图片将psd变成html代码的步骤如下:1.样式文件和初始化①可以新建三个文件夹。(css,images,js)然后在css下需要建立三个样式,”index,common(公共),reset(重置)”新手比如我,需要这样写,但是熟练以后就不用了js下需要建立(index)这一个样式。(…

  • 程序员法则xiazai_程序员手册

    程序员法则xiazai_程序员手册CSDN上很火的一帖子,全中国所有程序员都在集体YY(花了好半天时间才知道YY==意淫),http://community.csdn.net/Expert/topic/3881/3881210.xml?temp=.9396173CSDN上的帖子,可以看看人气http://www.javadict.com/profz.htm小说版,没有烦人的跟贴 …

  • springmvc源码下载_idea jar包导入

    springmvc源码下载_idea jar包导入1.首先需要下载源码码云地址:https://gitee.com/mirrors/Spring-Framework.git很快推荐GitHub地址:https://github.com/spring-projects/spring-framework.git不推荐然后打开idea,下载源码2.下载并配置gradle环境下载地址:https://services…

  • 2022保密教育线上培训考试题答案_最新保密法考试题及答案

    2022保密教育线上培训考试题答案_最新保密法考试题及答案卷4单选题1.下列关于涉密计算机使用的说法正确的是()。正确答案:D.涉密计算机及时安装和升级专业“木马”查杀工具2.涉密打印机与涉密计算机之间()。正确答案:D.不能采用无线连接方式3.下列说法正确的是()。正确答案:D.淘汰、报废涉密计算机时应将涉密计算机经过专业消磁处理4.定密责任人在职责范围内承担有关国家秘密()工作。正确答案:D.以上都正确5.涉密人员是指在()、涉及国家秘密的单位涉密岗位工作的人员。正确答案:D.以上都正确6.下列关于预防和查杀“木马

  • 图像双目视觉定位[通俗易懂]

    图像双目视觉定位[通俗易懂]今天与大家分享一下关于图像的双目定位法,对于实际工程有很大参考意义!!顾名思义:双目定位就是用两部相机来定位。双目定位过程中,两部相机在同一平面上,并且光轴互相平行,就像是人的两只眼睛一样,针对物体上某一个或某些特征点,用两部固定于不同位置的相机摄得物体的像,分别获得该点在两部相机像平面上的坐标。只要知道两部相机精确的相对位置,就可用几何的方法得到该特征点在固定一部相机的坐标系中的坐标,即确定…

  • C# OpenFileDialog SaveFileDialog Filter

    C# OpenFileDialog SaveFileDialog Filter那个Filter的格式每次都要忘,很讨厌,记录之: OpenFileDialogofd=newOpenFileDialog();ofd.Filter=”pc信息文件(*.vcf)|*.vcf|所有文件(*.*)|*.*”;if(ofd.ShowDialog()!=System.Windows.Forms.Di

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号