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

(0)

相关推荐

  • SpringAOP中的注解配置详解

    这篇文章主要介绍了SpringAOP中的注解配置详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 使用注解实现SpringAOP的功能: 例子: //表示这是被注入Spring容器中的 @Component //表示这是个切面类 @Aspect public class AnnotationHandler { /* * 在一个方法上面加上注解来定义切入点 * 这个切入点的名字就是这个方法的名字 * 这个方法本身不需要有什么作用 * 这个方法的

  • springAOP的三种实现方式示例代码

    这篇文章给大家介绍了springAOP的实现方式,三种分别是纯XML方式,XML+注解,纯注解方式. Spring 实现AOP思想使⽤的是动态代理技术 默认情况下, Spring会根据被代理对象是否实现接⼝来选择使⽤JDK还是CGLIB.当被代理对象没有实现 任何接⼝时, Spring会选择CGLIB.当被代理对象实现了接⼝, Spring会选择JDK官⽅的代理技术,不过 我们可以通过配置的⽅式,让Spring强制使⽤CGLIB. 接下来我们开始实现aop, 需求是:横切逻辑代码是打印⽇志,希望

  • SpringAOP事务配置语法及实现过程详解

    配置事务: 使用的tx前缀的标签, 导入tx的命名空间 配置事务管理器 , 把事务管理器交给Spring管理: <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!-- 注入DataSource --> <property name="dataSource" ref="

  • SpringAOP切点函数实现原理详解

    一:在函数入参中使用通配符 @AspectJ支持3种通配符 * :匹配任意字符,但它只能匹配上下文中的一个元素. .. :匹配任意字符,可以匹配上下文中多个元素,但在表示类时,必须和*联合使用,而在表示入参时则单独使用 + :表示按类型匹配指定类的所有类,必须跟在类名后面,如com.smart.Car+ ;继承或扩展指定类的所有类,同时还包括指定类本身. @AspectJ函数按其是否支持通配符及支持的程度,可以分为以下3类. 1):支持所有的通配符:execution(),within() 2)

  • SpringAOP+RabbitMQ+WebSocket实战详解

    背景 最近公司的客户要求,分配给员工的任务除了有微信通知外,还希望PC端的网页也能实时收到通知.管理员分配任务是在我们的系统A,而员工接受任务是在系统B.两个系统都是现在已投入使用的系统. 技术选型 根据需求我们最终选用SpringAOP+RabbitMQ+WebSocket. SpringAOP可以让我们不修改原有代码,直接将原有service作为切点,加入切面.RabbitMQ可以让A系统和B系统解耦.WebSocket则可以达到实时通知的要求. SpringAOP AOP称为面向切面编程,

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

    什么是代理? 为某一个对象创建一个代理对象,程序不直接用原本的对象,而是由创建的代理对象来控制原对象,通过代理类这中间一层,能有效控制对委托类对象的直接访问,也可以很好地隐藏和保护委托类对象,同时也为实施不同控制策略预留了空间 什么是静态代理? 由程序创建或特定工具自动生成源代码,在程序运行前,代理类的.class文件就已经存在 通过将目标类与代理类实现同一个接口,让代理类持有真实类对象,然后在代理类方法中调用真实类方法,在调用真实类方法的前后添加我们所需要的功能扩展代码来达到增强的目的. 优点

  • Spring AOP中的JDK和CGLib动态代理哪个效率更高?

    一.背景 今天有小伙伴面试的时候被问到:Spring AOP中JDK 和 CGLib动态代理哪个效率更高? 二.基本概念 首先,我们知道Spring AOP的底层实现有两种方式:一种是JDK动态代理,另一种是CGLib的方式. 自Java 1.3以后,Java提供了动态代理技术,允许开发者在运行期创建接口的代理实例,后来这项技术被用到了Spring的很多地方. JDK动态代理主要涉及java.lang.reflect包下边的两个类:Proxy和InvocationHandler.其中,Invoc

  • Linux静态库与动态库实例详解

    Linux静态库与动态库实例详解 1. Linux 下静态链接库编译与使用 首先编写如下代码: // main.c #include "test.h" int main(){ test(); return 0; } // test.h #include<iostream> using namespace std; void test(); // test.c #include "test.h" void test(){ cout<< &quo

  • JSP静态导入与动态导入使用详解

    JSP静态导入(JSP指令标记include) JSP页面第一次被请求时,会被JSP引擎转译成Servlet的Java文件,然后再被编译成字节码文件执行.JSP指令标记为JSP页面转译提供整个页面的相关信息. include指令用于在JSP页面静态插入一个文件,被插入的文件可以是JSP页面.HTML页面.文本文件或一段Java代码.使用了include指令的JSP页面在转换成Java文件时,将被插入的文件在当前JSP页面出来该指令的位置做整体的插入,合并成一个新的JSP页面,然后JSP引擎再将这

  • 深入探讨Linux静态库与动态库的详解(一看就懂)

    库从本质上来说是一种可执行代码的二进制格式,可以被载入内存中执行.库分静态库和动态库两种. 一.静态库和动态库的区别1. 静态函数库这类库的名字一般是libxxx.a:利用静态函数库编译成的文件比较大--空间,因为整个函数库的所有数据都会被整合进目标代码中,他的优点就显而易见了,即编译后的执行程序不需要外部的函数库支持,因为所有使用的函数都已经被编译进去了.当然这也会成为他的缺点,因为如果静态函数库改变了,那么你的程序必须重新编译.2. 动态函数库这类库的名字一般是libxxx.so;相对于静态

  • JavaScript静态作用域和动态作用域实例详解

    静态作用域指的是一段代码,在它执行之前就已经确定了它的作用域,简单来说就是在执行之前就确定了它可以应用哪些地方的作用域(变量). 动态作用域–函数的作用域是在函数调用的时候才决定的 JavaScript采用的是词法作用域即静态作用域: // 静态作用域: var a = 10; function fn() { var b = 1; console.log(a + b); } fn(); // 11 在创建fn函数时的时候就已经确定了它可以作用哪些变量,如果函数fn里面有变量a就直接操作变量a,

  • Spring的组合注解和元注解原理与用法详解

    本文实例讲述了Spring的组合注解和元注解原理与用法.分享给大家供大家参考,具体如下: 一 点睛 从Spring 2开始,为了相应JDK 1.5推出的注解功能,Spring开始加入注解来替代xml配置.Spring的注解主要用来配置和注入Bean,以及AOP相关配置.随着注解的大量使用,尤其相同的多个注解用到各个类或方法中,会相当繁琐.出现了所谓的样本代码,这是Spring设计要消除的代码. 元注解:可以注解到别的注解上去的注解. 组合注解:被注解的注解,组合注解具备其上的元注解的功能. Sp

  • Spring中基于Java的配置@Configuration和@Bean用法详解

    一.首先,需要xml中进行少量的配置来启动Java配置: <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://ww

  • Spring静态代理和动态代理代码详解

    本节要点: Java静态代理 Jdk动态代理 1 面向对象设计思想遇到的问题 在传统OOP编程里以对象为核心,并通过对象之间的协作来形成一个完整的软件功能,由于对象可以继承,因此我们可以把具有相同功能或相同特征的属性抽象到一个层次分明的类结构体系中.随着软件规范的不断扩大,专业化分工越来越系列,以及OOP应用实践的不断增多,随之也暴露了一些OOP无法很好解决的问题. 现在假设系统中有三段完全相似的代码,这些代码通常会采用"复制"."粘贴"方式来完成,通过这种方式开发

  • AOP从静态代理到动态代理(Emit实现)详解

    [前言] AOP(Aspect Orient Programming),我们一般称为面向方面(切面)编程,作为面向对象的一种补充,用于处理系统中分布于各个模块的横切关注点,比如事务管理.日志.缓存等等.AOP实现的关键在于AOP框架自动创建的AOP代理,AOP代理主要分为静态代理和动态代理,静态代理的代表为AspectJ:而动态代理则以Spring AOP为代表. 何为切面? 一个和业务没有任何耦合相关的代码段,诸如:调用日志,发送邮件,甚至路由分发.一切能为代码所有且能和代码充分解耦的代码都可

随机推荐