文章目录
  1. 1. 代理模式
  2. 2. 代理模式的三种角色
    1. 2.1. 主题抽象类
    2. 2.2. 实际主题类
    3. 2.3. 代理类
  3. 3. 两种代理
    1. 3.1. 静态代理
    2. 3.2. 动态代理
      1. 3.2.1. JDK动态代理
      2. 3.2.2. CGLIB动态代理
  4. 4. Spring两种代理方式

代理模式

所谓代理,就是一个人或者一个机构代表另一个人或者另一个机构采取行动。在一些情况下,一个客户不想或者不能够直接引用一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

代理模式是一种结构性设计模式。当客户端不想直接调用主题对象,而希望主题对象的行为前后加上预处理或者后续处理时,则可以使用代理模式。

代理模式的三种角色

主题抽象类

主题抽象类定义了主题对象的行为,下面的例子中主题抽象类定义了主题对象的request行为。

1
2
3
abstract public class Subject{
abstract public void request();
}

实际主题类

实际主题类继承了抽象主题类Subject,实现了抽象主题类中的行为request。实际主题类就是AOP中的目标对象。

1
2
3
4
5
public class RealSubject extends Subject{
public void request(){
System.out.println("RealSubject");
}
}

代理类

代理了继承了抽象主题类,同时关联了实际主题类。代理类中定义了preRequest和postRequest方法,对实际主题类中的request方法实施了控制。代理类对应Spring AOP中的ProxyFactoryBean类,用来生成代理对象。

1
2
3
4
5
6
7
8
9
10
11
12
public class ProxySubject extends Subject{
private RealSubject realSubject;
public void request(){
preRequest(); //自定定义的方法,方法中可以实现一些想在请求之前就要完成的逻辑
if(realSubject==null){
realSubject = new RealSubject();
}
realSubject.request();
postRequest(); //自定定义的方法,方法中可以实现一些想在请求之后想要完成的逻辑
}

}

两种代理

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

静态代理

由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。
上面的例子就是静态代理,与动态代理相比,静态代理有一些局限性,比如:有多少目标类就有多少proxy;在目标类中有多少方法,在代理类中就有多少方法。

动态代理

在程序运行时,运用反射机制动态创建而成。
JDK动态代理中包含一个类和一个接口:

  1. InvocationHandler接口:
    1
    2
    3
    4
    5
    6
    7
    8
    public interface InvocationHandler { 
    public Object invoke(Object proxy,Method method,Object[] args) throws Throwable;
    }
    /*参数说明:
    Object proxy:指被代理的对象。
    Method method:要调用的方法
    Object[] args:方法调用时所需要的参数
    */

可以将InvocationHandler接口的子类想象成一个代理的最终操作类,替换掉ProxySubject。

  1. Proxy类:
    Proxy类是专门完成代理的操作类,可以通过此类为一个或多个接口动态地生成实现类,此类提供了如下的操作方法:
    1
    2
    3
    4
    5
    6
    public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException 
    /*参数说明:
    ClassLoader loader:类加载器
    Class<?>[] interfaces:得到全部的接口
    InvocationHandler h:得到InvocationHandler接口的子类实例
    */

与静态代理类对照的是动态代理类,动态代理类的字节码在程序运行时由Java反射机制动态生成,无需程序员手工编写它的源代码。动态代理类不仅简化了编程工作,而且提高了软件系统的可扩展性,因为Java 反射机制可以生成任意类型的动态代理类。java.lang.reflect 包中的Proxy类和InvocationHandler 接口提供了生成动态代理类的能力。

JDK动态代理

1
2
3
public interface Subject {  
public void request();
}
1
2
3
4
5
public class SubjectImpl implements Subject {  
public void request(){
System.out.println("RealSubject");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class SubjectProxy implements InvocationHandler {  
private Object target;
/**
* 绑定委托对象并返回一个代理类
* @param target
* @return
*/

public Object bind(Object target) {
this.target = target;
//取得代理对象
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), this); //要绑定接口(这是一个缺陷,cglib弥补了这一缺陷)
}

@Override
/**
* 调用方法
*/

public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {

Object result=null;
System.out.println("事物开始");
//执行方法
result=method.invoke(target, args);
System.out.println("事物结束");
return result;
}

}
1
2
3
4
5
public static void main(String[] args) {  
SubjectProxy proxy = new SubjectProxy();
Subject subjectProxy = (Subject) proxy.bind(new SubjectImpl());
subjectProxy.request();
}

DK的动态代理依靠接口实现,如果有些类并没有实现接口,则不能使用JDK代理,这就要使用cglib动态代理了。

CGLIB动态代理

1
2
3
public interface Subject {  
public void request();
}
1
2
3
4
5
public class SubjectImpl{  
public void request(){
System.out.println("RealSubject");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class SubjectCglib implements MethodInterceptor {  
private Object target;
/**
* 创建代理对象
*
* @param target
* @return
*/

public Object getInstance(Object target) {
this.target = target;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.target.getClass());
// 回调方法
enhancer.setCallback(this);
// 创建代理对象
return enhancer.create();
}

@Override
// 回调方法
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable
{

System.out.println("事物开始");
proxy.invokeSuper(obj, args);
System.out.println("事物结束");
return null;
}

}
1
2
3
4
5
public static void main(String[] args) {  
SubjectCglib cglib=new SubjectCglib();
SubjectImpl subjectCglib=(SubjectImpl)cglib.getInstance(new SubjectImpl());
subjectCglib.request();
}

Spring两种代理方式

  1. 若目标对象实现了若干接口,spring使用JDK的java.lang.reflect.Proxy类代理。
    优点:因为有接口,所以使系统更加松耦合
    缺点:为每一个目标类创建接口
  2. 若目标对象没有实现任何接口,spring使用CGLIB库生成目标对象的子类。
    优点:因为代理类与目标类是继承关系,所以不需要有接口的存在。
    缺点:因为没有使用接口,所以系统的耦合性没有使用JDK的动态代理好。
    动态代理是Spring AOP技术的基础,掌握了动态代理,接下来学习AOP会更加得心应手。
文章目录
  1. 1. 代理模式
  2. 2. 代理模式的三种角色
    1. 2.1. 主题抽象类
    2. 2.2. 实际主题类
    3. 2.3. 代理类
  3. 3. 两种代理
    1. 3.1. 静态代理
    2. 3.2. 动态代理
      1. 3.2.1. JDK动态代理
      2. 3.2.2. CGLIB动态代理
  4. 4. Spring两种代理方式