精华内容
下载资源
问答
  • 动态代理在实现阶段不用关心代理谁,而在运行阶段才指定代理哪一个对象。相对来说,自己写代理类的方式就是静态代理。 静态代理 那我们先来写一个静态代理吧 静态代理 静态代理角色分析 抽象角色 : 一般使用...

    代理模式

    学习自狂神b站视频

    为什么要学习代理模式,因为AOP的底层机制就是动态代理!

    代理模式又分为静态代理和动态代理。动态代理在实现阶段不用关心代理谁,而在运行阶段才指定代理哪一个对象。相对来说,自己写代理类的方式就是静态代理。

    静态代理

    那我们先来写一个静态代理吧

    静态代理

    静态代理角色分析

    • 抽象角色 : 一般使用接口或者抽象类来实现

    • 真实角色 : 被代理的角色

    • 代理角色 : 代理真实角色 ; 代理真实角色后 , 一般会做一些附属的操作 .

    • 客户 : 使用代理角色来进行一些操作 .

    代码实现

    Rent . java 即抽象角色

    //抽象角色:租房
    public interface Rent {
       public void rent();
    }
    

    Host . java 即真实角色

    //真实角色: 房东,房东要出租房子
    public class Host implements Rent{
       public void rent() {
           System.out.println("房屋出租");
      }
    }
    

    Proxy . java 即代理角色

    //代理角色:中介
    public class Proxy implements Rent {
    
       private Host host;
       public Proxy() { }
       public Proxy(Host host) {
           this.host = host;
      }
    
       //租房
       public void rent(){
           seeHouse();
           host.rent();
           fare();
      }
       //看房
       public void seeHouse(){
           System.out.println("带房客看房");
      }
       //收中介费
       public void fare(){
           System.out.println("收中介费");
      }
    }
    

    Client . java 即客户

    //客户类,一般客户都会去找代理!
    public class Client {
       public static void main(String[] args) {
           //房东要租房
           Host host = new Host();
           //中介帮助房东
           Proxy proxy = new Proxy(host);
           //你去找中介!
           proxy.rent();
      }
    }
    

    分析:在这个过程中,你直接接触的就是中介,就如同现实生活中的样子,你看不到房东,但是你依旧租到了房东的房子通过代理,这就是所谓的代理模式,程序源自于生活,所以学编程的人,一般能够更加抽象的看待生活中发生的事情。

    静态代理的好处:

    • 可以使得我们的真实角色更加纯粹 . 不再去关注一些公共的事情 .
    • 公共的业务由代理来完成 . 实现了业务的分工 ,
    • 公共业务发生扩展时变得更加集中和方便 .

    缺点 :

    • 类多了 , 多了代理类 , 工作量变大了 . 开发效率降低 .

    我们想要静态代理的好处,又不想要静态代理的缺点,所以 , 就有了动态代理 !

    静态代理再理解

    1、创建一个抽象角色,比如咋们平时做的用户业务,抽象起来就是增删改查!

    //抽象角色:增删改查业务
    public interface UserService {
       void add();
       void delete();
       void update();
       void query();
    }
    

    2、我们需要一个真实对象来完成这些增删改查操作

    //真实对象,完成增删改查操作的人
    public class UserServiceImpl implements UserService {
    
       public void add() {
           System.out.println("增加了一个用户");
      }
    
       public void delete() {
           System.out.println("删除了一个用户");
      }
    
       public void update() {
           System.out.println("更新了一个用户");
      }
    
       public void query() {
           System.out.println("查询了一个用户");
      }
    }
    

    3、需求来了,现在我们需要增加一个日志功能,怎么实现!

    • 思路1 :在实现类上增加代码 【麻烦!】
    • 思路2:使用代理来做,能够不改变原来的业务情况下,实现此功能就是最好的了!

    4、设置一个代理类来处理日志!代理角色

    //代理角色,在这里面增加日志的实现
    public class UserServiceProxy implements UserService {
       private UserServiceImpl userService;
    
       public void setUserService(UserServiceImpl userService) {
           this.userService = userService;
      }
    
       public void add() {
           log("add");
           userService.add();
      }
    
       public void delete() {
           log("delete");
           userService.delete();
      }
    
       public void update() {
           log("update");
           userService.update();
      }
    
       public void query() {
           log("query");
           userService.query();
      }
    
       public void log(String msg){
           System.out.println("执行了"+msg+"方法");
      }
    
    }
    

    5、测试访问类:

    public class Client {
       public static void main(String[] args) {
           //真实业务
           UserServiceImpl userService = new UserServiceImpl();
           //代理类
           UserServiceProxy proxy = new UserServiceProxy();
           //使用代理类实现日志功能!
           proxy.setUserService(userService);
    
           proxy.add();
      }
    }
    

    OK,到了现在代理模式大家应该都没有什么问题了,重点大家需要理解其中的思想;

    我们在不改变原来的代码的情况下,实现了对原有功能的增强,这是AOP中最核心的思想

    动态代理

    jdk动态代理

    动态代理

    • 动态代理的角色和静态代理的一样 .

    • 动态代理的代理类是动态生成的 . 静态代理的代理类是我们提前写好的

    • 动态代理分为两类 : 一类是基于接口动态代理 , 一类是基于类的动态代理

      • 基于接口的动态代理----JDK动态代理
      • 基于类的动态代理–cglib
      • 现在用的比较多的是 javasist 来生成动态代理 .
    package com.company;
    
    public interface Rent {
        void rent();
    }
    
    package com.company;
    
    public class Host implements Rent{
        @Override
        public void rent() {
            System.out.println("房屋出租");
        }
    }
    
    
    package com.company;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public class Proxy2 implements InvocationHandler {
        private Rent rent;
    
        public void setRent(Rent rent) {
            this.rent = rent;
        }
    
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            // 这里可以做增强
            seeHouse();
            //核心:本质利用反射实现!
            Object result = method.invoke(rent, args);
            fare();
            return result;
        }
    
        // 生成代理类,重点是第二个参数,获取要代理的抽象角色!之前都是一个角色,现在可以代理一类角色
        public Object CreateProxyObj() {
            return Proxy.newProxyInstance(rent.getClass().getClassLoader(), rent.getClass().getInterfaces(), this);
        }
    
        //看房
        public void seeHouse() {
            System.out.println("带房客看房");
        }
    
        //收中介费
        public void fare() {
            System.out.println("收中介费");
        }
    }
    
    
    package com.company;
    
    public class Client {
        public static void main(String[] args) {
            Host host = new Host();
            Proxy2 proxy2 = new Proxy2();
            proxy2.setRent(host);
            Rent rent = (Rent)proxy2.CreateProxyObj(); //动态生成对应的代理类!
            rent.rent();
        }
    }
    

    深化理解

    我们来使用动态代理实现代理我们后面写的UserService!

    我们也可以编写一个通用的动态代理实现的类!所有的代理对象设置为Object即可!

    public class ProxyInvocationHandler implements InvocationHandler {
       private Object target;
    
       public void setTarget(Object target) {
           this.target = target;
      }
    
       //生成代理类
       public Object getProxy(){
           return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                   target.getClass().getInterfaces(),this);
      }
    
       // proxy : 代理类
       // method : 代理类的调用处理程序的方法对象.
       public Object invoke(Object proxy, Method method, Object[] args) throwsThrowable {
           log(method.getName());
           Object result = method.invoke(target, args);
           return result;
      }
    
       public void log(String methodName){
           System.out.println("执行了"+methodName+"方法");
      }
    
    }
    

    测试!

    public class Test {
       public static void main(String[] args) {
           //真实对象
           UserServiceImpl userService = new UserServiceImpl();
           //代理对象的调用处理程序
           ProxyInvocationHandler pih = new ProxyInvocationHandler();
           pih.setTarget(userService); //设置要代理的对象
           UserService proxy = (UserService)pih.getProxy(); //动态生成代理类!
           proxy.delete();
      }
    }
    

    测试,增删改查,查看结果!

    CGLIB动态代理

    先看下代码

    package proxy;
     
    import java.lang.reflect.Method;
     
    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
     
    public class CglibProxy implements MethodInterceptor
    {
        // 根据一个类型产生代理类,此方法不要求一定放在MethodInterceptor中
        public Object CreatProxyedObj(Class<?> clazz)
        {
            Enhancer enhancer = new Enhancer();
            
            enhancer.setSuperclass(clazz);
            
            enhancer.setCallback(this);
            
            return enhancer.create();
        }
        
        @Override
        public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable
        {
            // 这里增强
            System.out.println("收钱");
            
            return arg3.invokeSuper(arg0, arg2);
        } 
    }
    

    从代码可以看出,它和jdk动态代理有所不同,对外表现上看CreatProxyedObj,它只需要一个类型clazz就可以产生一个代理对象, 所以说是“类的代理”,且创造的对象通过打印类型发现也是一个新的类型。不同于jdk动态代理,jdk动态代理要求对象必须实现接口(三个参数的第二个参数),cglib对此没有要求。

    动态代理的好处

    静态代理有的它都有,静态代理没有的,它也有!

    • 可以使得我们的真实角色更加纯粹 . 不再去关注一些公共的事情 .
    • 公共的业务由代理来完成 . 实现了业务的分工 ,
    • 公共业务发生扩展时变得更加集中和方便 .
    • 一个动态代理 , 一般代理某一类业务
    • 一个动态代理可以代理多个类,代理的是接口!
    展开全文
  • 那么动态代理分为哪,相对应的区别又是什么呢? 首先什么是代理? 找一个东西或者一个人去帮你做事,比如常说的中介就是一个代理,各大经销商的代理商等等。JAVA中的代理即是指将自己的事情委派给别人帮忙去...

     众所周知,Spring AOP中涉及到了动态代理模式,那么有动态代理相应的就会有静态代理。那么动态代理分为哪几种,相对应的区别又是什么呢?

         首先什么是代理?

         找一个东西或者一个人去帮你做事,比如常说的中介就是一个代理,各大经销商的代理商等等。JAVA中的代理即是指将自己的事情委派给别人帮忙去完成。

         静态代理:代理的是程序员已经创建好的类,也就是说当前仅有一个对象能被成功代理。上代码看下

         首先是一个需要代理的接口类

         该类描述了两个方法,一个是eat(),一个是run();

    public interface UserAction {
     
        void eat();
     
        void run();
    }
    
     

         接下来是该类的实现类,较为简单的实现方式,仅仅打印内容而已。  

    
     
    1. public class UserActionImpl implements UserAction {

    2.  
    3. @Override

    4. public void eat() {

    5. System.out.println("吃饭");

    6. }

    7.  
    8. @Override

    9. public void run() {

    10. System.out.println("跑步");

    11. }

    12. }

        接口已经实现完成,剩下的即是代理对象了。上述的过程中静态代理和JDK的动态代理还没有区别。区别在于下面

    
     
    1. public class UserActionStaticProxy implements UserAction{

    2.  
    3. private UserAction userAction;

    4.  
    5. public UserActionStaticProxy(UserAction userAction){

    6. this.userAction = userAction;

    7. }

    8.  
    9. @Override

    10. public void eat() {

    11. System.out.println("静态代理eat方法开始");

    12. userAction.eat();

    13. System.out.println("静态代理eat方法结束");

    14. }

    15.  
    16. @Override

    17. public void run() {

    18. System.out.println("静态代理run方法开始");

    19. userAction.run();

    20. System.out.println("静态代理run方法结束");

    21. }

    22. }

      从上述代码可以看到,我们实现了接口类并定义了一个新的类 UserActionStaticProxy   。然后定义了他的有参构造方法,将接口类传入即可。方法重写的同时加入了方法的监控。

    
     
    1. public static void main(String[] args) {

    2. UserAction userAction = new UserActionStaticProxy(new UserActionImpl());

    3. userAction.eat();

    4. }

       在调用的时候,我们可以看到传入了UserActionImpl类去转换为UserAction类(向下转型),而后可以直接调用他的方法即可。运行上述方法以后即执行了 Proxy 类下的eat方法。甚至于我们可以在 Proxy 类下的eat方法调用一次 this.run() 方法,可以自己拼接为自己所想要的方式,有点和策略模式靠近了。(下图是在eat方法中加入了this.run()方法)

            从静态代理模式中可以看出来,如果我们需要代理多个类的话,那么就需要新建对应的接口实现类(Imp.class)和对应的代理类(Proxy,class)。所以实现起来会比较繁琐,因此就应运而生出了动态代理。

           动态代理和静态代理的本质区别其实不是很大,都需要接口以及接口的实现类,动态代理解决了需要重复新增大量的单体代理文件,而是把所有的对象都在一个地方进行了代理,也就是涉及到了JAVA中的反射机制。

          可以看下动态代理的代码:

        

    
     
    1. public class LogHandler implements InvocationHandler {

    2.  
    3. private Object object;

    4.  
    5. public Object newProxyInstance(Object object){

    6. this.object = object;

    7. return Proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces(),this);

    8. }

    9.  
    10. @Override

    11. public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {

    12. System.out.println("动态代理" + method.getName() + "开始");

    13. Object ret = method.invoke(object,args);

    14. System.out.println("动态代理" + method.getName() + "结束");

    15. return ret;

    16. }

    17. }

      可以看到,动态代理的时候,我们将对象替换成了所有对象的父类------Object类,在代理类的同时,我们通过反射Proxy.newProxyInstance 获取了该类的对象。而后使用了java对应的invoke方法去执行被代理类的方法。

       对应的执行的主方法:    

    
     
    1. public static void main(String[] args) {

    2. UserAction userAction1 = (UserAction)new LogHandler().newProxyInstance(new UserActionImpl());

    3. userAction1.eat();

    4. }

       执行的结果:

       因此可以看到,静态代理和动态代理的区在于代理类的区别,静态代理在代码扩容时,每增加一个接口类需要代理,那么就需要新增一个对应的代理类。而动态代理的好处在于需要新增代理接口时,不需要新增代理类,可以直接通过反射的方式调用被代理类。从上述就可以看出,代理的好处就是对方法的增强,可以在方法的前后进行一系列的操作,比如打印日志,验证权限,方法之后可以统一返回格式,统一异常捕获等等......(其实也就是AOP能做的事情)。所以动态代理相比于静态代理最本质的区别就在于我们需要对一个新的接口类代理时,不需要再去增加繁琐的代理类了。

       前文提到,动态代理又分为JDK的动态代理以及CGLIB的动态代理。JDK的动态代理是依据的JAVA强大的反射机制。而CGLIB动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理,也就是说生成被代理类的一个子类将其方法覆盖,以达到代理的目的。

        AOP是会自动切换这两种动态代理的类型的,具体的区别如下:

       1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP 

       2、如果目标对象实现了接口,可以强制使用CGLIB实现AOP

       3、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换

     这里借用别人的CGLIB代码来看下具体的区别

    
     
    1. public class UserAction {

    2.  
    3. public void eat(){

    4. System.out.println("CGLIB动态代理吃饭");

    5. }

    6. }

    7.  
    8.  
    9. public class CglibProxy implements MethodInterceptor {

    10. private Object target;//需要代理的目标对象

    11.  
    12. //重写拦截方法

    13. @Override

    14. public Object intercept(Object obj, Method method, Object[] arr, MethodProxy proxy) throws Throwable {

    15. System.out.println("Cglib动态代理,监听开始!");

    16. Object invoke = method.invoke(target, arr);//方法执行,参数:target 目标对象 arr参数数组

    17. System.out.println("Cglib动态代理,监听结束!");

    18. return invoke;

    19. }

    20. //定义获取代理对象方法

    21. public Object getCglibProxy(Object objectTarget){

    22. //为目标对象target赋值

    23. this.target = objectTarget;

    24. Enhancer enhancer = new Enhancer();

    25. //设置父类,因为Cglib是针对指定的类生成一个子类,所以需要指定父类

    26. enhancer.setSuperclass(objectTarget.getClass());

    27. enhancer.setCallback(this);// 设置回调

    28. Object result = enhancer.create();//创建并返回代理对象

    29. return result;

    30. }

    31.  
    32. public static void main(String[] args) {

    33. CglibProxy cglib = new CglibProxy();//实例化CglibProxy对象

    34. UserAction user = (UserAction) cglib.getCglibProxy(new UserAction());//获取代理对象

    35. user.eat();//执行方法

    36. }

    37.  
    38. }

     代码本质上其实和JDK的动态代理的区别并不是很大,而JDK的动态代理是基于接口的,必须要对应接口的实现类才可以实现JDK的动态代理,而CGLIB弥补了这方面的缺点,CGLIB是基于类的继承关系,因此在没有接口的实现下我们可以使用CGLIB去实现动态代理。

    展开全文
  • 主要分为创建型、结构型、行为型三大类。还有类:并发型模式和线程池模式 创建型模式:工厂方法、抽象工厂、单例、建造者、原型 结构型模式:适配器、装饰器、代理、外观、桥接、组合、享元 行为型模式:策略、...

    设计模式基础

    设计模式分类

    主要分为创建型、结构型、行为型三大类。还有两类:并发型模式和线程池模式

    • 创建型模式:工厂方法抽象工厂单例建造者、原型
    • 结构型模式:适配器、装饰器、代理、外观、桥接、组合、享元
    • 行为型模式:策略、模板方法、观察者、迭代子、责任链、命令、备忘录、状态、访问者、中介者、解释器

    设计模式六大原则

    总原则开闭原则。对扩展开房,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,而是要扩展原有代码。一句话概括就是:**为了使程序的扩展性好,易于维护和升级。**想要达到这样的效果,我们需要使用接口和抽象类等。

    六大原则

    1. 单一职责原则:不要存在多于一个导致类变更的原因,也就是说每个类应该实现单一的职责。不然的话,就应该把类拆分。
    2. 里氏替换原则:面向对象设计的基本原则之一。任何基类可以出现的地方,子类一定可以出现。LSP(Liskov Substitution Principle)是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。
        里氏替换原则是对“开-闭”原则的补充,实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏替换原则是对实现抽象化的具体步骤的规范。里氏替换原则中,子类对父类的方法尽量不要重写和重载。因为父类代表了定义好的结构,通过这个规范的接口与外界交互,子类不应该随便破坏它。里氏替换原则是对“开-闭”原则的补充,实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏替换原则是对实现抽象化的具体步骤的规范。里氏替换原则中,子类对父类的方法尽量不要重写和重载。因为父类代表了定义好的结构,通过这个规范的接口与外界交互,子类不应该随便破坏它。
    3. 依赖倒转原则:这是开闭原则的基础。面向接口编程,依赖于抽象而不依赖于具体。写代码用到具体类时,不与具体类交互,而与具体类的上层接口交互。
    4. 接口隔离原则:**每个接口中不存在子类用不到却必须实现的方法,如果不然,就要将接口拆分。**使用多个隔离接口,比使用单个接口(多个接口方法集合到一个)要好。
    5. 迪米特法则(最少知道原则):一个类对自己知道的类知道的越少越好。也就是说无论被依赖的类多么复杂,都应该将逻辑封装在方法的内部,通过public方法提供给外部,这样当被依赖的类变化时,才能最小的影响该类。
        最少知道原则的另一个表达方式是:只与直接的朋友通信。类之间只要有耦合关系,就叫朋友关系。耦合分为依赖、关联、聚合、组合等。我们成出现为成员变量、方法参数、方法返回值中的类为直接朋友。局部变量、临时变量则不是直接的朋友。我们要求陌生的类不要作为局部变量出现在类中。最少知道原则的另一个表达方式是:只与直接的朋友通信。类之间只要有耦合关系,就叫朋友关系。耦合分为依赖、关联、聚合、组合等。我们成出现为成员变量、方法参数、方法返回值中的类为直接朋友。局部变量、临时变量则不是直接的朋友。我们要求陌生的类不要作为局部变量出现在类中。
    6. 合成复用原则:尽量首先使用合成/聚合的方式,而不是使用继承。


    展开全文
  • 代理分为静态代理和动态代理,无论哪种代理,它们都是为了对目标方法进行增强,让增强的动作和目标动态分开,达到解耦的目的。目标类可以只关注业务,而不关注其他的东西,比如添加用户时,只关注业务实现,不关注谁...

    最近在看Spring AOP,里面使用到了动态代理,自己对两种代理模式进行了学习,这里做个总结。本文主要介绍动态代理,开始之前还是先介绍一下代理的相关内容。

    一、代理

    代理分为静态代理和动态代理,无论哪种代理,它们都是为了对目标方法进行增强,让增强的动作和目标动态分开,达到解耦的目的。目标类可以只关注业务,而不关注其他的东西,比如添加用户时,只关注业务实现,不关注谁调用相关的日志输出等操作。

    1、静态代理

    其实就是创建一个代理类去继承目标类,在代理类中重写目标方法,添加增强动作的同时对目标方法进行使用。下面是一个简单的静态代理示例,这里没有使用接口,直接使用类继承。代理类中包含了目标类的引用,并通过重写目标方法添加增强目的。

    // 目标类
    public class UserService {
    	public void delUser(String name) {
    		xxx;
    	}
    }
    
    // 代理类
    public class UserServiceProxy extend UserService{
        private UserService userService;
        
    	public UserServiceProxy(UserService userService) {
    		this.userService = userService;
    	}
    
        @Override
    	public void delUser(String name) {
    		System.out.println("我要增强了")
    		userService.delUser();
    		System.out.println("我增强完了")
    	}
    }
    
    // 主函数
    public static void main(String[] args) {
    	UserService userService = (UserService)new UserServiceProxy(new UserService);
    	userService.delUser("tom);
    }
    

    静态代理的优点其实就是代理的优点,将业务逻辑和增强逻辑解耦合。缺点是系统中如何有很多需要增强的类,则要创建很多代理对象。另外如果一个类中有很多方法需要增强,则可能会导致很多重复的代码,即使进行重构也会有重复。总结起来就是代码的可重用性弱。

    2、动态代理

    动态代理是使用反射和字节码的技术,在运行期创建指定接口和类的子类以及其实例对象的技术,通过动态代理可以对代码进行增强。动态代理并不存在代理类, 代理对象直接由代理生成工具动态生成。

    动态代理实现了只需要将被代理对象作为参数传入代理类就可以获取代理类对象,从而实现类代理,具有较强的灵活性。此外动态代理的服务内容不需要像静态代理一样写在每个代码块中,只需要写在invoke()(JDK动态代理)或intercept()(CGLib动态代理)方法中即可,降低了代码的冗余度。缺点是执行效率相对较低,需要动态修改字节码,同反射等技术的修改存在安全问题。

    Spring或者说是Java的动态代理有两种模式,一种是JDK动态代理,还有一种是CGLib动态代理。

    二、动态代理

    1、JDK动态代理

    JDK动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。JDK的动态代理机制是委托机制。

    JDK动态代理只能对实现了接口的类生成的代理,而不能针对类,这是因为JDK动态代理与静态代理类似,本质上还是生成一个子类来模拟“代理”,但是采用了动态生成的形式,而这个子类要继承Proxy类获得相关的方法(内部维护了一个InvocationHandler对象进行invoke操作),而Java是单继承多实现的形式,继承了Proxy类就不能再继承被代理类了,这也是为什么只能对实现了接口的类生成代理。

    这样子也就引入了另外一个问题,为什么JDK动态代理设计时要继承Proxy,而不将Proxy改成接口,这个没有明确的答案,就好像HashMap能存null,而HashTable不能,然后大家猜测,最后设计者说我就是看null不爽。我查阅了网上资料,主要有这几点,首先是基于继承的方式可以减少产生代理类时产生的性能消耗,我们知道有个InvocationHandler对象放在Proxy类里面,如果不放在里面,则每个代理类都要去持有这个,另外如果要代理的原始类不是接口的话,代理类会继承原始类的字段,这些字段没有用。还有就是代理的原始类中如果有final的方法,动态生成的类是无法覆盖这个方法的。

    那接下来我们看一个JDK动态代理的示例。比较简单代理类实现InvocationHandler接口的Invoke()方法,方法内实现增强逻辑以及调用原始类的目标方法。原始类的实例作为属性保存在代理类中。

    public interface UserService {
    	public void addUser(String name);
    }
    
    public class UserServiceImpl implements UserService {
    
    	@Override
    	public void addUser(String name) {
    		System.out.println("调用了UserServiceImpl.addUser()方法!");
    	}
    }
    
    public class JDKProxy implements InvocationHandler {
    	private Object targetObject;
    
    	public Object newProxy(Object obj) {
    		this.targetObject = obj;
    		return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
    				targetObject.getClass().getInterfaces(), this);
    	}
    
        // proxy - 代理的真实对象。
        // method - 所要调用真实对象的某个方法的 Method 对象
        // args - 所要调用真实对象某个方法时接受的参数
    	@Override
    	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    		System.out.println("增强");
    		Object ret =  method.invoke(targetObject, args);
    		return ret;
    	}
    
    	public static void main(String[] args) {
    	//loader - 一个 ClassLoader 对象,定义了由哪个 ClassLoader 对象来对生成的代理对象进行加载。
        //interfaces - 一个 Interface 对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了
        // h - 一个 InvocationHandler 对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个 InvocationHandler 对象上
    		((UserService)new JDKProxy().newProxy(new UserServiceImpl())).addUser("tom");
    	}
    }
    
    增强
    调用了UserServiceImpl.addUser()方法!
    
    Process finished with exit code 0
    

    这里要说明一下invoke()方法里面的proxy参数,使用较少,但是为什么要传入呢?第一点是可以使用反射获取代理对象的信息(也就是proxy.getClass().getName())。第二点 可以将代理对象返回以进行连续调用,这就是proxy存在的目的,因为this并不是代理对象。

    2、CGLib动态代理

    CGLib动态代理是利用ASM开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。CGLib的动态代理机制是继承机制,通过“继承”可以继承父类所有的公开方法,然后可以重写这些方法,在重写时对这些方法增强,这就是CGLib的思想。根据里氏代换原则(LSP),父类需要出现的地方,子类可以出现,所以CGLib实现的代理也是可以被正常使用的。

    CGlib是针对类实现代理,对指定的类生成一个子类,并覆盖其中的方法,因此代理类和代理类是继承关系,所以代理类是可以赋值给被代理类的,如果被代理类有接口,那么代理类也可以赋值给接口。注意CGLib不能代理final修饰的类和方法。

    来看一个CGLib的示例,和JDK类似,有两个关键元素:
    ①Enhancer:来指定要代理的目标对象,实际处理代理逻辑的对象,最终通过调用create()方法得到代理对象、对这个对象所有的非final方法的调用都会转发给MethodInterceptor
    ②MethodInterceptor:动态代理对象的方法调用都会转发到intercept方法进行增强

    // 接口与目标类的与JDK动态代理里面的示例一样,不多一起贴近来
    public interface UserService {
    	public void addUser(String name);
    }
    
    public class UserServiceImpl implements UserService {
    
    	@Override
    	public void addUser(String name) {
    		System.out.println("调用了UserServiceImpl.addUser()方法!");
    	}
    }
    
    public class CGLibProxy implements MethodInterceptor{
    	private Object targetObject;
    
    	public Object createProxyObject(Object obj) {
    		this.targetObject = obj;
    		Enhancer enhancer = new Enhancer();
    		enhancer.setSuperclass(obj.getClass());
    		enhancer.setCallback(this);
    		return enhancer.create();
    	}
    
    	@Override
    	public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
    		System.out.println("增强");
    		return method.invoke(targetObject, objects);
    	}
    
    	public static void main(String[] args) {
    		((UserService) new CGLibProxy().createProxyObject(new UserServiceImpl())).addUser("tom");;
    	}
    }
    
    增强
    调用了UserServiceImpl.addUser()方法!
    
    Process finished with exit code 0
    

    3、性能问题

    从表面上就可以看出,JDK动态代理性能主要受制于反射时,即实际运行时性能要低。CGLib主要受制修改类的字节码生成子类时,即在创建对象的时候所花费的时间要多。因此在执行次数较少时,修改字节码影响较大,CGLib性能差一些,大量执行情况下CGLib性能更好。这只是理论上的分析,JDK6时JDK动态代理性能比较差,现在版本已经进行了大量优化,追上甚至赶超CGLib,但是CGLib似乎很难前进,感兴趣的可以去研究一下JDK对动态代理的优化。

    4、Sping 动态代理

    Spring同时使用了两种动态代理机制,依据如下:
    ①当Bean实现接口时,Spring就会用JDK的动态代理
    ②当Bean没有实现接口时,Spring使用CGlib是实现
    ③可以强制使用CGlib(@EnableAspectJAutoProxy(proxyTargetClass = true))

    5、Spring AOP

    动态代理虽然实现了代码的解耦,但是还是需要自己去生成代理对象,自己手写拦截器,在拦截器里自己手动的去把要增强的内容和目标方法结合起来。可以使用Spring AOP来完成这些繁杂的操作,只要在配置文件里进行配置,配置好后让Spring去帮你生成代理对象,按照你的配置把增强的内容和目标方法结合起来。

    展开全文
  • 代理模式在我们开发中也是经常遇到,典型的就是Hook拦截技术。 代理模式分为静态代理和动态代理,不管一种他都...下面来介绍两种代理模式:静态代理: /** * 接口 制定代理对象和真实对象需要实现的方法 */ ...
  • 服务器按其使用目的又可分为一般服务器和专用服务器两种。通用性服务器是不针对特定服务而专门设计的服务器,它能够提供多种服务功能,目前大多数服务器都是通用性服务器。 专门化服务器是专为特定类型或特定功能而...
  • 服务器按其使用目的又可分为一般服务器和专用服务器两种。通用性服务器是不针对特定服务而专门设计的服务器,它能够提供多种服务功能,目前大多数服务器都是通用性服务器。 专门化服务器是专为特定类型或特定功能而...
  • 我们都知道目前有两种方法可以切换IP,分别是代理IP和ADSL拨号。那么这两者有什么区别呢?一个更适合换IP呢? (一)ADSL拨号 ADSL(非对称数字用户线路)是一种新的数据传输方式。ADSL拨号,也叫pppoe拨号,宽带...
  • DDOS攻击相关问题

    千次阅读 2011-12-19 00:09:37
    有有有 单位测试DDOS ... .ddos分为两种方式, a一种是纯流量,b一种是技巧性攻击(耗损cpu,耗损资源) 有有有 针对动态网站的CC在单机上可以用大量代理,如果是在大量肉鸡上也可以用代理来实现
  • C#23设计模式

    2013-06-02 16:49:43
    把一个类的接口变换成客户端所期待的另一接口,从而使原本因接口原因不匹配而无法一起工作的个类能够一起工作。适配类可以根据参数返还一个合适的实例给客户端。 7、BRIDGE 早上碰到MM,要说早上好,晚上...
  • 适配器模式: 从而使原本因接口原因不 适配器模式 把一个类的接口变换成客户端所期待的另一接口, 匹配而无法一起工作的个类能够一起工作。 适配类可以根据参数返还一个合适的实例给客 户端。 7、BRIDGE —...
  • 设计模式可被分为哪三种类型? 创建型:单例模式,抽象工厂模式,工厂方法模式 行为型:模板方法模式,命令模式,策略模式,状态模式,迭代器模式,观察者模式 结构型:适配器模式,组合模式...还可被分为哪两种类型?
  • DHCP服务器典型配置举例(路由应用)

    千次阅读 2017-11-04 21:53:02
    常见的DHCP组网方式可分为两种:一种是DHCP服务器和客户端在同一子网内,直接进行DHCP报文的交互;另一种是DHCP服务器和客户端处于不同的子网中,必须使用DHCP中继代理实现IP地址的分配。无论在种情况下,DHCP...
  • Spring AOP设计思想

    2021-05-07 08:24:44
    另外AOP思想的具体实现有Spring AOP和AspectJl两种,搞清楚你使用的是种。 一、Spring对AOP的设计 1、动态代理 首先从动态代理方式上我们就知道分为cglib和jdk动态代理2种,Spring AOP在这个的设计上如下: org...
  • ftp协议设计之初,分为了主动工作模式和被动工作模式,这两种工作模式一个是需要在服务器端随机开一个端口,让客户端连接这个新开的端口进行数据传输,一个是在ftp客户端随机开一个端口供服务器端主动连过来进行通讯...
  • 目前市场上的场效应管主要分为两种,但不论是种,场效应管都具有功耗低噪声小,而且不会出现二次击穿的特点,这让其成为替代晶体管的最佳产品。随着场效应管应用面积的扩大,对场效应管的选择和检查也成为了设计者...
  • Spring配置文件中关于事务配置总是由三个组成部分,分别是DataSource、TransactionManager和代理机制这三部分,无论哪种配置方式,一般变化的只是代理机制这部分。 DataSource、 TransactionManager这部分只是会...
  • kubernetes 源码编译,分为本地二进制可执行文件编译和 docker 镜像编译两种,不管种方式,都是直接使用并不需要修改任何 k8s 代码。当我们有特殊需求时,比如需要修改 kube-proxy 对 service 的代理逻辑等,就...
  • LVS负载均衡群集

    2017-10-01 21:06:00
    群集的类型:无论是哪种服务器,都至少包括台节点服务器,而对外表现为一个整体,只提供一个访问入口(域名或IP地址),相当于一台大型计算机。根据群集所针对的目标差异,可以分为以下三个类型: 1.负载均衡...
  • 2.2.1 IP地址的编码分为哪俩部分? 2.2.2 用户输入M,N值,从1至N开始顺序循环数数,每数到M输出该数值,直至全部输出。写出C程序。 2.2.3 不能做switch()的参数类型是 2.2.4 int A[nSize],其中隐藏着若干0,其余...
  • 15. java 中 IO 流分为? 7 16. BIO、NIO、AIO 有什么区别? 7 17. Files的常用方法都有哪些? 8 二、容器 8 18. java 容器都有哪些? 8 19. Collection 和 Collections 有什么区别? 9 20. List、Set、Map 之间...
  • 15.java 中 IO 流分为? 16.BIO、NIO、AIO 有什么区别? 17.Files的常用方法都有哪些? 二、容器 18.java 容器都有哪些? 19.Collection 和 Collections 有什么区别? 20.List、Set、Map 之间的区别是什么? 21....
  • 门店招贴:点击主窗口左侧的"房源管理" →"门店招贴"按钮,打开门店招贴窗口,此窗口主要提供招贴单的打印功能,可以选择不同区域的房源或具体到某个房源打印其招贴单,招贴单分小招贴单和大招贴单两种,应用于批量...
  • 网上书店系统源码

    热门讨论 2012-02-23 19:24:14
    第二大类模式也可以分为两种,第一种是企业与个人消费者通过网络进行产品销售和购买,第二种是企业与个人消费者通过网络提供服务和得到服务。第二类,企业与个人,对于个人,也就是消费者而言,电子商务就是我们常说...
  • 仿美萍房产中介管理系统源码20110803

    热门讨论 2011-08-03 16:47:05
    门店招贴:点击主窗口左侧的"房源管理" →"门店招贴"按钮,打开门店招贴窗口,此窗口主要提供招贴单的打印功能,可以选择不同区域的房源或具体到某个房源打印其招贴单,招贴单分小招贴单和大招贴单两种,应用于批量...
  • 门店招贴:点击主窗口左侧的"房源管理" →"门店招贴"按钮,打开门店招贴窗口,此窗口主要提供招贴单的打印功能,可以选择不同区域的房源或具体到某个房源打印其招贴单,招贴单分小招贴单和大招贴单两种,应用于批量...
  • 具体分为三个阶段:静态代理,动态代理,还有CGLIB代理。 以动态代理来说,测试动态代理加强类时,创建main方法直接测试即可: /** * 测试 * @param args */ public static void main(String[] args) { ...
  •  4、在软件的生命周期中,下列说法是错误的?  A、软件生命周期分为计划、开发和运行3个阶段  B、在开发初期要进行可行性研究和详细设计  C、在开发后期要进行代码编写和软件测试  D、运行阶段主要是进行...
  • 适配器(变压器)模式:把一个类的接口变换成客户端所期待的另一接口,从而使原本因接口原因不匹配而无法一起工作的个类能够一起工作。适配类可以根据参数返还一个合适的实例给客户端。       ...

空空如也

空空如也

1 2 3
收藏数 43
精华内容 17
关键字:

代理分为哪两种