本节要点:

  • Java静态代理
  • Jdk动态代理

面向对象设计思想遇到的问题

在传统OOP编程里以对象为核心,并通过对象之间的协作来形成一个完整的软件功能,由于对象可以继承,因此我们可以把具有相同功能或相同特征的属性抽象到一个层次分明的类结构体系中。随着软件规范的不断扩大,专业化分工越来越系列,以及OOP应用实践的不断增多,随之也暴露了一些OOP无法很好解决的问题。

现在假设系统中有三段完全相似的代码,这些代码通常会采用“复制”、“粘贴”方式来完成,通过这种方式开发出来的软件如图所示:

可能读者已经发现了这种做法的不足之处,如果有一天,蓝色背景的代码需要修改,

那是不是要同时修改三个地方?如果不仅仅是这三个地方包含这段代码,而是100个

,甚至是1000个地方,那会是什么后果?

记录日志在代码中无处不在---先来看一个例子:

为了跟踪应用程序的运行过程,很多方法都需要记录日志信息。我们一般这样写:

/log4j的使用见文章“log4j介绍”

import org.apache.log4j.Logger;

public class Person {

         private Logger logger = Logger.getLogger(Person.class);     

         public void sleep(){

                   logger.info(“开始执行时间:“ + new Date());

                   System.out.println("睡觉中");

                   logger.info(“执行结束时间:” + new Date());

         }       

         public void eating(){

                   logger.info("开始执行时间:“ + new Date()");

                   System.out.println("正在吃饭中");

                   logger.info("“执行结束时间:” + new Date()");

         }

}

问:弊端在哪里?

  • l  混淆了业务方法本身的职责
  • l  维护工作量巨大

解决方案1-静态代理

  • l  目地是将业务代码与日志代码完全分离,实现松散耦合.
  • l  代理对象与被代理对象必须实现同一接口,在代理对象中实现与日志记录的相关服务,并在需要的时候呼叫被代理对象,而被代理对象只保留业务代码.

静态代理的实现

1)         定义接口:

public interface IPerson {

         public abstract void sleep();

         public abstract void eating();

}

2)         被代理类

public class Person implements IPerson {

         public void sleep(){

                   System.out.println("睡觉中");

         }

         public void eating(){

                   System.out.println("正在吃饭中");

         }

}
import org.apache.log4j.Logger;

public class PersonProxy implements IPerson {

         private IPerson person;

         private Logger logger = Logger.getLogger(PersonProxy.class);

         public PersonProxy(IPerson person) {

                   this.person = person;

         }

         public void eating() {

                   logger.info("开始执行时间:“ + new Date()");

    person.eating();

                   logger.info("“执行结束时间:” + new Date()");

         }

         public void sleep() {

                   logger.info("开始执行时间:“ + new Date()");

    person.sleep();

                   logger.info("“执行结束时间:” + new Date()");

         }

}

4)         测试类

package com.aptech.aop2;

public class PersonTest {

         public static void main(String[] args) {

                   IPerson proxy = new PersonProxy(new Person());

                   proxy.eating();

                   proxy.sleep();

         }

}

静态代理的弊端:

一个代理接口只能服务于一种类型的对象.对于稍大点的项目根本无法胜任.

3  解决方案2-动态代理

  • 在JDK1.3之后加入了可协助开发的动态代理功能.不必为特定对象与方法编写特定的代理对象,使用动态代理,可以使得一个处理者(Handler)服务于各个对象.
  • 一个处理者的类设计必须实现java.lang.reflect.InvocationHandler接口.
  • 通过InvocationHandler接口实现的动态代理只能代理接口的实现类.

动态代理实现

1)         处理者(Handler)

public class DynaProxyHandler implements InvocationHandler {

         private Logger logger = Logger.getLogger(DynaProxyHandler.class);  

         private Object target;//被代理对象

         public void setTarget(Object target) {

                   this.target = target;

         }

         public Object invoke(Object proxy, Method method, Object[] args)

                            throws Throwable {

                   logger.info("执行开始时间:" + new Date());

                   Object result = method.invoke(target, args);

                   logger.info("执行结束时间:" + new Date());

                   return result;//返回method执行结果

         }

}

2)         生产代理对象的工厂

import java.lang.reflect.Proxy;

public class DynaProxyFactory {

         //obj为被代理对象

         public static Object getProxy(Object obj){

                   DynaProxyHandler handler = new DynaProxyHandler();

                   handler.setTarget(obj);

                   return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), handler);

         }

}

3)         测试类

public class PersonTest {

         public static void main(String[] args) {                       

                   IPerson person = (IPerson) DynaProxyFactory.getProxy(new Person());

        //返回代理类,代理类是JVM在内存中动态创建的,该类实现传入的接口数组的全部接口(的全部方法).

                   person.eating();

                   person.sleep();

         }

}

4)     将2)和3)合并的写法

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy; public class PersonTest { public static void main(String[] args) {
//是我们要代理的真实对象
IPerson person1 = new Person();
//我们要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法的
InvocationHandler handler = new DynaProxyHandler(person1);
/*
* 通过Proxy的newProxyInstance方法来创建我们的代理对象,我们来看看其三个参数
* 第一个参数 handler.getClass().getClassLoader() ,我们这里使用handler这个类的ClassLoader对象来加载我们的代理对象
* 第二个参数person1.getClass().getInterfaces(),我们这里为代理对象提供的接口是真实对象所实行的接口,表示我要代理的是该真实对象,这样我就能调用这组接口中的方法了
* 第三个参数handler, 我们这里将这个代理对象关联到了上方的 InvocationHandler 这个对象上
*/
IPerson person = (IPerson) Proxy.newProxyInstance(handler.getClass().getClassLoader(), person1.getClass().getInterfaces(), handler); person.eating();
person.sleep();
}
}

我在学习 Java 的动态代理的时候,一直在使用 Proxy.newProxyInstance 方法生成代理的时候报错(如标题)。

我使用各种方法都无法解决这个问题,后来我直接用别人博客上的代码跑了一下,结果成功了。

于是我对比两个代码之间的差别,发现问题可能处在委托对象上面。

我写的代码委托对象是继承一个虚基类,而不是一个接口,于是我把我的代码改了一下,于是乎就跑起来了。。。

总结:要使用动态代理,用来生成代理的委托对象必须是一个接口。

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动态代理 java代理模式 即Proxy Pattern,23种java常用设计模式之一.代理模式的定义:对其他对象提供一种代理以控制对这个对象的访问. 原理: 代理模式的主要 ...

  6. Spring 静态代理和动态代理

    现在我们来模拟一下,某位学生去考试. 假设他(小明)正常的考试. 运行结果:        结果: 突然某一天,他睡过头了,来不急去考试,所有他打算叫另一个人(Cheater)去代替他考试. 运行结果 ...

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

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

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

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

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

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

随机推荐

  1. 如何在mpvue下收集小程序的formId

    什么是formId formId是小程序可以向用户发送模板消息的通行证,简单而言,你只有获取到formId,把它交给后台,后台同学才能向用户发送通知消息,而这个通行证的有效期只有七天.这是微信为了防止 ...

  2. 微信小程序ios上时间字符串转换为时间戳时会报错,在开发工具上和安卓手机上运行成功

    给定一个时间字符串  var time="2017-02-27 16:42:53" js有三种转换为时间戳的方法: 1.var timestamp = Date.parse(tim ...

  3. 用javascript写原生ajax(笔记)

    AJAX  的全名叫做  Asynchronous JavaScript and XML(异步的 JavaScript 和 XML).它最大的优点是在不重新加载整个页面的情况下,可以与服务器交换数据并 ...

  4. 03 使用Tensorflow做计算题

    我们使用Tensorflow,计算((a+b)*c)^2/a,然后求平方根.看代码: import tensorflow as tf # 输入储存容器 a = tf.placeholder(tf.fl ...

  5. 菜鸟系列docker——docker镜像中(4)

    docker镜像命令 该章节主要介绍docker image相关命令实践操作,建议都一起跑一边 1. docker images 列举本机docker镜像 [centos@jiliguo docker ...

  6. 监控报I/O问题,怎么办?

    Linux系统出现了性能问题,一般我们可以通过top.iostat.free.vmstat等命令来查看初步定位问题.其中iostat可以给我们提供丰富的IO状态数据. 一.查询命令基本使用 1.命令介 ...

  7. PHP错误报告级别

    error_reporting = E_ALL & ~E_NOTICE ; 错误报告级别是位字段的叠加,推荐使用 E_ALL | E_STRICT ; 1 E_ERROR 致命的运行时错误 ; ...

  8. webmagic 的 helloworld

    <dependency> <groupId>us.codecraft</groupId> <artifactId>webmagic-core</a ...

  9. 第8章 CentOS包管理详解

    8.1 Linux上构建C程序的过程 在说明包相关的内容之前,我觉得有必要说一下在Linux上构建一个C程序的过程.我个人并没有学习过C,内容总结自网上,所以可能显得很小白,而且也并非一定正确,只希望 ...

  10. Perl的IO操作(1):文件句柄

    文件句柄 文件句柄用来对应要操作的文件系统中的文件,这么说不太严谨,但比较容易理解.首先为要打开的文件绑定文件句柄(称为打开文件句柄),然后在后续的操作中都通过文件句柄来操作对应的文件,最后关闭文件句 ...