一, 什么是代理模式
相信大家都知道代理商这个概念, 在商业中, 代理商无处不在. 假设你要去买东西, 你不可能去找真正的厂家去买, 也不可能直接跟厂家提出需求, 代理商就是这中间的一桥梁, 连接买家和厂商. 你要买或者定制什么产品, 需要什么规格样式和价格的直接跟代理商沟通就好了, 由代理商与真实的厂商沟通, 这样买家有什么问题都可以咨询代理商, 真实厂商也就可以专心做产品, 不需要管其他事务了, 买家也插手不了厂商的事.
在以上的关系中, 厂商就是被代理对象, 代理商就是代理对象, 而买家就是调用者了. java 中有多种动态代理的技术, 包括 JDK,CGLIB,Javassist 等, 这里我会拿 CGLIB 与 JDK 动态代理作比较.
二, 意义
动态代理的意义就是在与生成一个占位 (代理对象), 来代理真实对象, 从而达到控制真实对象的目的. 要了解动态代理, 首先要具备反射的知识.
三, 实现动态代理的步骤
代理的实现分为两个主要步骤:
1. 代理对象和真实对象建立代理关系
2. 实现代理对象的逻辑方法
四, JDK 动态代理
JDK 动态代理, JDK 自带的功能, 在 java.lang.reflect.* 包中. 要实现 JDk 动态代理, 必须要借助接口才能产生代理对象.
1. 先来定义一个简单的接口 HelloWorld.java
- public interface HelloWorld {
- void sayHello();
- }
2.HelloWorld 的实现类 HelloWorldImpl.java
- public class HelloWorldImpl implements HelloWorld {
- @Override
- public void sayHello() {
- System.out.println("hello world");
- }
- }
3. 建立代理关系, 必须实现接口 InvocationHandler.invoke 是接口里唯一需要实现的方法, 里面实现的代理逻辑, 当代理对象调度方法时, 就会映射到 invoke 方法来, 实际是通过反射来实现的. 而 bind() 方法就是建立代理关系, 通过 Proxy 的 newProxyInstance 来创建代理对象, 第一个参数类加载器, 第二个参数下挂的接口, 第三个参数则是实现了代理逻辑方法 invoke 的类.
- import java.lang.reflect.InvocationHandler;
- import java.lang.reflect.Method;
- import java.lang.reflect.Proxy;
- public class JdkProxy implements InvocationHandler {
- // 真实对象
- private Object target = null;
- /**
- * 建立真实对象与代理对象之间的关系
- * @param target 传入真实对象
- * @return
- */
- public Object bind(Object target){
- this.target = target;
- return Proxy.newProxyInstance
- (target.getClass().getClassLoader(),
- target.getClass().getInterfaces(),this);
- }
- /**
- * 代理逻辑
- * @param proxy 代理对象
- * @param method 当前调度的方法
- * @param args 调度方法的参数
- * @return
- * @throws Throwable
- */
- @Override
- public Object invoke(Object proxy, Method method, Object[] args)
- throws Throwable {
- System.out.println("进入代理逻辑方法");
- System.out.println("在调用真实对象之前的服务");
- Object object = method.invoke(target,args);
- System.out.println("在调用真实对象之后的服务");
- return object;
- }
- }
4. 测试
- import org.junit.Test;
- public class JdkProxyTest {
- @Test
- public void test_JdkProxy(){
- JdkProxy proxy = new JdkProxy();
- HelloWorld helloWorld = null;
- try {
- helloWorld = (HelloWorld) proxy
- .bind(Class.forName
- ("com.xcl.ssm.chapter2.jdkproxy.HelloWorldImpl")
- .newInstance());
- } catch (Exception e) {
- e.printStackTrace();
- }
- helloWorld.sayHello();
- }
- }
五, CGLIB 动态代理
上面说 JDK 动态代理的实现必须借助接口才能实现, 而 CGLIB 则不用, 只需要有一个非抽象类即可. 我们以上面的 HelloWorldImpl 类为例, 现在没有 HelloWorld 接口了. JDK 动态代理必须要实现 invocationHandler 接口, CGLIB 则必须要实现 MethodInterceptor 接口, 里面也只有一个需要实现的方法 intercept(), 需要在里面实现代理逻辑. 写代码之前记得导包哦, 这是第三方提供的技术.
- public class CglibProxy implements MethodInterceptor {
- /**
- * 生成 CGLIB 代理对象
- * @return
- */
- public Object getProxy(Class clz){
- //CGLIB 增强类对象
- Enhancer enhancer = new Enhancer();
- enhancer.setSuperclass(clz);
- // 设置当前对象为代理逻辑对象
- enhancer.setCallback(this);
- // 生成并返回代理对象
- return enhancer.create();
- }
- /**
- * 代理逻辑方法
- * @param proxy 代理对象
- * @param method 方法
- * @param args 参数
- * @param methodProxy 方法代理
- * @return
- * @throws Throwable
- */
- @Override
- public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
- System.out.println("调用真实对象前");
- // 通过反射调用真实对象的方法
- Object obj = methodProxy.invokeSuper(proxy,args);
- System.out.println("调用真实对象后");
- return obj;
- }
- }
六, 总结
要真正的参透动态代理就要非常熟悉反射机制, 从以上的代码可以得知, 动态代理的底层实现还是基于反射的, 我们知道反射是很消耗性能的, 这带来了一些性能问题, 但是为了开发上的简便, 这种牺牲是值得的. 我们学的框架, 比如 spring,mybatis 等, 都大量使用了动态代理技术, 有兴趣的童鞋可以去阅读源码 (表示本人也不是看过很多源码), 这里面用到了很多的设计模式, 对提高自己的代码水平很有帮助. 好啦, 就说到这里吧~
喜欢我的小伙伴记得扫描关注呦~
来源: http://www.bubuko.com/infodetail-3156888.html