首先我们先了解下设计模式:设计模式就是告诉我们应该如何写出高效且具有应用性和拓展性的代码
设计模式能够增加系统的健壮性,易于修改和可扩展性,当你进行开发的软件规模比较大的时候,良好的设计模式会给编程带来遍历,让系统更加的稳定。
现在大多框架都使用了很多设计模式。
代理模式:也是一种设计模式,提供了对目标对象的间接访问方式,即通过代理访问目标对象。如在功能实现的基础上增加的额外的功能操作。前拦截,后拦截,以满足自身的业务需求,同时代理模式便于增加目标对象功能的特点也为多人使用。
代理在不修改源码的情况下是的原本不具有某种行为能力的对象具有某种行为能力。
这使用到编程的一个思想:不要随意去修改别人已经写好的代码或者方法。如需修改可以通过代理的方式来扩展该方法。
实现模式:静态代理在使用时需要创建目标对象接口或者父类
1)创建一个接口
2)创建具体的实现类来实现这个接口
3)具体实现类的方法中需要将接口中定义的方法的业务逻辑功能实现
4)代理类实现该接口并调用具体类中的方法,可以为其添加功能等。
5)这样可以在不修改实现类中的方法的前提下,对具体实现类的方法进行扩展。
静态代理案例:模拟一个用户购买U盘的行为。
三者的关系:用户(客户端)— 商家(代理)—厂家(目标类)
商家和厂家都是卖U盘的,它们完成的功能是一致的,都是卖U盘的。
实现步骤:
1.创建一个接口,定义卖U盘的方法,他表示厂家和商家要做的事情。
public interface UsbSell {
//定义方法 参数account:表示一次购买的数量,暂时不用。
//返回值表示一个u盘的价格
float sell(int amount);
//可以定义多个其他的方法
}
2.创建厂家类,实现1步骤的接口。
//目标类:金士顿厂家,不接受用户的单独购买。
public class UsbKingFactory implements UsbSell {
@Override
public float sell(int amount) {
System.out.println("目标方法的调用,UsbKingFactory中的sell");
//一个128G的U盘是85元
return 85.0f;
}
}
3.创建商家,就是代理,也需要实习1步骤中的接口。
//淘宝是一个商家,代理金士顿U盘的销售。
public class TaoBao implements UsbSell {
//声明 商家代理的这个厂家具体是谁
private UsbKingFactory factory = new UsbKingFactory();
@Override
//实现销售U盘的功能
public float sell(int amount) {
//向厂家发送订单,告诉厂家,我买了U盘,厂家发货
float price = factory.sell(amount); //厂家的价格
//商家需要加价,也就是代理要增加价格
price = price + 25; //增强功能,代理类在完成目标类方法调用后,增强了功能。
//在目标类的方法调用后,你做的其他功能,都是增强的意思。
System.out.println("淘宝商家,给你返了一个优惠卷,或者红包");
//商家卖的价格
return price;
}
}
4.创建一个客户端类,调用商家的方法买一个U盘。
public class ShopMain {
public static void main(String[] args) {
//创建代理的商家taobao对象
// TaoBao taoBao = new TaoBao();
// float price = taoBao.sell(1);
// System.out.println("通过淘宝的商家购买的U盘单价:" + price);
WeiShang weiShang = new WeiShang();
//通过代理
float price = weiShang.sell(1);
System.out.println("通过微商购买的价格:"+price);
}
}
案列总结:
1.代理类完成的功能:
1).目标类中方法的调用
2).功能增强
2.问题:
1).当目标类增加了,代理类可能也需要成倍的增加。代理类数量过多。
2).当你的接口中功能增加了,或者修改了,会影响众多的实现类,厂家类,代理类都需要修改。
静态代理总结:
1.可在不修改目标对象的功能的前提下,对目标功能扩展
2.缺点:
1)、一旦接口增加新的方法,目标对象需要维护
2)、只能代理某一接口类型的实例。不能代理任意接口、任意方法的操作,如果代理的主体类有很多方法需要代理,那么代理类需要编写很多的实现方法,且有时在执行Proxy代理的Before、After业务逻辑代码都是相同的,重复代码太多。
那么改如何解决静态代理中的缺点呢?答案是可以使用动态代理的方式。
每个代理类只为一个接口服务。这样程序中必然会产生许多的代理类,而且在编译器就已确定了被代理的对象以及代理类,而动态代理是在运行时,通过反射机制实现动态代理,并且能够代理各种类型的对象。静态代理可以直接编码创建,而动态代理是利用反射机制来抽象出代理类的创建过程。
在静态代理中目标类很多时,可以使用动态代理,避免静态代理的缺点。
动态代理中目标类即使很多,但:
1).代理类数量可以很少
2).当你修改了接口中的方法,不会影响代理类。
动态代理:在程序执行过程中,使用jdk的反射机制,创建代理类的对象,并动态的指定要代理的目标类。换句话说,动态代理是一种创建java对象的能力,让你不用创建具体的代理类,就能创建代理类对象。
在java中要想创建对象
1.创建类文件,java文件编译为class(动态代理直接帮你省略这步)
2.使用构造方法,创建类的对象。
问题:
1.什么是动态代理?
使用jdk的反射机制,创建对象的能力,创建的时代理类的对象,而不用你创建类文件。不用写java文件。动态:在程序执行时,调用jdk提供的方法才能创建类的对象。
其实就是jdk运行期间,动态的创建class字节码并加载到JVM。
动态代理的实现方式常用的有两种:使用JDK代理 通过CGLIB动态代理(了解)
使用Java反射包中的类的类和接口实现动态代理的功能。使用jdk的Proxy实现代理,要求目标类与代理类实现相同的接口。
反射包:java.lang.reflect ,里面有三个类:InvocationHandler ,Method ,Proxy.
jdk动态代理:
1.反射:Method,表示方法。类中的方法。通过Method可以执行某个方法
2.如何实现:
1)处理类实现InvocationHandler接口(调用处理器)表示你的代理要干什么:就一个方法invoke() 表示代理对象要执行的功能代码。你的代理对象要完成的功能就写在invoke()方法中。
处理类完成的功能:(1)调用目标方法,执行目标方法的功能 (2)功能增强,目标方法调用时,增加功能。
public Object invoke(Object proxy, Method method, Object[] args)
Object proxy:jdk创建的代理对象
Method method:目标类中的方法,jdk会提供method对象的
Object[] args:目标类中方法的参数,jdk会提供的。
怎么用:
1.创建类实现InvocationHandler接口
2.重写invoke()方法,把原来静态代理中代理类要完成的功能,写在这里。
(2)**Method类:**表示方法的,确切说就是目标类中的方法。作用是通过Method可以执行某个目标类的方法。Method.invoke(); method.invoke(目标对象,方法的参数)就是用来执行目标方法的。
(3)**Proxy类:**核心的对象,创建代理对象。之前创建对象都是new类的构造方法,现在可以使用Proxy类的方法,代替new的使用。它有个静态方法newInstance()其作用是创建代理对象。
public static Object newProxyInstance(ClassLoader loader,Class>[ ]interfaces,InvocationHandler h)
参数:
1.classloader loader 类加载器:负责向内存中加载对象的。使用反射获取对象的classloader 如类a ,a.getClass().getClassLoader() ,目标对象的类加载器。
2.Class> interface:接口 ,目标对象实现的接口,也是反射获取的。
3.InvocationHandler h: 需要我们自己写的,代理对象要完成的功能。
返回值:就是代理对象
loader:⼀个 ClassLoader 对象,定义了由哪个 ClassLoader 对象来对⽣成的代理对象进⾏加载;
interfaces:⼀个 Interface 对象的数组,表示的是我将要给我需要代理的对象提供⼀组什么接⼝,如果我提供了⼀
组接⼝给它,那么这个代理对象就宣称实现了该接⼝(多态),这样我就能调⽤这组接⼝中的⽅法了
handler:⼀个 InvocationHandler 对象,表示的是当我这个动态代理对象在调⽤⽅法的时候,会关联到哪⼀个
InvocationHandler 对象上。
通过 Proxy.newProxyInstance 创建的代理对象是在 Jvm 运⾏时动态⽣成的⼀个对象,它并不是我们的
InvocationHandler 类型,也不是我们定义的那组接⼝的类型,⽽是在运⾏是动态⽣成的⼀个对象
3.实现动态代理的步骤
1.创建接口,定义目标类要完成的功能
public interface Usesell {
float sell(int amount);
void print();
}
2.创建目标类实现接口
public class UsbKingFactory implements Usesell {
@Override
//目标方法
public float sell(int amount) {
System.out.println("目标类中执行了sell目标方法");
return 85.f;
}
@Override
public void print() {
System.out.println("hello");
}
}
3.创建InvocationHandler 接口的实现类,在invoke方法中完成代理类的功能。 1.调用目标方法 2.增强功能
public class MySellHandler implements InvocationHandler {
private Object target = null;
//动态代理:目标对象是活动的,不是固定的,需要传入进来
//传入的是谁,就给谁创建代理
public MySellHandler(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); //args目标参数
//商家需要加价,也就是代理要增加价格
if(res != null){
Float price = (Float) res;
price = price + 25;
res = price;
}
//在目标对象的方法调用后,你做的其他功能,都是增强的意思
System.out.println("淘宝商家");
return res;
}
}
4.使用proxy类的静态方法(newProxyInstance)来创建代理对象。并把返回值转为接口类型
public class Myshop {
public static void main(String[] args) {
//创建代理对象
//1.创建目标对象
Usesell factory = new UsbKingFactory();
//2.创建InvocationHandler对象
InvocationHandler handler = new MySellHandler(factory); //告诉代理对象要代理哪个对象
//3.创建代理对象
//返回的proxy:就是代理对象
Usesell proxy = (Usesell) Proxy.newProxyInstance(factory.getClass().getClassLoader(),
factory.getClass().getInterfaces(), handler);
//4.通过代理执行方法
float price = proxy.sell(1);
System.out.println("通过动态代理对象,调用方法:" + price);
//这是jdk动态代理创建的对象类型:proxy:com.sun.proxy.$Proxy0
//$Proxy0看到这个就应该知道这是jdk的动态代理
System.out.println("proxy:"+proxy.getClass().getName());
}
}
CGLIB动态代理
cglib是第三方的工具库,创建代理对象。cglib的原理是继承,chlib通过继承目标类,创建它的子类,在子类中重写父类中同名的方法,实现功能的修改。因为是继承,重写方法,所以要求目标类不能是final的,方法也不能是final修饰的。cglib的要求目标比较宽松,只要能继承就可以了。cglib在很多框架中使用,比如mybatis,spring中都有使用的。代理效率要高于jdk代理。



