都知道Java中的Spring,有一重要思想:AOP,实现原理也就是Java的动态代理机制。初见代理这个名词时,觉得生活中常有代理的这一说法。
那么,在Java中,代理又是什么呢?它又是如何实现的?实现后是干什么的?
其实啊,Java中的代理也就是生活中代理的意思,我认为,在Java中,代理就是帮助类实现一些修改的动作。
也就是说,用代理类来修改已经存在的类。那么,问题又来了,为何这样做呢?在存在的类上修改不就完事了吗?
首先,这种想法时错的,Java中,已经编译好的类不要随随便便的修改,容易造成整个工程的一系列问题。所以,使用这种方法就不需要修改已存在的类了,可以对原类进行相应的动作。这样才符合编程的需求。
一、下面来具体说说代理:
大的方向来说代理可分为:静态代理和动态代理。而动态代理又有两种产生代理的方式:JDK和CGLiB。
二、静态代理的实现:
首先创建一个接口,然后创建具体实现类来实现这个接口,在创建一个代理类同样实现这个接口,不同之处在于,
具体实现类的方法中需要将接口中定义的方法的业务逻辑功能实现,而代理类中的方法只要调用具体类中的对应方法即可,
这样我们在需要使用接口中的某个方法的功能时直接调用代理类的方法即可,将具体的实现类隐藏在底层。
需要注意的是,代理对象与目标对象要实现相同的接口,然后通过调用相同的方法来调用目标对象的方法
1.首先需要创建的接口:
package com.xupt.proxy.classes; public interface IStudentProxyId {
void setId(String id);
String getId();
}
package com.xupt.proxy.classes; public interface IStudentProxyName {
void setName(String name);
String getName();
}
2.实现这个接口的类:
package com.xupt.proxy.classes; public class StudentMessage implements IStudentProxyId,IStudentProxyName { private String id;
private String name; public StudentMessage() {} @Override
public void setName(String name) {
this.name = name; System.out.println("StudentMessage.setName()");
} @Override
public String getName() {
System.out.println("StudentMessage.getName()"); return name;
} @Override
public void setId(String id) {
this.id = id; System.out.println("StudentMessage.setId()"); } @Override
public String getId() {
System.out.println("StudentMessage.getId()"); return id;
} }
无用框。。。。。。
3.实现类的代理类:
package com.xupt.proxy.proxy; import com.xupt.proxy.classes.IStudentProxyId;
import com.xupt.proxy.classes.IStudentProxyName;
import com.xupt.proxy.classes.StudentMessage; public class StudentProxy implements IStudentProxyId,IStudentProxyName{ StudentMessage message; public StudentProxy() {
} public StudentProxy(StudentMessage message) {
this.message = message;
} @Override
public void setName(String name) { System.out.println("置前拦截StudentProxy.setName()");
message.setName(name);
System.out.println("置后拦截StudentProxy.setName()");
} @Override
public String getName() { System.out.println("置前拦截StudentProxy.getName()");
String name = message.getName();
System.out.println("置后拦截StudentProxy.getName()"); return name;
} @Override
public void setId(String id) { System.out.println("置前拦截StudentProxy.setId()");
message.setName(id);
System.out.println("置后拦截StudentProxy.setId()");
} @Override
public String getId() {
System.out.println("置前拦截StudentProxy.getId()");
String id = message.getId();
System.out.println("置后拦截StudentProxy.getId()"); return id;
} }
4.定义测试类:
package com.xupt.proxy.demo; import com.xupt.proxy.classes.StudentMessage;
import com.xupt.proxy.proxy.StudentProxy; public class StudentProxyTest { public static void main(String[] args) {
StudentProxy proxy = new StudentProxy(new StudentMessage()); //使用的是代理类的对象
proxy.setId("222666");
proxy.setName("张三");
System.out.println("id: " + proxy.getId() + " " + "name: " +proxy.getName());
} }
5.执行结果:
静态代理总结:
1. 可以做到在不修改目标对象的功能前提下,对目标功能扩展.
2. 缺点:
代理类和委托类实现相同的接口,同时要实现相同的方法。这样就出现了大量的代码重复。如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。 实现一次就觉得挺简单的。但是,静态代理就是将接口、实现类、代理类一次全部手动执行。但细想一下,如果我们需要很多代理,每次都重新手动完成,是不是不现实呢?每一个都这么手动的去创建实属浪费时间,而且会有大量的重复代码。所以,动态代理就出现了。事情总有解决的办法。
三:动态代理的实现:
动态代理
动态代理有以下特点:
(1) 在运行期,通过反射机制创建一个实现了一组给定接口的新类
(2)在运行时生成的class,必须提供一组interface给它,然后该class就宣称它实现了
这些 interface。该class的实 例可以当作这些interface中的任何一个来用。
(3)动态代理也叫做:JDK代理,接口代理
(4)接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理
(InvocationHandler.invoke)。这样,在接口方法数量比较多的时候,我们可以
进行灵活处理,而不需要像静态代理那样每一个方法进行中转。而且动态代理的应用
使我们的类职责更加单一,复用性更强。
1、JDK动态代理的实现:
1.1.定义接口类:这个接口类和静态代理的接口没有区别。
package com.xupt.proxy.classes; public interface IStudentProxyId {
void setId(String id);
String getId();
}
package com.xupt.proxy.classes; public interface IStudentProxyName {
void setName(String name);
String getName();
}
1.2.接口的实现类;也是和静态代理一样。
package com.xupt.proxy.classes; public class StudentMessage implements IStudentProxyId,IStudentProxyName { private String id;
private String name; public StudentMessage() {} @Override
public void setName(String name) {
this.name = name; System.out.println("StudentMessage.setName()");
} @Override
public String getName() {
System.out.println("StudentMessage.getName()"); return name;
} @Override
public void setId(String id) {
this.id = id; System.out.println("StudentMessage.setId()"); } @Override
public String getId() {
System.out.println("StudentMessage.getId()"); return id;
} }
1.3.定义代理类:
package com.xupt.proxy.proxy; import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy; public class StudentsProxy { @SuppressWarnings("unchecked")
public <T> T getProxy(Class<?> kalss) throws Exception { return (T) getProxy(kalss,kalss.newInstance());
} @SuppressWarnings("unchecked")
public <T> T getProxy(Object object) { return (T) getProxy(object.getClass(),object);
} @SuppressWarnings("unchecked")
public <T> T getProxy(Class<?> klass,Object obj) {
return (T) Proxy.newProxyInstance( //返回值类型,用户可以给任意类型
klass.getClassLoader(), //原类的加载
klass.getInterfaces(), //原类实现的接口
new InvocationHandler() { //其实它是接口,需要实现它的invoke方法。(进入源码就可以查证)。 @Override
//利用反射中的参数分别为:代理类的对象、原类的方法(该方法时以后真的需要实现拦截的方法)、
//原类方法中的参数数组。
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null; //TODO 这里以后是真正的置前拦截操作,加上自己想要实现的东西。
System.out.println("置前拦截...."); result = method.invoke(obj, args);
//由上面一句可以看到,是一个方法的反射,反射的是原类的方法,得到的结果却给代理类返回去,
//这就把原类的代理类来呢西到一起了。 //TODO 这里以后是真正的置后拦截操作,加上自己想要实现的东西。
System.out.println("置后拦截...."); return result;
}
});
}
}
1.4定义测试类:
package com.xupt.proxy.demo; import com.xupt.proxy.classes.IStudentProxyId;
import com.xupt.proxy.classes.IStudentProxyName;
import com.xupt.proxy.classes.StudentMessage;
import com.xupt.proxy.proxy.StudentsProxy; public class ProxyDemo { public static void main(String[] args) {
IStudentProxyId ispi = null;
try {
ispi = new StudentsProxy().getProxy(StudentMessage.class);
ispi.setId("123456");
System.out.println("id:" + ispi.getId()); } catch (Exception e1) {
e1.printStackTrace();
} IStudentProxyName ispn = new StudentsProxy().getProxy(new StudentMessage());
ispn.setName("张三");
System.out.println("name:" + ispn.getName()); } }
执行结果:
由上面的代码可以看出,使用JDK动态代理,目标类必须实现的某个接口,如果某个类没有实现接口则不能生成代理对象。那么,问题又有了,
若我这个类就是没有完成某个接口又该如何??还是,事情总有解决的办法,还有CGLib动态代理机制呢。
下面我们来看看CGLib动态代理:
2.动态代理CGLib的实现:
上面的静态代理和动态代理模式都是要求目标对象实现一个接口或者多个接口,但是有
时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候就可以使用构建目标
对象子类的方式实现代理,这种方法就叫做:Cglib代理
Cglib代理,也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能
的扩展.
2.1:定义原类
package com.xupt.proxy.classes; public class CglibClass {
private String id;
private String name; public CglibClass() {
} public void setId(String id) {
this.id = id; System.out.println("CglibClass.setId(): "+ id); } public String getId() {
System.out.println("CglibClass.getId()"); return id;
} public String personName(String name) {
this.name = name; System.out.println("这是原类的personName方法...: " + name); return name;
}
}
2.2:定义原类的代理类:
package com.xupt.proxy.proxy; import java.lang.reflect.Method; import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy; public class CGLibProxy { public CGLibProxy() {
} @SuppressWarnings("unchecked")
public <T> T getProxy(Class<?> kalss) throws Exception { return (T) getProxy(kalss,kalss.newInstance());
} @SuppressWarnings("unchecked")
public <T> T getProxy(Object object) { return (T) getProxy(object.getClass(),object);
} @SuppressWarnings("unchecked")
public <T> T getProxy(Class<?> kalss,Object obj) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(kalss); //这句可以看出被代理类是代理类的父类。
enhancer.setCallback(new MethodInterceptor() { //设置回调。 @Override
public Object intercept(Object object, Method method, Object[] args, MethodProxy methodPorxy)
throws Throwable {
Object result = null; System.out.println("置前拦截原类的personName方法...."); result = method.invoke(obj, args);
System.out.println("置后拦截原类的personName方法...."); return result;
}
}); return (T) enhancer.create(); //enhancer.create()制作代理的对象。
}
}
2.3定义测试类:
package com.xupt.proxy.demo; import com.xupt.proxy.classes.CglibClass;
import com.xupt.proxy.proxy.CGLibProxy; public class CGLibTset { public static void main(String[] args) { CglibClass cglibClass;
try {
cglibClass = new CGLibProxy().getProxy(CglibClass.class);
cglibClass.setId("666666");
cglibClass.personName("张三");
} catch (Exception e) {
e.printStackTrace();
} } }
2.4执行结果:
以上就是我所总结的代理机制之静态代理、动态代理的JDK代理、动态代理的CGLib代理的实现方式。最后强调一点:
Cglib包的底层是通过使用字节码处理框架ASM来转换字节码并生成新的子类.
代理的类不能为final,否则报错;目标对象的方法如果为final/static,那么就不会
被拦截,即不会执行目标对象额外的业务方法.(尝试过)
还有CGLib代理的实现必须导CGLib的cglib-nodep-2.1_3.jar包才能实现。
若此博文有任何问题请指正,也希望对您有帮助!
Java代理机制之初见(理解及实现)的更多相关文章
-
JAVA代理机制
JAVA代理相关主要知识如下: (1)利用代理可以在运行时创建一个实现了一组给定接口的新类. 这种功能只有在编译时无法确定需要实现哪个接口时才有必要使用. (2)假设有一个表示接口的C ...
-
java反射机制的进一步理解
承上一篇. JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法:这种动态获取的信息以及动态调用对象的方法的功能称为java语 ...
-
从头捋了一遍 Java 代理机制,收获颇丰
尽人事,听天命.博主东南大学硕士在读,热爱健身和篮球,乐于分享技术相关的所见所得,关注公众号 @ 飞天小牛肉,第一时间获取文章更新,成长的路上我们一起进步 本文已收录于 「CS-Wiki」Gitee ...
-
Java事件处理机制(深入理解)
本文是关于Java事件处理机制的梳理,以及有重点的介绍一些注意点,至于基础的概念啥的不多赘述. 一.Java事件处理机制初步介绍(看图理解) 根据下图,结合生活实际,可以得知监护人可以有多个,坏人对小 ...
-
Java反射机制的浅显理解(这篇文章还没写好,留个草稿给自己看的)
目前只是有一个大概的理解,先把自己感觉容易立即的概念放这里,等以后结合实际工作理解深刻了再来补充. 一.什么是Java反射机制?(多种定义) 1. JAVA反射机制是在运行状态中,对于任意一个类,都能 ...
-
java反射机制的粗略理解
java反射机制: 涉及的对象:Class, Object, 函数:Class类:[forName(String className):static:getClass():public],Object ...
-
关于java类加载机制的一些理解
关于java的类加载机制加载顺序,这个东西可以说是基础的东西,不过很遗憾这方面很多人也都不是很在意,比如我自己,最近上班闲下来了,就开始看一些博客文章了,今天恰好被一篇博文给吸引了,并且他的示例题一开 ...
-
Java 代理机制
Table of Contents 1 引言 2 常见的代理 3 代理模式UML图 4 代理模式实例 5 java动态代理 5.1 java动态代理UML图 6 代理模式与装饰者模式的区别 6.1 装 ...
-
黑马程序猿_Java 代理机制学习总结
-------<a href="http://www.itheima.com/"">android培训</a>.<a href=" ...
随机推荐
-
SQL触发器中若取到null值可能引发的问题
declare @code varchar(20), @cs varchar(20),@zc varchar(20)set @cs='('+@cs+'*'+@zc+')'print '字符'+@csi ...
-
js中 this篇
以下文案皆来自<你不知道的JavaScript 上卷>——读书笔记摘要 this 到底是什么? 排除了一些错误理解之后,我们来看看 this 到底是一种什么样的机制. 之前我们说过 thi ...
-
Redis Sentinel 机制与用法(二)
概述 Redis-Sentinel是Redis官方推荐的高可用性(HA)解决方案,当用Redis做Master-slave的高可用方案时,假如 master宕机了,Redis本身(包括它的很多客户端) ...
-
Linux shell for while 循环
1.数字段形式for i in {1..10}do echo $idone 2.详细列出(字符且项数不多)for File in 1 2 3 4 5 do echo $File done ...
-
C语言如何输出%
两个%即可,C语言中%有两个作用: 第一种是作为运算符,取余,例如:9%4=1(9/4=2--1). 第二种是转义符,比如在scanf()和printf()中的输入参数常出现带有%的表示参数类型的变量 ...
-
java 易错选择题 编辑中
1 System.out.println(int(a+b)); 编译错误 应该是(int)(a+b) 2 String s="john"+3; 是正确的,结果就是 john3 3 ...
-
Nginx:Linux下安装Nginx与配置
准备目录 [root@sijizhen ~]# mkdir /usr/local/nginx [root@sijizhen ~]# cd /usr/local/nginx/ 下载 1.Nginx,在h ...
-
【UER #8】雪灾与外卖
题解: 这个东西的模型是个费用流 但是直接跑费用流能拿到5分的高分 $(nm)*(nm)*log{nm}$ 考虑优化一下建图 我们可以不用对每个店和人都连边 而是对人和店都连一条链 然后对每个人连店刚 ...
-
九、JSP入门(1)
JSP入门 1 JSP概述 1.1 什么是JSP JSP(Java Server Pages)是JavaWeb服务器端的动态资源.它与html页面的作用是相同的,显示数据和获取数据. 1.2 JSP的 ...
-
src引入js时添加时间戳参数方法
代码如下: <script>document.write('<script src="js/judgeLogin.js?t=' + new Date().getTime() ...