动态代理在Spring中的应用

Spring中主要使用cglib和jdk动态代理,主要在SpringAop中有大量应用。

JDK动态代理

jdk动态代理主要使用场景是被代理的对象有实现的接口。最终生成的代理类:

class $Proxy0 extends Proxy implements IDao

jdk动态代理主要是基于反射,其实我们完全可以自己模拟;其中两个比较关键的思路:

  1. 使用反射解析目标对象的属性、方法等
  2. 根据解析的内容生成proxy.class,说白了就是把要生成的class按照字符串的形式拼接,最终通过ClassLoader加载。
package com.tian.proxy;
import com.sun.jndi.toolkit.url.UrlUtil;
import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;

public class ProxyUtil {
    public static Object newInstance(Object target){
        Object proxy=null;
        Class targetInf = target.getClass().getInterfaces()[0];
        Method methods[] =targetInf.getDeclaredMethods();
        String line="\n";
        String tab ="\t";
        String infName = targetInf.getSimpleName();
        String content ="";
        String packageContent = "package com.tian;"+line;
        String importContent = "import "+targetInf.getName()+";"+line;
        String clazzFirstLineContent = "public class $Proxy implements "+infName+"{"+line;
        String filedContent  =tab+"private "+infName+" target;"+line;
        String constructorContent =tab+"public $Proxy ("+infName+" target){" +line
                                  +tab+tab+"this.target =target;"
                                  +line+tab+"}"+line;
        String methodContent = "";
        for (Method method : methods) {
            String returnTypeName = method.getReturnType().getSimpleName();
            String methodName =method.getName();
            // Sting.class String.class
            Class args[] = method.getParameterTypes();
            String argsContent = "";
            String paramsContent="";
            int flag =0;
            for (Class arg : args) {
                String temp = arg.getSimpleName();
                //String
                //String p0,Sting p1,
                argsContent+=temp+" p"+flag+",";
                paramsContent+="p"+flag+",";
                flag++;
            }
            if (argsContent.length()>0){
                argsContent=argsContent.substring(0,argsContent.lastIndexOf(",")-1);
                paramsContent=paramsContent.substring(0,paramsContent.lastIndexOf(",")-1);
            }
            methodContent+=tab+"public "+returnTypeName+" "+methodName+"("+argsContent+") {"+line
                          +tab+tab+"System.out.println(\"log\");"+line
                          +tab+tab+"target."+methodName+"("+paramsContent+");"+line
                          +tab+"}"+line;

        }
        content=packageContent+importContent+clazzFirstLineContent+filedContent+constructorContent+methodContent+"}";

        File file =new File("d:\\com\\tian\\$Proxy.java");
        try {
            if (!file.exists()) {
                file.createNewFile();
            }

            FileWriter fw = new FileWriter(file);
            fw.write(content);
            fw.flush();
            fw.close();

            JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
            StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
            Iterable units = fileMgr.getJavaFileObjects(file);

            JavaCompiler.CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);
            t.call();
            fileMgr.close();

            URL[] urls = new URL[]{new URL("file:D:\\\\")};
            URLClassLoader urlClassLoader = new URLClassLoader(urls);
            Class clazz = urlClassLoader.loadClass("com.tian.$Proxy");

            Constructor constructor = clazz.getConstructor(targetInf);

            proxy = constructor.newInstance(target);
        }catch (Exception e){
            e.printStackTrace();
        }
        return proxy;
    }
}

 IDao proxy = (IDao) ProxyUtil.newInstance(new 

CGLIB动态代理

cglib代理主要使用场景是:被代理对象的是类而没有任何接口实现。通过字节码增强实现动态代理(底层使用了asm)。在Spring中一个比较重要的应用就是解析@Configuration注解。Spring应用中只要在相应的class上添加了@Configuration注解,就被认为是一个全注解的类(看spring源码看实例化BeanDefinition对象时,会设置一个属性标识为full;当然与之对应一个是lite,就是标注了@Component,@ComponentScan,@Import,@ImportResource,@Bean这些注解的类)。

添加了@Configuration一个比较重要的作用就是会把该配置类使用cglib进行字节码增强,其实主要目的就是Spring可以更好的管理Bean的依赖关系了,如下示例:

定义要给配置类:

@Configuration
@ComponentScan("com.tian.*")
public class AppConfig {
   @Bean
   public UserDaoImpl1 userDaoImpl1(){
      return new UserDaoImpl1();
   }

   @Bean
   public UserDaoImpl2 userDaoImp2(){
      userDaoImpl1();
      return new UserDaoImpl2();
   }
}

相应的类:

public class UserDaoImpl1 {
   public UserDaoImpl1() {
      System.out.println("UserDaoImpl1 init......");
   }

   public void query() {
      System.out.println("UserDaoImpl1 query.......");
   }
}

public class UserDaoImpl2 {
   public UserDaoImpl2() {
      System.out.println("UserDaoImpl2 init......");
   }

   public void query() {
      System.out.println("UserDaoImpl2 query......");
   }

执行如下:会发现UserDaoImpl1 init......只会打印一次,当然如果把@Configuration注解去掉就会打印两次。增强带来的好处是:Spring可以更好的管理Bean的依赖关系了。比如@Bean之间方法之间的调用,其实是去Spring容器里去找Bean了,而并不是再生成了一个实例。

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

通过查看源码发现使用cglib做了动态代理:

Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);

同样可以简单模拟下,依然使用如上相关类。新增一个关键的类,主要就是通过它进行回调增强:

public class MyEnhancerCallBack implements MethodInterceptor {
   @Override
   public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
      System.out.println("cglib proxy.......");

      return methodProxy.invokeSuper(o, objects);
   }
}

测试:

//可以查看cglib生成的class
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "G:\\demo");
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserDaoImpl1.class);
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
enhancer.setCallback(new MyEnhancerCallBack());
UserDaoImpl1 userDaoImpl1 =(UserDaoImpl1) enhancer.create();
userDaoImpl1.query();

可以把生成class拷贝到idea中

取部分代码:

//其实就是把代理的对象作为父类
public class UserDaoImpl1$$EnhancerBySpringCGLIB$$a779f942 extends UserDaoImpl1 implements Factory {
  final void CGLIB$query$0() {
    super.query();
  }

  public final void query() {
      //通过这个进行回调增强
      MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
      if (var10000 == null) {
          CGLIB$BIND_CALLBACKS(this);
          var10000 = this.CGLIB$CALLBACK_0;
      }

      if (var10000 != null) {
          var10000.intercept(this, CGLIB$query$0$Method, CGLIB$emptyArgs, CGLIB$query$0$Proxy);
      } else {
          super.query();
      }
  }
}

原文地址:https://www.cnblogs.com/tianboblog/p/12625334.html

时间: 2024-10-14 19:55:36

动态代理在Spring中的应用的相关文章

AOP、静态代理、JDK动态代理、CGLIB动态代理、Spring实现AOP、IOC+AOP

一.为什么需要代理模式 假设需实现一个计算的类Math.完成加.减.乘.除功能,如下所示: 1 package com.zhangguo.Spring041.aop01; 2 3 public class Math { 4 //加 5 public int add(int n1,int n2){ 6 int result=n1+n2; 7 System.out.println(n1+"+"+n2+"="+result); 8 return result; 9 } 1

从动态代理到Spring AOP(上)

一.前言 虽然平时日常开发很少用到动态代理,但是动态代理在底层框架等有着非常重要的意义.比如Spring AOP使用cglib和JDK动态代理,Hibernate底层使用了javassit和cglib动态代理,Dubbo使用javassist字节码(具体可以看Dubbo SPI). 本文主要介绍什么是动态代理及原理,下文将介绍Spring AOP 我们先思考一个问题:如何统计一个类各个方法的执行时间?可能你心里有好多答案都可以解决问题. 那么如果是这个项目的多个不同类呢?可能心里也有答案,但是代

反射实现AOP动态代理模式(Spring AOP实现原理)

其实AOP的意思就是面向切面编程. OO注重的是我们解决问题的方法(封装成Method),而AOP注重的是许多解决问题的方法中的共同点,是对OO思想的一种补充! 还是拿人家经常举的一个例子讲解一下吧: 比如说,我们现在要开发的一个应用里面有很多的业务方法,但是,我们现在要对这个方法的执行做全面监控,或部分监控.也许我们就会在要一些方法前去加上一条日志记录. 我们写个例子看看我们最简单的解决方案 我们先写一个接口IHello.java代码如下: package sinosoft.dj.aop.st

奇妙的动态代理:EF中返回的对象为什么序列化失败

今天有如鹏的学生遇到一个问题:把一个对象保存到Session中(进程外Session)后,Web服务器重启,当从Session读取这个对象的时候报错,提示是一个“T_Users”后面跟着一大串数字的类型,不是“T_Users”类型. 凭着感觉,我问“这个对象是普通对象还是什么对象”,回复说“是Entity Framework返回的对象”,瞬间我知道了:是延迟加载造成的.下面写个程序验证一下. 数据库里建立两张表:一张T_Persons表,一张T_Dogs表,T_Dogs表中有一个MasterId

【设计模式】代理模式:静态代理,动态代理,spring aop

代理模式分为静态代理和动态代理.我们拿链家来举例子,我们本人是真实的对象,有真实的业务需求:需要去找房子:链家是中介,是代理类,他来帮我执行找房子的这个操作. 静态代理: 1.实现一个接口 public interface SearchHome { public void search(); } 2.构建实现接口的委托类 public class Master implements SearchHome { @Override public void search() { System.out.

[动态代理三部曲:中] - 从动态代理,看Class文件结构定义

前言 这篇内容是上一篇[动态代理三部曲:上] - 动态代理是如何"坑掉了"我4500块钱的补充,进一步分析篇.建议二者结合食用,醇香绵软,入口即化. 好了,不扯淡了,开始... 正文 2.Class 文件的格式 这里为啥是2开头呢?因为上篇文章是1 这部分内容不知道各位小伙伴是怎么感觉的.最开始学习的时候,我是一头雾水,不知道如何下手.当一步步结合反射.JVM内存模型,类加载机制后.再回过头来就会发现一起豁然开朗. 此篇内容的开始,让我们根据我们demo中所用的类:RentHouseP

动态代理实现Spring Aop

引言 我们在前两篇文章中,都为这篇做了铺垫,我们现在来做这样一件事情,在业务逻辑中添加Aop的非业务逻辑. AopClinetTest: package com.tgb.client; import com.tgb.config.BeanFactory; import com.tgb.config.ClassPathXmlApplicationContext; import com.tgb.dao.UserDao; import com.tgb.domain.User; /** * AOP效果测

Spring AOP中的动态代理

0  前言 1  动态代理 1.1 JDK动态代理 1.2 CGLIB动态代理 1.2.1 CGLIB的代理用法 1.2.2 CGLIB的过滤功能 2  Spring AOP中的动态代理机制 2.1 JdkDynamicAopProxy 2.2 CglibAopProxy 3 总结 0  前言 前一个季度旅游TDC的Thames服务有几次宕机,根据组内原因认真查找发现是数据库事务造成的,后来把服务中的事务配置全部去掉,服务恢复正常.根据这次教训,虽然现在还是很难确定是哪一个方面的真正原因,但是激

Spring框架中的JDK与CGLib动态代理

JDK和CGLib动态代理区别 JDK动态代理:利用拦截器(拦截器必须实现InvocationHanlder)加上反射机制生成一个实现代理接口的匿名类, 在调用具体方法前调用InvokeHandler来处理. CGLib动态代理:利用ASM开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理. 何时使用JDK和CGLib: 1)如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP. 2)如果目标对象实现了接口,可以强制使用CGLIB实现AOP. 3)如果目标