设计模式之代理模式

代理模式作用:一保护目标对象,二增强目标对象。

 

设计原则概览
设计原则简称解释说明备注
开闭原则(OCP)Open-Closed Principle,对扩展开放,对修改关闭。 
依赖倒置原则(DIP)Dependence Inversion Principle高层模块不应该依赖底层模块,二者都应该依赖其抽象。也就是说针对接口编程,不要针对实现编程,针对接口编程包括使用接口或抽象类,这样可以使得各个模块彼此独立,降低模块间的耦合性。而且在实现类中尽量不发生直接的依赖关系,依赖关系通过接口或抽象类产生。 
单一职责原则(SRP)Single Responsibility Principle一个类、接口、方法只做一件事。 
接口隔离原则(ISP)Interface Segregation Principle, 尽量保证接口的纯洁性,客户端不应该依赖不需要的接口。胖接口会导致他们的客户程序之间产生不正常的并且有害的耦合关系.当一个客户程序要求该胖接口进行一个改动时,会影响到所有其他的客户程序.因此客户程序应该仅仅依赖他们实际需要调用的方法. 
迪米特法则(LoD)Law of Demeter
又叫做最少知识原则(LKP)Least Knowledge Principle,
又叫最少知道原则,一个类对其所依赖的类知道得越少越好。就是说,如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用,如果其中的一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用。 
里氏替换原则

(LSP)Liskov Substitution Principle,

子类可以扩展父类的功能但不能改变父类原有的功能。一般而言,如果有两个具体类A,B有继承关系,那么一个最简单的修改方案是建立一个抽象类C,然后让类A和B成为抽象类C的子类. 
合成复用原则(CARP)Composite/Aggregate Reuse Principle,尽量使用对象组合、聚合,而不使用继承关系达到代码复用的目的。 

 

 

 

 

 

 

 

 

 

 

 

 

 






 

代理模式

代理模式是代码增强,其实就是在原本逻辑前后增加一些逻辑,而调用者无感知。代理模式属于结构型 模式,有静态代理和动态代理。
 

静态代理和动态的本质区别

1、静态代理只能通过手动完成代理操作,如果被代理类增加新的方法,代理类需要同步 新增,违背开闭原则。
2、动态代理采用在运行时动态生成代码的方式,取消了对被代理类的扩展限制,遵循开 闭原则。
3、若动态代理要对目标类的增强逻辑扩展,结合策略模式,只需要新增策略类便可完成, 无需修改代理类的代码。
 

代理模式优点:

1、代理模式能将代理对象与真实被调用的目标对象分离。
2、一定程度上降低了系统的耦合度,扩展性好。
3、可以起到保护目标对象的作用。
4、可以对目标对象的功能增强。

代理模式缺点:

1、代理模式会造成系统设计中类的数量增加。
2、在客户端和目标对象增加一个代理对象,会造成请求处理速度变慢。
3、增加了系统的复杂度。
 

静态代理:

静态代理:代理对象需要持有被代理对象的引用,不方便扩展,当需要为其他对象代理的时候还得增加一个代理类。是在编码阶段确定的关系。

例如:父亲为儿子找对象会持有儿子的引用,当需要为女儿找对象的时候就得在创建一个持有女儿引用的父亲。

public interface Person {
    public void findLove();
}
public class Son implements Person{
    @Override
    public void findLove() {
        System.out.println("条件必须白富美,家里有两套房。");
    }
}
public class Father {

    private Son son;

    public Father(Son son) {
        this.son = son;
    }

    public void findLove(){
        System.out.println("寻寻觅觅、寻寻觅觅找到了");
        son.findLove();
        System.out.println("很合适,明天举办婚礼。");
    }

}
public class Test {
    public static void main(String[] args) {
        Son son = new Son();
        Father father = new Father(son);
        father.findLove();
    }
}

动态代理:

动态代理完美的解决了静态代理的弊端:可以为某一类型需求做代理(比如单身人士找对象,比如接口的权限认证等)。

动态代理分为JDK的动态 代理和CGLIB的动态代理:

JDK的动态代理:

JDK的动态代理:被代理对象需要有实现的接口,它是对接口的一种代理。JDK的代理需要代理类实现InvocationHandler接口,需要在构造被代理对象的时候使用Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);传入被道理对象的类加载器和接口以及代理对象。

上代码:

public interface PersonJDK {
    public void findLove();
}
public class ZhangsanJDK implements PersonJDK {
    @Override
    public void findLove() {
        System.out.println("我是高富帅,我要找白富美!");
    }
}
public class MeiPoJDK implements InvocationHandler {

    private  Object target;

    public Object getInstance(Object org){
        this.target = org;
        Class<?> clazz = org.getClass();
        return Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("寻寻觅觅、寻寻觅觅,找对象");
        Object object = method.invoke(this.target,args);//调用目标对象的方法
        System.out.println("确认过眼神,就是你要找的人,办事儿吧!");
        return object;
    }
}

测试类:

public class Test {

    public static void main(String[] args) {
        PersonJDK zhangsanJDK = (PersonJDK) new MeiPoJDK().getInstance(new ZhangsanJDK());
        zhangsanJDK.findLove();

       
    }
}

扩展:假如现在又有一个需要找对象的人来了,跟张三一样实现接口

public class XiaoHuaJDK implements PersonJDK{
    @Override
    public void findLove() {
        System.out.println("我是白富美,我要找高富帅。");
    }
}

测试类:

public class Test {

    public static void main(String[] args) {
        PersonJDK zhangsanJDK = (PersonJDK) new MeiPoJDK().getInstance(new ZhangsanJDK());
        zhangsanJDK.findLove();

        PersonJDK xiaoHuaJDK = (PersonJDK) new MeiPoJDK().getInstance(new XiaoHuaJDK());
        xiaoHuaJDK.findLove();
    }
}

动态代理更方便扩展。

JDK Proxy 生成对象的步骤如下:
1、拿到被代理对象的引用,并且获取到它的所有的接口,反射获取。
2、JDK Proxy 类重新生成一个新的类、同时新的类要实现被代理类所有实现的所有的接 口。
3、动态生成 Java 代码,把新加的业务逻辑方法由一定的逻辑代码去调用(在代码中体 现)。
4、编译新生成的 Java 代码.class。
5、再重新加载到 JVM 中运行。
 
 
 

CGLIB动态代理:

CGLib 代理的目标对象不需要实现任何接口,它是通过动态继承目标对象实现的动态代理。 重写了 Customer 类的所有方法。我们通过代理类的源码可以看到,代理类会获得所有 在 父 类 继 承 来 的 方 法 , 并 且 会 有 MethodProxy 与 之 对 应
 
调 用 过 程 : 代 理 对 象 调 用 this.findLove() 方 法 -> 调 用 拦 截 器 ->methodProxy.invokeSuper->CGLIB$findLove$0->被代理对象 findLove()方法。 Customer$$EnhancerByCGLIB$$3feeb52a$$FastClassByCGLIB$$6aad62f1.class就 是代理类的 FastClass, Customer$$FastClassByCGLIB$$2669574a.class 就是被代理类的 FastClass。CGLib 动态代理执行代理方法效率之所以比 JDK 的高是因为 Cglib 采用了 FastClass 机 制,它的原理简单来说就是:为代理类和被代理类各生成一个 Class,这个 Class 会为代 理类或被代理类的方法分配一个 index(int 类型)。这个 index 当做一个入参,FastClass 就可以直接定位要调用的方法直接进行调用,这样省去了反射调用,所以调用效率比 JDK 动态代理通过反射调用高。

上代码:

public class ZhangsanCGLIB {
    public void findLove(){
        System.out.println("我是高富帅,我要找白富美.");
    }
}

代理对象注意点:实现MethodInterceptor接口,将被代理类设置为将要生成的类的父类,在intercept方法里是用代理方法

methodProxy调用父类的具体方法的methodProxy.invokeSuper(o,objects);
public class MeIPOCGLIB implements MethodInterceptor {

    public Object getIntence(Class<?> clazz){
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(clazz);//把被代理类设置成将要生成的新类的父类
        enhancer.setCallback(this);
        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("CGLIB媒介公司牵红线啦");
        Object result = methodProxy.invokeSuper(o,objects);//调用findLove被代理方法(methodProxy.invoke 会调用,这就是为什么在拦截器中调用 methodProxy.invoke 会死循环,一直在调 用拦截器)
        System.out.println("一眼万年,办事儿吧。");
        return result;
    }
}
public class Test {
    public static void main(String[] args) {
        ZhangsanCGLIB zhangsanCGLIB = (ZhangsanCGLIB)new MeIPOCGLIB().getIntence(ZhangsanCGLIB.class);
        zhangsanCGLIB.findLove();
    }
}

 

CGLib 和 JDK 动态代理对比
1.JDK 动态代理是实现了被代理对象的接口,CGLib 是继承了被代理对象。
2.JDK 和 CGLib 都是在运行期生成字节码,JDK 是直接写 Class 字节码,CGLib 使用 ASM
框架写 Class 字节码,Cglib 代理实现更复杂,生成代理类比 JDK 效率低。
3.JDK 调用代理方法,是通过反射机制调用,CGLib 是通过 FastClass 机制直接调用方法,
CGLib 执行效率更高。
 
 
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值