设计模式---代理模式,从实例看静态代理,动态代理,CGLIB

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_41376740/article/details/82857475

前言

  最近完成了自己的个人博客项目,要继续学习Spring了,AOP用的是动态代理,今天特地好好理解一下代理模式

路线

  • 静态代理
  • jdk动态代理
  • CGLIB动态代理

写在前面

  代理模式和装饰器模式,实现路线都是实现特地的接口,然后增加一些功能,那么它们的重要区别在哪呢?职能!,装饰器模式主要用于增强方法,而代理模式主要用于控制。举几个控制的例子,比如JDBC做事务,是否需要开启事务,可以用代理。类似下面的.

案例一:JDBC 例子

在这里插入图片描述
三部分

  • interface task 代表一个接口
  • Proxy 代表代理类
  • RealTask代表真正进行操作的类

实现:

public interface task {

    void doSomething();
}

public class RealTask implements task {

    @Override
    public void doSomething() {
        System.out.println("正在处理....");
    }

}

public class Proxy implements task {
    private task task;

    public Proxy(task task) {
        this.task = task;
    }

    @Override
    public void doSomething() {
        System.out.println("开启事务");
        task.doSomething();
        System.out.println("提交事务");
    }

}

测试:

public class Main {

    public static void main(String[] args) {
        task task = new RealTask();
        Proxy proxy = new Proxy(task);
        proxy.doSomething();
    }
}

----output----
开启事务
正在处理....
提交事务

解释:
  首先,最终doSomething的是谁?是RealTask,虽然这里调用的是Proxy.doSomething方法,但是最终还是由RealTask来执行,它只是在这个基础上做了两件额外的事情,开启事务和提交事务,它没有侵入到doSomeThing这个任务方法里面去----意思就是说,开启事务和你执行的各种数据库连接sql操作有关系吗?自然是没有的。Proxy就像是一个中介,然后对目标要干的事情加以控制。再想想装饰器模式,它是侵入到了doSomething里面。还不明白啥叫控制?再来个例子。

案例二:控制被执行的次数

public interface hello {
    void sayHello();
}

public class RealHello implements hello {
    @Override
    public void sayHello() {
        System.out.println("Hello");
    }
}

public class ProxyHello implements hello {
    private int time;
    private hello hello;

    @Override
    public void sayHello() {
        if (hello == null) {
            hello = new RealHello();
        }

        if (this.time < 5) {
            hello.sayHello();
        }
        this.time++;
    }
}

测试:

public class Main {

    public static void main(String[] args) {
        hello hello = new RealHello();
        hello proxy = new ProxyHello();
        for (int i = 0; i < 10; i++) {
            proxy.sayHello();
        }
    }
}

----output----
Hello
Hello
Hello
Hello
Hello

  这次的控制就比较明显了,本来执行10次,因为有了Proxy,当执行次数到达5次的时候,就不会再调用hello方法,代理控制没有侵入到sayHello把?

动态代理

了解了什么是代理模式,代理模式是干嘛呢,它和装饰器模式的区别在哪,之后呢,我们还要了解啥叫静态代理,啥叫动态代理。

  • 静态代理:静态 就是说在编译期就已经知道了,生成了class文件,我们知道某一个代理是为了某一个类的对象服务的。
  • 动态代理:动态 是说,当有大量的或者需要在运行期间确定代理行为的时候,就要用到这里的动态。我们也不用写很多的代理类了

JDK 动态代理

几个步骤

  • 被代理对象接口
  • 被代理对象
  • 处理Handler,处理代理过程
  • Proxy的静态方法,创建代理类
  • 通过代理类执行被代理的方法
public class Handler implements InvocationHandler {
    private Object target;
    private int time;

    public Handler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (this.time < 5) {
            method.invoke(target, args);
            this.time++;
        }
        return null;
    }

    public Object getProxy() {
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                this);
    }
}

    public static void main(String[] args) {
        Hello hello = new RealHello();
        Handler handler = new Handler(hello);

        Hello proxy = (Hello) handler.getProxy();
        for (int i = 0; i < 10; i++) {
            proxy.sayHello();
        }
    }
    
----output----
Hello
Hello
Hello
Hello
Hello

实际引用–事务处理

  看了上面的例子,我们用一个实际应用来演示上面的案例.Demo如下:

public interface ArticleService {
	void newArticle(ArticleForm form, User user) throws NewArticleException;
}

被代理类实现:

@Override
    public void newArticle(ArticleForm form, User user) throws NewArticleException {
//        try {
        Article article = Form2BeanUtils.form2Article(form);
        int userId = user.getUserId();

//            JDBCUtils.startTransaction();
        Connection conn = null;
        try {
            conn = JDBCUtils.getConnection();
        } catch (SQLException e) {
            e.printStackTrace();
        }

        Set<String> tagNames = form.getTags();
        articleService.createTags(tagNames, conn);
        Set<String> categories = form.getCategories();
        articleService.createCategories(categories, conn);

        // 获取新旧合并的的id号
        List<Integer> categoriesIds = categoryDao.getIds(categories, conn);
        List<Integer> tagIds = tagDao.getIds(tagNames, conn);

        //建立连接关系1:n user_article, m:n  article_categories, m:n article_tags
        BigInteger article_id = articleDao.createArticle(article, userId, conn);
        if (tagIds.size() != 0) {
            articleDao.joinTag(article_id, tagIds, conn);
        }
        if (categoriesIds.size() != 0) {
            articleDao.joinCategories(article_id, categoriesIds, conn);
        }
        /*    JDBCUtils.commit();
        } catch (Exception e) {
            try {
                e.printStackTrace();
                JDBCUtils.rollback();
                throw new NewArticleException(e);
            } catch (SQLException e1) {
                e1.printStackTrace();
            }
        } finally {
            try {
                JDBCUtils.release();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }*/
    }

处理类:

public class TransactionHandler implements InvocationHandler {
    private Object target;

    public TransactionHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            JDBCUtils.startTransaction();
            method.invoke(target, args);
            JDBCUtils.commit();
        } catch (Exception e) {
            try {
                e.printStackTrace();
                JDBCUtils.rollback();
                throw new NewArticleException(e);
            } catch (SQLException e1) {
                e1.printStackTrace();
            }
        } finally {
            try {
                JDBCUtils.release();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    public Object getProxy() {
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }
}

使用:

TransactionHandler handler = new TransactionHandler(articleService);
            ArticleService service = (ArticleService) handler.getProxy();
            service.newArticle(form, user);
//            articleService.newArticle(form, user);

注意: 注释部分是我之前没有用动态代理的方法,这样,事务每次运行就能复用这段开始事务,提交事务,而不用大量的写提交事务等语句。还是挺方便的~

CGLIB

CGLIB底层实现是ASM修改字节码,这个比较厉害了,我就从应用的基础上写个简单的例子,给自己留下一点印象:实现和上面一样的功能。

public class hello {

    public void sayHello() {
        System.out.println("hello");
    }
}

public class MyInterceptor implements MethodInterceptor {
    private int time;

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        if (this.time < 5) {
            proxy.invokeSuper(obj, args);
            this.time++;
        }
        return null;
    }
}

public class App {
    public static void main(String[] args) {
        // 增强对象
        Enhancer enhancer = new Enhancer();
        //设置父类
        enhancer.setSuperclass(hello.class);

        // 设置拦截器
        Callback interceptor = new MyInterceptor();
        enhancer.setCallback(interceptor);

        hello hello = (hello) enhancer.create();
        for (int i = 0; i < 10; i++) {
            hello.sayHello();
        }
    }
}

总结

  • CGLIB和JDK 动态代理两者的区别:
    • 实现角度: 后者需要实现接口,而CGLIB不需要,直接使用的super,当没有接口的时候就可以用CGLIB了
    • 关系角度: 后者实现的代理Proxy,更像是被代理对象的兄弟,属于兄弟关系。而CGLIB就像是继承关系。
    • 性能方面: 后者的实现是用的反射,创建对象速度优于CGLIB,而CGLIB虽然创建对象慢了点,但是它的方法执行速度却很快。(看的人家的,未验证)

猜你喜欢

转载自blog.csdn.net/qq_41376740/article/details/82857475