精华内容
下载资源
问答
  • Nginx各种配置(包括负载均衡、静态代理、动静分离)
  • NULL 博文链接:https://daxiaoli123.iteye.com/blog/940784
  • 静态代理设计模式1.1 静态代理 – 实现步骤1.2 静态代理 – 逻辑图 & 源码示例1.3 静态代理 – 优缺2. 装饰者设计模式 – 类似静态代理2.1 装饰者模式 – 实现步骤2.2 装饰者模式 – 逻辑图 & 源码示例2.3 装饰者...
  • 主要对Spring AOP的相关概念和简单的静态代理、动态代理以及常见的几种AOP配置方式做总结学习。主要包括:1. AOP的常见概念 2. 静态代理 3. jdk动态代理 4. Aspectj and Aspectjweaver 5. **aop-config** 6. CGLIB ...
  • 此资源为文章中《代理设计模式:静态代理和动态代理的理解、实现与区别(优缺点)》与《 AOP动态代理声明式的3种配置方式过程与区别》的案例代码,可下载参考学习。
  • NULL 博文链接:https://871656094.iteye.com/blog/2355335
  • 本资源包含了博主博客中的代码,用一个黄牛买票的例子帮助新手理解静态代理与动态代理的原理
  • 本工程是对静态代理,动态代理的测试,并且实现了对jdk动态代理的简单模拟!
  • NULL 博文链接:https://wmwork2010.iteye.com/blog/631978
  • 静态代理和动态代理

    2019-03-06 01:31:55
    博文链接:https://congpeixue.iteye.com/blog/159173
  • 静态代理代理模式,顾名思义就是提供一个代理类,可以访问原对象并且替原对象进行一些操作。 优点:使用代理模式可以在保证不修改原有类的同时(即满足对扩展开放,对修改关闭的原则),对原有类增加一些功能实现。
  • 静态代理详解

    千次阅读 2021-02-25 22:20:21
    1.什么是静态代理? 代理这个词是来源于Java设计模式中的代理模式,代理模式最简单的理解就是通过第三方来代理我们的工作 比如中介,房东需要将自己的房子租出去,而租客需要租房子,三者关系如此 租客租房子一般...

    什么是静态代理?

    代理这个词是来源于Java设计模式中的代理模式,代理模式最简单的理解就是通过第三方来代理我们的工作

    比如中介,房东需要将自己的房子租出去,而租客需要租房子,三者关系如此

    租客租房子一般都找不到房东,房东也不会轻易将自己暴露给广大租客,因此就需要中介来充当这个中间关系

    因此租客就只能通过中介来进行租房子这个工作,不需要通过房东,这就叫做代理----就是中介代理房东来处理租房子这件事情

    那么我们应用于Java中又是什么样的情况呢?

    首先创建一个主题接口(创建接口,JDK动态代理就是以接口、对象、代理对象为基础)

    public interface UserDao(){
        void add();
    }

    然后创建该接口的实现类(真实主题),也称为被代理类(主要功能就在于该类中),我们需要一个代理类来代理这个类实现他的add方法

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

    然后我们创建上述实现类的代理类(两个类需要实现共同的主题接口,这样才能对原有功能进行增强),使用代理类对原有类进行包装

    public class UserDaoProxy implements UserDao{
        private UserDao target;
        public UserDaoProxy(UserDao target){
            this.target = target;
        }
    
        public void add(){
            System.out.println("before...");
            target.add();
            System.out.println("after...");
        }
    }

    然后我们进行测试

    @Test
        public void test1(){
           UserDao userDao = new UserDaoImpl();
           UserDaoProxy proxy = new UserDaoProxy(userDao);
           proxy.add();
        }

    此图为结果输出

     

    可能有人会问,这样是不是多此一举,为什么要去代理呢,而且前两段代码就能实现的功能,为什么还需要一个代理类去代理它,这就涉及到我们的工作需求,在不改变实现类的情况下,对实现类进行功能的增加,而上述代码中两个输出语句就是对功能的增强(当然可以根据实际需求来添加实际代码),由此而产生了代理类,生成代理对象

    而所谓的静态就是在创建代理类的时候,接口和被代理类都已经被固定了,无法改变,代理类就只能这一种类,我们主要的是实现代理功能,实际上已经完成了这个功能,

    当实际开发中有很多这样的主题接口和类需要功能增强时,就需要更多的代理类,即每一个主题接口都得创建一个代理类,会造成代码的繁多和冗余,因此就产生了我们的动态代理技术https://blog.csdn.net/qq_42051306/article/details/114141307?spm=1001.2014.3001.5501

    展开全文
  • 思考:以常见的增删改查为例,在执行增删改的时候我们需要开启事务,执行完成后...于是就出现了代理模式的概念,那什么是代理模式呢? 1、什么是代理模式 简单来说:代理就是对目标方法进行增强。 什么意思呢?...

    视频功能审核通过了,可以看视频啦!记得点关注啊~

    注意:因为网络原因,视频前一两分钟可能会比较模糊,过一会儿就好了

    记得点关注啊,视频里的wx二维码失效了,wx搜索:“聊5毛钱的java 或 扫码关注公众号,欢迎一起学习交流

    快扫码关注啦!关注可领取博主的Java学习视频+资料,保证都是干货

    SpringAOP系列,帮你了解SpringAOP的来龙去脉

    思考:以常见的增删改查为例,在执行增删改的时候我们需要开启事务,执行完成后需要提交事务

    假如说你Service里有100个增删改的方法,那这100个方法里除了你自己真正需要处理的业务逻辑外,每个方法都还需要去关注开启事务,提交事务这些动作。那有没有稍微好点的办法解决呢?于是就出现了代理模式的概念,那什么是代理模式呢?

    1、什么是代理模式

    简单来说:代理就是对目标方法进行增强。

    什么意思呢?还是上边的例子,Service里你的业务逻辑我们称之为需要执行的目标方法;开启事务,提交事务这些我们就可以称之为对目标方法的增强。于是乎,我们可不可以这样:“需要执行的目标”单独写一个类(目标类),“需要增强的动作”单独写一个类(增强类),最后再写一个类(代理类),把它两者结合到一起。这样的话,是不是就实现了开启事务,提交事务这些动作只需要在增强类里写一遍,然后我们在业务目标类里就只关心我们的业务动作就行了,其他的乱七八糟的比如开启事务、提交事务(除了这些还可以有别的一些动作,统一的日志操作等等)等等这些增强的动作我都不关注了,在业务类里只写业务!最后在代理类里把增强的动作和目标动作结合起来就ok了,然后用的时候,用咱们的代理对象就好了。这样做就实现了在业务类里可以只专注于业务,其他的不管,而且到达了松耦合的效果(就是那些增强的动作,比如事务,统一的日志操作等等,这些动作和我的业务是分离开的)。

    代理分为静态代理和动态代理。

    下边先用静态写一下,简单实现一下上述的内容

    2、代码实现

    package com.cj.study.proxy;
    
    public interface PersonService {
    	
    	public void savePerson();
    	
    	public void updatePerson();
    	
    	public void deletePerson();
    	
    }
    
    package com.cj.study.proxy;
    
    public class PersonServiceImpl implements PersonService{
    
    	@Override
    	public void savePerson() {
    		System.out.println("添加");
    	}
    
    	@Override
    	public void updatePerson() {
    		System.out.println("修改");
    	}
    
    	@Override
    	public void deletePerson() {
    		System.out.println("删除");
    	}
    
    }
    
    package com.cj.study.proxy;
    
    public class Transaction {
    	public void beginTransaction(){
    		System.out.println("开启事务 ");
    	}
    	public void commit(){
    		System.out.println("提交事务");
    	}
    }
    
    package com.cj.study.proxy;
    
    public class PersonServiceProxy implements PersonService{
    	
    	//目标类
    	private PersonService personService;
    	
    	//增强类
    	private Transaction transaction;
    	
    	//利用构造函数将目标类和增强类注入
    	public PersonServiceProxy(PersonService personService,Transaction transaction){
    		this.personService = personService;
    		this.transaction = transaction;
    	}
    	
    	@Override
    	public void savePerson() {
    		transaction.beginTransaction();
    		personService.savePerson();
    		transaction.commit();
    	}
    
    	@Override
    	public void updatePerson() {
    		transaction.beginTransaction();
    		personService.updatePerson();
    		transaction.commit();
    	}
    
    	@Override
    	public void deletePerson() {
    		transaction.beginTransaction();
    		personService.deletePerson();
    		transaction.commit();
    	}
    
    }
    

    package com.cj.study.proxy;
    
    import org.junit.Test;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class ProxyTest {
    	@Test
    	public void test(){
    		ApplicationContext context = new ClassPathXmlApplicationContext("com/cj/study/proxy/applicationContext-proxy.xml");
    		PersonService personService = (PersonService)context.getBean("personServiceProxy");
    		personService.savePerson();
    	}
    }
    
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xsi:schemaLocation="http://www.springframework.org/schema/beans
               http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
        
    	<bean id="personServie" class="com.cj.study.proxy.PersonServiceImpl"></bean>
    	
    	<bean id="transaction" class="com.cj.study.proxy.Transaction"></bean>
    	
    	<bean id="personServiceProxy" class="com.cj.study.proxy.PersonServiceProxy">
    		<constructor-arg index="0" ref="personServie"></constructor-arg>
    		<constructor-arg index="1" ref="transaction"></constructor-arg>
    	</bean>
    	
    </beans>
    

    最后的执行结果:

    这样就是简单的静态代理。

    静态代理是在程序运行前,代理类的.class文件就已经存在了

    3、分析静态代理的缺点

    静态代理模式的缺点:

    1、假设一个系统中有100个Service,则需要创建100个代理对象

    2、如果一个Service中有很多方法需要事务(增强动作),发现代理对象的方法中还是有很多重复的代码

    3、由第一点和第二点可以得出:静态代理的重用性不强

    那怎么解决呢?

    用动态代理就可以很好的解决上述问题

    下一篇会讲一下动态代理:Java中动态代理的两种方式JDK动态代理和cglib动态代理以及区别

    铁子们,如果觉得文章对你有所帮助,可以点关注,点赞

    也可以关注下公众号:扫码或 wx搜索:“聊5毛钱的java”,欢迎一起学习交流,关注公众号可领取博主的Java学习视频+资料,保证都是干货

    3Q~

    纯手敲原创不易,如果觉得对你有帮助,可以打赏支持一下,哈哈,感谢~

               

    展开全文
  • JAVA静态代理和动态代理
  • Java中的代理模式--静态代理和动态代理 Java中的代理模式--静态代理和动态代理
  • 动态代理和静态代理demo
  • 资源列举了设计模式中的静态代理和动态代理的简单java实现,jdk1.8版本经过测试验证,对于想学习设计模式的童靴应该有所帮助
  • Java 静态代理模式

    2017-07-02 19:40:18
    Java 代理模式: 详见:http://blog.csdn.net/q610098308/article/details/74144046
  • 动态代理与静态代理区别

    万次阅读 多人点赞 2018-09-18 09:26:55
    一、代理概念  为某个对象提供一个代理,以控制对这个对象的访问。 代理类和委托类有共同的父类或父接口,这样在任何使用委托类对象的地方都可以用代理对象替代。代理类负责请求的预处理、过滤、将请求分派给委托...

    一、代理概念 

    为某个对象提供一个代理,以控制对这个对象的访问。 代理类和委托类有共同的父类或父接口,这样在任何使用委托类对象的地方都可以用代理对象替代。代理类负责请求的预处理、过滤、将请求分派给委托类处理、以及委托类执行完请求后的后续处理。 

    图1:代理模式 

    从图中可以看出,代理接口(Subject)、代理类(ProxySubject)、委托类(RealSubject)形成一个“品”字结构。

    根据代理类的生成时间不同可以将代理分为静态代理和动态代理两种。 

    下面以一个模拟需求说明静态代理和动态代理:委托类要处理一项耗时较长的任务,客户类需要打印出执行任务消耗的时间。解决这个问题需要记录任务执行前时间和任务执行后时间,两个时间差就是任务执行消耗的时间。 

    二、静态代理 

    由程序员创建或工具生成代理类的源码,再编译代理类。所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。 

    清单1:代理接口 

    /**  
     * 代理接口。处理给定名字的任务。 
     */  
    public interface Subject {  
      /** 
       * 执行给定名字的任务。 
        * @param taskName 任务名 
       */  
       public void dealTask(String taskName);   
    }  

    清单2:委托类,具体处理业务。

    /** 
     * 真正执行任务的类,实现了代理接口。 
     */  
    public class RealSubject implements Subject {  
      
     /** 
      * 执行给定名字的任务。这里打印出任务名,并休眠500ms模拟任务执行了很长时间 
      * @param taskName  
      */  
       @Override  
       public void dealTask(String taskName) {  
          System.out.println("正在执行任务:"+taskName);  
          try {  
             Thread.sleep(500);  
          } catch (InterruptedException e) {  
             e.printStackTrace();  
          }  
       }  
    }  

    清单3:静态代理类

    /** 
     * 代理类,实现了代理接口。 
     */  
    public class ProxySubject implements Subject {  
     //代理类持有一个委托类的对象引用  
     private Subject delegate;  
       
     public ProxySubject(Subject delegate) {  
      this.delegate = delegate;  
     }  
      
     /** 
      * 将请求分派给委托类执行,记录任务执行前后的时间,时间差即为任务的处理时间 
      *  
      * @param taskName 
      */  
     @Override  
     public void dealTask(String taskName) {  
      long stime = System.currentTimeMillis();   
      //将请求分派给委托类处理  
      delegate.dealTask(taskName);  
      long ftime = System.currentTimeMillis();   
      System.out.println("执行任务耗时"+(ftime - stime)+"毫秒");  
        
     }  
    } 

    清单4:生成静态代理类工厂

    public class SubjectStaticFactory {  
     //客户类调用此工厂方法获得代理对象。  
     //对客户类来说,其并不知道返回的是代理类对象还是委托类对象。  
     public static Subject getInstance(){   
      return new ProxySubject(new RealSubject());  
     }  
    }  

    清单5:客户类

    public class Client1 {  
      
     public static void main(String[] args) {  
      Subject proxy = SubjectStaticFactory.getInstance();  
      proxy.dealTask("DBQueryTask");  
     }   
      
    }  

    静态代理类优缺点 

    • 优点:
    1. 业务类只需要关注业务逻辑本身,保证了业务类的重用性。这是代理的共有优点。
    • 缺点:
    1. 代理对象的一个接口只服务于一种类型的对象,如果要代理的方法很多,势必要为每一种方法都进行代理,静态代理在程序规模稍大时就无法胜任了。 
    2. 如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。 

    三、动态代理 

    动态代理类的源码是在程序运行期间由JVM根据反射等机制动态的生成,所以不存在代理类的字节码文件。代理类和委托类的关系是在程序运行时确定。 

    1、先看看与动态代理紧密关联的Java API

    1)java.lang.reflect.Proxy

    这是 Java 动态代理机制生成的所有动态代理类的父类,它提供了一组静态方法来为一组接口动态地生成代理类及其对象。 

    清单6:Proxy类的静态方法

    // 方法 1: 该方法用于获取指定代理对象所关联的调用处理器  
    static InvocationHandler getInvocationHandler(Object proxy)   
      
    // 方法 2:该方法用于获取关联于指定类装载器和一组接口的动态代理类的类对象  
    static Class getProxyClass(ClassLoader loader, Class[] interfaces)   
      
    // 方法 3:该方法用于判断指定类对象是否是一个动态代理类  
    static boolean isProxyClass(Class cl)   
      
    // 方法 4:该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例  
    static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)   

    2)java.lang.reflect.InvocationHandler

    这是调用处理器接口,它自定义了一个 invoke 方法,用于集中处理在动态代理类对象上的方法调用,通常在该方法中实现对委托类的代理访问。每次生成动态代理类对象时都要指定一个对应的调用处理器对象。 

    清单7:InvocationHandler的核心方法

    // 该方法负责集中处理动态代理类上的所有方法调用。第一个参数既是代理类实例,第二个参数是被调用的方法对象  
    // 第三个方法是调用参数。调用处理器根据这三个参数进行预处理或分派到委托类实例上反射执行  
    Object invoke(Object proxy, Method method, Object[] args)  

    3)java.lang.ClassLoader

    这是类装载器类,负责将类的字节码装载到 Java 虚拟机(JVM)中并为其定义类对象,然后该类才能被使用。Proxy 静态方法生成动态代理类同样需要通过类装载器来进行装载才能使用,它与普通类的唯一区别就是其字节码是由 JVM 在运行时动态生成的而非预存在于任何一个 .class 文件中。 

    每次生成动态代理类对象时都需要指定一个类装载器对象

    2、动态代理实现步骤

    具体步骤是:

    1. 实现InvocationHandler接口创建自己的调用处理器
    2. 给Proxy类提供ClassLoader和代理接口类型数组创建动态代理类
    3. 以调用处理器类型为参数,利用反射机制得到动态代理类的构造函数
    4. 以调用处理器对象为参数,利用动态代理类的构造函数创建动态代理类对象

    清单8:分步骤实现动态代理 

    // InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发  
    // 其内部通常包含指向委托类实例的引用,用于真正执行分派转发过来的方法调用  
    InvocationHandler handler = new InvocationHandlerImpl(..);   
      
    // 通过 Proxy 为包括 Interface 接口在内的一组接口动态创建代理类的类对象  
    Class clazz = Proxy.getProxyClass(classLoader, new Class[] { Interface.class, ... });   
      
    // 通过反射从生成的类对象获得构造函数对象  
    Constructor constructor = clazz.getConstructor(new Class[] { InvocationHandler.class });   
      
    // 通过构造函数对象创建动态代理类实例  
    Interface Proxy = (Interface)constructor.newInstance(new Object[] { handler });  

    Proxy类的静态方法newProxyInstance对上面具体步骤的后三步做了封装,简化了动态代理对象的获取过程。

    清单9:简化后的动态代理实现 

    // InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发  
    InvocationHandler handler = new InvocationHandlerImpl(..);   
      
    // 通过 Proxy 直接创建动态代理类实例  
    Interface proxy = (Interface)Proxy.newProxyInstance( classLoader,   
         new Class[] { Interface.class },  handler );   

    3、动态代理实现示例 

    清单10:创建自己的调用处理器 

    /** 
     * 动态代理类对应的调用处理程序类 
     */  
    public class SubjectInvocationHandler implements InvocationHandler {  
       
     //代理类持有一个委托类的对象引用  
     private Object delegate;  
       
     public SubjectInvocationHandler(Object delegate) {  
      this.delegate = delegate;  
     }  
       
     @Override  
     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  
      long stime = System.currentTimeMillis();   
      //利用反射机制将请求分派给委托类处理。Method的invoke返回Object对象作为方法执行结果。  
      //因为示例程序没有返回值,所以这里忽略了返回值处理  
      method.invoke(delegate, args);  
      long ftime = System.currentTimeMillis();   
      System.out.println("执行任务耗时"+(ftime - stime)+"毫秒");  
        
      return null;  
     }  
    }   

    清单11:生成动态代理对象的工厂,工厂方法列出了如何生成动态代理类对象的步骤。

    /** 
     * 生成动态代理对象的工厂. 
     */  
    public class DynProxyFactory {  
     //客户类调用此工厂方法获得代理对象。  
     //对客户类来说,其并不知道返回的是代理类对象还是委托类对象。  
     public static Subject getInstance(){   
      Subject delegate = new RealSubject();  
      InvocationHandler handler = new SubjectInvocationHandler(delegate);  
      Subject proxy = null;  
      proxy = (Subject)Proxy.newProxyInstance(  
        delegate.getClass().getClassLoader(),   
        delegate.getClass().getInterfaces(),   
        handler);  
      return proxy;  
     }  
    }  

    清单12:动态代理客户类 

    public class Client {  
      
     public static void main(String[] args) {  
      
      Subject proxy = DynProxyFactory.getInstance();  
      proxy.dealTask("DBQueryTask");  
     }   
      
    }  

    4、动态代理机制特点

    首先是动态生成的代理类本身的一些特点。

    • 包:如果所代理的接口都是 public 的,那么它将被定义在顶层包(即包路径为空),如果所代理的接口中有非 public 的接口(因为接口不能被定义为 protect 或 private,所以除 public 之外就是默认的 package 访问级别),那么它将被定义在该接口所在包(假设代理了 com.ibm.developerworks 包中的某非 public 接口 A,那么新生成的代理类所在的包就是 com.ibm.developerworks),这样设计的目的是为了最大程度的保证动态代理类不会因为包管理的问题而无法被成功定义并访问;
    • 类修饰符:该代理类具有 final 和 public 修饰符,意味着它可以被所有的类访问,但是不能被再度继承;
    • 类名:格式是“$ProxyN”,其中 N 是一个逐一递增的阿拉伯数字,代表 Proxy 类第 N 次生成的动态代理类,值得注意的一点是,并不是每次调用 Proxy 的静态方法创建动态代理类都会使得 N 值增加,原因是如果对同一组接口(包括接口排列的顺序相同)试图重复创建动态代理类,它会很聪明地返回先前已经创建好的代理类的类对象,而不会再尝试去创建一个全新的代理类,这样可以节省不必要的代码重复生成,提高了代理类的创建效率。
    • 类继承关系:该类的继承关系如图: 

    图2:动态代理类的继承关系 

    由图可见,Proxy 类是它的父类,这个规则适用于所有由 Proxy 创建的动态代理类。而且该类还实现了其所代理的一组接口,这就是为什么它能够被安全地类型转换到其所代理的某接口的根本原因。 

    接下来让我们了解一下代理类实例的一些特点。每个实例都会关联一个调用处理器对象,可以通过 Proxy 提供的静态方法 getInvocationHandler 去获得代理类实例的调用处理器对象。在代理类实例上调用其代理的接口中所声明的方法时,这些方法最终都会由调用处理器的 invoke 方法执行,此外,值得注意的是,代理类的根类 java.lang.Object 中有三个方法也同样会被分派到调用处理器的 invoke 方法执行,它们是 hashCode,equals 和 toString,可能的原因有:一是因为这些方法为 public 且非 final 类型,能够被代理类覆盖;二是因为这些方法往往呈现出一个类的某种特征属性,具有一定的区分度,所以为了保证代理类与委托类对外的一致性,这三个方法也应该被分派到委托类执行。当代理的一组接口有重复声明的方法且该方法被调用时,代理类总是从排在最前面的接口中获取方法对象并分派给调用处理器,而无论代理类实例是否正在以该接口(或继承于该接口的某子接口)的形式被外部引用,因为在代理类内部无法区分其当前的被引用类型。 

    接着来了解一下被代理的一组接口有哪些特点。首先,要注意不能有重复的接口,以避免动态代理类代码生成时的编译错误。其次,这些接口对于类装载器必须可见,否则类装载器将无法链接它们,将会导致类定义失败。再次,需被代理的所有非 public 的接口必须在同一个包中,否则代理类生成也会失败。最后,接口的数目不能超过 65535,这是 JVM 设定的限制。 

    最后再来了解一下异常处理方面的特点。从调用处理器接口声明的方法中可以看到理论上它能够抛出任何类型的异常,因为所有的异常都继承于 Throwable 接口,但事实是否如此呢?答案是否定的,原因是我们必须遵守一个继承原则:即子类覆盖父类或实现父接口的方法时,抛出的异常必须在原方法支持的异常列表之内。所以虽然调用处理器理论上讲能够,但实际上往往受限制,除非父接口中的方法支持抛 Throwable 异常。那么如果在 invoke 方法中的确产生了接口方法声明中不支持的异常,那将如何呢?放心,Java 动态代理类已经为我们设计好了解决方法:它将会抛出 UndeclaredThrowableException 异常。这个异常是一个 RuntimeException 类型,所以不会引起编译错误。通过该异常的 getCause 方法,还可以获得原来那个不受支持的异常对象,以便于错误诊断。 

    5、动态代理的优点和美中不足 

    • 优点:动态代理与静态代理相比较,最大的好处是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理(InvocationHandler.invoke)。这样,在接口方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那样每一个方法进行中转。在本示例中看不出来,因为invoke方法体内嵌入了具体的外围业务(记录任务处理前后时间并计算时间差),实际中可以类似Spring AOP那样配置外围业务。 
    • 缺点:诚然,Proxy 已经设计得非常优美,但是还是有一点点小小的遗憾之处,那就是它始终无法摆脱仅支持 interface 代理的桎梏,因为它的设计注定了这个遗憾。回想一下那些动态生成的代理类的继承关系图,它们已经注定有一个共同的父类叫 Proxy。Java 的继承机制注定了这些动态代理类们无法实现对 class 的动态代理,原因是多继承在 Java 中本质上就行不通。 

    有很多条理由,人们可以否定对 class 代理的必要性,但是同样有一些理由,相信支持 class 动态代理会更美好。接口和类的划分,本就不是很明显,只是到了 Java 中才变得如此的细化。如果只从方法的声明及是否被定义来考量,有一种两者的混合体,它的名字叫抽象类。实现对抽象类的动态代理,相信也有其内在的价值。此外,还有一些历史遗留的类,它们将因为没有实现任何接口而从此与动态代理永世无缘。如此种种,不得不说是一个小小的遗憾。

     原文链接:动态代理与静态代理区别

    展开全文
  • 文章目录什么是代理为什么使用代理跨网络调用对象保证类的功能的单一性原则如何实现代理静态代理1. 基于继承的代理2. 基于接口的代理动态代理1. JDK动态代理2. cglib动态代理实现 什么是代理    &...

    什么是代理

        代理分为静态代理和动态代理,在未产生动态代理之前,代理只是为了给某一个类创建一个代理类来为这个类的对象动态添加一些职责和功能。而动态代理产生后,将这种在编译期实现的代理转到了运行时来实现,减少了代理对象和被代理对象之间的一个耦合程度。动态代理可以在运行时创建一个实现了一组给定接口的类,这种功能只有在编译时无法确定需要实现哪个接口时才有必要使用。


    为什么使用代理

    • 跨网络调用对象

        在程序设计当中,经常会出现客户端无法直接调用实际对象,因为可能客户端需要调用的对象在另外一台机器上,而我们则需要跨网络调用,如果直接调用的话,我们需要处理网络链接、打包和解包等非常复杂的步骤。所以为了简化客户端的处理,采用代理模式,在客户端创建一个代理对象,由代理对象去和实际对象联系。

    • 保证类的功能的单一性原则

        在设计类时,需要尽量保证类的功能单一性。一个类功能的冗余可能会导致两方面的问题:

    1. 对类的单一功能进行改动较为复杂,破坏了面向对象程序设计的一个封装性;
    2. 功能过多造成类加载耗时。在设计程序时,我们需要尽可能保证打开一个程序时不去加载本地不应该加载的一些文件,例如我们想打开word,我们没有必要去加载word之前创建的一个文件或者是一张图片,我们可以为这个文件对象设计一个代理对象,让代理对象去执行文件的打开,而不影响应用程序的加载速度;

    如何实现代理

    • 静态代理

        静态代理的实现有两种方式,最为常见也是我们平时最不经意的一种代理实现就是基于继承的代理实现,另一种则是基于接口的代理实现。

    1. 基于继承的代理

    public class Employee {
    
        Integer age;
    
        public Employee(Integer age){
            this.age = age;
        }
    
        public Integer getAge(){
            System.out.println("Employee getAge");
            return age;
        }
    
    }
    
    public class EmployeeProxy extends Employee {
    
        public EmployeeProxy(Integer age) {
            super(age);
        }
    
        @Override
        public Integer getAge() {
            System.out.println("增加功能!!!");
            return super.getAge();
        }
    }
    
    public class TestProxy {
        public static void main(String[] args) {
            Employee employee = new EmployeeProxy(23);
            employee.getAge();
        }
    }
    
    输出结果:
    增加功能!!!
    Employee getAge
    

        我们可以看到,EmployeeProxy类中的getAge()方法实际上最终调用的是Employee的getAge()方法,同时也可以动态为代理类增加功能。

    2. 基于接口的代理

    public interface FixEmployeeInfo {
        Integer FixAge(Integer age);
    }
    
    public class Employee implements FixEmployeeInfo {
    
        Integer age;
    
        public Employee(Integer age){
            this.age = age;
        }
    
        @Override
        public Integer FixAge(Integer age) {
            System.out.println("Employee FixAge");
            return this.age = age;
        }
    }
    
    public class EmployeeProxy implements FixEmployeeInfo {
    
        private FixEmployeeInfo employee;
    
        @Override
        public Integer FixAge(Integer age) {
            if(employee == null)
                employee = new Employee(age);
            System.out.println("记录修改日期");
            return employee.FixAge(age);
        }
    }
    
    public class TestProxy {
        public static void main(String[] args) {
            FixEmployeeInfo employee = new EmployeeProxy();
            employee.FixAge(23);
        }
    }
    
    输出结果:
    记录修改日期
    Employee FixAge
    
    

        基于接口的代理实现需要代理对象和被代理对象实现同一个接口,代理类在调用被代理对象的方法的同时还可以动态的增加或者是修改被代理对象的功能,很大程度上增加了程序设计的灵活性。

        静态代理也存在很大的不足。首先他的代理对象只能服务于一种类型的对象,例如Emlpoyee下可能会有Manager或者是staff等,我们要为这两个类型的对象生成代理对象时,只能逐一生成,在代理规模比较大的情况下,很明显静态代理模式就已经不适用了。其次,基于接口实现的代理,由于需要代理类和类实现同一个接口,所以一旦接口方法增加,不仅要改变普通的实现类改变,连代理类也会随之而改变。

    • 动态代理

        动态代理与静态代理不同的是,在实现阶段不用去关心代理类,将编译期间的代理转移到了运行期间。那么此时我们就会想,在运行时JVM是怎样定位代理对象以及怎样定位代理类的方法呢?实际上还是依赖于反射机制。Java中的动态代理分为两种,一种是JDK动态代理,另一种是cglib动态代理。

    1. JDK动态代理

        JDK动态代理的实现,主要依赖的是Proxy类的newProxyInstance方法和InvocationHandler类的invoke方法。此时看到invoke()方法后,我们首先会想到反射中对任意方法调用的实现,这也是动态代理的一个核心。JDK动态代理实现如下:

    1. 定义代理目标接口(JDk动态代理不能代理类,只能基于接口实现)
    
    public interface FixEmloyeeInfo {
        Integer FixEmployeeInfo(Integer age);
    }
    
    2. 定义代理目标类
    
    public class Employee implements FixEmloyeeInfo {
    
        @Override
        public Integer FixEmployeeInfo(Integer age) {
            System.out.println("执行  FixEmployeeInfo()");
            return age + 1;
        }
    }
    
    3. 定义扩展处理类
    
    public class EmployeeHandler implements InvocationHandler {
        private Object target;
        public EmployeeHandler(Object target){
            this.target = target;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
            return method.invoke(target , args);
        }
    }
    
    
    4. 测试JDK动态代理
    
    public class TestProxy {
        public static void main(String[] args) {
            FixEmloyeeInfo employee = (FixEmloyeeInfo) Proxy.newProxyInstance(Employee.class.getClassLoader(), Employee.class.getInterfaces(), new EmployeeHandler(new Employee()));
            employee.FixEmployeeInfo(23);
            System.out.println("代理类名:  " + employee.getClass().getName());
            Method[] methods = employee.getClass().getDeclaredMethods();
            for (Method method : methods) {
                System.out.println(method);
            }
        }
    }
    输出结果:
    执行  FixEmployeeInfo()
    代理类名:  com.sun.proxy.$Proxy0
    public final boolean com.sun.proxy.$Proxy0.equals(java.lang.Object)
    public final java.lang.String com.sun.proxy.$Proxy0.toString()
    public final int com.sun.proxy.$Proxy0.hashCode()
    public final java.lang.Integer com.sun.proxy.$Proxy0.FixEmployeeInfo(java.lang.Integer)
    

        我们从上述的测试可以看出来,JDK动态代理为代理接口生成了一个名为$Proxy0的类,不仅代理了FixEmployeeInfo()方法,还重写了Object类中的一些方法。JDk动态代理的核心和最难点就在于生成代理类,它依赖于Proxy类的newProxyInstance()方法。该方法接受参数为目标类、目标接口类和处理器InvocationHandler实现类:

       public static Object newProxyInstance(ClassLoader loader,
                                              Class<?>[] interfaces,
                                              InvocationHandler h)
            throws IllegalArgumentException
        {};
    

        在学习动态代理的时候,我们常常遇到了两个难点,分别是代理类是如何生成的和invoke方法是谁调用的,这也是代理实现原理的关键两部分:

        a. 查找或者生成指定的代理类

        生成指定的代理类主要依赖的传入的被代理类的类加载器和被代理类所实现的接口,虽然一个类可以同时实现多个接口,但在生成代理类时首先就会判断实现的接口个数是否超过了65535这个限额,如果超过这个限额则直接会抛出异常。在proxy类中会存在一个loaderToCache缓存变量,JVM所加载的类都会存入这个缓存之中,它的数据结构实际上是一个以ClassLoader为key的map,这也就是我们为什么需要传入被代理类的ClassLoader对象的原因,而在getProxyClass()方法中也会声明一个以interfaceName为key的map结构,value值则存储的是根据ClassLoader从JVM缓存中拿出来的代理类的class对象,具体的缓存结构如下:
    image

        代理类对象也并不是与生俱来的,在getProxyClass()方法中,我们会发现在获取代理类对象时,会进行几次判断如下:

        if (proxyClass != null) {  
            return proxyClass;  
        } else if (value == pendingGenerationMarker) {  
            try {  
                cache.wait();  
            } catch (InterruptedException e) {  
            }  
                continue;  
            } else {  
                cache.put(key, pendingGenerationMarker);  
                break;  
            }  
        } while (true);  
    

    这三次判断是判断JVM中是否存在代理类的Class对象,如果存在代理类的Class对象,我们则不需要在创建了,直接从缓存中返回Class 对象即可,如果Class对象正在被创建,则我们需要等待Class对象被创建完成之后才可获取,如果两种可能都不是则直接加载Class对象。而代理类Class对象又是什么时候如何被加载到JVM缓存中的呢?

    若要生成代理类class对象,我们只能先生成代理类的字节码文件然后再加载到JVM内存当中,字节码文件的生成很多都是调用了反射中的方法,然后根据接口和目标代理类类名来拼接字节码生成文件并写入本地硬盘。字节码文件生成之后则选取的类加载器就会将字节码文件存入JVM内存当中并生成代理类实例。

        b. 调用InvocationHandler的invoke()方法来拓展代理方法;

        在实现JDK动态代理时,我们发现InvocationHandler的实现类重写了invoke()方法,这个方法是拓展代理方法的关键,而在JDK实现中却没有一个类中调用了invoke()方法。实际上,invoke()方法的调用存在于JDK为我们产生的代理类中,只是在生成代理类时很多时候我们都没有去保存他的class文件,以文章中的JDK动态代理示例,我们可以反编译看一下代理的class文件的源码是怎样的:

    public final class $proxy1 extends Proxy implements FixEmloyeeInfo {
        private static Method m1;
        private static Method m2;
        private static Method m3;
        private static Method m0;
    
        public $proxy1(InvocationHandler var1) throws  {
            super(var1);
        }
    
        public final boolean equals(Object var1) throws  {
            try {
                return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
            } catch (RuntimeException | Error var3) {
                throw var3;
            } catch (Throwable var4) {
                throw new UndeclaredThrowableException(var4);
            }
        }
    
        public final String toString() throws  {
            try {
                return (String)super.h.invoke(this, m2, (Object[])null);
            } catch (RuntimeException | Error var2) {
                throw var2;
            } catch (Throwable var3) {
                throw new UndeclaredThrowableException(var3);
            }
        }
    
        public final Integer FixEmployeeInfo(Integer var1) throws  {
            try {
                return (Integer)super.h.invoke(this, m3, new Object[]{var1});
            } catch (RuntimeException | Error var3) {
                throw var3;
            } catch (Throwable var4) {
                throw new UndeclaredThrowableException(var4);
            }
        }
    
        public final int hashCode() throws  {
            try {
                return (Integer)super.h.invoke(this, m0, (Object[])null);
            } catch (RuntimeException | Error var2) {
                throw var2;
            } catch (Throwable var3) {
                throw new UndeclaredThrowableException(var3);
            }
        }
    
        static {
            try {
                m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
                m2 = Class.forName("java.lang.Object").getMethod("toString");
                m3 = Class.forName("com.actchen.demo.JDKProxy.FixEmloyeeInfo").getMethod("FixEmployeeInfo", Class.forName("java.lang.Integer"));
                m0 = Class.forName("java.lang.Object").getMethod("hashCode");
            } catch (NoSuchMethodException var2) {
                throw new NoSuchMethodError(var2.getMessage());
            } catch (ClassNotFoundException var3) {
                throw new NoClassDefFoundError(var3.getMessage());
            }
        }
    }
    

        我们可以清楚的看见,在代理类中存在一个static静态块,而其中就注定了在代理类被加载的时候必须调用四个方法,并且是通过forName来获取类名调用的。

        与此同时,我们也可以看到,代理类继承了Proxy类,这也就造成了JDK动态代理的一个致命缺陷,只能代理实现了接口的类而不能直接代理类,因为Java中不支持多继承,一旦一个类已经存在一个父类之后,只能通过实现接口来拓展代理方法。

    2. cglib动态代理实现

        cglib的产生可以说是为了改进JDK动态代理必须实现接口的缺点,cglib是针对类的代理模式,实现cglib必须实现MethodInterceptor接口:

    public class Employee  {
        private String name;
        private Integer age;
    
        final public String fixName(String name){
            this.name = name;
            System.out.println("Employee  fixName !");
            return name;
        }
    
        public Integer fixAge(Integer age){
            this.age = age;
            System.out.println("Employee fixAge !");
            return age;
        }
    
    }
    
    public class EmployeeInterceptor implements MethodInterceptor {
        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            System.out.println("EmployeeInterceptor : "+method.getName());
            Object object = methodProxy.invokeSuper(o , objects);
            return object;
        }
    }
    
    public class TestCglibProxy {
        public static void main(String[] args) {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(Employee.class);
            enhancer.setCallback(new EmployeeInterceptor());
            Employee proxy = (Employee) enhancer.create();
            System.out.println(proxy.getClass().getName());
            proxy.fixAge(23);
            proxy.fixName("  ");
        }
    }
    
    输出结果:
    com.actchen.demo.cglibProxy.Employee$$EnhancerByCGLIB$$ea016430
    EmployeeInterceptor : fixAge
    Employee fixAge !
    Employee  fixName !
    

        cglib与jdk不同的是,cglib摆脱了对Proxy的依赖,而避免了创建的代理类必须继承Proxy的限制。但cglib也依赖着一个类Enhancer,这个类也是为了生成代理类,但它不仅生成了代理类,还让代理类继承了被代理类,代理类会为委托方法生成两个方法,一个是重写的父类的方法,也就是被代理类的方法,另一个是代理类自身的同名方法,重写方法使用的是super调用了父类的方法,而代理类的方法则是对其的拓展。
    cglib是通过Enhancer类来获取代理对象的。在cglib中需要获取代理对象分以下四步:

        1)创建一个Enhancer对象;

        2)调用setSuperclass()方法将被代理的类设置为Enhancer类的父类;

        3)调用setCallback()方法设置Enhancer的回调对象为MethodInterceptor的实现类;

        4)调用create()方法来创建代理对象;

    展开全文
  • AOP实现的关键在于AOP框架自动创建的AOP代理,AOP代理主要分为静态代理和动态代理,静态代理的代表为AspectJ;而动态代理则以Spring AOP为代表。 何为切面? 一个和业务没有任何耦合相关的代码段,诸如:调用日志,...
  • 静态代理和动态代理 根据加载被代理类的时机不同,将代理分为静态代理和动态代理。 编译时就确定了被代理的类是哪一个,那么就可以直接使用静态代理; 运行时才确定被代理的类是哪个,那么可以使用类动态代理。 1...
  • 简单学习多线程有力的好帮手,我也是刚刚学习总结的自己的经验 希望大家借鉴,
  • 静态代理和动态代理的区别和联系

    千次阅读 2019-06-13 22:46:58
    静态代理和动态代理的区别和联系 代理Proxy: Proxy代理模式是一种结构型设计模式,主要解决的问题是:在直接访问对象时带来的问题 代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个...
  • 说起java动态代理,在我刚开始学java时对这项技术也是十分困惑,明明可以直接调通的对象方法为什么还要使用动态代理?随着学习的不断深入和工作经验的积累,慢慢的体会并理解了java动态代理机制。昨天再给公司新同事...
  • 【设计模式之代理模式】—静态代理和动态代理

    千次阅读 热门讨论 2018-07-31 18:01:31
    前言 ...今天咱们来说下代理模式。 正文 代理模式 &amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;为另一个对象提供一个替身或占位符以控制这个对象的访问。 &
  • 深入理解代理模式:静态代理与JDK动态代理

    万次阅读 多人点赞 2018-03-01 00:22:11
     代理模式为其他对象提供了一种代理以控制对这个对象的访问,具体实现包括两大类:静态代理和动态代理。Java动态代理机制的出现使得Java开发人员只需要简单地指定一组接口及委托类对象便能动态地获得代理类,并且其...
  • 之前本人在设计模式中有写过静态代理和动态代理的相关代码测试,可以看下。 今天我们主要学一下理论相关知识。 静态代理: 动态代理: 动态代理模式主要由四个元素共同组成: 接口:定义具体实现的方法 被代理类:...
  • java静态代理和动态代理

    千次阅读 2018-03-27 22:00:13
    一、代理概念为某个对象提供一个代理,以控制对这个对象的访问。 代理类和委托类有共同的父类或父接口,这样在任何使用委托类对象的地方都可以用代理对象替代。代理类负责请求的预处理、过滤、将请求分派给委托类...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 287,317
精华内容 114,926
关键字:

静态代理