设计模式是为了可扩展性,不要为了使用设计模式而使用
概念
为其他对象提供一种代理以控制对这个对象的访问
代理模式包含如下角色:
- Subject:抽象主题角色。可以是接口,也可以是抽象类。
- RealSubject:真实主题角色。业务逻辑的具体执行者。
- ProxySubject:代理主题角色。内部含有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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
| public interface ICoder {
Boolean implDemands(String demands);
}
public class JavaCoder implements ICoder {
private final String name = "java程序员";
@Override public Boolean implDemands(String demands) { System.out.println(String.format("%s收到需求[%s]", name, demands)); System.out.println(String.format("%s需求[%s]实现完成", name, demands)); return true; }
}
public class CoderProxy implements ICoder {
private final String name = "项目经理";
private final ICoder coder;
public CoderProxy(ICoder coder) { this.coder = coder; }
@Override public Boolean implDemands(String demands) { System.out.println(String.format("%s收到需求[%s]", name, demands)); if(demands.contains("像淘宝")) { System.out.println(String.format("%s回复: [%s]无法实现", name, demands)); return false; } else { Boolean result = coder.implDemands(demands); System.out.println(String.format("%s需求[%s]实现完成", name, demands)); return result; } }
}
public class Customer {
public static void main(String[] args) { Customer customer = new Customer();
CoderProxy coderProxy = new CoderProxy(new JavaCoder());
customer.putDemands(coderProxy, "做个管理系统"); System.out.println("--------------------------------------"); customer.putDemands(coderProxy, "做个网站,像淘宝一样就行"); }
public void putDemands(ICoder coder, String demands) { System.out.println(String.format("客户提出需求[%s]", demands)); Boolean result = coder.implDemands(demands); if(result) { System.out.println("感谢!!!"); } else { System.out.println("不能实现啊,好吧!!!"); } } }
|
示例二
OnJava8中的样例:
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 39 40 41 42 43 44 45
| interface ProxyBase { void f();
void g();
void h(); }
class Proxy implements ProxyBase { private ProxyBase implementation;
Proxy() { implementation = new Implementation(); } @Override public void f() { implementation.f(); } @Override public void g() { implementation.g(); } @Override public void h() { implementation.h(); } }
class Implementation implements ProxyBase { public void f() { System.out.println("Implementation.f()"); }
public void g() { System.out.println("Implementation.g()"); }
public void h() { System.out.println("Implementation.h()"); } }
public class ProxyDemo { public static void main(String[] args) { Proxy p = new Proxy(); p.f(); p.g(); p.h(); } }
|
具体实现不需要与代理对象具有相同的接口;只要代理对象以某种方式“代表具体实现的方法调用,那么基本思想就算实现了。然而,拥有一个公共接口是很方便的,因此具体实现必须实现代理对象调用的所有方法
动态代理
代理类在程序运行时创建的代理方式被成为动态代理
使用java.lang.reflect 包中的 Proxy 类与 InvocationHandler 接口
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 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| public interface Teacher {
boolean teach(String bookName);
}
public class EnglishTeacher implements Teacher { @Override public boolean teach(String bookName) { System.out.println("英语老师准备上课"); System.out.println("今天讲: " + bookName); try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(bookName + "讲完了"); return true; } }
public class DynamicRecordProxy implements InvocationHandler {
private Object target;
public DynamicRecordProxy() {
}
public Object bind(Object target) { this.target = target; return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); }
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("开始记录"); long startTime = System.currentTimeMillis(); Object result = method.invoke(target, args); System.out.println("记录完成,花费: " + (System.currentTimeMillis() - startTime) + " 毫秒"); return result; } }
public class DynamicProxyTest {
public static void main(String[] args) { DynamicRecordProxy dynamicRecordProxy = new DynamicRecordProxy(); Teacher teacher = (Teacher) dynamicRecordProxy.bind(new EnglishTeacher()); teacher.teach("Oxford University Press");
} }
|
其实就是JDK帮我们自动编写了类(不需要源码,可以直接生成字节码):
1 2 3 4 5 6 7 8 9 10 11 12
| public class HelloDynamicProxy implements Hello { InvocationHandler handler; public HelloDynamicProxy(InvocationHandler handler) { this.handler = handler; } public void morning(String name) { handler.invoke( this, Hello.class.getMethod("morning", String.class), new Object[] { name }); } }
|
CgLib
JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口,如果想代理没有实现接口的类,就可以使用Cglib实现
如果是spring 项目直接使用就行,非spring项目可引用:
1 2 3 4 5
| <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.2.10</version> </dependency>
|
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 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| public class UserService {
public Boolean login(String name, String password) { System.out.println(name + "用户准备登录, 密码为: " + password);
try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(name + "登录成功"); return true; } }
public class CglibProxy implements MethodInterceptor {
private Enhancer enhancer = new Enhancer();
public Object getProxy(Class<?> clazz) { enhancer.setSuperclass(clazz); enhancer.setCallback(this); return enhancer.create(); }
@Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("before"); Object result = methodProxy.invokeSuper(o, objects); System.out.println("after"); return result; } }
public class CglibProxyTest {
public static void main(String[] args) { CglibProxy cglibProxy = new CglibProxy();
UserService userService = (UserService) cglibProxy.getProxy(UserService.class); userService.login("admin", "admin123"); }
}
public class ProxyFactory { public static Object getGcLibDynProxy(Object target) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(target.getClass()); enhancer.setCallback(new CglibProxy()); Object targetProxy = enhancer.create(); return targetProxy; }
}
|
Spring中的应用
在Spring的AOP编程中:
- 如果加入容器的目标对象有实现接口,用JDK代理
- 如果目标对象没有实现接口,用Cglib代理
不过需要注意:
- 代理的类不能为final,否则报错
- 目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法