java反射


title: java反射
date: 2018-12-16 15:54:20
updated: 2020-03-15 16:09:23
categories: java
tags:
- java


此文档为java反射的学习总结

动态代理详解

详看这篇博客Java的三种代理模式

动态代理有以下特点:

  • 1.代理对象,不需要实现接口
  • 2.代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象(需要我们指定创建代理对象/目标对象实现的接口的类型)
  • 3.动态代理也叫做:JDK代理,接口代理

在Spring的AOP编程中:

如果加入容器的目标对象有实现接口,用JDK代理
如果目标对象没有实现接口,用Cglib代理

1.动态代理之jdk动态代理:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

//动态代理demo

//接口
interface UserManager{
    public void call(String name);
}
//具体对象
class UserManagerImpl implements UserManager {

    @Override
    public void call(String name) {
        System.out.println("call.." + name);
    }
}

//代理类
class ProxyT implements InvocationHandler{

    private Object objectTarget;
    public Object newProxyInstance(Object objectTarget){
        this.objectTarget = objectTarget;
        return Proxy.newProxyInstance(objectTarget.getClass().getClassLoader(), objectTarget.getClass().getInterfaces(), this);
    }

    @Override
    /*InvocationHandler接口的方法,proxy表示代理,method表示原对象被调用的方法,args表示方法的参数*/
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("startProxy...");
        Object ret = null;
        try{
            System.out.println("日志记录start....");
            ret = method.invoke(objectTarget, args);
            System.out.println("日志记录end....");
        }catch (Exception e){
            e.printStackTrace();
        }
        return ret;
    }
}
public class Test{
    public static void main(String[] args) {

        UserManager userManager = (UserManager)new ProxyT().newProxyInstance(new UserManagerImpl());
        userManager.call("张三");
    }
}

2.Cglib代理

上面的静态代理和动态代理模式都是要求目标对象是实现一个接口的目标对象,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候就可以使用以目标对象子类的方式类实现代理,这种方法就叫做:Cglib代理

Cglib代理,也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展.

JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口,如果想代理没有实现接口的类,就可以使用Cglib实现.
Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口.它广泛的被许多AOP的框架使用,例如Spring AOP和synaop,为他们提供方法的interception(拦截)
Cglib包的底层是通过使用一个小而块的字节码处理框架ASM来转换字节码并生成新的类.不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉.

Cglib子类代理实现方法:

  • 1.需要引入cglib的jar文件,但是Spring的核心包中已经包括了Cglib功能,所以直接引入spring-core-3.2.5.jar即可.
  • 2.引入功能包后,就可以在内存中动态构建子类
  • 3.代理的类不能为final,否则报错
  • 4.目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法.
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

/**
 * 动态代理之cglib动态代理
 */
class UserDao{
    public void save(){
        System.out.println("is saved.");
    }
}
class ProxyFactory implements MethodInterceptor {
    //维护一个目标对象
    private Object target;
    public ProxyFactory(Object target){
        this.target = target;
    }
    //给目标对象生成代理对象
    public Object getProxyInstance(){
        //1.工具类
        Enhancer en = new Enhancer();

        //2.设置父类
        en.setSuperclass(target.getClass());

        //3.设置回调函数
        en.setCallback(this);

        //4.创建子类(代理对象)
        return en.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("开始事务...");

        //执行目标对象的方法
        Object returnValue = method.invoke(target, objects);

        System.out.println("提交事务...");

        return returnValue;
    }
}
public class Test
{
    public static void main(String args[]){
        // 目标对象
        UserDao target = new UserDao();
        //原始类型
        System.out.println(target.getClass());

        // 给目标对象,创建代理对象
        UserDao proxy = (UserDao) new ProxyFactory(target).getProxyInstance();
        System.out.println(proxy.getClass());

        // 执行方法   代理对象
        proxy.save();
    }
    /**
     * out:
     *   class UserDao  //原始的类型 class cn.itcast.b_dynamic.UserDao
         class $Proxy0  // class UserDao$$EnhancerByCGLIB$$90f08d08   内存中动态生成的代理对象
         开始事务2
         is saved.
         提交事务2
     */
}

反射

反射机制并没有什么神奇之处,当通过反射与一个未知类型的对象打交道时,JVM只是简单地检查这个对象,看它属于哪个特定的类。因此,那个类的.class对于JVM来说必须是可获取的,要么在本地机器上,要么从网络获取。所以对于RTTI和反射之间的真正区别只在于

RTTI,编译器在编译时打开和检查.class文件

反射,运行时打开和检查.class文件

反射调用对象的过程:获取反射的 Class 对象、通过反射创建类对象、通过反射获取类属性方法及构造器。

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

/**
 * 反射
 */
public class Test
{
    private int price;

    public int getPrice(){
        return price;
    }

    public void setPrice(int price){
        this.price = price;
    }

    public static void main(String args[]) throws Exception{
        //正射调用,代码在未运行时就已经确定了要运行的类(Test)
        Test test = new Test();
        test.setPrice(5);
        System.out.println(test.getPrice());

        //反射调用,在运行时才知道要操作的类是什么,并且可以在运行时获取类的完整构造,并调用对应的方法。
        Class clazz = Class.forName("Test");
        Method setMethod = clazz.getMethod("setPrice", int.class);
        Constructor testConstructor = clazz.getConstructor();
        Object testObj = testConstructor.newInstance();
        setMethod.invoke(testObj, 5);
        Method getMethod = clazz.getMethod("getPrice");
        System.out.println(getMethod.invoke(testObj));

        //out: 5 5
    }
}

一个例子

小知识点:obj.getClass().getName()–Class类方法getName()获得对象所属类的类名字符串

反射机制最重要的内容–检查类的结构(一般编写应用程序用很少,而编写应用工具用较多)

import java.util.*;
import java.lang.reflect.*;
 
/**
 * This program uses reflection to print all features of a class.
 * @version 1.1 2004-02-21
 * @author Cay Horstmann
 */
public class ReflectionTest
{
   public static void main(String[] args)
   {
      // read class name from command line args or user input
      String name;
      if (args.length > 0) name = args[0];
      else
      {
         Scanner in = new Scanner(System.in);
         System.out.println("Enter class name (e.g. java.util.Date): ");
         name = in.next();
      }
 
      try
      {
         // print class name and superclass name (if != Object)
         Class cl = Class.forName(name);
         Class supercl = cl.getSuperclass();
         String modifiers = Modifier.toString(cl.getModifiers());
         if (modifiers.length() > 0) System.out.print(modifiers + " ");
         System.out.print("class " + name);
         if (supercl != null && supercl != Object.class) System.out.print(" extends "
               + supercl.getName());
 
         System.out.print("\n{\n");
         printConstructors(cl);
         System.out.println();
         printMethods(cl);
         System.out.println();
         printFields(cl);
         System.out.println("}");
      }
      catch (ClassNotFoundException e)
      {
         e.printStackTrace();
      }
      System.exit(0);
   }
 
   /**
    * Prints all constructors of a class
    * @param cl a class
    */
   public static void printConstructors(Class cl)
   {
      Constructor[] constructors = cl.getDeclaredConstructors();
 
      for (Constructor c : constructors)
      {
         String name = c.getName();
         System.out.print("   ");
         String modifiers = Modifier.toString(c.getModifiers());
         if (modifiers.length() > 0) System.out.print(modifiers + " ");         
         System.out.print(name + "(");
 
         // print parameter types
         Class[] paramTypes = c.getParameterTypes();
         for (int j = 0; j < paramTypes.length; j++)
         {
            if (j > 0) System.out.print(", ");
            System.out.print(paramTypes[j].getName());
         }
         System.out.println(");");
      }
   }
 
   /**
    * Prints all methods of a class
    * @param cl a class
    */
   public static void printMethods(Class cl)
   {
      Method[] methods = cl.getDeclaredMethods();
 
      for (Method m : methods)
      {
         Class retType = m.getReturnType();
         String name = m.getName();
 
         System.out.print("   ");
         // print modifiers, return type and method name
         String modifiers = Modifier.toString(m.getModifiers());
         if (modifiers.length() > 0) System.out.print(modifiers + " ");         
         System.out.print(retType.getName() + " " + name + "(");
 
         // print parameter types
         Class[] paramTypes = m.getParameterTypes();
         for (int j = 0; j < paramTypes.length; j++)
         {
            if (j > 0) System.out.print(", ");
            System.out.print(paramTypes[j].getName());
         }
         System.out.println(");");
      }
   }
 
   /**
    * Prints all fields of a class
    * @param cl a class
    */
   public static void printFields(Class cl)
   {
      Field[] fields = cl.getDeclaredFields();
 
      for (Field f : fields)
      {
         Class type = f.getType();
         String name = f.getName();
         System.out.print("   ");
         String modifiers = Modifier.toString(f.getModifiers());
         if (modifiers.length() > 0) System.out.print(modifiers + " ");         
         System.out.println(type.getName() + " " + name + ";");
      }
   }
}

output:

Enter class name (e.g. java.util.Date): 
java.util.Date
public class java.util.Date
{
   public java.util.Date(int, int, int, int, int, int);
   public java.util.Date(java.lang.String);
   public java.util.Date();
   public java.util.Date(long);
   public java.util.Date(int, int, int);
   public java.util.Date(int, int, int, int, int);
 
   public boolean after(java.util.Date);
   public boolean before(java.util.Date);
   public boolean equals(java.lang.Object);
   public java.lang.String toString();
   public int hashCode();
   public java.lang.Object clone();
   public int compareTo(java.util.Date);
   public volatile int compareTo(java.lang.Object);
   private void readObject(java.io.ObjectInputStream);
   private void writeObject(java.io.ObjectOutputStream);
   private final sun.util.calendar.BaseCalendar$Date normalize();
   private final sun.util.calendar.BaseCalendar$Date normalize(sun.util.calendar.BaseCalendar$Date);
   public static long parse(java.lang.String);
   public static long UTC(int, int, int, int, int, int);
   public long getTime();
   private static final java.lang.StringBuilder convertToAbbr(java.lang.StringBuilder, java.lang.String);
   private final sun.util.calendar.BaseCalendar$Date getCalendarDate();
   private static final sun.util.calendar.BaseCalendar getCalendarSystem(long);
   private static final sun.util.calendar.BaseCalendar getCalendarSystem(sun.util.calendar.BaseCalendar$Date);
   private static final sun.util.calendar.BaseCalendar getCalendarSystem(int);
   public int getDay();
   public int getHours();
   private static final synchronized sun.util.calendar.BaseCalendar getJulianCalendar();
   static final long getMillisOf(java.util.Date);
   public int getMinutes();
   public int getMonth();
   public int getSeconds();
   private final long getTimeImpl();
   public int getTimezoneOffset();
   public int getYear();
   public void setDate(int);
   public void setHours(int);
   public void setMinutes(int);
   public void setMonth(int);
   public void setSeconds(int);
   public void setYear(int);
   public java.lang.String toGMTString();
   public java.time.Instant toInstant();
   public java.lang.String toLocaleString();
   public int getDate();
   public static java.util.Date from(java.time.Instant);
   public void setTime(long);
 
   private static final sun.util.calendar.BaseCalendar gcal;
   private static sun.util.calendar.BaseCalendar jcal;
   private transient long fastTime;
   private transient sun.util.calendar.BaseCalendar$Date cdate;
   private static int defaultCenturyStart;
   private static final long serialVersionUID;
   private static final [Ljava.lang.String; wtb;
   private static final [I ttb;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值