一.什么是代理
代理的本质是一种设计模式
我们以银行转账系统为例:
1.在不适用代理的情况下实现银行转账系统:
我们需要将验证、转账、事后服务三个方法写成一个大方法,这样一来代码的功能量过大,我们需要通过代理来降低代码功能量。
2.通过代理实现银行转账系统:
我们不妨只将转账这个核心功能交给银行自己负责,其他功能交给需要的代理商(比如支付宝、微信等)。这样一来,我们既能降低银行自己本身的工作量,也可以使用户通过代理商进行转账的核心功能,用户通过代理商也无法直接接触到核心功能
二.代理的好处
1.防止用户直接访问核心,带来一些不必要的危机
2.能够对核心方法进行功能的增强
三.代理的应用
代理分为静态代理和动态代理
静态代理是基于jdk实现
动态代理分为基于jdk实现的动态代理和基于CGLB实现的动态代理
四.静态代理
1.静态代理的运行模式
其模式为由核心类(目标类)生成核心对象(目标对象),由代理类生成代理对象,在由代理对象代理核心对象,而核心类和代理类都会实现同一个接口。
接口起到了一个沟通(通知)的作用,即通知代理类所要代理的核心是什么。
2.通过静态代理实现银行转账系统
zhuanzhang接口:
public interface ZhuanZhang {
//定义核心方法(转账)
public void zhuanzhang(String A,String B,Double money);
}
YinHang类:
public class YinHang implements ZhuanZhang{
public void zhuanzhang(String A,String B,Double money){
System.out.println(A+"给"+B+"转了"+money+"元");
}
}
ZhiFuBao类:
public class ZhiFuBao implements ZhuanZhang{
//定义被代理的类---核心类
private YinHang yinHang=new YinHang();
private void yanzheng(String A,String B,Double money){
System.out.println("对A进行了身份验证");
System.out.println("对B进行了身份验证");
System.out.println("对转账金额进行了验证");
}
public void zhuanzhang(String A,String B,Double money){
yanzheng(A,B,money);
yinHang.zhuanzhang(A,B,money);
fuwu();
}
private void fuwu(){
System.out.println("转账完成后进行了服务");
}
}
Test类:
public class Test {
public static void main(String[] args) {
ZhiFuBao zhiFuBao=new ZhiFuBao();
zhiFuBao.zhuanzhang("张三","李四",100.0);
}
}
结果如下:
执行流程如下:
main()方法入栈,创建zhifubao对象,其中包括转账的方法、验证和服务方法。还会创建yinhang对象。该对象中也有转账方法。首先是zhifubao的转账方法入栈,然后调用yinhang的转账方法在这个过程中也完成了业务的增强。
3.静态代理的优点
1.代理对象和目标对象在编译时就确定了关系,所以这种代理方式的安全性较高。
2.代理类可以拥有额外的功能,比如可以在调用目标方法前后增加一些额外的操作。
4.静态代理的缺点
1.用一个代理类代理多个目标类是很难实现的,工作量巨大。
2.一旦目标接口增加方法,代理类也需要修改,不符合开闭原则(对扩展开放,对修改关闭)
举例:
以衣服工厂和鞋子工厂为例:
销售衣服:
Clothes接口:
public interface Clothes {
public void ByClothes(String size);
}
ClothesFactory类:
public class ClothesFactory implements Clothes{
@Override
public void ByClothes(String size) {
System.out.println("定制一件"+size+"的衣服");
}
}
XSD类:
public class XSD implements Clothes{
//代理目标类
private ClothesFactory clothesFactory=new ClothesFactory();
private void fuwu(){
System.out.println("销售店进行一系列服务");
}
@Override
public void ByClothes(String size) {
clothesFactory.ByClothes(size);
fuwu();
}
}
Test类:
public class Test {
public static void main(String[] args) {
//静态代理
XSD xsd=new XSD();
xsd.ByClothes("XXL");
}
继续销售鞋子:
Shoes接口:
public interface Shoes {
public void ByShoes(String size);
}
ShoesFactory类:
public class ShoesFactory implements Shoes{
@Override
public void ByShoes(String size) {
System.out.println("定制一件"+size+"的鞋子");
}
}
XSD类:
public class XSD implements Clothes,Shoes{
//代理目标类
private ClothesFactory clothesFactory=new ClothesFactory();
private ShoesFactory shoesFactory=new ShoesFactory();
private void fuwu(){
System.out.println("销售店进行一系列服务");
}
@Override
public void ByClothes(String size) {
clothesFactory.ByClothes(size);
fuwu();
}
@Override
public void ByShoes(String size) {
shoesFactory.ByShoes(size);
fuwu();
}
}
首先定义一个Clothes接口和一个核心方法ByClothes,在创建一个核心类ClothesFactory并实现核心方法ByClothes,接着创建代理类XSD,实现核心方法,首先第一步代理目标类,创建ClothesFactory对象,然后实现代理类的核心方法,调用核心类对象的核心方法。然后添加一个服务方法。在创建一个Test类测试。
接着定义一个Shoes接口和一个核心方法ByShoes,在创建一个核心类ShoeFactory并实现核心方法Byshoes。接着在代理类继承Shoes接口并 在代理类当中创建ShoeFactory对象,并实现Shoes接口当中的核心方法。
此时我们发现每增加一个目标类,就需要创建新的接口并在代理类当中不断进行扩充。每个代理对象代理的目标太多,模式图如下。
五.动态代理
动态代理是指在程序运行时动态地创建代理类,无需程序员手动编写代理类的源代码。动态代理主要依靠JDK的反射(Reflection)API来实现。Java中提供了两种动态代理机制:JDK动态代理和CGLIB动态代理。
基于上述问题就引出了动态代理。以上述问题为例(衣服工厂、鞋子工厂)。一个代理类可以实现多个的代理对象,每个代理对象只匹配一个目标对象,即代理类动态的生成一个代理对象去匹配一个目标对象。
而对于静态代理来说,哪怕如动态代理一样生成一个新的对象,新的对象依旧包含所有目标对象,无法单独匹配一个对象,比如在上述举例中Test类中创建两个XSD对象,其展示图和内存图如下。
1.动态代理的实现
DTXSD类:
public class DTXSD{
private Object object;
private DTXSD(Object o){
object=o;
}
}
Test类:
public class Test{
public static void main(String[] args){
ClothesFactory clothesFactory=new ClothesFactory();
DTXSD dtxsd1=new DTXSD(clothesFactory);
//也可以写成:DTXSD dtxsd1=new DTXSD(new ClothesFactory());
ShoesFactory shoesFactory=new ShoesFactory();
DTXSD dtxsd2=new DTXSD(shoesFactory);
}
}
流程图:
2.动态代理模式的调用
(1)DTXSD类中写一个getProxyInstance()方法
//动态实现相关接口
//object.getClass():获取目标类的类对象
public Object getProxyInstance(){
return Proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces() ,this);
}
(2)在Test类中调用getProxyInstance()方法
调用getProxyInstance()方法就能获取接口,就能知道目标类中的核心方法是什么
DTXSD dtxsd1=new DTXSD(new ClothesFactory());
dtxsd1.getProxyInstance();
(3)调用该核心方法
DTXSD类中实现InvocationHandler接口,实现一个invoke()方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return null;
}
三个参数 :
(1)Object:jdk创建的代理类,无需赋值
(2)Method:目标类中的方法,jdk提供,无需赋值
(3)Object[]:目标类中的方法的参数,jdk提供,无需赋值
调用核心方法:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
method.invoke(object,args);
return null;
}
(4)知道核心方法了,用接口去接收
Clothes clothe=(Clothes) dtxsd1.getProxyInstance();
Shoes shoes=(Shoes) dtxsd2.getProxyInstance();
为什么用接口进行接收?
因为在整个DTXSD类中,只是理论的知道了核心方法是什么,想要调用只能通过Clothes接口接收,对里面的ByClothes()方法进行调用,因为在整个流程中没有任何一个地方能显性的调用ByClothes()方法。
如果想要调用,只能通过Clothes接口或者ClothesFactory类,但是通过ClothesFactory类调用不行,原因如下
public class Test {
public static void main(String[] args) {
ClothesFactory clothesFactory=new ClothesFactory();
clothesFactory.ByClothes("XXL");
System.out.println("--------------------------------");
DTXSD dtxsd1=new DTXSD(clothesFactory);
Clothes clothes=(Clothes) dtxsd1.getProxyInstance();
clothes.ByClothes("XL");
}
}
第一种调用方法的流程图:
第二种调用方法的流程图:
代码如下:
Clothes接口:
public interface Clothes {
public void ByClothes(String size);
}
ClothesFactory类:
public class ClothesFactory implements Clothes{
@Override
public void ByClothes(String size) {
System.out.println("定制一件"+size+"的衣服");
}
}
Shoes接口:
public interface Shoes {
public void ByShoes(String size);
}
ShoesFactory类:
public class ShoesFactory implements Shoes{
@Override
public void ByShoes(String size) {
System.out.println("定制一件"+size+"的鞋子");
}
}
DTXSD类:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//动态代理类
public class DTXSD implements InvocationHandler {
private Object object;
public DTXSD(Object o){
object=o;
}
public Object getProxyInstance(){
return Proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces() ,this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
method.invoke(object,args);
fuwu();
return null;
}
private void fuwu(){
System.out.println("销售点进行一系列服务");
}
}
Test类:
public class Test {
public static void main(String[] args) {
//动态代理
DTXSD dtxsd1=new DTXSD(new ClothesFactory());
Clothes clothe=(Clothes) dtxsd1.getProxyInstance();//这里是用接口进行接收
clothe.ByClothes("XXL");
DTXSD dtxsd2=new DTXSD(new ShoesFactory());
Shoes shoes=(Shoes) dtxsd2.getProxyInstance();//用接口进行接收
shoes.ByShoes("42");
}
}
输出如下: