博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring 静态代理和动态代理
阅读量:3936 次
发布时间:2019-05-23

本文共 4704 字,大约阅读时间需要 15 分钟。

 

动态代理:当想要给实现了某个接口的类中的方法,加一些额外的处理。比如说加日志,加事务等。可以给这个类创建一个代理,故名思议就是创建一个新的类,这个类不仅包含原来类方法的功能,而且还在原来的基础上添加了额外处理的新类。这个代理类并不是定义好的,是动态生成的。具有解耦意义,灵活,扩展性强。

动态代理实现:首先必须定义一个接口,还要有一个InvocationHandler(将实现接口的类的对象传递给它)处理类。再有一个工具类Proxy(习惯性将其称为代理类,因为调用他的newInstance()可以产生代理对象,其实他只是一个产生代理对象的工具类)。利用到InvocationHandler,拼接代理类源码,将其编译生成代理类的二进制码,利用加载器加载,并将其实例化产生代理对象,最后返回。

静态代理:

优点:

  1、  实现松散耦合。

  2、做到在不修改目标对象的功能前提下,对目标功能扩展。

 

缺点:

 如果项目中有多个类,则需要编写多个代理类,工作量大,不好修改,不好维护,不能应对变化。

模拟某位学生去考试作为例子:

 

下面创建 考试接口

public interface Examable {    void exam();}

创建Student学生实现这个考试接口(被代理)

public class Student implements Examable {      @Override    public void exam() {        System.out.println("奋笔疾书,完成考题。。。");    }}

创键Cheater(代理)对象,同时也去实现Examable接口 

 

public class Cheater implements Examable {  //被代理对象    private final Examable student;    public Cheater(Examable student){        this.student=student;    }    @Override    public void exam() {        System.out.println("在现场唱了一首歌,差点被劝退");        student.exam();//调用Student类的方法    }}

测试:

public class Main {    //组合优于继承    public static void main(String[] args) {        //cheater 就是一个代理对象        //因为它是受student 委托,完成某个功能        //它要完成的主要功能,来自student        //除了委托做的事情,可能还会扩展一些行为        Examable xiaoming = new Student();//原来的行为        xiaoming.exam();        System.out.println("------下面是代理行为------");        Examable cheater = new Cheater(xiaoming);        cheater.exam();    }}

结果:

奋笔疾书,完成考题。。。------下面是代理行为------在现场唱了一首歌,差点被劝退奋笔疾书,完成考题。。。

 

动态代理:

使用JDK内置的Proxy实现

接着拿上面作为例子

 

下面创建 考试接口

public interface Examable {    void exam();}

创建Student学生实现这个考试接口(被代理)

public class Student implements Examable {      @Override    public void exam() {        System.out.println("奋笔疾书,完成考题。。。");    }}

创建一个 JdkProxy 类 实现 InvocationHandler 接口

public class JdkProxy implements InvocationHandler {    private Object object;//被代理    public JdkProxy() {    }    public JdkProxy(Object object) {        this.object = object;    初始化的时候就赋值    }    /**     * 当用户调用对象中的每个方法时都通过下面的方法执行,方法必须在接口     * proxy 被代理后的对象     * method 将要被执行的方法信息(反射)     * args 执行方法时需要的参数     */    @Override    public Object invoke(Object proxy, Method method, Object[] args) {        Object invoke = null;        try {            invoke = method.invoke(object, args);        } catch (Exception e) {            System.out.println("异常的信息" + e.getMessage());        }        return invoke;//调用被代理对象原来的方法(行为)    }}

测试:

public class Main {public static void main(String[] args) {
/*         * 通过Proxy的newProxyInstance方法来创建我们的代理对象,我们来看看其三个参数         * 第一个参数 handler.getClass().getClassLoader() ,我们这里使用handler这个类的ClassLoader对象来加载我们的代理对象         * 第二个参数person1.getClass().getInterfaces(),我们这里为代理对象提供的接口是真实对象所实行的接口,表示我要代理的是该真实对象,这样我就能调用这组接口中的方法了         * 第三个参数handler, 我们这里将这个代理对象关联到了上方的 InvocationHandler 这个对象上         */        ClassLoader cl= Thread.currentThread().getContextClassLoader();        Examable o = (Examable) Proxy.newProxyInstance(                cl,//类加载器                new Class[]{Examable.class},//获取被代理对象的所有接口                new JdkProxy(new Student())//InvocationHandler对象        );        o.exam();//代理后的行为    }}

结果:

 

 

使用内置的Proxy实现动态代理有一个问题:被代理的类必须实现接口,未实现接口则没办法完成动态代理。

2、动态代理,使用cglib实现

还是拿上面的例子来说吧。

这次我们不写接口了。不过 我们要实现MethodInterceptor接口,并实现方法

去maven 中心仓库 找到 CGlib 的依赖

 

 

Student类

复制代码
public class Student {    public void exam(){        System.out.println("奋笔疾书,完成考试啦");    }}
复制代码

创建一个 CglibProxy 的类 并 实现  MethodInterceptor 接口 ,并实现方法

 

package com.nf147.sim.proxy.p5;import net.sf.cglib.proxy.MethodInterceptor;import net.sf.cglib.proxy.MethodProxy;import java.lang.reflect.Method;public class CglibProxy implements MethodInterceptor {    /*     * 参数     * Object 为由CGLib动态生成的代理类实例     * method 为上文中实体类所调用的被代理的方法引用     * objects 为参数值列表     * methodProxy 为生成的代理类对方法的代理引用     * return 从代理实例的方法调用返回的值     * */    @Override    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {        return methodProxy.invokeSuper(o, objects);    }}

 

测试:

package com.nf147.sim.proxy.p5;import com.nf147.sim.proxy.p1.Student;import net.sf.cglib.proxy.Enhancer;public class Main {    public static void main(String[] args) {        //第一种方式        //增强器,动态代码生成器        Enhancer enhancer = new Enhancer();        //设置 生成类 的 父类        enhancer.setSuperclass(Student.class);        //回调函数        enhancer.setCallback(new CglibProxy());        //动态生成字节码并返回代理对象        Student o = (Student) enhancer.create();        o.exam();        System.out.println("-----------");        //第二种方式        //这里是简化写法        //第一个参数 设置 生成类 的父类 ,第二参数 被代理类的所有接口 ,回调函数        Student student = (Student) Enhancer.create(Student.class, null, new CglibProxy());        student.exam();    }}

 结果:

 

 
 
 
 
 
 
 
posted @ 2018-12-18 19:36 阅读(...) 评论(...)

转载地址:http://fxhgn.baihongyu.com/

你可能感兴趣的文章
闾丘露薇:我的婚姻错在哪
查看>>
挖掘创业机会的七种方式
查看>>
CIO时间>金钱新逻辑:从刷牙洗澡开始
查看>>
中国房价长期暴跌不可避免!
查看>>
10部令人泣不成声的经典电影(多图)
查看>>
一声叹息:高考满分少年为何自杀(附满分作文)?
查看>>
十一五规划解读:房地产健康是十一五的关键
查看>>
zabbix服务器监控(运维)
查看>>
ElasticSearch-SQL 5.5 安装及配置
查看>>
mysql表没有索引,并发的情况下导致CPU飙升
查看>>
Java8 Lambda表达式使用集合(笔记)
查看>>
微服务分布式事务
查看>>
python搭建简易web服务器,局域网
查看>>
mac下载iterm2,以及安装及配置rz sz(转载)
查看>>
12306并发解决思路(转)
查看>>
Mysql之group by GROUP_CONCAT的保证顺序
查看>>
selenium入门--环境搭建
查看>>
Katalon--Mac环境搭建,Mobile之Android调试
查看>>
java调用webservice的2种方式代码
查看>>
GitHack针对.git的漏洞以及修复
查看>>