spring的oop是怎么实现的我想大家都知道吧,动态代理实现的。

为什么会出现oop的这种编程思想,因为我们某些特殊的业务需要在执行某个方法之前需要进行预处理或者方法执行以后需要进行后续的处理,如果直接在方法里写业务,那样势必会破坏面向对象的逻辑,所以我们的oop就出来了。

饭锅电饭锅

代理的基本概念

  先上图   

spring 切面 获取方法参数 spring切面实现原理_spring 切面 获取方法参数


代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。

为了保持行为的一致性,代理类和委托类通常会实现相同的接口,所以在访问者看来两者没有丝毫的区别。通过代理类这中间一层,能有效控制对委托类对象的直接访问,也可以很好地隐藏和保护委托类对象,同时也为实施不同控制策略预留了空间,从而在设计上获得了更大的灵活性。

 

更通俗的说,代理解决的问题当两个类需要通信时,引入第三方代理类,将两个类的关系解耦,让我们只了解代理类即可,而且代理的出现还可以让我们完成与另一个类之间的关系的统一管理,但是切记,代理类和委托类要实现相同的接口,因为代理真正调用的还是委托类的方法。

 

 

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

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

 

动态代理 

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

spring 切面 获取方法参数 spring切面实现原理_设计模式_02

spring 切面 获取方法参数 spring切面实现原理_动态代理_03

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


// 方法 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 方法,用于集中处理在动态代理类对象上的方法调用,通常在该方法中实现对委托类的代理访问。每次生成动态代理类对象时都要指定一个对应的调用处理器对象。 


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


 

按照代理的创建时期,代理类可以分为两种: 

静态:由程序员创建代理类或特定工具自动生成源代码再对其编译。在程序运行前代理类的.class文件就已经存在了。

动态:在程序运行时运用反射机制动态创建而成。

 

下面分别用静态代理与动态代理演示一个示例:

添加打印日志的功能,即每个方法调用之前和调用之后写入日志

委托接口


package com.testproxy;

public interface UserService {

    public void add();

    public void select();

}


 

委托实现类


1 package com.testproxy;
 2 
 3 public class UserServiceImpl implements UserService {
 4 
 5     @Override
 6     public void add() {
 7         // TODO Auto-generated method stub
 8         System.out.println("at add method");
 9 
10     }
11 
12     @Override
13     public void select() {
14         // TODO Auto-generated method stub
15 
16     }
17 
18 }


 

代理类


package com.testproxy;

public class UserProxyService implements UserService {

    private UserService userService;

    // 通过构造方法传入目标对象
    public UserProxyService(UserService userService) {
        this.userService = userService;
    }

    @Override
    public void add() {
        // TODO Auto-generated method stub
        System.out.println("start add");
        userService.add();
        System.out.println("add end");
    }

    @Override
    public void select() {
        // TODO Auto-generated method stub

    }

}


 

 测试类


1 package com.test;
 2 
 3 import org.junit.Test;
 4 
 5 import com.testproxy.UserProxyService;
 6 import com.testproxy.UserServiceImpl;
 7 
 8 public class TestBase {
 9 
10     @Test
11     public void testev() {
12         UserProxyService user = new UserProxyService(new UserServiceImpl());
13         user.add();
14     }
15 
16 }


 

 结果

spring 切面 获取方法参数 spring切面实现原理_面试_04

优点:

代理使客户端不需要知道实现类是什么,怎么做的,而客户端只需知道代理即可(解耦合),对于如上的客户端代码,newUserManagerImpl()可以应用工厂将它隐藏,如上只是举个例子而已。

 

缺点

1)代理对象的一个接口只服务于一种类型的对象,如果要代理的方法很多,势必要为每一种方法都进行代理,静态代理在程序规模稍大时就无法胜任了。 
2)如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。 

 

引入动态代理:

 

根据如上的介绍,你会发现每个代理类只能为一个接口服务,这样程序开发中必然会产生许多的代理类

所以我们就会想办法可以通过一个代理类完成全部的代理功能,那么我们就需要用动态代理

在上面的示例中,一个代理只能代理一种类型,而且是在编译器就已经确定被代理的对象。而动态代理是在运行时,通过反射机制实现动态代理,并且能够代理各种类型的对象

Java中要想实现动态代理机制,需要java.lang.reflect.InvocationHandler接口和 java.lang.reflect.Proxy 类的支持

 


java.lang.reflect.InvocationHandler接口的定义如下:
[java] view plain copy print?
//Object proxy:被代理的对象  
//Method method:要调用的方法  
//Object[] args:方法调用时所需要参数  
public interface InvocationHandler {  
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;  
}  

java.lang.reflect.Proxy类的定义如下:
[java] view plain copy print?
//CLassLoader loader:类的加载器  
//Class<?> interfaces:得到全部的接口  
//InvocationHandler h:得到InvocationHandler接口的子类的实例  
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException


下面上代码

动态代理类的实现


1 package com.testproxy;
 2 
 3 import java.lang.reflect.InvocationHandler;
 4 import java.lang.reflect.Method;
 5 import java.lang.reflect.Proxy;
 6 
 7 public class HandlerProxyImpl implements InvocationHandler {
 8 
 9     // 目标对象
10     private Object targetObject;
11 
12     // 绑定关系,也就是关联到哪个接口(与具体的实现类绑定)的哪些方法将被调用时,执行invoke方法。
13     public Object newProxyInstance(Object targetObject) {
14         this.targetObject = targetObject;
15         // 该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
16         // 第一个参数指定产生代理对象的类加载器,需要将其指定为和目标对象同一个类加载器
17         // 第二个参数要实现和目标对象一样的接口,所以只需要拿到目标对象的实现接口
18         // 第三个参数表明这些被拦截的方法在被拦截时需要执行哪个InvocationHandler的invoke方法
19         // 根据传入的目标返回一个代理对象
20         return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(),
21                 this);
22     }
23 
24     // 关联的这个实现类的方法被调用时将被执行
25     /* InvocationHandler接口的方法,proxy表示代理,method表示原对象被调用的方法,args表示方法的参数 */
26     @Override
27     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
28         System.out.println("start-->>");
29 
30         if (args != null) {
31             for (int i = 0; i < args.length; i++) {
32                 System.out.println(args[i]);
33             }
34         }
35         Object ret = null;
36         try {
37             /* 原对象方法调用前处理日志信息 */
38             System.out.println("satrt method-->>");
39             // 调用目标方法
40             ret = method.invoke(targetObject, args);
41             /* 原对象方法调用后处理日志信息 */
42             System.out.println("success-->>");
43         } catch (Exception e) {
44             e.printStackTrace();
45             System.out.println("error-->>");
46             throw e;
47         }
48         return ret;
49     }
50 
51 }


 

测试结果


1 package com.test;
 2 
 3 import org.junit.Test;
 4 
 5 import com.testproxy.HandlerProxyImpl;
 6 import com.testproxy.UserProxyService;
 7 import com.testproxy.UserService;
 8 import com.testproxy.UserServiceImpl;
 9 
10 public class TestBase {
11 
12     /**
13      * 静态代理测试
14      */
15     public void testev() {
16         UserProxyService user = new UserProxyService(new UserServiceImpl());
17         user.add();
18     }
19 
20     /**
21      * 动态代理测试
22      */
23     @Test
24     public void testev2() {
25         HandlerProxyImpl handlerProxyImpl = new HandlerProxyImpl();
26         UserService userService = (UserService) handlerProxyImpl.newProxyInstance(new UserServiceImpl());
27         userService.add();
28         userService.select("zhangsan");
29     }
30 
31 }


 

spring 切面 获取方法参数 spring切面实现原理_面试_05

其实到了现在我们可以想想spring的两大核心不就是面向切面和依赖注入吗,依赖注入是用的反射相信大家都知道,而spring的面向切面是用的动态代理,而动态代理底层使用不也是反射吗,之前有个面试问我spring的面向切面是怎么实现的,开始我说底层也是依赖于依赖注入,这是没有问题的,但是不太准确,他听出有点问题就又从新问了一遍,无奈只能说是动态代理了,

 

美中不足
诚然,Proxy 已经设计得非常优美,但是还是有一点点小小的遗憾之处,那就是它始终无法摆脱仅支持 interface 代理的桎梏,因为它的设计注定了这个遗憾。回想一下那些动态生成的代理类的继承关系图,它们已经注定有一个共同的父类叫 Proxy。Java 的继承机制注定了这些动态代理类们无法实现对 class 的动态代理,原因是多继承在 Java 中本质上就行不通。