结合JDK,Spring,Mybatis,Netty,Tomcat,Dubbo等经典框架的源码对设计模式展开分析(五)

结构型设计模式

  • 代理模式(Proxy Pattern)

定义:为其他对象提供一种代理,以控制这个对象的访问,在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端与目标对象之间起到中介的作用

应用目的:保护目标对象,动态增强目标对象,代理模式分为静态代理和动态代理

角色:抽象目标角色,目标角色,代理角色

代理对象通过实现和目标对象所实现的接口,然后在实现类中,持有目标对象,于是在代理类中对抽象方法实现,并在调用目标对象的行为的基础上,增加自己的行为增强

通用静态代理方式实现:

抽象目标角色

package com.ProxyPattern;

/**
 * @author yangxiaozhen
 * @date 2022/5/8-20:23
 */
public interface Isubject {
    void request();
    void doSomthing();
}

目标角色

package com.ProxyPattern;

/**
 * @author yangxiaozhen
 * @date 2022/5/8-20:23
 */
public class RealSubject implements Isubject {
    @Override
    public void request() {
        System.out.println("真实角色的自带行为");

    }

    @Override
    public void doSomthing() {
        System.out.println("真实角色自带的另一个行为");
    }
}

代理类和测试

package com.ProxyPattern;

/**
 * @author yangxiaozhen
 * @date 2022/5/8-20:25
 * 代理类通用写法
 */
public class ProxySubject implements Isubject {
    private Isubject real;
    @Override
    public void request() {
        before();
        real.request();
        after();
    }

    @Override
    public void doSomthing() {
        System.out.println("啥玩意儿");
    }

    private void after() {
        System.out.println("在目标行为运行前代理织入行为增强");
    }

    private void before() {
        System.out.println("在目标行为运行后代理织入行为增强");
    }



    public ProxySubject(Isubject real){
        this.real=real;
    }

    public static void main(String[] args) {
        new ProxySubject(new RealSubject()).request();
    }

}

JDK中有内置的Proxy类(java.lang.reflect)做动态代理从原理上看是对对象的的代理

使用上主要有InvocationHandler,Method,Proxy三个类和接口

Proxy的newProxyInstance()方法返回代理类,需要提供三个参数:目标对象的类加载器,目标对象类所实现的接口,InvocationHandler接口的实例

InvocationHandler接口定义了invoke()方法,入参:目标实例,目标对象的方法(Method类型 ),参数Object数组,需要用户去是实现,invoke()方法是对目标对象所有方法的增强逻辑  我的理解,Proxy.newProxyInstance()通过入参用户实现的InvocationHandler对象中的Invoke()方法,来构造代理对象所代理的目标对象的方法的代理方法,在Invoke()方法里面,可以通过入参的methon,用method.invoke调用目标对象的方法,也可以不调用,完全去“覆盖”目标对象的方法。在定义InvocationHandler的实现时,经常习惯性的把获取代理类的方法封装在其中,然后在使用时直接调用即可

package com.ProxyPattern;

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

/**
 * @author yangxiaozhen
 * @date 2022/5/8-21:03
 */
public class JDKproxy implements InvocationHandler {
    private Isubject target;
    public Isubject getInstance(Isubject target){
        this.target=target;
        Class<? extends Isubject> aClass = target.getClass();
       return (Isubject) Proxy.newProxyInstance(aClass.getClassLoader(),aClass.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 after() {
        System.out.println("在目标行为运行前代理织入行为增强");
    }

    private void before() {
        System.out.println("在目标行为运行后代理织入行为增强");
    }

    public static void main(String[] args) {
        Isubject proxy = new JDKproxy().getInstance(new RealSubject());
        proxy.request();
        proxy.doSomthing();

    }
}

JDK动态代理以及Cglib动态代理其实底层实现原理都是字节码的重组,不过各自对应的代理场景和实现方式不同,手动也可实现jdk动态代理

package com.ProxyPattern;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * @author yangxiaozhen
 * @date 2022/5/10-14:19
 */
public interface MyInvocationHandle {
    Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException;
}
package com.ProxyPattern;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * @author yangxiaozhen
 * @date 2022/5/10-20:53
 */
public class MyInvocationHandleImpl implements MyInvocationHandle {
    private static Isubject taget;
    static {
        taget=new RealSubject();
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {
        System.out.println("方法之前增强");
        Object o = method.invoke(taget,args);
        return o;

    }
}

package com.ProxyPattern;

import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

/**
 * @author yangxiaozhen
 * @date 2022/5/10-14:20
 */
public class MyProxy {
    private static final String ln = "\r\n";

    public static Object getNewInstance(MyInvocationHandle handle, Class[] i, ClassLoader classLoader) {
        try {
            String codeSrc = generateSrc(i[0]);

            String path = MyProxy.class.getResource("").getPath();
            File file = new File(path + "$MyProxy01.java");
            FileWriter fileWriter = new FileWriter(file);
            fileWriter.write(codeSrc);
            fileWriter.flush();
            fileWriter.close();
            // 编译源代码,并且生成.class文件
            JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
            StandardJavaFileManager manager = compiler.getStandardFileManager(null, null, null);
            Iterable iterable = manager.getJavaFileObjects(file);
            JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, null, null, iterable);
            task.call();
            manager.close();
            //
            Class $MyProxy01 = classLoader.loadClass("com.ProxyPattern.$MyProxy01");
//            Class $MyProxy01 = Class.forName("$MyProxy01");
            Constructor constructor = $MyProxy01.getConstructor(MyInvocationHandle.class);
            Object o = constructor.newInstance(handle);
            return o;

        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 生成代理类源代码
     *
     * @param aClass 接口类型
     */
    private static String generateSrc(Class aClass) {
        StringBuilder src = new StringBuilder();
        src.append("package com.ProxyPattern;" + ln);
        //引入反射相关的包
        src.append("import java.lang.reflect.Method;" + ln);
        src.append("import com.ProxyPattern.MyInvocationHandle;"+ln);
        //动态代理类实现被代理接口,在此为Person类
        src.append("public class $MyProxy01 implements " + aClass.getName() + "{" + ln);
        src.append("MyInvocationHandle h;" + ln);
        src.append("public $MyProxy01(MyInvocationHandle h) {" + ln);
        src.append("this.h = h;" + ln);
        src.append("}" + ln);
        for (Method m : aClass.getMethods()){
            src.append("public " + m.getReturnType().getName() + " " + m.getName() + "(){" + ln);
            src.append("try{" + ln);
            src.append("Method m = " + aClass.getName() + ".class.getMethod(\"" + m.getName() + "\",new Class[]{});" + ln);
            src.append("this.h.invoke(this,m,null);" + ln);
            src.append("}catch(Throwable e){e.printStackTrace();}" + ln);
            src.append("}" + ln);
        }

        src.append("}");
        return src.toString();
    }

    public static void main(String[] args) {
        ((Isubject) MyProxy.getNewInstance(new MyInvocationHandleImpl(), RealSubject.class.getInterfaces(), MyInvocationHandle.class.getClassLoader())).doSomthing();
    }
}

CGLIB相比于JDK动态代理更加强大,JDK动态代理虽然简单易用,但是其有一个致命缺陷是,只能对接口进行代理。如果要代理的类为一个普通类、没有接口,那么Java动态代理就没法使用了,CGLIB底层使用了ASM(一个短小精悍的字节码操作框架)来操作字节码生成新的类,这里只举例使用

 

package com.ProxyPattern;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * @author yangxiaozhen
 * @date 2022/5/8-20:23
 */
public class RealSubject implements Isubject {
    @Override
    public void request() {
        System.out.println("真实角色的自带行为");

    }

    @Override
    public void doSomthing() {
        System.out.println("真实角色自带的另一个行为");
    }

    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(RealSubject.class);
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                if (method.getName().equals("request")){
                    System.out.println("target方法调用前织入");
                }

                methodProxy.invokeSuper(o,objects);
                System.out.println("target方法调用后织入");
                return null;
            }
        });
        RealSubject realSubject = (RealSubject) enhancer.create();
        realSubject.doSomthing();
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值