代理模式
代理模式(Proxy Pattern)是使一个类具有另外一个类的功能,使这个类代理另外一个类来完成一些工作,属于结构型模式。
目的:在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。
优点: 1、职责清晰。 2、高扩展性。 3、智能化。
缺点: 1、由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。 2、实现代理模式需要额外的工作,有些代理模式的实现非常复杂。
代理模式实现
以周杰伦为例,某公司想要请周杰伦在年会上演出,所以找了周杰伦(目标对象)的经纪人(代理对象)
1.1 静态代理
接口:周杰伦工作室1
2
3
4
5
6
7
8
9
10
11package StaticAgent;
/**
* 接口
*
* @date 2018/1/21 16:57:54
* @auther Pyctay
*/
public interface Jay {
void sing();
}
目标对象:周杰伦本人1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23package StaticAgent;
/**
* 周杰伦真身
*
* @date 2018/1/21 16:59:44
* @auther Pyctay
*/
public class RealJay implements Jay {
private String songName;
public RealJay (String songName){
this.songName = songName;
ready();
}
public void ready() {
System.out.println("周杰伦被请来了,正在准备唱歌");
}
@Override
public void sing() {
System.out.println("周杰伦在唱"+songName);
}
}
代理对象:周杰伦的经纪人1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24package StaticAgent;
/**
* 周杰伦的经纪人
*
* @version 2018/1/21 17:01:30
* @auther Pyctay
*/
public class Agent implements Jay {
private String songName;
private RealJay realJay;
public Agent (String songName){
this.songName = songName;
}
@Override
public void sing() {
System.out.println("主持人:今天有幸请到了周杰伦来为大家演唱一首歌");
if(realJay==null){
realJay = new RealJay(songName);
}
realJay.sing();
System.out.println("主持人:晚会结束,谢谢大家");
}
}
测试类:1
2
3
4
5
6
7
8
9
10
11
12package StaticAgent;
/**
* 静态代理测试主类
*/
public class Main {
public static void main(String[] args) {
Jay jay = new Agent("菊花台");
jay.sing();
}
}
输出结果:
静态代理总结:
优点:可以做到在不修改目标对象的功能前提下,对目标功能扩展.
缺点:代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多.同时,一旦接口增加方法,目标对象与代理对象都要维护.维护成本太大。
1.2 动态代理
1.在运行期,通过反射机制创建一个实现了一组给定接口的新类
2.在运行时生成的class,必须提供一组interface给它,然后该class就宣称它实现了这些 interface。该class的实 例可以当作这些interface中的任何一个来用。但是这个Dynamic Proxy其实就是一个Proxy, 它不会替你作实质性的工作,在生成它的实例时你必须提供一个handler,由它接管实际的工 作。
3.动态代理也叫做:JDK代理,接口代理
4.接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理(InvocationHandler.invoke)。这样,在接口方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那样每一个方法进行中转。而且动态代理的应用使我们的类职责更加单一,复用性更强
代码实现:
接口:周杰伦工作室1
2
3
4
5
6
7
8
9
10
11package DynamicAgent;
/**
* 接口
*
* @date 2018/1/21 16:57:54
* @auther Pyctay
*/
public interface Jay {
void sing();
}
目标对象:周杰伦本人1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23package DynamicAgent;
/**
* 周杰伦真身
*
* @version 2018/1/21 16:59:44
* @auther Pyctay
*/
public class RealJay implements Jay {
private String songName;
public RealJay (String songName){
this.songName = songName;
ready();
}
public void ready() {
System.out.println("周杰伦被请来了,正在准备唱歌");
}
@Override
public void sing() {
System.out.println("周杰伦在唱"+songName);
}
}
代理对象:周杰伦经纪人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
30
31
32
33
34
35package DynamicAgent;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 周杰伦的经纪人
*
* @date 2018/1/22 14:21:05
* @auther Pyctay
*/
public class Agent implements InvocationHandler{
// 目标对象
private Object target;
// 构造目标对象
public Agent (Object target){
super();
this.target = target;
}
// 执行目标方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("主持人:今天有幸请到了周杰伦来为大家演唱一首歌");
Object resultValue = method.invoke(target,args);
System.out.println("晚会结束,谢谢大家");
return resultValue;
}
//ClassLoader loader:指定当前目标对象使用类加载器,写法固定
//Class<?>[] interfaces:目标对象实现的接口的类型,写法固定
//InvocationHandler h:事件处理接口,需传入一个实现类.
public Object getProxy() {
return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),target.getClass().getInterfaces(),this);
}
}
测试类:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16package DynamicAgent;
/**
* 动态代理测试主方法
*
* @date 2018/1/22 14:19:30
* @auther Pyctay
*/
public class Main {
public static void main(String[] args){
Jay realJay = new RealJay("菊花台");
Agent agent = new Agent(realJay);
Jay proxy = (Jay) agent.getProxy();
proxy.sing();
}
}
静态代理总结:
优点:可以做到在不修改目标对象的功能前提下,对目标功能扩展.并且维护方便,加入接口增加方法,代理类并不一定需要增加方法(如果增加的方法不需要代理)
缺点:和静态代理一样都需要目标对象实现一个或多个接口,但是很多时候有些类并没有实现接口,这样怎么办呢?我们可以使用cglib代理。
Cglib代理
注意事项
使用Cglib代理需要引入cglib的jar文件,由于Spring的核心包中已经包括了Cglib功能,所以也可以直接引入Spring核心包,由于Cglib的原理是构造一个目标类的子类,所以目标类不能为final,并且目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法。
代码实现:
目标对象:周杰伦本人1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22package CglibAgent;
/**
* 周杰伦真身
*
* @date 2018/1/21 16:59:44
* @auther Pyctay
*/
public class RealJay {
private String songName;
public RealJay(String songName){
this.songName = songName;
ready();
}
public RealJay(){};
public void ready() {
System.out.println("周杰伦被请来了,正在准备唱歌");
}
public void sing() {
System.out.println("周杰伦在唱"+songName);
}
}
代理对象:周杰伦经纪人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
30
31
32
33
34
35
36
37
38
39package CglibAgent;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* 周杰伦经纪人
*
* @date 2018/1/22 14:50:20
* @auther Pyctay
*/
public class Agent implements MethodInterceptor {
// 维护一个目标
private Object target;
// 构造一个目标对象
public Agent(Object target){
super();
this.target = target;
}
public Object getProxyInstance() {
Enhancer en = new Enhancer();
en.setCallback(this);
en.setSuperclass(target.getClass());
return en.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("主持人:今天有幸请到了周杰伦来为大家演唱一首歌");
Object resultValue = method.invoke(target,objects);
System.out.println("晚会结束,谢谢大家");
return resultValue;
}
}
测试类:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16package CglibAgent;
/**
* cglib代理测试主类
*
* @date 2018/1/22 14:57:34
* @auther Pyctay
*/
public class Main {
public static void main(String[] args){
RealJay realJay = new RealJay("发如雪");
Agent agent = new Agent(realJay);
RealJay proxy = (RealJay) agent.getProxyInstance();
proxy.sing();
}
}
到此,三种代理模式已经介绍完毕,在实际开发中,如果目标对象实现了接口,就可以选择静态代理或者是动态代理模式,如果目标对象没有实现接口,就可以使用Cglib代理模式
代理模式与装饰器模式区别:装饰器模式是为了增强功能,代理模式是控制访问。
完整代码实现:https://github.com/pyctay/proxy.git
同时欢迎关注我的微信公众号:猿人族永不为奴。
二维码: