什么是代理模式

代理模式顾名思义就是为一个对象提供一个代理,并让其他对象通过代理对象来控制这个对象。代理模式又分为两种,一种叫静态代理,一种叫动态代理。

静态代理

角色分析

  1. Isubject
    抽象角色,一般使用接口和抽象类,不过抽象类不怎么好用,大多都是接口。
  2. RealSubject
    真实角色,被代理的角色。
  3. Proxy
    代理角色,代理真实角色

代码实现1

光用文字描述,会觉得十分的绕,还是用代码提高一下理解吧。首先先模拟一下环境,比如买火车票这件事情。卖火车票这件事情就是一个抽象角色,我们为它创建一个接口。

1
2
3
public interface buyTickets {
void buytickets();
}

有买票就有卖的人,然后就是火车站卖票,火车站就是一个真实角色,为它创建一个类。

1
2
3
4
5
public class Station implements buyTickets {
public void buytickets() {
System.out.println("火车站在卖票");
}
}

那么问题就来了,我们学生需要买票回家,是该怎么买呢?应该不会有多少人去到火车站买票吧。大多都是网上订票吧,就比如使用软件买票,方便也快捷,这个软件就是一个代理角色,代理了火车站卖票。我们也为他创建一个类。一般代理都会有很多特定的作用,即附属一些其他的方法,就比如使用软件卖票会十分方便。

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Software implements buyTickets {
private Station station;
public Software(){}
public Software(Station station) {
this.station = station;
}
public void buytickets() {
station.buytickets();
}
public void Convenient(){
System.out.println("用软件卖票十分方便");
}
}

好了,那就模拟一下买票的我们吧,就比如这个test就是我们,我们通过软件就能看到火车站在卖票,也很方便,就不用亲自去火车站了。

1
2
3
4
5
6
7
8
9
public class test01 {
public static void main(String[] args) {
//首先是一个火车站要卖票
Station station = new Station();
//然后就是软件代理火车站卖票
Software software = new Software(station);
software.buytickets();
}
}

代码实现2

上述是我们日常生活中一个小小的例子,可能没能够体会到代理模式的好处,我们来看看项目中的一些简单实现吧,比如经常见到的增删改查操作。首先是业务接口和接口实现类。

1
2
3
4
5
6
public interface userService {
void add();
void delete();
void update();
void select();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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 select() {
System.out.println("查询了一个用户");
}
}

这是没有代理角色的情况,我们也可以直接创建userServiceImpl对象调用里面的方法,但是要是之后需要了增加一些业务需求,比如增加一个日志功能,我们常规就是直接修改userServiceImpl类,比如这样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class userServiceImpl implements userService{
public void add() {
System.out.println("使用add方法增加了一个用户");
System.out.println("增加了一个用户");
}
public void delete() {
System.out.println("使用delete方法删除了一个用户");
System.out.println("删除了一个用户");
}
public void update() {
System.out.println("使用update方法修改了一个用户");
System.out.println("修改了一个用户");
}
public void select() {
System.out.println("使用select方法查询了一个用户");
System.out.println("查询了一个用户");
}
}

这只是四个增删改查的操作,你都会发现这样很麻烦,要是再加几个功能,或者再加几个操作,难道还要一个个改吗?这时候用代理就能很好的解决这种问题。那我们先创建这个代理类。

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
public class Proxy  implements userService{
private userServiceImpl userserviceimpl;
public Proxy() {}
public Proxy(userServiceImpl userserviceimpl) {
this.userserviceimpl = userserviceimpl;
}
public void add() {
log("add");
userserviceimpl.add();
}
public void delete() {
log("delete");
userserviceimpl.delete();
}
public void update() {
log("update");
userserviceimpl.update();
}
public void select() {
log("select");
userserviceimpl.select();
}
public void log(String logs){
System.out.println("使用了"+logs+"方法");
}
}
1
2
3
4
5
6
7
public class test01 {
public static void main(String[] args) {
userServiceImpl userserviceimpl = new userServiceImpl();
Proxy proxy = new Proxy(userserviceimpl);
proxy.add();
}
}

当然有人会觉得代理也没差多少,也是挺麻烦的,的确都是需要加的,但代理会更加方便地拓展,比如需要加功能就再添加一个方法,不用总是改接口实现类里的方法,代码一多就能感受到代理模式的方便了,随便改源码不是一个好的习惯。

动态代理

静态代理大概了解了,就能发现静态代理会有一个很大的问题。当需要代理的类多了,就会发现代理的类也会增多,因为静态代理是一个类绑定一个代理类的,这就很麻烦取管理了,因为代理类的创建就是为了方便,所以动态代理就出现了。动态代理的代理类是动态生成的,我们写好一个工具类就能动态生成很多类的代理类了。

动态代理分为两大类。

  1. JDK动态代理
  2. CGLib动态代理

JDK动态代理

JDK动态代理是Java自带的一种动态代理的方式,核心原理就是通过反射来实现的。首先要实现JDK的动态代理就要先了解一个类和一个接口,直接看文档的说法。

  1. Proxy
    Proxy提供了创建动态代理类和实例的静态方法,它也是由这些方法创建的所有的动态代理类的超类。这里也不深入了解了,只要了解它主要的一个方法,生成动态代理类的方法。

    Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),

                                          new Class<?>[] { Foo.class },
                                          handler); 
  2. InvocationHandler
    每个代理实例都有一个关联的调用处理程序。当在代理实例上调用方法时,方法调用将会被编码分派到其调用处理程序的invoke方法。官方说法有点绕,简单来说是这个接口就只有一个invoke方法,这个方法是处理被代理实例即真实角色的,并会返回一个结果。

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

大概了解了这两个,就写一个生成动态代理的工具类吧。(基本固定,可以当作万能的工具类)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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);
}

//处理要被代理实例,并返回结果
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = method.invoke(target, args);
return result;
}
}

写完了代理类就来写测试一下吧,例子就选之前的那个增删改查的接口吧。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class test01 {
public static void main(String[] args) {
//先创建一个真实角色
userServiceImpl userservice = new userServiceImpl();

ProxyInvocationHandler pih = new ProxyInvocationHandler();
//闯入需要代理的角色
pih.setTarget(userservice);
//生成一个代理类
userService proxy = (userService) pih.getProxy();

proxy.add();
proxy.delete();
proxy.select();
proxy.update();
}
}

当我们需要添加功能的时候,比如添加一个日志的功能,就改写一下工具类就好了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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);
}
//处理要被代理实例,并返回结果
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log(method.getName());
Object result = method.invoke(target, args);
return result;
}
public void log(String msg){
System.out.println("使用了"+msg+"方法");
}
}

CGLib动态代理

emm,这个有点复杂,等之后再写一篇文章来详细说。