spring静态代理和动态代理
本节要点:
- Java静态代理
- Jdk动态代理
1 面向对象设计思想遇到的问题
在传统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 维护工作量巨大
2 解决方案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静态代理和动态代理的更多相关文章
- java中代理,静态代理,动态代理以及spring aop代理方式,实现原理统一汇总
若代理类在程序运行前就已经存在,那么这种代理方式被成为 静态代理 ,这种情况下的代理类通常都是我们在Java代码中定义的. 通常情况下, 静态代理中的代理类和委托类会实现同一接口或是派生自相同的父类. ...
- java:struts框架2(方法的动态和静态调用,获取Servlet API三种方式(推荐IOC(控制反转)),拦截器,静态代理和动态代理(Spring AOP))
1.方法的静态和动态调用: struts.xml: <?xml version="1.0" encoding="UTF-8"?> <!DOCT ...
- Spring专题1: 静态代理和动态代理
合集目录 Spring专题1: 静态代理和动态代理 为什么需要代理模式? 代理对象处于访问者和被访问者之间,可以隔离这两者之间的直接交互,访问者与代理对象打交道就好像在跟被访者者打交道一样,因为代理者 ...
- spring——AOP(静态代理、动态代理、AOP)
一.代理模式 代理模式的分类: 静态代理 动态代理 从租房子开始讲起:中介与房东有同一的目标在于租房 1.静态代理 静态代理角色分析: 抽象角色:一般使用接口或者抽象类来实现(这里为租房接口) pub ...
- spring的静态代理和动态代理
Java静态代理 Jdk动态代理 java代理模式 即Proxy Pattern,23种java常用设计模式之一.代理模式的定义:对其他对象提供一种代理以控制对这个对象的访问. 原理: 代理模式的主要 ...
- Spring 静态代理和动态代理
现在我们来模拟一下,某位学生去考试. 假设他(小明)正常的考试. 运行结果: 结果: 突然某一天,他睡过头了,来不急去考试,所有他打算叫另一个人(Cheater)去代替他考试. 运行结果 ...
- Spring AOP里的静态代理和动态代理,你真的了解嘛?
什么是代理? 为某一个对象创建一个代理对象,程序不直接用原本的对象,而是由创建的代理对象来控制原对象,通过代理类这中间一层,能有效控制对委托类对象的直接访问,也可以很好地隐藏和保护委托类对象,同时也为 ...
- Atitit 代理CGLIB 动态代理 AspectJ静态代理区别
Atitit 代理CGLIB 动态代理 AspectJ静态代理区别 1.1. AOP 代理主要分为静态代理和动态代理两大类,静态代理以 AspectJ 为代表:而动态代理则以 spring AOP 为 ...
- 【Java】代处理?代理模式 - 静态代理,动态代理
>不用代理 有时候,我希望在一些方法前后都打印一些日志,于是有了如下代码. 这是一个处理float类型加法的方法,我想在调用它前打印一下参数,调用后打印下计算结果.(至于为什么不直接用+号运算, ...
随机推荐
- Spring Boot SOAP Webservice例子
前言 本文将学习如何利用Spring boot快速创建SOAP webservice服务: 虽然目前REST和微服务越来越流行,但是SOAP在某些情况下,仍然有它的用武之地: 在本篇 spring b ...
- 如何用vue-router为每个路由配置各自的title
传统方法 以前在单页面路由中,就只能在html文件中定一个固定的网站的title.如果想要动态的去修改,需要使用如下的方法. document.title = '这是一个标题'; 这样会让我们做很多无 ...
- C#生成MD5的方法
///C#生成MD5的方法 public static string GetMD5(string sDataIn) { MD5CryptoServiceProvider md5 = new MD5Cr ...
- zabbix-proxy使用配置
简介 zabbix proxy可以代替zabbix server检索客户端的数据,然后把数据汇报给zabbix server,并且在一定程度上分担了zabbix server的压力.zabbix pr ...
- Java——代码块
前言 在程序编写之中可以直接使用{...}定义的一段语句就是代码块.根据代码块的位置以及关键字的不同可以分为4种:普通代码块.构造块.静态块以及同步代码块(多线程相关).下面将先介绍前3种以及Java ...
- 翻译:group_concat()函数(已提交到MariaDB官方手册)
本文为mariadb官方手册:group_concat()函数的译文. 原文:https://mariadb.com/kb/en/group_concat/ 我提交到MariaDB官方手册的译文:ht ...
- 翻译:如何向MariaDB中快速插入数据(已提交到MariaDB官方手册)
本文为mariadb官方手册:How to Quickly Insert Data Into MariaDB的译文. 原文:https://mariadb.com/kb/en/how-to-quick ...
- Linux命令-基本操作和软件安装
配置JDK环境:JAVA_HOME:D:\software\JDK1.7Path:%JAVA_HOME%\bin;%JAVA_HOME%\lib; ls(查看当前目录下的文件及文件夹)sh initN ...
- asp.net 中Repeater和Gridview的区别
Griview: 优点:1.GridView是从WebControl派生出来的,拥有WebControl样式属性,自身会被解析为table,其中的每一行会被 ...
- [转]bitcoin: 通过 rpc 请求节点数据
本文转自:https://laravel-china.org/index.php/articles/8919/bitcoin-requests-node-data-through-rpc 文章来自本人 ...