首页 > 编程语言 > Spring AOP里的静态代理和动态代理用法详解
2020
10-08

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 静态代理 动态代理内容请搜索自学编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持自学编程网!

编程技巧