代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
1、什么是代理
当一个对象不能直接使用,可以在客户端和目标对象中间创建一个中介,这个中介就是代理。
2、代理的作用
- 控制访问:代理中,控制是否调用目标对象的方法
- 功能增强:代理类在完成目标类的调用后,附加一些功能,这些额外功能就是功能增强
3、代理的实现方式
1、静态代理:
1. 实现:手写代理类,同时目标类是规定的
2. 优点:易理解,易使用
3. 缺点:会产生大量代理类;接口改变时,影响大量代理类
/*静态代理的创建步骤
1、创建目标类(厂家)与代理类(商家)的接口(封装了厂家与商家共同实现的功能)
public interface Sell{float sell(amount);}
2、创建目标类,实现1中的接口
public class Factory implements Sell{
public float sell(int amount){
return 80.0f;
}
}
3、创建代理类,实现1中接口同时调用目标类中的方法
调用目标类中的方法目的是为了完成用户期望的功能(不向厂家拿货,怎么卖给客户)
public class Merchantimplements Sell{
public float sell(int amount){
Factory f =new Factory();
float price =f.sell(amount);
price += 25;
return price;
}
}
4、创建客户端,调用代理类的方法
Sell m =new Merchant();
float price =m.sell(1);
*/
2、动态代理:
1. 实现:动态代理是创建Java对象的一种能力,创建的代理类是活动的
- JDK动态代理:使用JDK java反射包中的接口和类实现动态代理的功能。反射包:java.lang,reflect,里面有三个类:InvocationHandler、Method、Proxy
- cglib动态代理:cglib是第三方库,通过继承目标类并重写目标类方法,实现功能修改;要求目标不能被final修饰。
2. 优点:不用创建代理类;代理类是活动的,对不同目标类可随时创建代理,可设置的
/*动态代理的创建步骤
1、创建目标类(厂家)实现的接口(封装了厂家实现的功能)
public interface Sell{float sell(amount);}
2、创建目标类,实现1中的接口
调用目标类中的方法目的是为了完成用户期望的功能(不向厂家拿货,怎么卖给客户)
public class Factory implements Sell{
public float sell(int amount){
return 80.0f;
}
}
3、创建InvocationHandler接口的实现类,在invoke方法中写人代理类的功能操作
调用目标类方法
功能增强
public class SellHandler implements InvocationHandler{
private Object obj =null;
//从外界传入代理目标对象
public SellHandler(Object obj){
this.obj =obj;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
Object res =null;
//执行目标类方法
res =method.invoke(obj,args);
//功能增强
if(res!=null){
float price =(float)res;
price += 25;
res =price;
}
return res;
}
}
4、通过Proxy类的静态方法,创建代理类对象,把返回值转变为目标类实现接口的类型
//创建目标类对象(第二步)
Factory f =new Factory();
//创建InvocationHandler接口的实现类(第三步)
InvocationHandler h = new SellHandler(f);
//创建代理对象(第四步)
Sell proxy=(Sell)Proxy.newProxyInstance(
s.getClass().getClassLoader(),
s.getClass().getInterface(),
h);
//最后,调用接口中的方法
float price =proxy.sell(1);
*/
4、java.lang,reflect中的InvocationHandler(接口)、Method、Proxy
1、Method类(表示方法,目标类中的方法)
//Method类中的invoke底层是实现了接口sun.reflect.MethodAccessor中的invoke方法
public interface MethodAccessor {
Object invoke(Object var1, Object[] var2) throws IllegalArgumentException, InvocationTargetException;
}
public void doMethod() throw Exception{
HelloService target =new HelloServiceImpl();
//Method[] method =HelloService.class.getMethods();
//获取目标类中的方法
Method method =HelloService.class.getMethod("方法名",String.class);
/*
invoke是Method类中的一个方法,表示执行这个对象方法
参数:
Object obj 执行的对象
Object...args 方法执行的参数值
返回值:
Object 方法执行后返回值类型(getMethod中使用的方法返回类型)
*/
Object obj =method.invoke(target,"参数");//目标类方法调用
/*等价于静态代理中第三步代理类实现目标类方法
float price = factory.sell(amunot);
factory是目标类
sell是目标类中的方法(重写于接口)
*/
}
2、InvocationHandler接口(调用处理器)
/*InvocationHandler接口下的唯一方法invoke,
表示代理对象要执行的功能代码,代理类想实现的操作写在该方法里面
代理类完成的功能:
调用目标类:执行目标类的方法
功能增强:在目标类调用时增强功能
方法原型:
Object proxy jdk创建的代理对象,jdk自动赋值
Method method 目标类中的方法,jdk提供
Object[] args 目标类中的方法参数,jdk提供
使用步骤:
1、实现InvocationHandler接口
2、重写invoke方法,写入对代理对象实现功能的操作(代理类对目标类的操作)
*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
3、proxy类(核心对象,用于创建代理对象)
/*创建代理对象
使用Proxy类的静态方法:newProxyInstance
等价于静态代理中第三步创建代理类
TaoBao tb =new TaoBao();
newProxyInstance参数列表:
ClassLoader loader 类加载器,负责向内存中加载对象,使用反射拿取对象的ClassLoader类a(a.getClass().getClassLoader(),目标对象的类加载器)
Class<?>[] interfaces 接口,目标类实现的接口,反射获取
InvocationHandler h 调用处理器,编写代理类要完成的功能
return Object 目标对象的代理对象
*/
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) throws IllegalArgumentException{}
5、总结
1、什么是动态代理?其中动态是什么?
- 动态代理:是使用jdk的反射机制去创建对象的能力,创建的是代理类对象,不用你手动创建类文件
- 动态:在程序执行时,调用jdk提供的方法创建代理类对象
2、动态代理执行流程
/*
在下面例子中:
当执行sell方法时,先创建代理对象proxy再执行方法sell,
执行前将要执行的操作:
通过反射获取类加载器
通过反射获取目标对象的接口
要代理的目标对象f通过new获取,创建InvocationHandler实例时传入h
h中的invoke通过jdk一系列操作后获得proxy(创建的代理对象),method(执行的方法),args(方法的参数)
执行h中invoke的操作:
实现目标类方法:method.invoke(目标类对象,args);
增强功能
方法执行完,返回结果
注意:代理对象proxy的类型为com.sun.proxy.$proxy0
*/
Factory f =new Factory();
InvocationHandler h = new SellHandler(f);
Sell proxy=(Sell)Proxy.newProxyInstance(
f.getClass().getClassLoader(),
f.getClass().getInterface(),
h);
float price =proxy.sell(1);
3、动态代理可以做什么
在不改变原理目标方法的前提下,可以在代理中实现增加功能。
假如你用别人给你的一个方法getFun,在Fun.class 中,Fun fun =new Fun(); fun.getFun(); 现在这个方法还可以用但少了功能,这时你就可以以这个class文件为目标对象,创建代理对象,实现功能增强。(jdk动态代理目标类是必需要实现接口的)