動態代理在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();
      }
  }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM