代理模式

代理模式

昨天是第一次投简历后,第一次接到了电话面试,然后发现对几个知识点还是挺模糊的,所以打算写几篇博客来加深一下理解

本文内容
  1. 什么是代理模式
  2. 静态代理
  3. 动态代理
  4. 与其他的类似的设计模式的区别
什么是代理模式

先来一段百度的解释:

  • 代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用

就是为一个对象提供一个代理以控制对这个对象的访问,很容易理解,举个例子,我们想要邀请小明表演节目,必须先要通过他的经纪人,再让他的经纪人去和他沟通,在这个流程里面,小明就是被代理对象,经纪人就是他的代理对象,这样的好处是什么呢,好处就在于小明只需要在指定时间,指定地点参加表演,而其他的比如什么时候,什么地点,这些都交给经纪人去处理,所以在代码层面来说,就是可以在被代理对象的基础上,实现功能的扩展和业务的控制

代理模式根据代理类创建的时期,分为静态代理,动态代理

静态代理

静态代理,是指程序运行之前,代理类就已经被程序员写好,这种是最简单的代理模式,举个例子,当我写了一个接口IA,然后我再写一个被代理类A,再写一个PA,然后写main方法,然后运行,这种在运行之前,代理类就已经写好的,就是静态代理,上代码

  1. 先创建一个接口
/**
 * 
 * @author Dong
 *
 */
public interface ApplicationSoftware {
	public void mehtod();
}

  1. 再创建这个接口的子类作为被代理类
/**
 * 
 * @author Dong
 *
 */
public class Software implements ApplicationSoftware {
	
	private String name;
	
	public Software(String name) {
		this.name = name;
	}

	@Override
	public void mehtod() {
		System.out.println("我是" + name);
	}
}


  1. 再创建这个接口的另一个子类,作为代理类
/**
 * 
 * @author Dong
 *
 */
public class SoftwareProxy implements ApplicationSoftware {
	
	private ApplicationSoftware applicationSoftware;
	private String name;
	
	public SoftwareProxy(String name) {
		this.name = name;
	}
	
	@Override
	public void mehtod() {
		if(applicationSoftware == null){
			applicationSoftware = new Software(name);
		}
		System.out.println("我是代理");
		applicationSoftware.mehtod();
	}
}

  1. 编写main方法
/**
 * 
 * @author Dong
 *
 */
public class Test {

	public static void main(String[] args) {
		ApplicationSoftware applicationSoftware = new SoftwareProxy("idea");
		applicationSoftware.mehtod();
	}

}

代码已经很简单了,完全没有需要解释的地方,再展示一下结果
在这里插入图片描述

从代码中可以看出,静态代理可以做到,在符合开闭原则的前提下,实现对代理类的功能扩展,但是缺点是,如果存在多个被代理类,就需要对应的写多个代理类,工作量很大,而且接口一旦更改,那么所有的子类都需要做对应的更改,十分不方便

所以为了解决这些问题,就出现了动态代理

动态代理

动态代理,是指我们不需要再手动的去写代理类,而是让jvm,在程序运行的时候,动态的创建代理对象(注意:不再是创建代理类了,而是直接创建代理对象),他和静态代理相比,就是代理对象可以不实现接口,可能不是很容易理解,等会用代码来解释,在上代码之前,需要知道,实现动态代理的两种方式,利用JDK中自带生成代理类的API,利用Cglib代理

  1. JDK中自带生成代理类的API

JDK实现代理,首先需要我们自己创建动态处理器,然后使用newProxyInstance方法获取代理对象,该方法有三个参数,第一个参数是指定被代理对象所使用的类加载器,获取加载器的方法是固定的,第二个参数是指定被代理对象实现的接口的类型,使用泛型方式确认类型,是一个数组,因为一个类可以实现多个接口嘛,第三个参数是我们自己创建的动态处理器,在执行被代理对象的方法时,会触发动态处理器里面的方法

因为这种方式实现动态代理,是使用了java的反射机制,所以有两个要求,一就是被代理对象必须要实现接口,二就是代理对象必须要指定接口类型,否则无法实现动态代理,下面上代码

首先,还是上面的例子,接口和被代理类不变,我们创建一个动态处理器

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

/**
 * 
 * @author Dong
 *
 */
public class ProxyHandler implements InvocationHandler {

	// 被代理对象
    private Object object;

    public ProxyHandler(Object object) {
        this.object = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("动态代理开始");
        Object result = method.invoke(object, args);
        System.out.println("动态代理结束");
        return result;
    }
}

接下来不再使用我们之前创建的静态代理类,修改main方法

import java.lang.reflect.Proxy;

/**
 * 
 * @author Dong
 *
 */
public class Test {

	public static void main(String[] args) {
		ApplicationSoftware as = (ApplicationSoftware) Proxy.newProxyInstance(Test.class.getClassLoader(),
				new Class[]{ApplicationSoftware.class}, 
				new ProxyHandler(new Software("idea")));
		as.mehtod();
	}

}

结果
在这里插入图片描述

这就是利用JDK中自带生成代理类的API的动态代理了,主要通过java的反射机制,拦截被代理类的方法,然后对拦截的方法进行控制,从而生成代理对象,可以看到,在动态处理器里面,被代理类使用的Object类,这样的话可以使得程序更灵活,可以处理各种被代理类

和静态代理相比,这种方式是不再需要创建多个代理类了,同时也降低了程序的耦合,已经基本上解决了我们的问题,但是美中不足的是,被代理类还是需要实现接口才能使用这个方式,那么还有没有更强大的方式呢,Cglib可以做到

  1. Cglib代理

对于没有实现接口的单一对象,我们要对其实现代理,可以从其子类入手,Cglib就是通过底层字节码,为被代理类创建一个子类,从而实现对被代理类的功能扩展,但是因为使用的继承,所以对final修饰的类,和private修饰的方法,不能进行代理

使用Cglib之前,需要先导包,需要导入

  • asm
  • asm-commons
  • asm-util
  • cglib-nodep

这个在maven上去下载吧,我就不提供了,毕竟用的不多

maven仓库网址:mvnrepository.com

下面上代码
首先修改被代理类

/**
 * 
 * @author Dong
 *
 */
public class Software{
	
	private String name;
	
	public Software(String name) {
		this.name = name;
	}
	
	public Software(){
		
	}

	public void mehtod() {
		System.out.println("我是" + name);
	}

}

然后编写Cglib代理类

import java.lang.reflect.Method;

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

/**
 * 
 * @author Dong
 *
 */
public class CglibProxy implements MethodInterceptor  {

	// 被代理类
	private Object object;

	/**
	 * 获取代理对象
	 * @return
	 */
	public Object getProxyInstance(Object object){
		this.object = object;
		Enhancer en = new Enhancer();
		en.setSuperclass(object.getClass());
		en.setCallback(this);
		return en.create();
	}

	@Override
	public Object intercept(Object object, Method method, Object[] args,
			MethodProxy proxy) throws Throwable {
		System.out.println("动态代理开始");
        Object result = method.invoke(this.object, args);
        System.out.println("动态代理结束");
        return result;
	}

}

修改main方法

/**
 * 
 * @author Dong
 *
 */
public class Test {

	public static void main(String[] args) {
		Software software = (Software) new CglibProxy().getProxyInstance(new Software("idea"));
		software.mehtod();
	}

}

需要注意的地方是,在被代理类里面,我加上了一个无参的构造方法,原因是在return en.create(); 这个地方我没有传递参数,这句话的意思是创建被代理类的子类对象,然后返回,如果父类的构造方法需要参数,那么就需要传递参数,否则会出错,所以为了方便就直接提供一个无参的构造方法

然后对于被代理的拦截处理都是一样的操作,所以就没有再截结果的图了

可以看出,Cglib弥补了使用JDK自带的API的不足的地方,对于没有实现接口的类也可以进行动态代理,而且性能也更高,但是Cglib创建代理对象也要比JDK的更费事,二者各有所长,在Spring-AOP中就是使用了这两种方式结合

与其他的类似的设计模式的区别

再来说一下和别的类似的设计模式的区别,代理模式属于结构型模式,那么在结构型模式里面,适配器模式和装饰者模式是和代理模式最相似的,都是通过一个中间类,来间接访问目标类,但是区别在于

  1. 和适配器模式相比,适配器模式的主要作用就是把,由于接口不兼容而不能一起工作的那些类可以在一起工作,所以他的重点是转换,适配,而代理模式是不能改变所代理类的接口的
  2. 和装饰者模式相比,装饰者模式的主要作用是动态地给一个对象添加一些额外的职责,所以他的重点是添加功能,而代理模式除了添加功能,更多的是为了控制,控制对被代理类的访问

所以对于全部的23种设计模式,有很多的实现方式是很相似的,除了创建型,结构型,行为型三个大类很容易区分,大部分是很相似的,只有根据设计模式的原本作用去区分

总结
  • 对于代理模式的理解算是更上一层楼了,感觉自己以前的理解都只停留在静态代理的层面上,代理模式很好的诠释了开闭原则,在不破坏封装的前提下,实现对功能的扩展与控制
  • 代理模式是Spring-AOP的核心,而动态代理又是代理模式的核心,所以掌握了动态代理模式很重要,当然别的设计模式也都很重要
  • 对于面试经验系列的,还是要持续更新,毕竟每次面试都能发现自己不足的地方,写博客加深印象,争取让自己变得更强
  • 最后推荐两个我查的博客,写的挺好的,有一定的借鉴

https://www.cnblogs.com/cenyu/p/6289209.html
https://www.cnblogs.com/daniels/p/8242592.html

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值