动态代理 ---- 框架基础技术

JSK动态代理

Javaweb — 深化一下反射,之后会再看一下线程和并发


java设计模式 — 代理模式: 动态代理jdk实现


JQuery虽然被新的技术取代了,但是通过实例就发现相比原生的JS,JQuery简化了很多;接下来再来看看动态代理— 其实就是之前的反射机制,J2EE中使用过多次了,这里正式来康康

动态代理 — 基于反射机制的;之前使用过多次反射,尤其是几个工具类,第一个就是DBUtil使用了简单的forname方式进行类加载;之后再JSONUtil中使用了Class和Field;和方法一样设置可以access变可获取私有的属性,正常的情况下是获取不到的

代理模式 proxy pattern

proxy

代理就比如中介一样,帮助去实现某些操作,也就是允许客户端通过这个服务与另外一个end system进行非直接的服务,所以说路由器等就相当于是一个代理

从中介来分析: 比如房产中介,中介和代理所做的事情都是一致的,那就是招揽客人; 中介是房源的代理,房源是目标;这个过程就是 【客户 -----> 中介 ------> 房源】,中介是代理,会收费的;尽管如此,还是有很多中介存在;因为中介是专业的、非常的方便;其次就是客户不能独自找到房源,或者找房源十分困难

在实际开发中,也就有了代理的情况,也就是对客户----代理 ---- 目标链式关系的抽象,比如有一个类A,还有一个类C,如果A类的功能需要调用C类,但是C类不允许A调用,所以这个时候可以找一个代理类B,B访问C;A再访问B即可,完成功能 【比如项目的短信验证码的功能,项目是不可能直接发送短信的,短信只有电信运营商才能发送,所以找一个中介关联公司 项目 ------------- 关联公司、中介 ------ 中国移动、电信、联通】

proxy pattern

为其他对象提供一种代理可以控制对这个对象的访问,在某些情况下,一个对象不舍和或者不能直接引用另一个对象,而代理对象可以在客户类和目标对象之间起到中介的作用。使用代理模式,是为了在不修改目标对象的基础上,增强业务逻辑,客户类对目标对象的访问时通过访问代理对象来实现

proxy role

上面介绍了代理模式,那么其作用是什么呢?

  1. 功能增强、易于扩展 --------- 原来比如new Dao(),那么如果使用代理类来执行这个操作,在代理类中所写的其他的代码就是属于功能增强;但是这并没有修改原来的代码【符合开闭原则】
  2. 控制访问,保护对象 ------ 目标类不让客户类直接访问,这样可以对目标类起到保护的作用
  3. 使职责更加明确 ----- 比如在service中应该全部都是业务逻辑,而创建对象不属于业务逻辑,使用代理可以明确分工

代理模式分为静态代理和动态代理,二者的不同?

静态代理 static proxy

  1. 代理类是自己手工实现,自己创建一个java类,来表示代理类
  2. 代理的目标类是确定的
  3. 实现很简单,容易理解

这里来做一个例子来模拟一下代理,比如现在抽象一个关系链条 : 用户到商家位置买雪糕; 商家的雪糕从厂家进的;现在用户想要购买雪糕,实现功能【不能直接从厂家拿雪糕】,那么实现这个功能: 首先需要创建一个接口,定义卖雪糕的方法,展示商家和厂家的做的事情;其次创建厂家和商家类都要实现接口,最后创建客户类,调用商家的方法买一个雪糕

通过代理模式才能进行功能增强,也就是扩展功能;对扩展开发,对修改关闭

package cfeng.proxy;

//表示功能,厂家和商家都要实现这个方法
public interface IcecreamSell {
	//定义方法,售卖雪糕param表示单价
	float sell(int amount);
	//可以定义其他的方法
}

这里就是接口,商家和厂家都会实现这个接口

package cfeng.proxy;

public class CfengFactory implements IcecreamSell {
//厂家不支持用户的单独购买
	@Override
	public float sell(int amount) {
		//简单return即可
		return 85.0f;
	}

}

厂家继承了接口,对接口进行了继承,完成了雪糕的销售

package cfeng.proxy;

public class Merchant implements IcecreamSell {

	//声明商家所代理的生产雪糕厂家
	private IcecreamSell factory = new CfengFactory();
    
	@Override
	public float sell(int amount) {
		//向厂家调用订单,告诉厂家,让其发货
		float pri = factory.sell(amount);
		//商家作为中间商需要加价
		pri += 25;  //这部分代码就是属于功能增强,因为原来只会有上面的代码,这里都是额外的,易于扩展
		//返回给用户价格
		return pri;
	}
}

商家也实现了接口,这里商家完成了两件事情,第一件事情就是调用厂家的方法,第二件事就是进行加价;加价这部分【除了原来的方法的调用之外】,都是属于功能的增强的部分

package cfeng.proxy;

public class Client {
	//从商家购买雪糕;不能直接从
	public static void main(String[] args) {
		Merchant mercha = new Merchant();
		System.out.println(mercha.sell(1));
	}
}

最后就是客户类,客户通过调用商家【代理】的【增强之后的】方法,完成了业务;这里的代理可以有多个,因为可以有多个商家,它们所做的处理不同;所以不同的代理可以完成不同的扩展

因此: 代理的最主要的功能就是 : 目标类的方法的调用【代理是没有直接的目标的功能的】, 功能的增强

静态代理的缺点

  • 静态代理的目标固定,所以如果有很多目标,那就需要很多个代理,这样代码冗杂了

  • 当接口中功能增加了,或者修改了,因为众多的代理实现类,那么修改的工作量大,这样就容易出现问题

dynamic proxy 动态代理

在程序的执行过程中,创建代理对象,动态指定代理目标类;动态代理就是一种创建对象能力,不用创建类,就可以获得对象 ------ 就是反射机制,获得类的字节码,再invoke即可; 动态代理是指代理类对象在程序运行时有JVM根据根舍机制动态生成的,动态代理不需要定义代理类的java源文件,动态获取类,(比如通过对象,通过完整的类名) 动态创建class字节码加载到JVM

动态代理的方式:

  • JDK 动态代理: 使用java反射包中的类和接口实现动态代理的功能 反射包java.lang.reflect : 其中的三个类Method,InvocationHandler、Proxy
  • cglib动态代理 : cglib时第三方工具库,创建代理对象,cglib的原理就是继承,通过继承目标类,创建其子类,在子类中重写超类的方法,实现功能的修改 — 所以父类不能是final的;cglib在很多框架中使用【比如mybatis、springAOP就会使用

反射 – Method

之前分享反射的时候着重分享了几个类,首先就是Class类,承载的是类的字节码;之后还有Method类表示的是类中的方法【公私均可】,Field类表示的是类的属性【公私均可】; 但是私有的一定要设置可访问,setAccess;这是之前最经常使用的

这里可以用反射的方式实现之前的客户类

public class Client {
	//从商家购买雪糕;不能直接从
	public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
		/*
		Merchant mercha = new Merchant();
		System.out.println(mercha.sell(1));
		*/
		//这里不通过创建对象的方式,如何得到上面的结果?反射即可 --- 使用Method
		//获取类的字节码  
		try {
			Class cls = Class.forName("cfeng.proxy.Merchant");
			//通过字节码创建一个对象;通过构造方法创建
			Object obj = cls.getDeclaredConstructor().newInstance();
			//通过类的字节码获取到所有的方法
			Method method = cls.getDeclaredMethod("sell",int.class);
			//执行该方法通过method的invoke方法;第一个参数是对象,第二个是方法执行时的参数值 方法执行后的返回值
			Object o = method.invoke(obj, 1);//o就是函数的返回值
			System.out.println(o);
			
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
}
  • 这里通过类的字节码创建对象,不能直接使用newInstance了,since version 9就过时了,需要先获得构造器,之后再创建对象;
  • 获得类的方法可以通过Class的getDeclaredMethod方法,第一个参数时方法的名称,第二个参数是方法的参数的字节码;
  • 执行方法通过Method的invoke方法,这个方法的第一个参数为该类的一个具体的对象,第二个参数是该方法传入的具体的参数的值;返回值为方法的返回值【invoke 调用】

动态代理步骤

动态代理的目标不是固定的,根据代理的对象,动态创建代理类,这样就避免了静态代理中的类过多的问题,动态代理的实现方式---- 反射; 可以直接使用java.lang.reflect.Proxy; 动态代理的实现步骤如下

jdk动态代理要求目标对象必须要实现接口,没有接口就实现不了动态代理

  1. 编写一个委托类、目标类的接口【定义目标类要完成的功能】
  2. 实现一个真正的委托类【编写目标类】
  3. 创建InvocationHandler接口的实现类,重写invoke方法,完成代理类的功能—调用目标方法、增强功能
  4. 使用Proxy类的newProxyInstance()静态方法生成动态代理的对象 ,并将返回值转为接口类型

动态代理主要依托的reflect包中的三个类: invocationHandler、Method、Proxy

  • invocationHandler【调用处理器】 接口 ------- 就是表明代理要做什么: 这是一个函数式接口,其中就一个方法invoke;invoke方法表示代理对象要执行的功能代码;代理类要完成的功能就卸载invoke代码中【目标方法的调用、功能增强】
 public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
Object proxy : 这是jdk创建的代理对象,无需赋值
Method  method : 目标类中的方法,jdk提供method ---->这里的method代指的就是目标方法
Object[] args : 目标类中方法的参数   -----> 所以说如果有多个参数,就写成一个集合即可 --->目标方法的参数 {int.class,String.class,……}
  • Method类: 表示方法的,也就是通过Method可以执行某个目标类的方法,通过invoke方法执行目标的方法

  • Proxy类: 核心的对象,用于创建代理对象,之前创建对象都是new 类构造方法;现在可以直接使用Proxy类的方法,代替new的使用

静态方法 ---- newProxyInstance()方法   
作用就是创建代理对象,等同于静态代理的new ……

这里可以看一下源码

    @CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h) {
        Objects.requireNonNull(h);

        final Class<?> caller = System.getSecurityManager() == null
                                    ? null
                                    : Reflection.getCallerClass();

       
        Constructor<?> cons = getProxyConstructor(caller, loader, interfaces);

        return newProxyInstance(caller, cons, h);
    }



ClassLoader loader : 类加载器,负责向内存中加载对象 --- 使用反射机制就可以获得对象的classLoader ----比如对于类String ---String.class.getClassLoader()  也就是获得目标的类
    
Class<?>[] interfaces  接口,目标对象实现的接口,也是通过反射获取的
    
InvocationHandler h :也就是之前实现的InvocationHandler接口,完成代理类的功能

通过Proxy类的这个newProxyInstance方法就可以生成一个目标对象的代理对象

动态代理实例

这里就不另外编写例子了,就以刚刚静态代理的买雪糕的例子 ---- 用户不能直接从从某一个具体的工厂买雪糕,那么就需要创建一个代理类商家来进行间接购买,同时商家增强了功能【抬价】 所以这里的核心应该是用户和目标;商家是为了处理这个业务应运而生的类型

按照动态代理的固定步骤,首先要规范目标类【服务类】的行为,面向接口编程,定义一个接口 ----- 定义接口的目的就是因为,客户类想使用的其实就是该接口定义的方法,所以真正要代理的是方法,而不是某一个具体实现类;多态的核心就是抽象,这样就便于扩展而少修改

package cfeng.proxy;

//表示功能,厂家和商家都要实现这个方法
public interface IcecreamSell {
	//定义方法,售卖雪糕param表示单价
	float sell(int amount);
	//可以定义其他的方法
}

这个接口也就是为了完成客户类想要实现的功能;之后定义实现类来实现这个功能接口

package cfeng.proxy;

public class CfengFactory implements IcecreamSell {
//厂家不支持用户的单独购买
	@Override
	public float sell(int amount) {
		//简单return即可
		System.out.println("执行服务接口的具体实现类的实现方法");
		return 85.0f;
	}

}

本来按照静态代理,接下来就要手动去创建针对这个服务类的代理类;但是动态代理接下来就是创建InvocationHandler接口的实现类

这里的参数中,method就代表的是目标方法,args代表的就是目标方法的参数,所以为了执行方法,这里需要执行Method的invoke方法,需要传入目标类型的对象,这里就使用带有参数的构造方法实现传入目标对象

package cfeng.proxy;

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

/**
 * 完成代理类要执行的功能,这里需要注意的是后两个对象就是目标参数,其实就是接口中的方法---代理方法而不是代理具体的实现类
 * 通过构造器获得方法执行的具体的对象【多态】
 */
public class SellHandler implements InvocationHandler {// sell功能的处理器【代理功能】
	private Object obj;

	public SellHandler(Object obj) {
		super();
		this.obj = obj;
	}

	// 完成代理类执行的功能
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		Object result = null; //目标方法的返回值
		// 执行目标方法
		result = method.invoke(obj, args);
		//进行功能增强
		if(result != null) {
			float pri = (float)result;
			pri += 25;
			result = pri;
		}
		System.out.println("你获得2元优惠");
		return result;
	}
}

第四步就是利用Proxy类的静态方法生成代理对象;代理对象的目的也是执行接口中的sell方法,所以类型是接口的类型

package cfeng.proxy;

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

public class Client {
	//从商家购买雪糕;不能直接从
	public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
		//创建需要代理的目标对象
		IcecreamSell fact = new CfengFactory();
		//创建调用处理器对象
		InvocationHandler handler = new SellHandler(fact);
		//创建代理对象  ---- obj.getClass   类.class Class.forName()
		IcecreamSell proxy = (IcecreamSell)Proxy.newProxyInstance(fact.getClass().getClassLoader(), fact.getClass().getInterfaces() , handler);
		//通过代理对象执行方法
		Object o = proxy.sell(1);
		System.out.println(o);
	}
}

单从这里来看,这里的动态代理就是类似于一种别样的重载,比如sell;给出的代理类还是有接口中的方法;JDK的动态代理必须有接口,Proxy的第二个参数就一定是接口

执行服务接口的具体实现类的实现方法      执行目标接口实现类的功能方法
你获得2元优惠                      执行代理对象的功能增强代码
110.0                        执行代理对象的功能增强代码

只能说动态代理和JDBC一样固化,非常明确的几个步骤

首先就是编写功能接口,【多态】实现接口;

之后就是实现invocationHandler接口来封装代理的功能 — 调用原方法、功能增强 ---- 最后就是使用Proxy的静态方法生成代理对象;这个代理对象就是最开始定义的接口类型的;它的目的还是有这个sell功能 ----- 代理可以在不改变原有的功能的前提下,增加新的功能; 代理类和接口实现类都是接口类型的🎄

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值