代理模式
![](https://img.haomeiwen.com/i4017523/62abbe05717164f9.png)
因为接下去将会看spring方面的源码,所以就把这些常用的先更新下,完整笔记在我git上,需要的可以私信我。
概念: 代理模式是JAVA常用的设计模式之一,代理模式不直调用代理对象,而是通过对象的代理类来处理,就像是中介、黄牛、经纪人,代理模式又分为静态代理和动态代理
代理对象:面向调用者,在调用者和被代理对象之间作为隔离层加以控制,可以增强被代理对象的方法。
被代理对象:真正的执行者,需要对代理对象暴露
下面以歌手-Singer 经纪公司-Agent为例分析几种代理模式
静态代理
何为静态?其实就是在编码阶段,写好代理类,然后编译运行,在程序运行前,代理类已经存在。
![](https://img.haomeiwen.com/i4017523/16ba3f6fb61c2161.png)
代码理解如下:
Person类: 被代理和代理类需要实现的抽象接口
public interface Person {
void doSomething();
}
Agent 类: 代理类,这里作为歌手的代理为经济公司
public class Agent implements Person {
private Person target;
public Person getProxyInstance(Person target){
this.target=target;
return target;
}
@Override
public void doSomething() {
System.out.println("经纪公司,接收演出");
target.doSomething();
System.out.println("经济公司,开始收钱");
}
}
歌手类: 被代理的对象
public class Single implements Person {
@Override
public void doSomething() {
System.out.println("开始准备演唱会。。。。唱歌");
}
}
测试类
public class Test {
public static void main(String[] args) {
Person proxy=new Agent().getProxyInstance(new Single());
proxy.doSomething();
}
}
结果:
![](https://img.haomeiwen.com/i4017523/8b8d23bcbca9b4ed.png)
动态代理
相对于静态代理,动态代理的代理类是在运行时生成,动态代理在实现上又有两种,分别是jdk动态代理和cglib动态代理。
JDK动态代理
JDK动态代理要求被代理对象必须实现接口,其原理是代理对象实现该接口的方法,同时调用被代理对象的方法。将生成后的代理对象,强制转换为接口被调用者调用。
Agent类没有直接实现Person接口,而是通过反射生成字节码文件的方式,动态生成真正的代理类,那么生成的代理类内部具体细节是怎样的?写个方法输出代理类object.
JDK动态代理的原理如下:
//原理:
//1、拿到被代理对象的引用,并且获取到它的所有的接口,反射获取
//2、JDK Proxy类重新生成一个新的类、同时新的类要实现被代理类所有实现的所有的接口
//3、动态生成Java代码,把新加的业务逻辑方法由一定的逻辑代码去调用(在代码中体现)
//4、编译新生成的Java代码.class
//5、再重新加载到JVM中运行
//以上这个过程就叫字节码重组
//JDK中有个规范,只要要是$开头的一般都是自动生成的
//通过反编译工具可以查看源代码
byte [] bytes = ProxyGenerator.generateProxyClass("$Proxy0",new Class[]{Person.class});
FileOutputStream os = new FileOutputStream("E://$Proxy0.class");
os.write(bytes);
os.close();
代码理解如下:
Person类: 被代理和代理类需要实现的抽象接口
public interface Person {
void doSomething();
}
Agent 类: 代理类,这里作为歌手的代理为经济公司
public class Agent implements InvocationHandler {
private Person target;
public Object getProxyInstance(Person 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("经纪公司接单:");
method.invoke(target,args);
System.out.println("经纪公司:演出完了,收钱");
return null;
}
歌手类: 被代理的对象
public class Singer implements Person {
public Singer() {
}
@Override
public void doSomeThing() {
System.out.println("歌手,负责唱歌");
}
}
客户端类
public class Client {
public static void main(String[] args) {
//返回的是singer的代理类
Person singer = (Person) new Agent().getProxyInstance(new Singer());
singer.doSomeThing();
}
}
结果:
![](https://img.haomeiwen.com/i4017523/8cfe50793f45a770.png)
cglib动态代理
与JDK动态代理不同的是,cglib代理不强制被代理类实现接口,它是通过生成代理类的子类,并重写代理类方法来实现的,所以代理类不能用final修饰.
Person类: 被代理和代理类需要实现的抽象接口
public interface Person {
void doSomething();
}
Agent 类: 代理类,这里作为歌手的代理为经济公司
public class Agent implements MethodInterceptor {
private Person target;
public Object getProxyInstance(Object target){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("经纪公司:接单");
methodProxy.invokeSuper(o,objects);
System.out.println("经纪公司收钱:");
return null;
}
}
歌手类: 被代理的对象
public class Singer implements Person {
@Override
public void doSomeThing() {
System.out.println("歌手,负责唱歌");
}
}
测试类
public class Client {
public static void main(String[] args) {
Singer singer = (Singer) new Agent().getProxyInstance(new Singer());
singer.doSomeThing();
}
}
结果:
应用场景
代理模式随处可见,最常见的是spring的AOP,比如spring的事务代理,在对数据进行操作时会涉及事务开启、事务提交、事务回滚等操作,简化的数据操作模型如下图:
![](https://img.haomeiwen.com/i4017523/80ab3020dc01aaac.png)
业务场景中存在大量的事务操作(红色),而这些往往不是开发者的关注点,开发者更注重于业务逻辑(蓝色)。因此代理模式可以增强业务逻辑代码,在事务执行前开启事务,在事务执行后进行回滚/提交/关闭。
总结
- 静态代理需要编写代理类,动态代理不需要;
- 静态代理在无需修改被代理类的前提下,对代理类进行增强和扩展,但是静态代理只能对一个被代理类服务,如果被代理类过多,就会产生相应数量的代理类实现与被代理类一致的接口,产生冗余,不易维护;
- JDK动态代理只要求代理类实现InvocationHandler接口,被代理类实现业务接口即可。
- 静态代理编译时生成的class的性能高于JDK动态代理通过反射生成class;
- cglib不要求被代理类实现接口,而是通过继承的方式实现,因此类和方法都不能用final修饰。cglib底层用高性能的字节码生成器,性能高于反射。
感谢您阅读我的文章,如果满意可以帮我点赞,谢谢哈。
如果对文章部分还有什么见解或者疑惑,可以私信评论我,欢迎技术讨论。如果需要获取完整的文件资源,可以加我微信z985085305,获取我整理的全套笔记。
思想的碰撞最能促进技术的进步哦。
网友评论