zoukankan      html  css  js  c++  java
  • 动态代理在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="
    ";
            String tab ="	";
            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();
          }
      }
    }
  • 相关阅读:
    今天面试一些程序员(新,老)手的体会
    UVA 10635 Prince and Princess
    poj 2240 Arbitrage
    poj 2253 Frogger
    poj 2485 Highways
    UVA 11258 String Partition
    UVA 11151 Longest Palindrome
    poj 1125 Stockbroker Grapevine
    poj 1789 Truck History
    poj 3259 Wormholes
  • 原文地址:https://www.cnblogs.com/tianboblog/p/12625334.html
Copyright © 2011-2022 走看看