什么是代理模式
代理模式顾名思义就是为一个对象提供一个代理,并让其他对象通过代理对象来控制这个对象。代理模式又分为两种,一种叫静态代理,一种叫动态代理。
静态代理
角色分析
- Isubject
抽象角色,一般使用接口和抽象类,不过抽象类不怎么好用,大多都是接口。
- RealSubject
真实角色,被代理的角色。
- 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("用软件卖票十分方便"); } }
|
好了,那就模拟一下我们买票的过程吧。比如我们通过软件就能看到火车站在卖票,然后在软件上买票,这样就不用去到火车站在买票了,也就是我们不需要使用真实角色的卖票方法,使用的是代理对象的卖票方法,代理对象就会帮我们去真实对象中调用想要的方法(代理对象有点类似中介)。
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(); } }
|
当然有人会觉得代理也没差多少,也是挺麻烦的,都是需要手动添加很多代码,但代理模式可以更加方便地拓展,比如需要加功能就再添加一个方法,不用总是改接口实现类里的方法,代码一多就能感受到代理模式的方便了,随便改源码不是一个好的习惯。
动态代理
静态代理大概了解了,就能发现静态代理会有一个很大的问题。当需要代理的类多了,就会发现代理的类也会增多,因为静态代理是一个类绑定一个代理类的,这就很麻烦取管理了,因为代理类的创建就是为了方便,所以动态代理就出现了。动态代理的代理类是动态生成的,我们写好一个工具类就能动态生成很多类的代理类了。
动态代理分为两大类。
- JDK动态代理
- CGLib动态代理
JDK动态代理
JDK动态代理是Java自带的一种动态代理的方式,核心原理就是通过反射来实现的。首先要实现JDK的动态代理就要先了解一个类和一个接口,直接看文档的说法。
Proxy
Proxy提供了创建动态代理类和实例的静态方法,它也是由这些方法创建的所有的动态代理类的超类。这里也不深入了解了,只要了解它主要的一个方法,生成动态代理类的方法。
Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
new Class<?>[] { Foo.class },
handler);
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
| 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动态代理