什么是静态代理和动态代理,两者的区别(笔记)

1.什么是代理?

​ 代理: 就是让代理角色帮助真实角色完成一件事情;

​ 举例:过年回家让朋友代买火车票,朋友帮你买火车票的过程就是代理

2.静态代理

​ 什么是静态代理:

静态代理相当于是多写了一个代理类,在调用的时候调用的是代理类,在代理类中的处理还是原生的处理逻辑,不过在前后添加上需要添加的代码。
缺点:需要为每一个被代理的对象都创建一个代理类。

​ 特点:

​ 代理角色和真实角色都需要实现同一个接口,

​ 真实角色专注于自己的事情,

​ 代理角色目的就是帮助真实角色完成一件事情

​ 多线程的实现方式2:实现一个接口Runnable 使用的就是"静态代理"的思想

现在我们用代码模拟一下结婚和婚庆公司演示静态代理

代码:

// 定义一个接口结婚的接口
interface  Mary{
    void mary() ;
}

//每个人都要结婚
class You implements Mary{//真实角色

    @Override
    public void mary() {
        System.out.println("要结婚了,很开心...");
    }
}


//婚庆公司---代理角色
class WeddingCompany implements Mary{
    //声明一个接口Mary类型变量
    private Mary mary ;
    public WeddingCompany(Mary mary){ //形式参数是一个接口,需要接口实现类对象
        this.mary = mary ;
    }

    @Override
    public void mary() {
        //代理角色需要帮助真实角色完成一件 "结婚这件事情"
        System.out.println("结婚之前,需要布置婚礼现场....");
        mary.mary() ;
        System.out.println("结婚之后,给婚庆公司付尾款....");
    }
}

public class StaticProxyDemo {
    public static void main(String[] args) {

        //没有使用静态代理之前
        //接口多态或者是具体类new 具体类
        Mary mary = new You() ;
        mary.mary() ;
        System.out.println("-----------------------------------------");
        //使用静态代理
        You you = new You() ;
        //创建代理角色
        WeddingCompany weddingCompany = new WeddingCompany(you) ;
        weddingCompany.mary();
    }
}

输出结果:

要结婚了,很开心…

-----------------------------------------

结婚之前,需要布置婚礼现场…
要结婚了,很开心…
结婚之后,给婚庆公司付尾款…

3.动态代理

什么是动态代理?

Java标准库提供了动态代理功能,允许在运行期动态创建一个接口的实例; 动态代理是通过 Proxy 创建代理对象,然后将接口方法“代理”给 InvocationHandler 完成的。

代码:

针对用户访问的数据接口

public interface UserDao {
    /**
     * 添加功能
     */
    void add() ;

    /**
     * 修改功能
     */
    void update() ;

    /**
     * 查询功能
     */
    void select() ;

    /**
     * 删除功能
     */
    void delete() ;
}

针对用户数据访问接口的实现

public class UserDaoImpl implements UserDao {
    @Override
    public void add() {
        System.out.println("添加功能");
    }

    @Override
    public void update() {
        System.out.println("修改功能") ;
    }

    @Override
    public void select() {
        System.out.println("查询功能");
    }

    @Override
    public void delete() {
        System.out.println("删除功能");
    }
}

版本2:对业务方法 add,update,select.delete进行增强

public class UserDaoImpl2 implements UserDao {
    @Override
    public void add() {
        System.out.println("权限校验") ;

        System.out.println("执行添加功能");

        System.out.println("产生日志文件");
    }

    @Override
    public void update() {
        System.out.println("权限校验") ;

        System.out.println("执行修改功能");

        System.out.println("产生日志文件");
    }

    @Override
    public void select() {

        System.out.println("权限校验") ;

        System.out.println("执行查询功能");

        System.out.println("产生日志文件");
    }

    @Override
    public void delete() {

        System.out.println("权限校验") ;

        System.out.println("执行删除功能");

        System.out.println("产生日志文件") ;
    }
}
/*Proxy类下边的方法newProxyInstance
*   public static Object newProxyInstance(ClassLoader loader,   参数1:实现的接口的类加载器
*                                       Class<?>[] interfaces,  参数2:基于接口的字节码文件对象数组
*                                       InvocationHandler h)    参数3:是接口InvocationHandler :代理的处理程序
*                                throws IllegalArgumentException

*                                                    参数3是一个接口:自定一个类实现这个接口
*                                                       重写这个接口中的invoke方法
*                                                Object invoke(Object proxy,Method method,Object[] args)throws Throwable
*/

基于代理的处理程序

public class MyInvocation  implements InvocationHandler {

    //要针对谁产生代理: ud                 UserDao ud = new UserDaoImpl() ;
    private Object target ;  //因为代理角色可以是任意Java类型,所以用Object
    public MyInvocation(Object target){
        this.target = target ;
    }


    //方法的返回值就是代理对象
    @Override                           //就是我们的接口的方法
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("权限校验") ;

        //调用接口的列表自己的方法:update(),delete(),add(),select()
        //当前实例--->真实角色
        Object obj = method.invoke(target, args); //代理角色产生

        System.out.println("产生日志文件");
        return obj;
    }
}

测试类:

public class JdkProxyDemo {
    public static void main(String[] args) {

        //接口多态:测试UserDao
        UserDao ud = new UserDaoImpl() ; //真实角色
        //版本1:现在需要对功能增强,这个代码不合适!
        ud.add();
        ud.update();
        ud.select();
        ud.delete();
        System.out.println("----------------------------------");

        UserDao ud2 = new UserDaoImpl2() ;
        ud2.add();
        ud2.update();
        ud2.select();
        ud2.delete();

        System.out.println("----------------Jdk动态代理------------------");

        //前提示需要一个接口:UserDao

        MyInvocation handler = new MyInvocation(ud) ;
        // public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
        UserDao proxyInstance = (UserDao) Proxy.newProxyInstance(
                ud.getClass().getClassLoader(),
                ud.getClass().getInterfaces(),
                handler);
        proxyInstance.add();
        proxyInstance.update();
        proxyInstance.select();
        proxyInstance.delete();
    }
}

输出结果:

​ 添加功能
​ 修改功能
​ 查询功能
​ 删除功能
​ ----------------------------------

​ 权限校验
​ 执行添加功能
​ 产生日志文件
​ 权限校验
​ 执行修改功能
​ 产生日志文件
​ 权限校验
​ 执行查询功能
​ 产生日志文件
​ 权限校验
​ 执行删除功能
​ 产生日志文件
​ ----------------Jdk动态代理------------------
​ 权限校验
​ 添加功能
​ 产生日志文件
​ 权限校验
​ 修改功能
​ 产生日志文件
​ 权限校验
​ 查询功能
​ 产生日志文件
​ 权限校验
​ 删除功能
​ 产生日志文件

4.总结:

​ 静态代理总结:
1)代理类的信息在jvm运⾏之前就已经⽣成,逻辑由开发者实现;
2)代理类与⽬标类的定义应该严格参照规范,定义公共接⼝并实现它,需要代理的⽅法在接⼝中都要定义好;

静态代理原理:在代理类中包含⼀个⽬标类的对象引⽤,然后在使⽤时创建⼀个⽬标类对象并且创建⼀个代理类对象,并把⽬标类对象传给代
理类对象,然后将它赋予代理类中的⽬标类对象引⽤,然后代理类所代理的⽅法中通过其所包含的⽬标类对象引⽤调⽤⽬标类的⽅法,从⽽实现通过代理调⽤⽬标类⽅法的效果。

​ 动态代理总结:
1) 动态代理是指 在java程序运⾏过程(程序已经启动在运⾏了)由jvm⽣成代理类的class信息,该class信息⽣成后是直接处于内存中的,并没有写⼊磁盘保存起来;然后通过反射⽅式实例化代理类对象,因为代理类的class信息已经存在于内存中,所以可以通过反射⽅式实例化。
这个应该怎么理解呢?
​ 可以跟上⾯讲过的静态代理对⽐下,静态代理是需要开发⼈员⾃⼰实现代理类的逻辑的,且代理类的class信息是在程序运⾏之前就已经可以获取到的.java⽂件经过编译后可以得到.class⽂件;
​ ⽽动态代理是不需要开发⼈员⾃⼰实现代理类的,也就是说使⽤动态代理⽅式的话,项⽬代码中是不存在代理类的.java⽂件的,既然代理类未由开发者实现,那么程序经过编译之后肯定也不会有代理类的.class⽂件,
​ 也就是说经过编译之后程序未启动运⾏之前,关于代理类的信息我们⼀⽆所知,它是在程序运⾏过程中需要⽤到的时候才会由jvm动态⽣成的,⽽且⽣成之后也只存在于内存中,不会写到磁盘保存成.class⽂件,更加不会保存为.java⽂件;
​ 在程序重启或者说发⽣了gc,这个代理类的class信息从内存中被卸载之后,关于这个代理类的信息就没有了,只有当代码再次访问到代理对象时,才⼜会重新⽣成代理类的class信息。
2)动态代理与静态代理的区别是什么?
​ 上⾯已经讲述,不再赘述。
3)为什么需要引⼊动态代理?
​ 这就不得不说到静态代理的弊端,我们引⼊新事物,必定是因为旧事物存在不合理之处,所以才引⼊新的事物来弥补它的缺陷。
​ 那静态代理有什么缺陷呢?
​ 我们知道静态代理是需要开发者⾃⼰实现代理类逻辑的,也就是说要对某个类进⾏代理的话,需要实现这个类相应的代理类;
​ 如果⽬标类的数量很多的话,代理类的实现也必然得很多,可能会造成代码量过于庞⼤,可能会增加代码的冗余度…
​ 再者,如果⽬标类需要代理的⽅法很多的话,代理类需要对这些⽅法⼀⼀实现代理逻辑,代理类的实现也将会很庞⼤。

考虑到这些问题,催⽣了动态代理这种⽅式,它相⽐于静态代理来说,由于不需要开发者⾃⼰再实现代理类了,所以在实际⼤型项⽬中可能代码量会⼤⼤减少。

  • 14
    点赞
  • 62
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
AspectJ是一种静态代理框架,它可以在编译期间将切面代码织入到目标对象的字节码中。相对于Spring AOP来说,AspectJ的静态代理方式具有更好的性能,但需要特定的编译器进行处理。 AspectJ的演变过程可以分为三个方面,首先是对静态代理框架的分析和解释,然后是增加AspectJ案例,最后是对AspectJ字节码的分析。 AspectJ可以通过在编译期间生成AOP代理对象,将切面代码直接织入目标对象的字节码中,从而实现静态代理功能。这种方式可以在运行时不需要额外的代理对象,因此具有较好的性能表现。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [AOP的实现原理 —— 静态代理动态代理( Spring AOP)](https://blog.csdn.net/xiaojin21cen/article/details/79487769)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *3* [Android+反射+代理+静态代理+AspectJ](https://download.csdn.net/download/u013620306/86727235)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值