设计模式-代理模式

7、代理模式
代理模式是指为其他对象提供一种代理,以控制对这个对象的访问,属于结构型模式。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
代理模式一般包含三种角色:
抽象主题角色(Subject):抽象主题类的主要职责是声明真实主题与代理的共同接口方法,该类可以是接口也可以是抽象方法。
真实主题角色(RealSubject):该类也被称为被代理类,该类定义了代理所表示的真实对象,是负责执行系统真正的业务逻辑对象。
代理主题角色Proxy:也被称为代理类,其内部有RealSubjuct的引用,因此具备完全的对RealSubject的代理权。客户端调用代理对象的方法,但是会在代理对象前后增加一些处理代码。

代理模式的通用写法:

package com.jdwa.nomalproxy;

public interface ISubject {
    void request();
}
package com.jdwa.nomalproxy;

public class RealSubject implements ISubject{
    @Override
    public void request() {
        System.out.println("real service is called ");
    }
}

package com.jdwa.nomalproxy;

public class Proxy implements ISubject {

    private ISubject subject;

    public Proxy(ISubject subject){
        this.subject = subject;
    }


    @Override
    public void request() {
        before();
        subject.request();
        after();
    }

    private void before(){
        System.out.println("called before real service ...");
    }

    private void after(){
        System.out.println("called after real service ...");
    }

}
package com.jdwa.nomalproxy;

public class Client {
    public static void main(String[] args) {
        ISubject subject = new RealSubject();
        Proxy proxy = new Proxy(subject);
        proxy.request();
    }

}

从静态代理带动态代理:

package com.jdwa.staticproxy;

public interface IPerson {
    void findLove();
}
package com.jdwa.staticproxy;

public class Tom implements IPerson {
    @Override
    public void findLove() {
        System.out.println("must be a beautiful girl");
    }
}
package com.jdwa.staticproxy;

public class TomFather implements IPerson {

    private IPerson tom;

    public TomFather(IPerson tom){
        this.tom = tom;
    }
    @Override
    public void findLove() {
        before();
        tom.findLove();
        after();
    }

    private void before(){
        System.out.println("find a suitable girl ...");
    }

    private void after(){
        System.out.println("Get ready to be together ...");
    }
}
package com.jdwa.staticproxy;

public class Client {
    public static void main(String[] args) {
        IPerson tom = new Tom();
        IPerson tomFather = new TomFather(tom);
        tomFather.findLove();
    }
}

动态代理

package com.jdwa.staticproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class MatchMarker implements InvocationHandler {

    private IPerson target;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        Object result = method.invoke(this.target,args);
        after();
        return result;
    }

    private void before(){
        System.out.println("I'm matchmarker,I hava already collect your infomation ,start to look for");
    }

    private void after(){
        System.out.println("They are very satisfied with each other");
    }
}

测试

package com.jdwa.staticproxy;

public class Client {
    public static void main(String[] args) {
//        IPerson tom = new Tom();
//        IPerson tomFather = new TomFather(tom);
//        tomFather.findLove();

        MatchMarker matchMarker = new MatchMarker();
        IPerson tom = matchMarker.getInstance(new Tom());
        tom.findLove();
    }
}

这就是JDK自带的动态代理实现。
我们都知道jdk动态代理采用字节重组,重新生成对象来代替原始对象,以达到动态代理的目的。jdk动态代理生成对象的步骤如下:
a、获取被代理对象的引用,并获取他的所有接口,反射获取。
b、jdk动态代理类重新生成一个新的类,同时新的类要实现被代理类实现的所有接口
c、动态生成Java代码新加的业务逻辑方法由一定的逻辑代码调用
d、编译新生成的Java代码,得到class
e、重新加载到JVM中运行

我们通过将内存中的对象字节码输出,然后反编译,可以查看代理对象的源代码。

package com.jdwa.staticproxy;

import sun.misc.ProxyGenerator;

import java.io.FileOutputStream;

public class Client {
    public static void main(String[] args) throws Exception{
//        IPerson tom = new Tom();
//        IPerson tomFather = new TomFather(tom);
//        tomFather.findLove();

//        MatchMarker matchMarker = new MatchMarker();
//        IPerson tom = matchMarker.getInstance(new Tom());
//        tom.findLove();
        
        IPerson person = new MatchMarker().getInstance(new Tom());
        person.findLove();
        byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0",new Class[]{IPerson.class});
        FileOutputStream fos = new FileOutputStream("E://$Proxy0.class");
        fos.write(bytes);
        fos.close();
    }
}

通过源代码我们可以发现$Proxy0类继承了Proxy类,同时还实现了IPerson接口,重写了findLove方法。在静态块中用反射查找了目标 对象的所有方法,而且保存了所有方法的引用,重写的方法用反射调用目标对象的方法。这些代码,都是jdk帮我们自动生成的。

CGLIB实现

package com.jdwa.staticproxy;


import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class CGLibMatchMarker implements MethodInterceptor {

    public Object getInstance(Class<?> clazz) throws Exception{
        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 {
        before();
        Object obj = methodProxy.invokeSuper(o,objects);
        after();
        return obj;
    }

    private void before(){
        System.out.println("cglib ----I'm matchmarker,I hava already collect your infomation ,start to look for");
    }

    private void after(){
        System.out.println("cglib ----They are very satisfied with each other");
    }
}

package com.jdwa.staticproxy;

import sun.misc.ProxyGenerator;

import java.io.FileOutputStream;

public class Client {
    public static void main(String[] args) throws Exception{
//        IPerson tom = new Tom();
//        IPerson tomFather = new TomFather(tom);
//        tomFather.findLove();

//        MatchMarker matchMarker = new MatchMarker();
//        IPerson tom = matchMarker.getInstance(new Tom());
//        tom.findLove();

//        IPerson person = new MatchMarker().getInstance(new Tom());
//        person.findLove();
//        byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0",new Class[]{IPerson.class});
//        FileOutputStream fos = new FileOutputStream("E://$Proxy0.class");
//        fos.write(bytes);
//        fos.close();

        Tom tom = (Tom) new CGLibMatchMarker().getInstance(Tom.class);
        tom.findLove();

    }
}



cglib的执行效率要高于jdk的,就是因为CGLib采用了FastClass机制,他的原理简单来说就是:为代理类和被代理类各生成一个类,这个类会为代理类和被代理类的方法分配一个INDEX(int类型);这个INDEX当作一个入参,FastClass就可以直接定位要调用的方法并直接进行调用,省去了反射调用,所以调用效率比JDK代理通过反射调用高。

欢迎大家留言,以便于后面的人更快解决问题!另外亦欢迎大家可以关注我的微信公众号,方便利用零碎时间互相交流。共勉!

posted @ 2021-07-27 12:32  东方欲晓_莫道君行早  阅读(103)  评论(0编辑  收藏  举报