现在我们来模拟一下,某位学生去考试。

  假设他(小明)正常的考试。

  运行结果:

       结果:

突然某一天,他睡过头了,来不急去考试,所有他打算叫另一个人(Cheater)去代替他考试。

  运行结果:

 结果:

上面的这些例子就是一个简单的代理行为。这个简单代理,耦合性太强了。作为演示就好了。

静态代理:

优点:

  1、  实现松散耦合。

  2、做到在不修改目标对象的功能前提下,对目标功能扩展。

还是拿上面的例子进行修改吧,小明还是一样起晚了,叫Cheater去代替他考试。

定义一个Exam接口 ,代表着考试的行为。

public interface Exam {//考试的接口
void exam();
}

去Student类中实现此接口,并实现方法。 (被代理)

public class Student implements Exam {

    public void exam(){
System.out.println("奋笔疾书,完成考试啦");
}
}

Cheater也实现Exam接口,并实现方法 (代理)

public class Cheater implements Exam {
  //被代理的对象
private final Exam student; public Cheater(Exam student){
this.student = student;
} public void exam() {
System.out.println("考试的时候唱了一首凉凉,差点被劝退了。");
student.exam();//调用Student类的方法
}
}

测试:

public class Main {

    public static void main(String[] args) {

        Exam xiaoMing = new Student();
xiaoMing.exam();//原来的行为
System.out.println("-----------下面是代理的行为------------");
Exam cheater = new Cheater(xiaoMing);
cheater.exam();//代理的行为 } }

结果:

 奋笔疾书,完成考试啦
-----------下面是代理的行为------------
考试的时候唱了一首凉凉,差点被劝退了。
奋笔疾书,完成考试啦

静态代理的缺点:

如果项目中有多个类,则需要编写多个代理类,工作量大,不好修改,不好维护,不能应对变化。

如果想解决上面的问题,可以使用动态代理。

动态代理:

1、使用JDK内置的Proxy实现

还是拿上面的例子进行修改吧,小明还是一样起晚了,叫人去代替他考试。(重新开始哈)

定义一个Exam接口。

public interface Exam {
void exam();
}

Student 实现 Exam接口

public class Student implements Exam {
public void exam() {
System.out.println("奋笔疾书,完成考试啦");
}
}

创建一个 JdkProxy 类 实现 InvocationHandler 接口

public class JdkProxy implements InvocationHandler {

    private Object object;//被代理的对象

    public JdkProxy(){}

    public JdkProxy(Object object){//初始化的时候就赋值
this.object = object;
}/**
* 当用户调用对象中的每个方法时都通过下面的方法执行,方法必须在接口
* proxy 被代理后的对象
* method 将要被执行的方法信息(反射)
* args 执行方法时需要的参数
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("考试的时候,吃颗糖压压惊,没有人知道,这次不是本人来考试的");
Object invoke = null;
try{
invoke = method.invoke(object, args);
}catch (Exception x){
System.out.println("异常信息:"+x.getMessage());
}return invoke;//调用被代理对象原来的方法(行为)
}
}

测试:

public class Main {
public static void main(String[] args) { /*
* 通过Proxy的newProxyInstance方法来创建我们的代理对象,我们来看看其三个参数
* 第一个参数 handler.getClass().getClassLoader() ,我们这里使用handler这个类的ClassLoader对象来加载我们的代理对象
* 第二个参数person1.getClass().getInterfaces(),我们这里为代理对象提供的接口是真实对象所实行的接口,表示我要代理的是该真实对象,这样我就能调用这组接口中的方法了
* 第三个参数handler, 我们这里将这个代理对象关联到了上方的 InvocationHandler 这个对象上
*/
ClassLoader cl = Thread.currentThread().getContextClassLoader();
Exam o = (Exam) Proxy.newProxyInstance(
cl,//类加载器
new Class[]{Exam.class},//获取被代理对象的所有接口
new JdkProxy(new Student())//InvocationHandler对象
);
o.exam();//代理后的行为 // 另一种写法
// Exam o1 = (Exam) Proxy.newProxyInstance(
// Thread.currentThread().getContextClassLoader(),
// Student.class.getInterfaces(),
// new JdkProxy(new Student())
// );
// o1.exam(); }
}

运行结果:

 考试的时候,吃颗糖压压惊,没有人知道,这次不是本人来考试的
奋笔疾书,完成考试啦

使用内置的Proxy实现动态代理有一个问题:被代理的类必须实现接口,未实现接口则没办法完成动态代理。

2、动态代理,使用cglib实现

还是拿上面的例子来说吧。

这次我们不写接口了。不过 我们要实现MethodInterceptor接口,并实现方法

去maven 中心仓库 找到 CGlib 的依赖

Student类

public class Student {

    public void exam(){
System.out.println("奋笔疾书,完成考试啦");
} }

创建一个 CglibProxy 的类 并 实现  MethodInterceptor 接口 ,并实现方法

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; public class CglibProxy implements MethodInterceptor { /*
* 参数
* Object 为由CGLib动态生成的代理类实例
* method 为上文中实体类所调用的被代理的方法引用
* objects 为参数值列表
* methodProxy 为生成的代理类对方法的代理引用
* return 从代理实例的方法调用返回的值
* */
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("考试的时候,唱了一首 我爱洗澡 ,差点被劝退 ");
return methodProxy.invokeSuper(o,objects);
} }

测试:

import net.sf.cglib.proxy.Enhancer;

public class Main {
public static void main(String[] args) { //增强器,动态代码生成器
Enhancer enhancer = new Enhancer();
//设置 生成类 的 父类
enhancer.setSuperclass(Student.class);
//回调函数
enhancer.setCallback(new CglibProxy());
//动态生成字节码并返回代理对象
Student o = (Student) enhancer.create();
o.exam(); //这里是简化写法
//第一个参数 设置 生成类 的父类 ,第二参数 被代理类的所有接口 ,回调函数
Student student = (Student) new Enhancer().create(Student.class, null, new CglibProxy());
student.exam();
}
}

运行结果:

 考试的时候,唱了一首 我爱洗澡 ,差点被劝退
奋笔疾书,完成考试啦 

这种代理的缺点:被代理的类必须不是final类。

小结:

使用cglib可以实现动态代理,即使被代理的类没有实现接口,但被代理的类必须不是final类。

示例代码下载地址:https://github.com/oukele/Spring-Proxy

Spring 静态代理和动态代理的更多相关文章

  1. java中代理,静态代理,动态代理以及spring aop代理方式,实现原理统一汇总

    若代理类在程序运行前就已经存在,那么这种代理方式被成为 静态代理 ,这种情况下的代理类通常都是我们在Java代码中定义的. 通常情况下, 静态代理中的代理类和委托类会实现同一接口或是派生自相同的父类. ...

  2. java:struts框架2(方法的动态和静态调用,获取Servlet API三种方式(推荐IOC(控制反转)),拦截器,静态代理和动态代理(Spring AOP))

    1.方法的静态和动态调用: struts.xml: <?xml version="1.0" encoding="UTF-8"?> <!DOCT ...

  3. Spring专题1: 静态代理和动态代理

    合集目录 Spring专题1: 静态代理和动态代理 为什么需要代理模式? 代理对象处于访问者和被访问者之间,可以隔离这两者之间的直接交互,访问者与代理对象打交道就好像在跟被访者者打交道一样,因为代理者 ...

  4. spring——AOP(静态代理、动态代理、AOP)

    一.代理模式 代理模式的分类: 静态代理 动态代理 从租房子开始讲起:中介与房东有同一的目标在于租房 1.静态代理 静态代理角色分析: 抽象角色:一般使用接口或者抽象类来实现(这里为租房接口) pub ...

  5. spring静态代理和动态代理

    本节要点: Java静态代理 Jdk动态代理 1 面向对象设计思想遇到的问题 在传统OOP编程里以对象为核心,并通过对象之间的协作来形成一个完整的软件功能,由于对象可以继承,因此我们可以把具有相同功能 ...

  6. spring的静态代理和动态代理

    Java静态代理 Jdk动态代理 java代理模式 即Proxy Pattern,23种java常用设计模式之一.代理模式的定义:对其他对象提供一种代理以控制对这个对象的访问. 原理: 代理模式的主要 ...

  7. Spring AOP里的静态代理和动态代理,你真的了解嘛?

    什么是代理? 为某一个对象创建一个代理对象,程序不直接用原本的对象,而是由创建的代理对象来控制原对象,通过代理类这中间一层,能有效控制对委托类对象的直接访问,也可以很好地隐藏和保护委托类对象,同时也为 ...

  8. Atitit 代理CGLIB 动态代理 AspectJ静态代理区别

    Atitit 代理CGLIB 动态代理 AspectJ静态代理区别 1.1. AOP 代理主要分为静态代理和动态代理两大类,静态代理以 AspectJ 为代表:而动态代理则以 spring AOP 为 ...

  9. 【Java】代处理?代理模式 - 静态代理,动态代理

    >不用代理 有时候,我希望在一些方法前后都打印一些日志,于是有了如下代码. 这是一个处理float类型加法的方法,我想在调用它前打印一下参数,调用后打印下计算结果.(至于为什么不直接用+号运算, ...

随机推荐

  1. 英特尔® 图形性能分析器 2019 R1 版本

    了解并下载全新英特尔® 图形性能分析器 2019 R1 版本.新版本新增了 DX11 和 Vulkan 多帧流捕获模式,可以在“帧和图形跟踪分析器”中分析 Vulkan 应用.此外,帧分析器还添加了 ...

  2. UOJ#494K点最短路

    #include <cstdio> #include <iostream> #include <cstring> #include <queue> #d ...

  3. 【DSP开发】HyperLink 编程和性能考量

    冯华亮/Brighton Feng---Communication Infrastructure 摘要 HyperLink 为两个 KeyStone 架构 DSP 之间提供了一种高速,低延迟,引脚数量 ...

  4. PTA(Basic Level)1032.挖掘机技术哪家强

    为了用事实说明挖掘机技术到底哪家强,PAT 组织了一场挖掘机技能大赛.现请你根据比赛结果统计出技术最强的那个学校. 输入格式: 输入在第 1 行给出不超过 105 的正整数 N,即参赛人数.随后 N ...

  5. (5.4)mysql高可用系列——MySQL异步复制(实践)

    关键词:mysql复制,mysql异步复制,mysql传统异步复制 [1]实验环境 操作系统:CentOS linux 7.5 数据库版本:5.7.24 数据库架构:主从复制,主库用于生产,从库用于数 ...

  6. 设计模式:解释器模式(Interpreter)

    为人处事是一门大学问,察言观色.听懂弦外之音都是非常重要的,老板跟你说“XX你最近表现平平啊,还得要多努力”,如果你不当回事,平常对待,可能下次就是“XX,恩,你人还是不错,平常工作也很努力,但是我想 ...

  7. PreparedStatement 以及事务的注意事项

    a).PreparedStatement 可以进行批量操作,但是与Statement有一定的区别 1. Statement可以进行不同sql语句的批量操作 即可以同时进行 crud 操作. Strin ...

  8. 使用Redis實現秒殺功能

    <?php $id = 1; $pdo=new PDO("mysql:host=127.0.0.1;dbname=test","root","r ...

  9. 小白学习django第六站-http相关

    请求与相应 HttpRequest对象API def home(request): print('path:', request.path) print('mothod:', request.meth ...

  10. 接口测试-免费开放的api

    归纳一些不错的免费开放的api 1.Apizza免费开放的Api接口 链接: https://www.jianshu.com/p/e6f072839282 接口文档:https://www.apiop ...