Spring AOP里的静态代理和动态代理用法详解

所属分类: 软件编程 / java 阅读数: 62
收藏 0 赞 0 分享

什么是代理?

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

什么是静态代理?

  由程序创建或特定工具自动生成源代码,在程序运行前,代理类的.class文件就已经存在

  通过将目标类与代理类实现同一个接口,让代理类持有真实类对象,然后在代理类方法中调用真实类方法,在调用真实类方法的前后添加我们所需要的功能扩展代码来达到增强的目的。

优点

  •   代理使客户端不需要知道实现类是什么,怎么做,而客户端只需知道代理即可
  •   方便增加功能,扩展业务逻辑

缺点

  •   代理类中常出现大量冗余的代码,非常不利于扩展和维护
  •   如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度

案例演示

PayService.java(接口)

package net.cybclass.sp.proxy;

public interface PayService {
 /**
  * 支付回调
  * @param outTradeNo 订单号
  * @return
  */
 String callback(String outTradeNo);

 /**
  * 下单
  * @param userId 用户id
  * @param productId 产品id
  * @return
  */
 int save(int userId,int productId);
}

PayServiceImpl.java(接口实现类)

package net.cybclass.sp.proxy;

public class PayServiceImpl implements PayService{
 public String callback(String outTradeNo) {
  System.out.println("目标类 PayServiceImpl 回调 方法 callback");
  return outTradeNo;
 }

 public int save(int userId, int productId) {
  System.out.println("目标类 PayServiceImpl 回调 方法 save");
  return productId;
 }
}

StaticProxyPayServiceImpl.java(接口实现类,静态代理)

package net.cybclass.sp.proxy;

public class StaticProxyPayServiceImpl implements PayService{
 private PayService payService;
 public StaticProxyPayServiceImpl(PayService payService)
 {
  this.payService=payService;
 }
 public String callback(String outTradeNo) {
  System.out.println("StaticProxyPayServiceImpl callback begin");
  String result=payService.callback(outTradeNo);
  System.out.println("StaticProxyPayServiceImpl callback end");
  return result;
 }

 public int save(int userId, int productId) {
  System.out.println("StaticProxyPayServiceImpl save begin");
  int id = payService.save(userId, productId);
  System.out.println("StaticProxyPayServiceImpl save end");
  return id;
 }
}

演示

什么是动态代理?

  在程序运行时,运用反射机制动态创建而成,无需手动编写代码

JDK动态代理

CGLIB动态代理(原理:是对指定的业务类生成一个子类,并覆盖其中的业务方法来实现代理)

jdk动态代理演示

定义一个类,去实现InvocationHandler这个接口,并车从写invoke方法

//Object proxy:被代理的对象
//Method method:要调用的方法
//Object[] args:方法调用时所需要参数
public Object invoke(Object proxy, Method method, Object[] args){}

PayService.java(接口)

package net.cybclass.sp.proxy;

public interface PayService {
 /**
  * 支付回调
  * @param outTradeNo 订单号
  * @return
  */
 String callback(String outTradeNo);

 /**
  * 下单
  * @param userId 用户id
  * @param productId 产品id
  * @return
  */
 int save(int userId,int productId);
}

PayServiceImpl.java(接口实现类)

package net.cybclass.sp.proxy;

public class PayServiceImpl implements PayService{
 public String callback(String outTradeNo) {
  System.out.println("目标类 PayServiceImpl 回调 方法 callback");
  return outTradeNo;
 }

 public int save(int userId, int productId) {
  System.out.println("目标类 PayServiceImpl 回调 方法 save");
  return productId;
 }
}

JDKProxy.java(jdk动态代理类)

package net.cybclass.sp.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class JDKProxy implements InvocationHandler {
 //目标类
 private Object targetObject;

 /**
  * 获取代理对象
  * @param targetObject 目标类
  * @return
  */
 public Object newProxyInstance(Object targetObject) {
  this.targetObject = targetObject;
  //绑定关系,也就是和具体的那个实现类关联
  return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
    targetObject.getClass().getInterfaces(), this);
 }

 /**
  * JDK动态代理
  *
  * @param proxy 静态代理对象
  * @param method 要调用的方法
  * @param args 方法调用时所需要参数
  * @return
  * @throws Throwable
  */
 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  Object result = null;
  try {
   System.out.println("通过JDK动态代理调用"+method.getName()+",打印日志 begin");
   result = method.invoke(targetObject, args);
   System.out.println("通过JDK动态代理调用"+method.getName()+",打印日志 end");
  } catch (Exception ex) {
   ex.printStackTrace();
  }
  return result;
 }
}

CGLIB动态代理演示

PayService.java(接口)

package net.cybclass.sp.proxy;

public interface PayService {
 /**
  * 支付回调
  * @param outTradeNo 订单号
  * @return
  */
 String callback(String outTradeNo);

 /**
  * 下单
  * @param userId 用户id
  * @param productId 产品id
  * @return
  */
 int save(int userId,int productId);
}

PayServiceImpl.java(接口实现类)

package net.cybclass.sp.proxy;

public class PayServiceImpl implements PayService{
 public String callback(String outTradeNo) {
  System.out.println("目标类 PayServiceImpl 回调 方法 callback");
  return outTradeNo;
 }

 public int save(int userId, int productId) {
  System.out.println("目标类 PayServiceImpl 回调 方法 save");
  return productId;
 }
}

CGLIBProxy.java(CGLIB动态代理类)

package net.cybclass.sp.proxy;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class CGLIBProxy implements MethodInterceptor {
 //目标类
 private Object targetObject;
 //绑定关系
 public Object newProxyInstance(Object targetObject){
  this.targetObject=targetObject;
  Enhancer enhancer=new Enhancer();
  //设置代理类的父类(目标类)
  enhancer.setSuperclass(this.targetObject.getClass());
  //设置回调函数
  enhancer.setCallback(this);
  //创建子类(代理对象)
  return enhancer.create();
 }
 public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
  Object result=null;
  try
  {
   System.out.println("通过CGLIB动态代理调用"+method.getName()+",打印日志 begin");
   result=methodProxy.invokeSuper(o,args);
   System.out.println("通过CGLIB动态代理调用"+method.getName()+",打印日志 end");
  }
  catch (Exception ex){
   ex.printStackTrace();
  }
  return result;
 }
}

  动态代理与静态代理相比较,最大的好处是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理,解耦和易维护。

两种动态代理的区别

  • JDK动态代理:要求目标对象实现一个接口,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候就可以用CGLIB动态代理
  • JDK动态代理是自带的,CGLIB需要引入第三方包
  • CGLIB动态代理,它是内存中构建一个子类对象从而实现对目标对象功能的扩展
  • CGLIB动态代理基于继承来实现代理,所以无法对final类,private方法和static方法实现代理

Spring AOP中的代理使用的默认策略

  • 如果目标对象实现类接口,则默认采用JDK动态代理
  • 如果目标对象没有实现接口,则采用CGLIB进行动态代理
更多精彩内容其他人还在看

Java输入输出流复制文件所用时间对比

这篇文章主要介绍了Java输入输出流复制文件所用时间对比的相关资料,非常不错,具有参考解决价值,需要的朋友可以参考下
收藏 0 赞 0 分享

Java线程中start和run方法全面解析

这篇文章主要介绍了Java线程中start和run方法的相关资料,非常不错,具有参考借鉴价值,需要的朋友可以参考下
收藏 0 赞 0 分享

Java的JSON处理器fastjson使用方法详解

下面小编就为大家带来一篇Java的JSON处理器fastjson使用方法详解。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
收藏 0 赞 0 分享

Java 二维码,QR码,J4L-QRCode 的资料整理

本文主要介绍Java 中二维码,QR码,J4L-QRCode,这里整理了详细的资料供大家学习参考关于二维码的知识,有需要的小伙伴可以参考下
收藏 0 赞 0 分享

java哈夫曼树实例代码

这篇文章主要为大家介绍了java哈夫曼树实例代码,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
收藏 0 赞 0 分享

Android读取本地或网络图片并转换为Bitmap

这篇文章主要为大家详细介绍了Android读取本地或网络图片,并转换为Bitmap,感兴趣的小伙伴们可以参考一下
收藏 0 赞 0 分享

Java日期时间操作的方法

这篇文章主要为大家详细介绍了Java日期时间操作的一些方法,获得Calendar,定义日期/时间的格式等,感兴趣的小伙伴们可以参考一下
收藏 0 赞 0 分享

java 获取路径的各种方法(总结)

下面小编就为大家带来一篇java 获取路径的各种方法(总结)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
收藏 0 赞 0 分享

java数据结构与算法之奇偶排序算法完整示例

这篇文章主要介绍了java数据结构与算法之奇偶排序算法,较为详细的分析了奇偶算法的原理并结合完整示例形式给出了实现技巧,需要的朋友可以参考下
收藏 0 赞 0 分享

java数据结构与算法之双向循环队列的数组实现方法

这篇文章主要介绍了java数据结构与算法之双向循环队列的数组实现方法,结合实例形式分析了双向循环队列的原理与数组实现技巧,并附带说明了该算法的用途,需要的朋友可以参考下
收藏 0 赞 0 分享
查看更多