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

 更新时间:2020年07月24日 16:36:23   作者:陈彦斌  
这篇文章主要介绍了 Spring AOP里的静态代理和动态代理用法详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

什么是代理?

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

什么是静态代理?

  由程序创建或特定工具自动生成源代码,在程序运行前,代理类的.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进行动态代理

到此这篇关于 Spring AOP里的静态代理和动态代理用法详解的文章就介绍到这了,更多相关 Spring AOP 静态代理 动态代理内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • java 方法重写与权限修饰符以及多态和抽象类详解概念和用法

    java 方法重写与权限修饰符以及多态和抽象类详解概念和用法

    重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写,权限修饰符用于控制被修饰变量、方法、类的可见范围,说明了面向对象的封装性,所以我们要适用他们尽可能的让权限降到最低,从而安全性提高
    2021-10-10
  • Spring事务@Transactional注解四种不生效案例场景分析

    Spring事务@Transactional注解四种不生效案例场景分析

    这篇文章主要为大家介绍了Spring事务@Transactional注解四种不生效的案例场景示例分析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-07-07
  • Java操作itextpdf实现PDF添加文字,图片和签名

    Java操作itextpdf实现PDF添加文字,图片和签名

    这篇文章主要为大家详细介绍了Java如何操作itextpdf实现PDF添加文字,图片和签名等功能,文中的示例代码讲解详细,感兴趣的小伙伴可以参考一下
    2025-01-01
  • spring boot 使用profile来分区配置的操作

    spring boot 使用profile来分区配置的操作

    这篇文章主要介绍了spring boot使用profile来分区配置的操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-07-07
  • 在maven工程里运行java main方法

    在maven工程里运行java main方法

    这篇文章主要介绍了在maven工程里运行java main方法,需要的朋友可以参考下
    2014-04-04
  • Java中BitMap(位图)hutool版、IntMap、LongMap示例详解

    Java中BitMap(位图)hutool版、IntMap、LongMap示例详解

    这篇文章主要给大家介绍了关于Java中BitMap(位图)hutool版、IntMap、LongMap的相关资料,通过位运算高效存储和检索整数,相比于传统数组,它们在内存占用和性能上都有显著优势,需要的朋友可以参考下
    2024-12-12
  • Java执行JS脚本工具

    Java执行JS脚本工具

    今天小编就为大家分享一篇关于Java执行JS脚本工具,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2018-12-12
  • SpringBoot实现其他普通类调用Spring管理的Service,dao等bean

    SpringBoot实现其他普通类调用Spring管理的Service,dao等bean

    这篇文章主要介绍了SpringBoot实现其他普通类调用Spring管理的Service,dao等bean,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-11-11
  • java8中的lambda表达式,看这篇绝对够

    java8中的lambda表达式,看这篇绝对够

    这篇文章主要介绍了java8中的lambda表达式,看这篇绝对够!具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-03-03
  • Spring Boot项目如何使用Maven打包并带上依赖

    Spring Boot项目如何使用Maven打包并带上依赖

    在这篇博客中,介绍如何使用Maven将Spring Boot项目及其依赖项打包成一个可执行的jar文件。我们将使用Spring Boot的spring-boot-maven-plugin插件来完成这个任务,感兴趣的朋友跟随小编一起看看吧
    2023-06-06

最新评论