当前位置:主页 > java教程 > Java动态代理(设计模式)代码详解

Java设计模式-动态代理代码详解

发布:2020-02-26 16:42:09 184


为网友们分享了Java相关的编程文章,网友璩霓云根据主题投稿了本篇教程内容,涉及到java、动态代理、设计模式、Java动态代理(设计模式)代码详解相关内容,已被524网友关注,相关难点技巧可以阅读下方的电子资料。

Java动态代理(设计模式)代码详解

基础:需要具备面向对象设计思想,多态的思想,反射的思想;

Java动态代理机制的出现,使得Java开发人员不用手工编写代理类,只要简单地指定一组接口及委托类对象,便能动态地获得代理类。代理类会负责将所有的方法调用分派到委托对象上反射执行,在分派执行的过程中,开发人员还可以按需调整委托类对象及其功能,这是一套非常灵活有弹性的代理框架。通过阅读本文,读者将会对Java动态代理机制有更加深入的理解。本文首先从Java动态代理的运行机制和特点出发,对其代码进行了分析,推演了动态生成类的内部实现。

代理模式的基本概念和分类

代理模式:为其他对象提供一个代理,来控制对这个对象的访问。代理对象起到中介作用,可以去掉服务或者增加额外的服务,或者引用别人的话:“代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。”

代理模式在开发中的应用场景

远程代理:为不同地理的对象提供局域网代表对象。

虚拟代理:根据需要将资源消耗很大的对象进行延迟,真正需要的时候进行创建。比如网页中的先显示文字再显示图片。

保护代理:控制不同用户的访问权限。比如:只有当客户注册成功之后,才可以进行增删改查等操作。

智能引用代理:提供对目标代理额外的服务。

代理模式的实现方式

使用继承和聚合实现动态代理,哪种更好呢!

public interface Moveable {
	public void move();
}
public class Car implements Moveable{
	@Override 
	  public void move() {
		try {
			Thread.sleep(new Random().nextint(1000));
			System.out.println("……行驶中……");
		}
		catch(InterruptedException e) {
			// TODO Auto-generated catch block 
			e.printStackTrace();
		}
	}
}
public class Car2 extends Car{
	@Override 
	  public void move() 
	  {
		//分离代码,增加业务逻辑 
		long startTime=System.currentTimeMillis();
		System.out.println("汽车开始行驶……");
		super.move();
		long endTime=System.currentTimeMillis();
		System.out.println("汽车结束行驶……时间:"+(endTime-startTime)+"ms");
	}
}

继承方式实现代理

Moveablecar2=newCar2();
car2.move();

聚合方式实现代理

Carcar=newCar();
Moveablem=newCar3(car);
m.move();

总结

使用继承方式不够灵活,当功能叠加的时候,只能臃肿的扩展代理类;
使用聚合的方式,代理之间可以相互传递,灵活的组合代理;

public class CarLogProxy extends Car{
	@Override 
	  public void move() 
	  {
		//分离代码,增加业务逻辑 
		long startTime=System.currentTimeMillis();
		System.out.println("日志开始……");
		super.move();
		long endTime=System.currentTimeMillis();
		System.out.println("日志结束……");
	}
}
public class CarTimeProxy implements Moveable {
	public CarTimeProxy(Car car) 
	  {
		super();
		this.car=car;
	}
	private Carcar;
	@Override 
	  public void move() {
		//分离代码,增加业务逻辑 
		long startTime=System.currentTimeMillis();
		System.out.println("汽车开始行驶……");
		car.move();
		long endTime=System.currentTimeMillis();
		System.out.println("汽车结束行驶……时间:"+(endTime-startTime)+"ms");
	}
}
@Test: 
Car car =new Car();
CarTimeProxy ctp=new CarTimeProxy(car);
CarLogProxy clp=new CarLogProxy(ctp);
clp.move();
//还可以通过接口相互传递代理实例 
CarLogProxy clp1=new CarLogProxy(car);
CarTimeProxy ctp1=new CarTimeProxy(clp1);
ctp1.move();

JDK动态代理和CGlib动态代理

JDK动态代理

代理实现

如果不同的对象要实现相同功能的代理类,应该如何处置?

此时可以试着将其集成在同一个代理类中-----动态代理:实现对不同类/不同方法的代理;

大致过程如下:

Java动态代理(设计模式)代码详解

Java动态代理类位于java.lang.reflect包下,一般主要涉及到一下两个类:

(1)InterfaceInvocationHandler:该接口中仅定义了一个方法Publicobjectinvoke(Objectobj,Methodmethod,Object[]args)

obj:一般是指代理类

method:是被代理的方法

args为该方法的参数数组。

这个抽象的方法在代理类中动态实现。

(2)Proxy:该类即为动态代理类

statixObjectnewProxyInstance(ClassLoaderloader,Class[]interfaces,InvocationHandlerh)

返回甙类类的一个实例,返回后的代理类可以当做被代理类使用(可以使用被代理类在接口中声明过的方法);

实现实例:

@ TimeHandler 
public class TimeHandler   implements InvocationHandler {
	public TimeHandler(Object target) {
		super();
		this.target = target;
	}
	private Objecttarget;
	/* 
  * 参数: 
  * proxy 被代理对象 
  * method 被代理对象的方法 
  * args 方法的参数 
  * 
  * 返回值: 
  * Object 方法返回值 
  */
	@Override 
	  public Object invoke(Object proxy, Method method,Object[] args) 
	    throws Throwable {
		long startTime=System.currentTimeMillis();
		System.out.println("汽车开始行驶……");
		method.invoke(target);
		long endTime=System.currentTimeMillis();
		System.out.println("汽车结束行驶……时间:"+(endTime-startTime)+"ms");
		return null;
	}
}
@被代理类的接口
public interface Moveable {
  public void move();
}
@被代理的类
public class Car implements Moveable{
	@Override 
	  public void move() {
		try {
			Thread.sleep(new Random().nextint(1000));
			System.out.println("……行驶中……");
		}
		catch (InterruptedException e) {
			// TODO Auto-generated catch block 
			e.printStackTrace();
		}
	}
}

@测试

public class Test { 
  /** 
  * JDk动态代理的测试类 
  */ 
  public static void main(String[] args) { 
   Car car=new Car(); 
   InvocationHandler h=new TimeHandler(car); 
   Class<?>cls=car.getClass(); 
   /* 
    * loader 类加载器 
    * interfaces 实现接口 
    * h InvocationHandler 
    */ 
   Moveable m=(Moveable)Proxy.newProxyInstance(cls.getClassLoader(),cls.getInterfaces(),h); 
   m.move(); 
  } 
} 

&&测试结果

Java动态代理(设计模式)代码详解

梳理总结

所为的DynamicProxy是这样一种class:

它是在运行时生成的class,该class需要实现一组interface,使用动态代理类的时候,必须实现InvocationHandler接口。

JDK动态代理的一般步骤

1.创建一个实现接口InvocationHandler的类,它必须实现invoke()

2.创建被代理的类以及接口

3.调用Proxy的静态方法,创建一个代理类

newProxyInstance(ClassLoaderloader,Class[]interfaces,InvocationHandlerh)

4.通过代理调用方法

Java动态代理(设计模式)代码详解

CGlib动态代理的实现

代理实现

@引入cglib-node-2.2.jar包

@CglibProxy拦截类实现接口MethodInterceptor:重写intercept拦截方法

public class CglibProxy implements MethodInterceptor {
	private Enhancerenhancer=new Enhancer();
	public Object getProxy(Class cl) 
	  {
		//设置创建子类的类 
		enhancer.setSuperclass(cl);
		enhancer.setCallback(this);
		return enhancer.create();
	}
	/* 
  * 拦截所有目标类方法的调用 
  * object 目标类的实例 
  * m 目标方法的反射对象 
  * args 方法的参数 
  * proxy 代理类的实例 
  * 
  */
	@Override 
	  public Object intercept(Object obj, Method m,Object[] args,  MethodProxy proxy)throws Throwable 
	{
		System.out.println("日志开始……");
		//代理类调用父类的方法 
		proxy.invokeSuper(obj, args);
		System.out.println("日志结束……");
		return null;
	}
}

@被代理类Train

public class Train { 
  public void move() 
  { 
   System.out.println("火车行驶中……"); 
  } 
} 

@测试类

public class Test { 
  /** 
  * cglibProxy动态代理测试类 
  */ 
  public static void main(String[] args) { 
   CglibProxy proxy=new CglibProxy(); 
   Train t=(Train)proxy.getProxy(Train.class); 
   t.move(); 
  } 
} 

##测试结果:

Java动态代理(设计模式)代码详解

梳理总结

使用CglibProxy实现动态代理的一般步骤

1、创建类实现接口MethodInterceptor,并重写intercept方法

2、创建被代理类

3、调用代理类自定义的方法,得到一个代理实例

4、通过代理实例调用被代理类的需要执行的方法

比较总结

JDK动态代理

1、只能代理实现了接口的类

2、没有实现接口的类不能实现JDK的动态代理

CGlib动态代理

1、针对类来实现代理

2、对执行目标类产生一个子类,通过方法拦截技术拦截所有父类方法的调用。

模拟代理产生步骤

思路:

实现功能:通过Proxy的newProxyInstance返回代理对象

1、声明一段源码(动态产生代理)

2、编译源码(JDKCompilerAPI)产生新的类(代理类)

3、将这个类load到内存当中,产生一个新的对象(代理对象)

4、返回代理对象

完善动态代理实现

首先得到系统编译器,通过编译器得到文件管理者,然后获取文件,然后编译器执行编译任务,完成编译之后,将class文件加载到类加载器中,通过构造方法得到实例,然后调用newInstance()接收一个对象的实例。

(1)拿到编译器JavaCompilercompiler=ToolProvider.getSystemJavaCompiler();

(2)文件管理者StandardJavaFileManagerfileMgr=Compiler.getStandardFileManager(null,null,null);

(3)获取文件Iterableunits=fileMgr.getJavaFileObjects(filename);

(4)编译任务CompilationTaskt=compiler.getTask(null,fileMgr,null,null,null,units);

(5)load到内存

ClassLoadercl=ClassLoader.getSystemClassLoader();

Classc=cl.loadClass(”com.imooc.proxy.$Proxy0”);

(6)通过代理对象的构造器构造实例

Constructorctr=c.getConstructor(infce);

ctr.newInstance(newCar());

-------

上说所说,内部的业务逻辑是硬编码的,如何实现真正的动态代理,动态的指定业务逻辑呢?

1、需要创建一个事务处理器,首先创建一个接口也就是InvocationHandler,为了模拟JDK,这里把接口的名字和JDK事务处理器名称一样,同样写一个方法叫做invoke(),用来表示对某个对象的某个方法进行业务处理,所以需要把某个对象以及对象的方法作为invoke()方法的参数传递进来,invoke(Objectobj,Methodmethod),方法作为参数使用到了java反射,需要把此包引入。这样InvocationHandler接口就完成了。

2、创建事务处理实现类比如说时间代理TimerProxy,实现了InvocationHandler接口,这样结构就成了

——————TimerProxyimplementsInvocationHandler{ 
————————-@override 
————————-voidinvoke(Objectobj,Methodmethod){ 
———————————//业务逻辑<br> 
—————————————method.invoke(目标对象,参数); 
————————————//业务逻辑<br> 
——————————} 
—————————}

需要将目标对象传入,没有参数可以不写参数,创建代理对象的构造方法,初始化目标对象

3、在Proxy类的newProxyInstance()方法中,除了要把目标Class接口作为参数外,还需要把事务处理器InvocationHandler传进去,然后更改创建实例对象中硬编码的部分用事务处理器方法替代即可。难点在于字符串的拼接。

总结

在我们项目中代理模式有自己的实际意义,比如说我们想要调用某个jar包下的某个类,可以在调用这个类之前之后添加一些特殊的业务逻辑,这种方式也叫作AOP面向切面编程。(在不改变原有功能的基础上,添加额外的功能。)

以上就是本文关于Java动态代理(设计模式)代码详解的全部内容,希望对大家有所帮助。感兴趣的朋友可以继续参阅本站其他相关专题,如有不足之处,欢迎留言指出。感谢朋友们对本站的支持!


参考资料

相关文章

  • java泛型的本质(非类型擦除)知识点详解

    发布:2019-07-09

    泛型在java中有很重要的地位,在面向对象编程及各种设计模式中有非常广泛的应用。泛型是参数化类型的应用,操作的数据类型不限定于特定类型,可以根据实际需要设置不同的数据类型,以


  • Java中逆序遍历List集合的实现

    发布:2023-04-24

    本文主要介绍了Java中逆序遍历List集合的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧


  • JAVA快速搭建基本的springboot

    发布:2021-05-28

    本文主要入门者介绍怎么搭建一个基础的springboot环境,本文通过图文并茂的形式给大家介绍从spring boot到spring cloud的完美搭建过程,适用java入门教学,需要的朋友可以参考下


  • javascript模拟鼠标点击事件的实例代码

    发布:2020-06-15

    这篇文章主要介绍了javascript触发模拟鼠标点击事件,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下


  • 关于java线程池重要设置详解

    发布:2019-07-02

    这篇文章主要介绍了关于线程池你不得不知道的一些设置,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧


  • 详解Java如何实现在PDF中插入,替换或删除图像

    发布:2023-03-06

    图文并茂的内容往往让人看起来更加舒服,如果只是文字内容的累加,往往会使读者产生视觉疲劳。搭配精美的文章配图则会使文章内容更加丰富。那我们要如何在PDF中插入、替换或删除图像呢?别担心,今天为大家介绍一种高效便捷的方法


  • Java的构造方法和对象的创建

    发布:2020-02-10

    这篇文章主要介绍了Java编程构造方法与对象的创建详解,具有一定参考价值,需要的朋友可以了解下。


  • 史上最通俗理解的Java死锁代码演示

    发布:2023-01-13

    给网友朋友们带来一篇关于Java的教程,这篇文章主要给大家介绍了关于Java死锁代码演示的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧


网友讨论