要想明白动态代理,先理解一下什么是代理,怎样"静态代理":
基于反射实现
代理: 代购,中介,换ip,商家 …
留学中介(代码) :帮助美国学校招生,中介是学校的代理,中介代替学校完成招生功能
代理的特点:
1.中介和代理他们做的事情一致
2.中介是学校的代理,学校是目标
3.中介是代理,不能白干活,需要收取费用
4.代理不让你访问到目标
为什么使用代理:
1.中介是专业的,方便
2.家长现在不能自己访问学校,家长没有能力访问学校,或者美国学校不接受个人来访
类似于:
买家–(买东西)—>商家—(代理)—>厂家
卖东西都是商家卖,商家是某个商品的代理,个人卖东西,不会接触到厂家
在开发中的使用: a类调用c类的方法,完成某个功能,但是c不让a调用,a不能调用c的方法
解决:
在a和c之间创建一个b代理 使c让b访问
a—访问—>b—访问—>c
实际案例:登录,注册验证码,验证码是手机短信
发短信项目—访问—>移动子公司/关联公司----访问—>中国移动
代理模式:
代理模式是指,为其他对象提供一种代理以控制这个对象的访问.在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户类和目标对象之间起到中介作用
使用代理模式的作用:
1.功能增强:在原有的功能上,增加了额外的功能,新增加的功能叫做功能增强
2.访问控制:代理类不让访问目标,例如商家不让用户直接访问厂家
实现代理的方式:
1.静态代理:
1)代理类是自己手工实现的,自己创建一个java类,表示代理类
2)同时你所要代理的目标类是确定的
特点: 1)实现简单 2)理解容易
实现:
用户:客户端类
商家:代理,代理某个U盘品牌
厂家:目标类
三者关系:用户(客户端)----商家(代理)----厂家(目标)
商家和厂家都是卖u盘的,他们完成的功能一致:卖U盘
实现步骤:
1.创建一个接口,实现卖U盘方法,表示厂家和商家做的事情
2.创建厂家类,实现1中的接口
3.创建商家,就是代理,也需要实现1中的接口
4.创建客户端类,调用商家的方法买一个U盘
静态代理的实现
静态代理优点:1.实现简单 2.容易理解
静态代理的缺点:(当目标类和代理类很多的时候)
1.当目标类增加了,代理类可能需要成倍增加 (代理类数量过多)
2.当接口中功能增加了,或者修改了,会影响众多的实现类,厂家类,代理类会影响比较多
示例代码:
//表示功能,厂家,商家都要完成的功能
public interface UsbSell {
//定义方法 参数 amount:表示一次购买的数量
//返回值表示一个u盘的价格
float sell(int amount);
//可以写多个别的方法
}
代表一个制造U盘的厂家sx
//目标类:三星厂家,不接受用户的单独购买
public class UsbSamaungFactory implements UsbSell {
/*
一个128G盘价格160元
后期根据amount,实现不同的价格
根据数量不同,返回的价格不同
*/
@Override
public float sell(int amount) {
if(amount>=3&&amount<5){
System.out.println("厂家发货价格为150");
return 150.0f*amount;
}else if (amount>=5&&amount<10){
System.out.println("厂家发货价格为145");
return 145.0f*amount;
}else if(amount>=10&&amount<30){
System.out.println("厂家发货价格为140");
return 140.0f*amount;
}else if(amount>=30&&amount<50){
System.out.println("厂家发货价格为138");
return 138.0f*amount;
}else if (amount>=50&&amount<100){
System.out.println("厂家发货价格为135");
return 135.0f*amount;
}else if(amount>=100){
System.out.println("厂家发货价格为130");
return 130.0f;
}
System.out.println("厂家发货价格为160");
return 160.0f*amount;
}
}
模拟代理:pdd
public class Pdd implements UsbSell {
private UsbSell facotory=new UsbSamaungFactory();
@Override
public float sell(int amount) {
//向厂家发送订单,告诉厂家,买了u盘,厂家发货
float price = facotory.sell(amount);
//亲民dd
System.out.println("pdd出售的单价为"+(price+5));
price=(price+5)*amount;
//返回增加后的价格
return price;
}
}
模拟用户购买U盘
public class shopMan {
public static void main(String[] args) {
Scanner s=new Scanner(System.in);
System.out.println("请输入您要购买的数量:");
int count = s.nextInt();
//创建pdd代理的对象
Pdd pdd=new Pdd();
System.out.println("pdd向你的亲朋好友们砍了一刀");
float ddprice = pdd.sell(count);
System.out.println("通过pdd销售"+count+"个三星u盘总价格为"+ddprice);
}
}
针对静态代理的缺点使用动态代理解决:
在静态代理中目标类很多的时候,可以使用动态代理,避免静态代理的缺点
动态代理中目标类即使很多(很多U盘厂家):
1.代理类数量可以很少
2.当你修改了接口的方法时,不影响代理类(不影响淘宝/pdd)
动态代理:
在程序执行过程中,使用jdk的反射机制,创建代理类对象,并动态的指定要代理的目标类
不用写淘宝/pdd的java文件,jdk使用反射机制创建代理类淘宝/pdd对象,并且动态的获取代理的目标类三星/金士顿…
(商家PDD中new的厂家是动态的,可以是三星也可以是pdd),也就是说动态代理是一种创建java对象的能力,让你不用创建TaoBao类
动态代理就是使用jdk的反射机制创建对象的能力,创建的是代理类的对象,而不是创建的类文件,不用写java文件.
动态:执行时,调用jdk提供的方法才能创建类的对象
动态代理的两种实现方式:
1.JDK动态代理(理解):使用java反射包中的类和接口实现动态代理的功能
反射包 java.lang.reflect 里面有三个类;InvocationHandler,Method,
proxy.
2.CGLIB动态代理(了解):cglib通过继承目标类,创建它的子类,在子类中重写父类的同名方法,实现功能的修改.因为cglib是继承,重写方法,所以要求目标类不能是final的,方法也不能是final的.oglib的要求目标类比较宽松,只要能继承就可以,oglib在很多框架中使用,如mybatis,spring框架中都有使用
1.基于反射:Method 类,表示类中的方法,通过Method可以执行某个方法
2.jdk动态代理的实现:
反射包: java.lang .reflect 里面有三个类: InvocationHandler,Method,Proxy
一:InvocationHandler
invoke():表示代理对象要执行的功能代码,你的代理类要完成的功能就写在invoke()方法中
代理类完成的功能:
1.调用目标方法,执行目标方法中的功能
2.功能增强,在目标方法调用时,增加功能
方法原型:
public Object invoke(Object proxy,Method method,Object[] args)
参数:Object proxy : jdk创建的代理对象,无需赋值
Method method:目标类中的方法,jdk提供method对象的
Object[] args:目标类中方法的参数,jdk提供的
InvocationHandler 接口:表示你的代理要干什么
使用: 1.创建类实现接口InvocationHandler
2.重写invoke()方法,把原来静态代理中的代理类要完成的功能写在invike中
二:Method类:表示方法的,确切的说就是目标类中的方法
作用:通过Method可以执行某个目标类的方法,Method.invoke();
method.invoke(目标对象,方法的参数)
通过method.invoke() 可以执行任意对象的方法,而不需要知道方法的名称,因为method执行的方法是jdk帮你确定的,不需要管理
method.invoke()就是用来执行目标方法的,等同于静态代理中的:
//向厂家发送订单,告诉厂家,买了u盘,厂家发货
float price = facotory.sell(amount);
三:Proxy类:核心类,创建代理对象,之前创建对象都是new类的构造方法() 现在我们是使用Proxy类的方法,代替new的使用
方法: newProxyInstance()
作用:创建代理对象,等同于静态代理中的TaoBao taobao=new TaoBao();
方法原型:
public static Object newProxyInstance(ClassLocader loader,
Class<?>[] interfance,
InvocationHandler h)
参数:
1,classLoader loader 类加载器,负责向内存中加载对象的,使用反射获取对象的ClassLoader
例: 类a a.getClass().getClassLoader() 目标对象的类加载器
2.Class<?>[] interfance:接口,目标对象实现的接口,也是反射获取的
3.InocationHandler h:我们自己写的,代理类要完成的功能
Proxy是代理对象,
实现动态代理的步骤
1.创建接口,定义目标类要完成的功能
2.创建目标类实现接口
3.创建InvocatiionHandler接口的实现类,在invoke方法中完成代理类的功能:
1.调用目标方法
2.增强功能
代码实例:还是上例的接口和厂家(换个变量名),所以没有列出
public class PddShellHandler implements InvocationHandler {
private Object target=null;
public PddShellHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object res=null;
res=method.invoke(target,args);
if(res!=null){
Float pries=(float)res;
pries=pries+5;
res=pries;
}
System.out.println(" pdd给你的亲戚朋友们一人砍了一刀");
return res;
}
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class MainShop {
public static void main(String[] args) {
//使用Proxy创建代理对象
/*
public static Object newProxyInstance(ClassLocader loader,
Class<?>[] interfance,
InvocationHandler h)
*/
//创建一个目标对象(给谁创建代理)
UsbShell factory=new UsbKingFactory();
//创建InvocationHandler对象
InvocationHandler handler=new UsbShellHandler(factory);
//创建代理对象
UsbShell proxy= (UsbShell) Proxy.newProxyInstance(factory.getClass().getClassLoader(),
factory.getClass().getInterfaces(),
handler);
float price = proxy.sell(100 );
System.out.println("通过动态代理对象 调用方法:"+price);
}
}
动态代理的执行原理:
在开发中的作用:
你所在的项目中有一部分功能是其他部门已经开发好的,可以使用,但是这个功能还有缺点,不能满足自己的项目需要自己增加代码,而不去修改原来的文件