Java Web 基础篇 L1 关于代理

文章目录

1 代理

代理是一种非侵入式更新软件的手段,可以通过代理为原本要执行的方法添加更多的逻辑和功能,且不会对原先的代码产生影响

考虑如下一个场景:
在一个学生信息管理网站中,教师登录和学生登录以及管理人员登录后,需要显式不同的页面,但是进入网站的第一步都是需要先点击“登录”,在后台的登录方法中,虽然可以在登录方法执行前,进行一系列的逻辑判断,但是这会导致软件的逻辑变得复杂,维护性变差,这时通过代理,可以在不影响源码的情况下,对软件的功能进行更新迭代

Java Web 基础篇 L1 关于代理
Java Web 基础篇 L1 关于代理
所谓代理,就是不直接执行方法,而是交给某个对象在预期的时间去执行该方法,最大的好处就是在执行该方法前后,可以增加一些代码来为该方法增加更多的功能,且不影响源码

2 JDKProxy

代理是一个对象,可以通过代理来执行被代理的类所含的方法

JDKProxy是JDK提供的一种代理,它基于接口,本质是Interface类型的实例,即就是被代理的类中的抽象方法所在的接口

2.1 获取JDKProxy

/**
 * @author 雫
 * @date 2021/2/18 - 18:02
 * @function 产生JDK代理
 */
public class AboutJDKProxy {

    public AboutJDKProxy() {}

    /**
     * @Author 雫
     * @Description 通过对象实例生成JDK代理
     * @Date 2021/2/18 18:08
     * @Param [object]
     * @return T
     **/
    @SuppressWarnings("all")
    public static <T> T getJDKProxy(Object object) {

        Class<?> klass = object.getClass();

        ClassLoader classLoader = klass.getClassLoader();
        Class<?> [] interfaces = klass.getInterfaces();

        return (T) Proxy.newProxyInstance(classLoader, interfaces, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("方法执行前,添加前置拦截");

                Object result = method.invoke(object, args);

                System.out.println("方法执行后,添加后置拦截");
                return result;
            }
        });
        
    }

    /**
     * @Author 雫
     * @Description 重载方法,通过Class对象生成JDK代理
     * @Date 2021/2/18 18:08
     * @Param [klass]
     * @return T
     **/
    public static <T> T getJDKProxy(Class<?> klass) throws IllegalAccessException, InstantiationException {
        return getJDKProxy(klass.newInstance());
    }

}

获得一个JDK代理需要三个参数:

1,classLoader 被代理类的类加载器
2,interfaces 被代理类所实现的所有接口
3,new InvocationHandler() 本质是一个接口,它可以监听到方法何时被调用
	通过在内部类增加代码,可以进行"前置拦截"和"后置拦截"来增加更多功能
	也可以什么都不做,直接执行代码,类似于窗口编程中的监听器

2.2 JDKProxy测试

测试需要的接口和实现该接口的类:
Java Web 基础篇 L1 关于代理
Java Web 基础篇 L1 关于代理
为JDKProxy添加简单的拦截:
Java Web 基础篇 L1 关于代理

获取JDKProxy:

/**
 * @author 雫
 * @date 2021/2/8 - 15:59
 * @function 代理测试
 */
public class ProxyTest {

    public static void main(String[] args) {

        //根据静态方法获得被代理的类的JDK代理,这个代理的类型是将要被执行的方法所在的接口类型
        IMyProxy jdkProxy = JDKProxy.getJDKProxy(new MyImplement());

        /*这里就是通过JDK代理执行了被代理的类所实现的接口中的方法,在该方法被指向前和执行后都可以添加一些代码来执行
        * 通过生成JDK代理时所需要实现的InvocationHandler(),就可以在执行该方法前后完成 "过滤/筛选/拦截/完善"*/
        jdkProxy.doFirstThing("abcd");
        jdkProxy.doSecondThing(10, "abcd");

    }

}

测试结果:
Java Web 基础篇 L1 关于代理
可以看到:通过代理,一个接口直接执行了接口的实现类中的方法
通过这样的方式,可以给方法增加额外的参数以便方法执行前识别拦截,执行完方法后对结果进行补充进行后置拦截

3 CGLibProxy

CGLib代理是是被代理类的子类,即把被代理的类作为代理的父类这样子类(代理)就能调用父类(被代理类)中的方法

3.1 获取CGLib代理

需要jar包

/**
 * @author 雫
 * @date 2021/2/18 - 18:30
 * @function 产生CGLib代理
 */
public class AboutCGLibProxy {

    public AboutCGLibProxy() {}


    /**
     * @Author 雫
     * @Description 根据对象生成CGLib代理
     * @Date 2021/2/18 18:35
     * @Param [object]
     * @return T
     **/
    @SuppressWarnings("all")
    public static <T> T getCGLibProxy(Object object) {

        Class<?> klass = object.getClass();

        /*这里的enhancer就是代理,将被代理的类作为了代理的父类
        * 从而让代理间接调用父类中的方法*/
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(klass);

        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                System.out.println("添加前置拦截");

                Object result = method.invoke(object, objects);

                System.out.println("添加后置拦截");
                return result;
            }
        });

        return (T) enhancer.create();
    }
    
    /**
     * @Author 雫
     * @Description 重载方法,根据Class对象生成CGLib代理
     * @Date 2021/2/18 18:36
     * @Param [klass]
     * @return T
     **/
    public static <T> T getCGLibProxy(Class<?> klass) throws IllegalAccessException, InstantiationException {
        return getCGLibProxy(klass.newInstance());
    }
    
}

3.2 CGLib代理测试

测试需要的被代理类:
Java Web 基础篇 L1 关于代理
测试结果:
Java Web 基础篇 L1 关于代理

4 添加拦截器

现在虽然可以通过代理来反射执行方法,但是却没有实用意义,因为前置拦截 和后置拦截 过于简单,实际应用中更多的是调用配置好的方法,而不是简单的输出,下面在方法执行前后执行配置的方法

以CGLib代理为例,CGLib代理可以执行被代理类中的方法,每个方法都需要不同的前置拦截方法和后置拦截方法,这些方法应该是配置好的,因此建立一个HashMap,键取被代理类的方法名,值取该方法对应的前置拦截方法和后置拦截方法具体实现类

前置拦截和后置拦截需要的接口:
Java Web 基础篇 L1 关于代理
前置拦截和后置拦截接口的适配器:
Java Web 基础篇 L1 关于代理
对于每个方法都需要一对前置拦截方法和后置拦截方法,让所有实现类继承InterceptAdapter,并作为值存储在HashMap中,键则采用方法名

Java Web 基础篇 L1 关于代理
通过MethodFactory可以获取被代理类的所有方法作为键,该方法对应的拦截方法所在类作为值,在使用前通过setMethodIntercept方法将具体的实现类作为值替换掉空适配器

代理执行方法前进行判断,根据方法名从MethodFactory中取出对应的前置拦截和后置拦截方法并执行,从而让代理工作功能更强
Java Web 基础篇 L1 关于代理

上一篇:再谈 apache设置virtualhost + apache的一些相关设值


下一篇:在C#中有一个Java等效于空合并运算符(??)吗?