java动态代理

本文介绍了Java中的代理模式,包括静态代理和动态代理(JDK动态代理与CGLib),探讨了它们的原理、作用、分类以及在实际开发中的应用,如通过Maven创建项目并实现明星演唱会代理场景。
摘要由CSDN通过智能技术生成

相关笔记(先学maven,一下项目都是通过maven创建的)

maven笔记

动态代理讲义.doc

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

1.代理模式

目标对象不可访问,通过代理对象进行访问,而且增强式的访问,适当进行业务的扩展

生活中:
房东 ===>目标对象
房屋中介 ===>代理对象
你,我 ===>客户端对象

服务生产厂 ===>目标对象
门店(旗舰店) ===>代理对象
你,我 ===>客户端

开发中:
运营商(电信,移动,联通) ====>目标对象
第三方公司 ====>代理对象
开发的应用程序需要发送短信的功能 ===>客户端

开发的应用程序需要支付的功能

2.代理模式的作用
  1. 控制目标对象的访问

  2. 增强功能(主要功能由目标对象实现,附加功能由代理对象实现)

3.代理模式的分类
  1. 静态代理

  2. 动态代理,又分为JDK动态代理CGLib动态代理(子类代理)

4.什么是静态代理

它是代理模式的一种.

它具备以下特点:

  1. 目标对象和代理对象实现同一个业务接口
  2. 目标对象必须实现接口
  3. 代理对象在程序运行前就已经存在
  4. 能够灵活的进行目标对象的切换,却无法进行功能的灵活处理(使用动态代理解决此问题)
5.静态代理实现

业务功能:请明星进行节目表演.

  • 明星刘德华:目标对象(无法直接访问)

  • 刘德华助理:代理对象(我们可以访问,他可以跟明星对接)

  • 我们:客户端对象

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

代码实现

项目路径

image-20231214193218546

通过maven创建 java工程:quickstart(具体见maven笔记

  1. 业务接口Service

    public interface Service {
        //规定的唱歌的业务功能
        void sing();
    }
    
  2. 目标对象:

    /**
    
    	目标对象:刘德华, 周润发。实现业务接口中的功能,进行唱歌表演
     */
    public class SuperStarLiu implements Service {
        @Override
        public void sing() {
            System.out.println("我是刘德华,我正在表演唱歌............");
        }
    }
    
    public class SuperStartZhou implements Service {
        public void sing() {
            System.out.println("周润发正在唱歌");
        }
    }
    
  3. 代理对象:

    public class Agent implements Service {
        //类中的成员变量设计为接口
        public Service target;  //目标对象
    
        //传入目标对象,方法的参数设计为接口(实现多态,以后不管目标对象有多少个都能用一行代码实现,不必单独为每个目标对象创建实例)
        public Agent(Service target){
            this.target = target;
        }
    
        @Override
        public void sing() {
            System.out.println("预订时间..........");
            System.out.println("预订场地..........");
    
            //切记切记:业务功能必须由目标对象亲自实现
            //        SuperStarLiu liu = new SuperStarLiu();
            //        liu.sing();
            //
            //        SuperStarZhou zhou = new SuperStarZhou();
            //        zhou.sing();
            //面向接口编程:调用时,接口指向实现类
            target.sing();
    
            System.out.println("结算费用..........");
        }
    }
    
  4. 客户端对象

    public class MyTest{
        @Test
        public void testAgent1(){
            //测试功能
            //        SuperStarLiu liu = new SuperStarLiu();
            //        liu.sing();
            //        Agent agent = new Agent();
            //        agent.sing();
    
            //有接口和实现类,必须使用接口指向实现类(规范)
            Service agent = new Agent();
            agent.sing();
        }
        
        @Test
        public void testAgent2(){
            //测试功能
            //        SuperStarLiu liu = new SuperStarLiu();
            //        liu.sing();
            //        Agent agent = new Agent();
            //        agent.sing();
    
            //有接口和实现类,必须使用接口指向实现类(规范)
            Service agent = new Agent(new SuperStartZhou());
            //Service agent = new Agent(new SuperStartLiu());
            agent.sing();
        }
    }
    
6.面向接口编程(重要)

类中的成员变量设计为接口

方法的形参设计为接口

方法的返回值设计为接口

调用时接口指向实现类(多态,动态绑定)【父类指向子类 father a = new son();】

===》如第5节中的 Service 接口

【在项目中表现为多建了一个 SuperStarZhou 类实现 Service接口,如果要调用该类的对象,必须在代理对象中 new 该类对象,这样没办法 灵活的进行目标对象的切换,于是就采取了上述的步骤,得到新的 代理对象 代码如第5节】

7.动态代理

静态代理的弊端:当 我们在 Service 接口中添加新的功能(方法)时,所有实现该接口的类必须重写该方法(目标对象,代理对象),则 就需要更改很多地方,程序失去了灵活性。所以我们需要动态代理

代理对象在程序运行的过程中动态在内存构建。可以灵活的进行业务功能的切换.

8.JDK动态代理
  1. 目标对象必须实现业务接口(与静态代理相同)
  2. JDK代理对象不需要实现业务接口(与静态代理不同)
  3. JDK动态代理的对象在程序运行前不存在,在程序运行时动态的在内存中构建
  4. JDK动态代理能够灵活的进行业务功能的切换
  5. 目标对象类中的方法(非接口中的方法)不能被代理
9.JDK动态代理用到的类和接口

它是使用现在的工具类完成JDK动态代理的实现.

  • Proxy 生成动态代理对象.
  • Method 进行目标对象的方法的反射调用
  • InvocationHandle 实现代理和业务功能的
  1. Proxy类

    它是java.lang.reflect.Proxy包下的类.

    它有一个方法Proxy.newProxyInstance(…)专门用来生成动态代理对象.

      public static Object newProxyInstance(ClassLoader loader,  // 类加载器
               Class<?>[] interfaces,  // 目标对象实现的所有接口,泛型数组
               InvocationHandler h  // 它就类似于Agent的功能,代理的功能和目标对象的业务功能调用在这
                )throws IllegalArgumentException{...}
    
  2. Method类
    反射用的类,用来进行目标对象的方法的反射调用.

    method对象接住我们正在调用的方法sing(),show()

    method == sing(),show()

    method.invoke();==>手工调用目标方法 sing(); show();

  3. InvocationHandler接口
    它是实现代理和业务功能的。我们在调用时使用匿名内部实现.

接口的匿名内部实现:

@Test
public testA(){
	Service service = new Servcie(){
        @Override
        public void sing(){
            System.out.println("哈哈哈哈....");
        }
    };
    service.sing();
}

代码实现:

image-20231214203140131

项目路径

  1. 业务接口

    public interface Service {
        void sing();
    
        String show(int age);
    }
    
  2. 目标对象

    public class SuperStartLiu implements Service {
        public void sing() {
            System.out.println("我是刘德华,我正在唱歌。。。。");
        }
    
        public String show(int age) {
            System.out.println("刘德华的show。。。。" + age);
            return "liu";
        }
    
        // 此方法不能被代理(不能增强功能)
        public void one() {
            System.out.println("one.......");
        }
    }
    
    public class SuperStartZhou implements Service {
        public void sing() {
            System.out.println("我是周润发,我正在唱歌。。。。");
        }
    
        public String show(int age) {
            System.out.println("周润发的show。。。。" + age);
            return "zhou";
        }
    }
    
  3. 获取 动态代理对象,进行封装是因为 返回动态代理对象 的代码太繁杂,在客户端获取不美观

    public class ProxyFactory {
        // 类中的成员变量设计为接口,目标对象
        Service target;
        // 传入目标对象
        public ProxyFactory(Service target){
            this.target = target;
        }
    
        // 返回动态代理对象
        public Object getAgent(){
            return Proxy.newProxyInstance(
                // ClassLoader loader, 类加载器,完成目标对象的加载
                target.getClass().getClassLoader(),
                
                // Class<?>[] interfaces,目标对象实现的所有接口
                target.getClass().getInterfaces(),
              	
                // InvocationHandler h,实现代理功能的接口 ,我们传入的是匿名内部实现
                new InvocationHandler() {
                    @Override
                    public Object invoke(
                        Object proxy,  // 创建代理对象
                        Method method,  // method就是目标方法sing(),show()
                        Object[] args  // 目标方法的参数
                    		) throws Throwable {	
                        // 代理功能
                        System.out.println("预订时间........");
                        // 代理功能
                        System.out.println("预订场地........");
                        // 主业务功能实现
                        // target.sing();还是写死了方法的调用, 不成
                        // sing(),show(),one()
                        Object obj = method.invoke(target,args);
                        // 代理功能
                        System.out.println("结算费用........");
                        return obj;  //切记:这个是目标方法的返回值
                    }
                }
            );
        }
    }
    
  4. 客户端对象

    public class MyTest {
        @Test
        public void testJDK() {
            // ProxyFactoty factoty = new ProxyFactoty(new SuperStartLiu());
            ProxyFactoty factoty = new ProxyFactoty(new SuperStartLiu());
            // factoty.getAgent(); 的返回值是 Object,无法直接用它调用sing方法
            Service agent = (Service) factoty.getAgent();
            agent.sing();
        }
        @Test
        public void testJDK1() {
            // ProxyFactoty factoty = new ProxyFactoty(new SuperStartLiu());
            ProxyFactoty factoty = new ProxyFactoty(new SuperStartLiu());
            Service agent = (Service) factoty.getAgent();
            String s = agent.show(66);
            System.out.println(s);
        }
    
        @Test
        public void testObject() {
            ProxyFactoty factoty = new ProxyFactoty(new SuperStartLiu());
            Service agent = (Service) factoty.getAgent();
             // class com.sun.proxy.$Proxy4 代理对象类型(运行时类型) 
            System.out.println(agent.getClass()); 
    
            Service liu = new SuperStartLiu();
            System.out.println(liu.getClass());
        }
    
        @Test
        public void testJDK2() {
            // ProxyFactoty factoty = new ProxyFactoty(new SuperStartLiu());
            ProxyFactoty factoty = new ProxyFactoty(new SuperStartLiu());
            // one方法是 子类 独有的
            // Service agent = (Service) factoty.getAgent();
            // agent.one();
            
            // 类型转换异常
            // SuperStartLiu agent = (SuperStartLiu) factoty.getAgent();
            // agent.one();
        }
    
    }
    

    使用动态代理,在Service中新增业务功能不用更改 ProxyFactoty类中的代码;而静态代理中,需要更改代理对象中的代码。

10.CGLib动态代理(用的少)

又称为子类。通过动态的在内存中构建子类对象,重写父类的方法进行代理功能的增强.

如果目标对象没有实现接口,则只能通过CGLib子类代理来进行功能增强.

子类代理是对象字节码框架ASM来实现的.

项目路径【自制的一个简单的CGLib代理】

注意:

  • 被代理的类不能为final, 否则报错.

  • 目标对象的方法如果为final/static, 那么就不会被拦截,即不会执行目标对象额外的业务方法。

public Object getProxyInstance(){		
    //1.使用工具类
    Enhancer en=new Enhancer();
    //2.设置父类
    en.setSuperclass(target.getClass());
    //3.设置回调函数
    en.setCallback(this);
    //4.创建子类(代理)对象
    return en.create();  ===>返回的是子类代理对象
}

**

子类代理是对象字节码框架ASM来实现的.

项目路径【自制的一个简单的CGLib代理】

注意:

  • 被代理的类不能为final, 否则报错.

  • 目标对象的方法如果为final/static, 那么就不会被拦截,即不会执行目标对象额外的业务方法。

public Object getProxyInstance(){		
    //1.使用工具类
    Enhancer en=new Enhancer();
    //2.设置父类
    en.setSuperclass(target.getClass());
    //3.设置回调函数
    en.setCallback(this);
    //4.创建子类(代理)对象
    return en.create();  ===>返回的是子类代理对象
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值