• Java设计模式--代理模式+动态代理+CGLib代理


    静态代理

    抽象主题角色:声明真实主题和代理主题的共同接口。

    代理主题角色:代理主题内部含有对真实主题的引用,从而在任何时候操作真实主题对象;代理主题提供一个与真实主题相同的接口,以便在任何时候都可以代替真实主题。代理角色通常在将客户端调用传递给真实的主题之前或之后,都要执行某个操作,而不是单纯的传递调用。

    真实主题角色:定义代理角色所代表的的真实对象。

    UML图:

    抽象主题

    public interface Subject {
        void request();
    }

    真实主题

    public class RealSubject implements Subject {
        @Override
        public void request() {
            System.out.println("真实对象的方法");
        }
    }

    代理主题

    public class ProxySubject implements Subject {
    
        private RealSubject subject;
    
        public ProxySubject() {
        }
    
        @Override
        public void request() {
            pre();
            if (subject == null){
                subject = new RealSubject();
            }
            subject.request();
            post();
        }
    
        private void pre(){
            System.out.println("方法执行之前");
        }
    
        private void post(){
            System.out.println("方法执行之后");
        }
    }

    执行:

        public static void main(String[] args) throws Exception {
            ProxySubject subject = new ProxySubject();
            subject.request();
        }

    输出:

    方法执行之前
    真实对象的方法
    方法执行之后 

    动态代理

    JDK自带的动态代理,实现InvocationHandler接口。

    声明接口

    public interface MyConnection extends AutoCloseable {
    
        void createStatement() throws Exception;
    
        @Override
        void close() throws Exception;
    }

    真实主题

    public class MyDefaultConnection implements MyConnection {
        @Override
        public void createStatement() throws Exception {
            System.out.println("Create Statement ...");
        }
    
        @Override
        public void close() throws Exception {
            System.out.println("Close Connection ...");
        }
    }

    代理主题

    public class MyConnectionProxy implements InvocationHandler {
    
        private MyConnection conn;
        private MyConnection proxyConn;
    
        public MyConnectionProxy(MyConnection conn) {
            this.conn = conn;
            this.proxyConn = (MyConnection) Proxy.newProxyInstance(MyConnection.class.getClassLoader(), new Class<?>[] {MyConnection.class}, this);
        }
    
        public MyConnection getConn() {
            return conn;
        }
    
        public MyConnection getProxyConn() {
            return proxyConn;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            String methodName = method.getName();
            System.out.println("==代理方法:" + methodName);
            if("close".equals(methodName)){
                System.out.println("**不执行close方法");
            }else{
                return method.invoke(conn, args);
            }
            return null;
        }
    }

    执行:

        public static void main(String[] args) throws Exception {
            MyConnection connection = new MyDefaultConnection();
            MyConnectionProxy proxy = new MyConnectionProxy(connection);
            proxy.getProxyConn().createStatement();
            proxy.getProxyConn().close();
        }

    你会发现我的代理对象去哪里了?实际上我放在InvocationHandler的实现类里面了,这里参考的是mybatis源码的设计。

    输出:

    ==代理方法:createStatement
    Create Statement ...
    ==代理方法:close
    **不执行close方法

    CGLib

    CGLib不需要接口就能实现动态代理。

    CGLIB 原理:动态生成一个要代理类的子类,子类重写要代理的类的所有不是final的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。它比使用java反射的JDK动态代理要快。

    CGLIB 底层:使用字节码处理框架ASM,来转换字节码并生成新的类。

    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>3.2.12</version>
    </dependency>

    需要代理的对象

    public class Programmer {
    
        public void work(){
            System.out.println("程序员正在敲代码...");
        }
    
        public final void finalCannotOverride(){
            System.out.println("final方法不能被生成的子类覆盖");
        }
    
        private void privateCannotOverride(){
            System.out.println("private方法不能被生成的子类覆盖");
        }
    }

    代理类

    public class ProgrammerProxy implements MethodInterceptor {
    
        // 真实对象
        private Object realObject;
        // 代理对象
        private Object proxyObject;
    
        public ProgrammerProxy(Object realObject) {
            this.realObject = realObject;
            Enhancer enhancer = new Enhancer();
            // 设置需要代理的对象
            enhancer.setSuperclass(realObject.getClass());
            // 设置代理人
            enhancer.setCallback(this);
            this.proxyObject = enhancer.create();
        }
    
        public Programmer getProxyObject() {
            return (Programmer) proxyObject;
        }
    
        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            pre();
            Object result = method.invoke(realObject, objects);
            post();
            return result;
        }
    
        private void pre(){
            System.out.println("==先吃早餐");
        }
    
        private void post(){
            System.out.println("==下班打卡");
        }
    }

    执行:

        public static void main(String[] args) throws Exception {
            Programmer programmer = new Programmer();
            ProgrammerProxy proxy = new ProgrammerProxy(programmer);
            proxy.getProxyObject().finalCannotOverride();
            proxy.getProxyObject().work();
        }

    输出:

    final方法不能被生成的子类覆盖
    ==先吃早餐
    程序员正在敲代码...
    ==下班打卡
  • 相关阅读:
    洛谷 P2616 [USACO10JAN]购买饲料II Buying Feed, II
    洛谷 P3654 First Step (ファーストステップ)
    洛谷 P1223 排队接水
    洛谷 【P1252】马拉松接力赛
    codevs 4927 线段树练习5
    洛谷 P1678 烦恼的高考志愿
    初识 线段树
    开学第一测
    洛谷 P1531 I Hate It
    CSS3 过渡
  • 原文地址:https://www.cnblogs.com/LUA123/p/10882784.html
Copyright © 2020-2023  润新知