本节要点:

  • 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. 后端不会写页面怎么办?推荐几个好用的前端UI模板、组件对比

    前言 下面推荐并对比几个好用的前端UI模板 推荐给以下的人使用: 1.不想重复造轮子的后端 2.不想学bootstrap的后端 3.后端开发想自己写简单页面的 4.偷懒的前端 本文注重手机端,对web ...

  2. vue-07-自定义指令

    1, 全局指令 在main.js中引入 // 注册一个全局自定义指令 `v-focus` Vue.directive('focus', { // 当被绑定的元素插入到 DOM 中时…… inserte ...

  3. linux服务器部署tomcat和Nginx

    项目需要,申请了三台测试机器,好在测试机里面光秃秃的什么都没有,我就可以好好的学习一把玩一把了!接下来以图文的形式讲一下我所碰到的坑以及小小的收获吧! 一.准备工作 首先你得有一台可以玩的linux服 ...

  4. 【API知识】RestTemplate的使用

    前言 在某种情况下,后台服务可能需要访问另一台服务器的REST接口.以前估计不少人用的都是HttpRequest类来着,结合Paser解析JSON格式的Body.现在Spring Boot的Web S ...

  5. OJ:重载 << 运算符

    Description 补足程序,使得下面程序输出的结果是: ****100 #include <iostream> #include <string> using names ...

  6. 实战!基于lamp安装Discuz论坛-技术流ken

    简介 我前面的博客已经详细介绍了lamp采用yum安装以及编译安装的方式,这篇博客将基于yum安装的lamp架构来实战安装Discuz论坛,你可以任选其一来完成. 系统环境 centos7.5 服务器 ...

  7. final关键字。

    final关键之代表最终,不可变的. 用法: 1.修饰类 2.修饰方法 3.修饰局部变量. 4.修饰成员变量. 修饰类: 不能有任何子类.(太监类) public final class MyClas ...

  8. 前端axios下载excel,并解决axios返回header无法获取所有数据的问题

    需求:通过后端接口下载excel文件,后端没有文件地址,返回二进制流文件 实现:axios(ajax类似) 主要代码: axios:设置返回数据格式为blob或者arraybuffer 如: var ...

  9. 如何快速将一个list<a>集合中的部分字段值组合成新的的list<b>部分*

    有的时候,我们只需要从老数据中拿一部分数据作为新的绑定数据,比如说绑定下拉框的时候需要构造我们需要的数据格式可以采用以下的方法 public class SelectDataViewModel { p ...

  10. .NET CORE 设置cookie以及获取cookie

    使用我这个方式的前提是在mvc中,确认你安装了:Microsoft.AspNetCore.Mvc. 然后在继承了Controller的类型中使用我所说的方法. 直接使用即可,我是封装了方法供我自己使用 ...