目录
首先回顾一下代理
简而言之,就是隐藏自己的身份干自己想干的事
对于正向代理
比如说你去访问美国的网站,但是靠你个人是访问不到的(因为不支持国内用户)。所以你得利用中介(也就是代理服务器),对于美国网站来说,他不知道是你还是代理服务器访问的,所以就ok;了;
好处:隐藏了客户端的IP,保证了用户的安全
对于反向代理:
比如说,你是农村种水果的,你要卖水果,但是你在农村不好卖出去,所以你需要通过中介,来进行出售,对于买水果的我们来说,我们是不知道这个水果的来源的,这就是反向代理;
好处:保护了服务器的安全,让用户不知道服务器的ip地址
还涉及了负载均衡,当用户的请求较大时,可以利用负载均衡,设计多个服务器进行处理;
两者区分:
再回到代理
比如说a要调用c的方法,c不给a来访问,所以我们可以利用代理,创建一个b代理,c让b访问,c访问b来访问c;
根据我们实际情况:
比如说我们发短信,肯定是不能直接发短信的,用的是电信移动的子公司或者关联公司,它们有发短信的功能;
代理模式:
代理模式:
学过设计模式的都知道,因为代理是代理的需求,所以说他需要实现被代理类的方法,当然,代理类可以增加额外的功能;
控制访问:
代理类不让被代理类访问目标对象:例如商家不让用户访问厂家;
静态代理
厂家和商家都需要实现接口方法;
而用户是通过商家来买U盘;
测试
接口:
package 代理模式.静态代理;
/**
* @author diao 2022/3/27
*/
public interface UsbSell {
//返回U盘价格,amount表示一次购买的数量
float sell(int amount);
}
工厂:
package 代理模式.静态代理.厂家;
import 代理模式.静态代理.UsbSell;
/**
* @author diao 2022/3/27
*/
//目标类:金士顿厂家,不接受用户单独购买
public class UsbFactory implements UsbSell {
/**
* 厂家实现卖U盘的方法
* @param amount:U盘数量
* @return
*/
@Override
public float sell(int amount) {
//一个U盘是85元
return 85.0f;
}
}
代理类:商家
作用:调用厂家(目标类)的方法 ,完成方法增强,为了面向用户
package 代理模式.静态代理.商家;
import 代理模式.静态代理.UsbSell;
import 代理模式.静态代理.厂家.UsbFactory;
/**
* @author diao 2022/3/27
*/
//淘宝是一个商家,代理金士顿厂家U盘销售
public class TaoBao implements UsbSell {
//需要声明商家代理的厂家是谁,这里体现出多态(里氏替换原则)
private UsbSell factory=new UsbFactory();
//实现销售U盘
@Override
public float sell(int amount) {
//1.先向厂家发送订单
float price = factory.sell(amount);
//2.商家对用户出售,价格得加,因为是代理类
price+=price;//属于增强功能,代理类在完成目标类方法调用后增强了功能
System.out.println("淘宝商家返回给一个优惠券");
//3.增强后的结果
return price;
}
}
用户也就是测试类
package 代理模式.静态代理;
import 代理模式.静态代理.商家.TaoBao;
/**
* @author diao 2022/3/27
*/
public class ShopMain {
public static void main(String[] args) {
//1.创建代理的商家,也就是taobao对象
TaoBao taoBao = new TaoBao();
float price = taoBao.sell(1);
System.out.println("用户通过淘宝商家购买U盘单价"+price);
}
}
当然我们也可以一个代理类(商家)可以代理多个厂家的商品面向用户
错了,不能说代理多个商家,因为你一个代理类,也实现了卖U盘这个接口,但是这个接口中只能出售一种厂家的U盘
但是你可以自定义一些方法,走私其他厂家的U盘;
代理类:
package 代理模式.静态代理.商家;
import 代理模式.静态代理.UsbSell;
import 代理模式.静态代理.厂家.UsbFactory;
import 代理模式.静态代理.厂家.ZaPaiZi;
/**
* @author diao 2022/3/27
*/
//代理类weishang
public class WeiShang implements UsbSell {
//代理的是金士顿和杂牌子,目标类也就是厂家类
private UsbSell factory=new UsbFactory();
private UsbSell factory2=new ZaPaiZi();
@Override
public float sell(int amount) {
//1.调用目标类方法
float price = factory2.sell(amount);
//2.微商增强目标类的方法
price+=1;
return price;
}
//3.这里再出售另外一种Upan
public float sell2(int amount){
float price = factory.sell(1);
price+=5;
return price;
}
}
用户测试类
package 代理模式.静态代理;
import 代理模式.静态代理.商家.TaoBao;
import 代理模式.静态代理.商家.WeiShang;
/**
* @author diao 2022/3/27
*/
public class ShopMain {
public static void main(String[] args) {
//1.创建代理的商家,也就是taobao对象
TaoBao taoBao = new TaoBao();
float price = taoBao.sell(1);
System.out.println("用户通过淘宝商家购买U盘单价"+price);
System.out.println("------------------------");
WeiShang weiShang = new WeiShang();
float price2 = weiShang.sell(1);
System.out.println("微商出售的杂牌子U盘价格为:"+price2);
float price3 = weiShang.sell2(1);
System.out.println("微商出售的金士顿U盘价格为:"+price3);
}
}
缺点:
1、当目标类(厂家)增加了,代理类可能会成倍的增加,因为正常来说,一个代理类只能代理一个厂家;
2、耦合太高,当接口增加 一个方法时,目标类,代理类都会需要更改;
但是你可以采用适配器模式,增加一个新的接口对于新的方法和对应的实现类,然后再在厂家中增加实现类对象从而增加方法;
(9条消息) 代理模式-装饰器模式-适配器模式_Fairy要carry的博客-CSDN博客
动态代理
目标类(厂家)很多,但是代理类可以很少,当你修改接口方法时,不会影响代理类
代理=增强代码+目标对象
作用:是一种创建Java对象的能力,让你不用创建代理类,直接能够创建代理类对象;
之前创建对象:
1.创建类文件,编译为class文件 2.使用构造方法创建类对象
什么是动态代理:
使用JDK反射机制,创建对象能力,创建的是代理类的对象,不用Java文件了;
mybatis就是就是利用动态代理实现了与之绑定的接口的实现类;
所以说为什么我们在业务层中能够实例化接口的对象,实际上这个接口是已经被Mybatis利用动态代理技术实现了的;
个人认为:一切皆递归,一切皆封装,这里的代理也是,无非是递到目标类(厂家),然后返回,归值,直至返回到用户展现的效果;
区别:
静态代理的目标对象是确定的,容易理解,使用方便,目标类过多,代理类会很多;
动态代理的话,目标对象是不确定的,可以给不同的目标类随时创建代理(利用反射),在运行时生成代理类,方便维护;
总结:我们不写代理类,直接利用代理Class对象,根据他创建实例;
JDK动态代理
介绍:动态代理事先是不知道要代理什么,只有在运行时才确定,并且JDK代理只能代理接口;
使用java反射包中的类和接口实现动态代理的功能;
InvocationHandler,Method,Proxy;
JDK动态代理中必须要有接口
代理类中的这些核心增强方法就放到invoke中进行执行;
java.lang.reflect反射包下,有三个类:InvocationHandler,Method,Proxy。
InvocationHandler:(调用处理器):核心就一个invoke()——>表示代理要干什么
invoke():表示代理对象要执行的功能代码,你的代理类要完成的功能就写到invoke中;
而代理类要完成的功能:
1.调用目标类的目标方法
2.功能增强,在目标方法调用时,增强功能;
package 动态代理;
import 动态代理.impl.HelloServiceImpl;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* @author diao 2022/3/27
*/
/**
* 区分:非反射是通过new对象然后通过这个对象进行调用方法
* 而反射就是通过,得到这个对象的method类对象,根据这个method进行invoke得到值
*/
public class Test {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
// HelloService service=new HelloServiceImpl();
// service.sayHello("张三");
//通过反射机制执行sayHello方法
HelloService target=new HelloServiceImpl();
//获取sayHello名称对应的Method类对象
Method method = HelloService.class.getMethod("sayHello", String.class);
System.out.println(method);
//invoke是Object类,他是Method中的一个方法
//执行target对象的sayHello,为什么是sayHello?,因为method表示的是sayHello这个方法
Object res = method.invoke(target, "李四");
}
}
target对应proxy代理对象 ; “李四”对应方法参数 ;method就代表Method method;
Method我们是通过JDK中的API通过代理类—>字节码文件调用getMethod()方法获得的
代理类中的这些核心增强方法就放到invoke中进行执行;
Method类:表示目标类中的方法
Method.invoke()表示执行目标里的方法;
Proxy类:创建代理对象(也激素是我们要的Class对象),之前创建对象都是new类的构造方法,我们使用Proxy类的方法代替new的使用;
newProxyInstance():创建代理对象,代替new
参数:
1.ClassLoader loader 类加载器,负责向内存中加载对象的:a.getClass().getClassLoader()——>用来指名代理对象使用那个类加载器;
2.Class<?>interfaces:接口,用来指名生成哪个对象的代理对象;用接口指定
3.InvocationHandler h:代理类要完成的功能
个人认为,其实反射很简单就是利用类对象获取我们要的东西,为什么要通过类对象呢?
因为他有instanceKlass的指针,可以从内存中调取Class信息,而Class信息是通过类加载器加载字节码文件产生的,这段时期是运行时;
实现步骤(思路)
实现步骤(代码)
接口
package 代理模式.动态代理;
/**
* @author diao 2022/3/28
*/
//定义目标接口
public interface USB {
float sell(int amount);
String get(String name);
}
目标类
package 代理模式.动态代理.factory;
import 代理模式.动态代理.USB;
/**
* @author diao 2022/3/28
*/
public class USBFactory implements USB {
@Override
public float sell(int amount) {
System.out.println("目标类中,执行sell方法");
return 85.0f;
}
@Override
public String get(String name) {
return "Offer";
}
}
代理方法的实现(实现InvocationHandler)
package 代理模式.动态代理.factory.handler;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* @author diao 2022/3/28
*/
public class MySellHander implements InvocationHandler {
//1.目标对象
private Object target=null;
public MySellHander(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/**target:目标对象,args参数
* 执行目标方法
*/
Object res = method.invoke(target, args);
//进行代理中的增强方法操作
if(res!=null){
Float price= (Float) res;
price+=25;
res=price;
}
return res;
}
}
测试(通过newProxyInstance创建代理对象(需要代理目标的装载器,代理对象挂载的接口(与目标类实现一致)),代理方法,代理对象执行的代理方法));
package 代理模式.动态代理;
import 代理模式.动态代理.factory.USBFactory;
import 代理模式.动态代理.factory.handler.MySellHander;
import java.lang.reflect.Proxy;
/**
* @author diao 2022/3/28
*/
public class MainShop {
public static void main(String[] args) {
//1.先创建目标对象
USBFactory factory = new USBFactory();
//2.然后创建InvocationHandler对象
MySellHander handler = new MySellHander(factory);
//3.创建代理对象,handler表示要完成的功能
USB proxy = (USB) Proxy.newProxyInstance(factory.getClass().getClassLoader(),
factory.getClass().getInterfaces(),
handler);
//proxy:com.sun.proxy.$Proxy0是proxy的类型
System.out.println("proxy:"+proxy.getClass().getName());
//4.通过代理执行代理方法,因为proxy此时是一个代理对象,不会执行USBFactory中的sell方法了
float price = proxy.sell(1);
System.out.println("动态代理"+price);
}
}
补充
在AOP中,我们不是进行切面编程扩展了方法嘛,其实用的也是动态代理,在不改变原有方法(目标类)的基础上,通过动态代理——>实现InvocationHandler以此增强方法,然后通过Proxy对象进行调用;
cglib动态代理
在mybatis和Spring中使用的,所以说 目标类的方法与类声明不能有final关键字,不然它的子类怎么能够重写他父类的方法进而实现功能修改呢?
那么问题来了,为什么cglib要用目标类的子类呢?
因为子类对象你可以理解为代理对象,他是被增强过了的,所以说我们要求目标类必须是可继承状态;