Java代理(静态/动态 JDK,cglib)
Java的代理模式是应用非常广泛的设计模式之一,也叫作委托模式,其目的就是为其他的对象提供一个代理以控制对某个对象的访问和使用,代理类负责为委托类预处理消息,过滤消息并转发消息,以及对消息执行后续处理。
代理就是充当一个中间人的角色。
按照代理的创建时期,代理类可以分为两种:
静态代理:指由程序员直接创建生成源代码,在对其编译生成.class文件,在程序运行之前就已经存在
动态代理:在程序运行时,使用java的反射机制动态创建而成。其中动态代理又分为JDK代理(需要接口)和cglib代理(不需要接口)
下面将以程序案例的方式演示Java静态代理和动态代理的区别
假设现在需要实现计算机的加减乘除操作,现有如下接口和实现类;
- package Reflection.proxy;
- /**
- * Created by : Infaraway
- * DATE : 2017/3/3
- * Time : 15:12
- * Funtion : 计算器的功能
- */
- public interface Calculator {
- int add(int a, int b);
- int sub(int a, int b);
- void mul(int a, int b);
- void div(int a, int b);
- }
- package Reflection.proxy;
- /**
- * Created by : Infaraway
- * DATE : 2017/3/3
- * Time : 15:14
- * Funtion :
- */
- public class CalculatorImpl implements Calculator{
- @Override
- public int add(int a, int b) {
- //System.out.println("The method add begin...);
- int result = a + b;
- //System.out.println("The method add end...);
- System.out.println(result);
- return result;
- }
- @Override
- public int sub(int a, int b) {
- int result = a - b;
- System.out.println(result);
- return result;
- }
- @Override
- public void mul(int a, int b) {
- int result = a * b;
- System.out.println(result);
- }
- @Override
- public void div(int a, int b) {
- int result = a / b;
- System.out.println(result);
- }
- }
如上部分add方法所示,现在希望在每个方法的实现之前打印方法开始和方法结束的日志信息,那么最容易的方法就是在源代码中的每个方法全部加上,但是这样非常的繁琐(需要编写大量的相同的代码),并且代码的维护性非常的差!
为了解决这个问题,我们需要使用代理的方法来解决。
静态代理:
首先我们使用静态代理的解决方法:
- package Reflection.proxy;
- /**
- * Created by : Infaraway
- * DATE : 2017/3/3
- * Time : 19:51
- * Funtion : java静态代理类实现
- */
- public class StaticProxy implements Calculator {
- private CalculatorImpl calculatorImpl;
- public StaticProxy(CalculatorImpl calculatorImpl){
- this.calculatorImpl = calculatorImpl;
- }
- @Override
- public int add(int a, int b) {
- System.out.println("the add method begin...");
- //调用被代理类的方法
- int result = calculatorImpl.add(a, b);
- System.out.println("the add method end...");
- return result;
- }
- @Override
- public int sub(int a, int b) {
- System.out.println("the sub method begin...");
- //调用被代理类的方法
- int result = calculatorImpl.sub(a, b);
- System.out.println("the sub method end...");
- return result;
- }
- @Override
- public void mul(int a, int b) {
- System.out.println("the mul method begin...");
- //调用被代理类的方法
- calculatorImpl.mul(a, b);
- System.out.println("the mul method end...");
- }
- @Override
- public void div(int a, int b) {
- System.out.println("the div method begin...");
- //调用被代理类的方法
- calculatorImpl.div(a, b);
- System.out.println("the div method end...");
- }
- }
显然,静态代理方法并不能改变原来繁琐的步骤,并且每个代理类只能为一个借口服务,这样的话,程序中必然会存在非常多的代理,并且这样的代理仍然会产生修改代码困难的问题;
因此,解决这个问题的一个非常好的办法出现了,那就是动态代理~
动态代理:
动态代理又分为两种:JDK代理 和 cglib代理
JDK代理:主要针对的是有接口的情况;
其中JDK动态代理包含了一个接口和一个类:
Proxy类:
Proxy类是专门完成代理的操作类,可以通过此类为一个或多个接口动态地生成实现类,此类提供了如下的操作方法:
public static Object newProxyInstance(
ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h) throws IllegalArgumentException
- /**
* ClassLoader :由动态代理产生的对象由哪个类加载器来加载 通常情况下和被代理对象使用同样的类加载器;
* Class<?>[] : 由动态代理产生的对象必须要实现的接口的Class数组;
* InvocationHandler : 当具体调用代理对象方法时,将产生的具体行为; 通常使用匿名内部类的方式实现。
*/
InvocationHandler接口:
public interface InvocationHandler {
public Object invoke(Object proxy,Method method,Object[] args) throws Throwable;
}
参数说明:
- /**
* @param proxy 被代理的对象
* @param method: 正在被调用的方法
* @param args :调用方法时传入的参数
* @return 被调用方法的返回值
* @throws Throwable
*/- 因此,当时用JDK代理方式实现上述的需求时,则如下代码所示:
- package Reflection.proxy;
- import org.junit.Test;
- import java.lang.reflect.InvocationHandler;
- import java.lang.reflect.Method;
- import java.lang.reflect.Proxy;
- /**
- * Created by : Infaraway
- * DATE : 2017/3/3
- * Time : 20:07
- * Funtion :
- */
- public class DynamicJDKProxy {
- @Test
- public void dynamicJDKProxy(){
- /**
- * ClassLoader :由动态代理产生的对象由那个类加载器来加载 通常情况下和被代理对象使用同样的类加载器
- * Class<?>[] : 由动态代理产生的对象必须要实现的接口的Class数组
- * InvocationHandler : 当具体调用代理对象方法时,将产生什么行为。 通常使用匿名内部类的方式实现
- */
- Calculator calculator = new CalculatorImpl();
- Calculator calculatorProxy = (Calculator) Proxy.newProxyInstance(
- calculator.getClass().getClassLoader(),
- new Class[]{Calculator.class},
- new InvocationHandler() {
- /**
- * @param proxy 代理
- * @param method: 正在被调用的方法
- * @param args :调用方法时传入的参数
- * @return 被调用方法的返回值
- * @throws Throwable
- */
- @Override
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- //System.out.println("invoke...");
- System.out.println("The method "+method.getName()+" begin...");
- Object obj = method.invoke(calculator, args);
- System.out.println("The method "+method.getName()+" end...");
- return obj;
- }
- });
//测试打印输出结果- calculatorProxy.mul(2, 3);
- int result = calculatorProxy.add(1, 5);
- System.out.println(result);
- }
- }
运行结果:
- The method mul begins...
- 6
- The method mul ends...
- The method add begins...
- 6
- The method add ends...
- 6
虽然上述方法很好的解决了问题,但是JDK动态代理必须依靠接口实现,如果某些类并没有实现接口,那么就不能使用JDK代理方式;
所以这里又给出一种新的代理方法:cglib动态代理来解决接口的问题。
cglib代理:针对没有接口的情况;主要是针对类来实现,主要思想是对指定的目标类来生成一个子类,然后覆盖子类的方法实现增强。
需要实现MethodInterceptor接口,实现intercept方法。该代理中在add方法前后加入了自定义的切面逻辑,目标类add方法执行语句为proxy.invokeSuper(object, args);
- package Reflection.proxy;
- import net.sf.cglib.proxy.Enhancer;
- import net.sf.cglib.proxy.MethodInterceptor;
- import net.sf.cglib.proxy.MethodProxy;
- import java.lang.reflect.Method;
- /**
- * Created by : Infaraway
- * DATE : 2017/3/3
- * Time : 20:46
- * Funtion : 使用cglib动态代理
- */
- public class DynamicCglibProxy implements MethodInterceptor {
- //被代理对象
- private Object target;
- /**
- * 创建代理对象
- * @param target 被代理对象
- * @return 创建的代理对象
- */
- public Object createProxy(Object target){
- this.target = target;
- Enhancer enhancer = new Enhancer();
- enhancer.setSuperclass(this.target.getClass());
- // 回调方法
- enhancer.setCallback(this);
- // 创建代理对象
- return enhancer.create();
- }
- /**
- * @param obj 被代理对象的
- * @param method 正在被调用的方法
- * @param objects 调用方法的参数
- * @param methodProxy 代理对象
- * @return 返回方法的结果
- * @throws Throwable
- */
- @Override
- public Object intercept(Object obj, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
- System.out.println("The method "+method.getName()+" begins...");
- Object object = methodProxy.invokeSuper(obj, objects);
- System.out.println("The method "+method.getName()+" ends...");
- return object;
- }
- }
- package Reflection.proxy;
- import org.junit.Test;
- /**
- * Created by : Infaraway
- * DATE : 2017/3/3
- * Time : 21:15
- * Funtion :
- */
- public class TestDynamicCglibProxy {
- @Test
- public void testProxy(){
- DynamicCglibProxy CglibProxy = new DynamicCglibProxy();
- CalculatorImpl testCalculator = (CalculatorImpl) CglibProxy.createProxy(new CalculatorImpl());
- testCalculator.add(3,5);
- }
- }
想要更进一步了解Java代理模式,则需要认真学习Java的反射机制
本文的所有代码可获取:https://git.oschina.net/infaraway/basisJava/tree/master/src/proxy
Java代理(静态/动态 JDK,cglib)的更多相关文章
- 浅谈Java代理一:JDK动态代理-Proxy.newProxyInstance
浅谈Java代理一:JDK动态代理-Proxy.newProxyInstance java.lang.reflect.Proxy:该类用于动态生成代理类,只需传入目标接口.目标接口的类加载器以及Inv ...
- Java代理和动态代理机制分析和应用
本博文中项目代码已开源下载地址:GitHub Java代理和动态代理机制分析和应用 概述 代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问.代理类负责为委托类预处理消息 ...
- java代理的深入浅出(二)-CGLIB
java代理的深入浅出(二)-CGLIB 1.基本原理 CGLIB的原理就是生成一个要代理类的子类,子类重写要代理的类的所有不是final的方法.在子类中拦截所有父类方法的调用,拦截下来交给设置的Me ...
- java代理与动态代理的学习
静态代理比较简单,就是代理对象与被代理对象继承相同的接口,代理类负责调用被代理类(委托类)的对象的相关方法去提供具体的服务,一个代理类只能为一个接口服务,要是有很多服务的话需要开很多代理类.而动态代理 ...
- Java代理和动态代理
code from <Thinking in java> 代理模式 interface Interface { void doSomething(); void somethingElse ...
- 【Java入门提高篇】Day10 Java代理——静态代理
今天要介绍的是一个Java中一个很重要的概念--代理. 什么是代理?联系生活想想看,代理似乎并不陌生,最形象的代表便是经纪人,明星一般都有经纪人,经纪人作为中间人,负责代理明星的相关事宜,比如说,有人 ...
- Spring专题1: 静态代理和动态代理
合集目录 Spring专题1: 静态代理和动态代理 为什么需要代理模式? 代理对象处于访问者和被访问者之间,可以隔离这两者之间的直接交互,访问者与代理对象打交道就好像在跟被访者者打交道一样,因为代理者 ...
- Java 的静态代理 动态代理(JDK和cglib)
转载:http://www.cnblogs.com/jqyp/archive/2010/08/20/1805041.html JAVA的动态代理 代理模式 代理模式是常用的java设计模式,他的特征是 ...
- Java代理(jdk静态代理、动态代理和cglib动态代理)
一.代理是Java常用的设计模式,代理类通过调用被代理类的相关方法,并对相关方法进行增强.加入一些非业务性代码,比如事务.日志.报警发邮件等操作. 二.jdk静态代理 1.业务接口 /** * 业务接 ...
随机推荐
- Python3基础 闭包 简单示例
镇场诗: 诚听如来语,顿舍世间名与利.愿做地藏徒,广演是经阎浮提. 愿尽吾所学,成就一良心博客.愿诸后来人,重现智慧清净体.-------------------------------------- ...
- XML解析之SAX解析技术案例
Java代码: package com.xushouwei.xml; import java.io.File; import java.io.IOException; import java.text ...
- C# Unity游戏开发——Excel中的数据是如何到游戏中的 (一)
引言 现在做游戏开发的没有几个不用Excel的,用的最多的就是策划.尤其是数值策划,Excel为用户提供强大的工具,各种快捷键,各种插件,各种函数.但是作为程序来说其实关注的不是Excel而是它最终形 ...
- c#中struct和class的区别
1.struct 是值类型,class是对象类型 2.struct 不能被继承,class可以被继承 3.struct 默认的访问权限是public,而class默认的访问权限是private. 4. ...
- Unity基础学习-Unity概述
Unity 概述 Unity是一个强大的引擎,里面包括大量的工具用来满足各种各样的需求.Unity的编辑器是直观的可定制的,让您在您的工作流中有较大的自由度. 本小节是开始学习Unity的关键部分.里 ...
- IOS苹果手机上 iframe 滚动失效条问题,局部滚动开启弹性滚动!
html:bo<div class="scroll-wrapper"> <iframe src=""></iframe> & ...
- Bootstrap入门(二十二)组件16:列表组
Bootstrap入门(二十二)组件16:列表组 列表组是灵活又强大的组件,不仅能用于显示一组简单的元素,还能用于复杂的定制的内容. 1.默认样式列表组 2.加入徽章 3.链接 4.禁用的列表组 5. ...
- 关于Visual Studio未能加载各种Package包的解决
参考微软社区的一个答复解决了VS2013的问题: 进入VS对应的用户缓存文件夹,删掉那个Microsoft.VisualStudio.Default.cache缓存文件,就可以了. 这个错误估计是我们 ...
- 如何在Crystal框架项目中内置启动Zookeeper服务?
当Crystal框架项目需要使用到Zookeeper服务时(如使用Dubbo RPC时,需要注册服务到Zookeeper),而独立部署和启动Zookeeper服务不仅繁琐,也容易出现错误. 在小型项目 ...
- Kafka 0.10 SocketServer源代码分析
1概要设计 Kafka SocketServer是基于Java NIO来开发的,采用了Reactor的模式,其中包含了1个Acceptor负责接受客户端请求,N个Processor负责读写数据,M个H ...