关注公众号 JavaStorm 获取更多技术。java
为另外一个对象提供表明,以便控制客户对对象的访问。其定义为:为另外一个对象提供替身或占位符以访问这个对象。具体地吗能够浏览 github.com/UniqueDong/…git
官话上讲是一种设计模式,目的是但愿代码重用。跟咱们以往访问对象的方式不一样,代理模式不是直接经过目标对象,而是经过代理访问咱们的目标对象以及方法。由于有的时候咱们没法直接与目标对象创建联系或者,咱们要控制客户端访问。因此便经过代理来访问咱们的真实对象。github
就比如「客户」-> 「明星经纪人」-> 「明星」。咱们不是直接与明星联系,明星很忙的,要唱歌跳舞烫头拍电影,给的价格足够好,经纪人才告知明星接下这个任务。设计模式
咱们根据加载被代理类的时机不一样,将代理分为静态代理和动态代理。若是咱们在代码编译时就肯定了被代理的类是哪个,那么就能够直接使用静态代理;若是不能肯定,那么可使用类的动态加载机制,在代码运行期间加载被代理的类这就是动态代理,好比RPC框架和Spring AOP机制。bash
咱们按照 UML 类图来实现一个简单的静态代理模式,首先先建立一个 Subject 接口。网络
public interface Subject {
public void request();
}
复制代码
建立一个真实对象去实现该接口app
public class RealSubjbect implements Subject {
@Override
public void request() {
System.out.println("this is RealSubjbect.request()");
}
}
复制代码
建立咱们的代理类,持有真实对象的引用,同时实现了 Subject 接口。框架
public class ProxySubject implements Subject {
private Subject realSubjbect = null;
/** * 除了代理真实角色作该作的事,代理角色提供附加操做 * 如 */
@Override
public void request() {
preRequest(); //真实角色操做前的附加操做
if (realSubjbect == null) {
realSubjbect = new RealSubjbect();
}
realSubjbect.request();
postRequest();//真实角色操做后的附加操做
}
/** * 真实角色操做前的附加操做 */
private void postRequest() {
System.out.println("真实角色操做后的附加操做");
}
/** * 真实角色操做后的附加操做 */
private void preRequest() {
System.out.println("真实角色操做前的附加操做");
}
}
复制代码
编写咱们的客户端接口,经过代理类调用。ide
public class Client {
public static void main(String[] args) {
Subject subject = new ProxySubject();
subject.request(); //代理者代替真实者作事情
}
}
复制代码
打印的结果以下所示,咱们实现了对真实对象的控制,而且新增一些操做。就像Spring的AOP实现的功能同样。post
真实角色操做前的附加操做
this is RealSubjbect.request()
真实角色操做后的附加操做
复制代码
另外,若是要按照上述的方法使用代理模式,那么真实角色(委托类)必须是事先已经存在的,并将其做为代理对象的内部属性。可是实际使用时,一个真实角色必须对应一个代理角色,若是大量使用会致使类的急剧膨胀;此外,若是事先并不知道真实角色(委托类),该如何使用代理呢?这个问题能够经过Java的动态代理类来解决。
动态代理类的源码是在程序运行期间 JVM 根据反射机制动态生成的,因此不存在代理类的字节码文件。代理类和委托类的关系是在程序运行时肯定。
主要有两种实现方式:
UML 类图以下所示
Handler
实现 InvocationHandler
,同时持有真实对象的引用,会依赖咱们的业务类。Proxy.newProxyInstance()
生成,而该方法以来于 Handler
对象和 真实对象 。经过类图其实咱们也能够知道,当经过动态代理生成的代理字节码调用的时候就会委托到 Handler 的 invoke 方法。同时 Handler 又持有真正的业务对象。因此能在执行调用真实的对象以前控制其行为以及访问。主要优势:
经过类图,建立 JDK 动态代理咱们能够分为三步。
InvocationHandler
接口,并依赖被代理类(真实对象的引用)。Proxy.newProxyInstance()
方法建立具体代理对象。如今咱们有一个需求,为咱们的业务逻辑统一记录调用日志,或者事务控制,在这里咱们那就编写一个日志记录为咱们的被代理类记录日志。模拟相似 Spring AOP功能,一个简化版的例子让你们明白其使用场景以及原理。
第一步:咱们定义本身的业务接口 OrderService
跟 ProductService
public interface OrderService {
String createOder(String no);
}
public interface ProductService {
String getProduct(String no);
}
复制代码
业务逻辑的实现类
public class OrderServiceImpl implements OrderService {
@Override
public String createOder(String no) {
return "生成订单" + no + "成功";
}
}
public class ProductServiceImpl implements ProductService {
@Override
public String getProduct(String no) {
return "获取商品" + no + "成功";
}
}
复制代码
第二步:定义咱们的 Handler 实现 JDK 的 Invocationhandler,将持有的委托引用定义 Object
类型,这样能够代理全部须要日志记录的委托类,同时咱们有一个 bind()
方法,用于设置 target 同时返回 对应的代理类让客户端调用。第三步的 Proxy.newProxyInstance() 咱们放在 bind 方法里。代码更加简洁。
public class LoggerInterceptor implements InvocationHandler {
/** * 持有委托类的引用 */
private Object target;
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long start = System.currentTimeMillis();
System.out.println(proxy.getClass().getInterfaces()[0]);
//经过反射调用
System.out.println("Entered " + target.getClass().getName() + "-" + method.getName() + ",with arguments{" + args[0] + "}");
//调用目标对象的方法
Object result = method.invoke(target, args);
long end = System.currentTimeMillis();
System.out.println("执行结果:" + result.toString());
System.out.println("共耗时:" + (end - start));
return result;
}
/** * 获取代理类:并将 target 委托类绑定到 咱们定义的Targe中 * @param target 真实的委托类 * @return */
public synchronized Object bind(Object target) {
this.target = target;
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
}
复制代码
最后咱们新建启动类看效果,分别有两个业务逻辑类须要日志记录。具体可看代码注释
public class BootStrap {
public static void main(String[] args) {
// 新建业务逻辑
OrderService orderService = new OrderServiceImpl();
ProductService productService = new ProductServiceImpl();
// 新建咱们的 日志 Handler
LoggerInterceptor loggerInterceptor = new LoggerInterceptor();
//绑定 委托类同时生产代理类调用 。
OrderService orderServiceProxy = (OrderService) loggerInterceptor.bind(orderService);
orderServiceProxy.createOder("12927381");
ProductService productServiceProxy = (ProductService) loggerInterceptor.bind(productService);
productServiceProxy.getProduct("34010234");
}
}
复制代码
结果打印以下所示:咱们能够看到,每一个咱们所代理的接口执行方法先后都打印了日志以及返回结果。其实 Spring AOP 就是经过动态代理实现。
nterface com.zero.headfirst.proxy.service.OrderService
Entered com.zero.headfirst.proxy.service.OrderServiceImpl-createOder,with arguments{12927381}
执行结果:生成订单12927381成功
共耗时:2
interface com.zero.headfirst.proxy.service.ProductService
Entered com.zero.headfirst.proxy.service.ProductServiceImpl-getProduct,with arguments{34010234}
执行结果:获取商品34010234成功
共耗时:1
复制代码
CGLIB是一个强大的高性能的代码生成包。它普遍的被许多AOP的框架使用,例如Spring AOP为他们提供方法的interception(拦截)。CGLIB包的底层是经过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。
不能是 final 修饰的类,以及final方法,能够不定义接口。
使用方式很简单:咱们要先添加cglib 依赖 ,新建一个类实现 MethodInterceptor 。
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class LoggerProxy implements MethodInterceptor {
/** * 建立代理类 * @param targetClass 委托类 * @return */
public Object bind(Class targetClass) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(targetClass);
//设置回调方,当客户端经过代理调用方法的时候会会调用咱们重写的 intercept() 方法
enhancer.setCallback(this);
//建立代理类
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
long start = System.currentTimeMillis();
//经过反射调用
System.out.println("Entered " + o.getClass().getName() + "-" + method.getName() + ",with arguments{" + args[0] + "}");
Object result = methodProxy.invokeSuper(o, args);
long end = System.currentTimeMillis();
System.out.println("执行结果:" + result.toString());
System.out.println("共耗时:" + (end - start));
return result;
}
}
复制代码
是否是很简单?新建 Enhancer ,只要设置好 委托类以及回调类。在这里咱们能够利用工厂模式 建立不一样的代理类对应的回调。这里简单实例就不写了。
接下来看咱们的测试类以及结果
public class CglibBootStrap {
public static void main(String[] args) {
LoggerProxy loggerProxy = new LoggerProxy();
OrderService orderService = (OrderService) loggerProxy.bind(OrderServiceImpl.class);
orderService.createOder("12873051209g");
ProductService productProxy = (ProductService) loggerProxy.bind(ProductServiceImpl.class);
productProxy.getProduct("2780935782309");
}
}
复制代码
打印结果以下所示
Entered com.zero.headfirst.proxy.service.OrderServiceImpl$$EnhancerByCGLIB$$54d983a1-createOder,with arguments{12873051209g}
执行结果:生成订单12873051209g成功
共耗时:14
Entered com.zero.headfirst.proxy.service.ProductServiceImpl$$EnhancerByCGLIB$$4e2c7c36-getProduct,with arguments{2780935782309}
执行结果:获取商品2780935782309成功
共耗时:5
复制代码