当前位置:首页 > CN2资讯 > 正文内容

Java 反射、代理java反射动态代理

2天前CN2资讯

目录

      • 反射(reflect)
        • 获取class对象的三种方式
        • 使用反射创建对象
        • 使用反射操作字段
        • 使用反射调用方法
        • 使用反射动态创建、操作数组
      • 代理(proxy)
        • 静态代理
        • jdk动态代理
        • cglib动态代理
        • 使用建议

 

反射(reflect)

反射:通过类的class对象来获取类的信息,动态操作类中的字段、调用类中的方法。
 

获取class对象的三种方式

// Class.forName("全限定类名"),类名是String形式,编译时并不知道Class对象的泛型,所以是?,需要强转 Class<?> class1 = Class.forName("com.chy.mall.model.User"); Class<User> class2 = (Class<User>)class1; //类名.class Class<User> class3=User.class; //对象名.getClass() Class<? extends User> class4=user.getClass();

使用反射创建对象

Class<User> userClass = User.class; //获取class对象 Constructor<User> constructor = userClass.getConstructor(int.class, String.class, int.class); //通过class对象获取指定的构造器,形参表要写成class对象的形式 User user = constructor.newInstance(1, "chy", 20); //传入实参创建实例

使用反射操作字段

Class<User> userClass = User.class; //获取私有字段 Field nameField = userClass.getDeclaredField("name"); //取消权限检查 nameField.setAccessible(true); User user = new User(1, "zhangsan", 20); //调用方法,传入实参,即要操作的对象,获取该字段的值,返回Object String name = (String) nameField.get(user); //设置对象该字段的值 nameField.set(user, "lisi");
  • private成员不允许在类外操作,需要取消权限检查才能操作private成员;public成员默认不检查权限。

使用反射调用方法

Class<User> userClass = User.class; //获取带参的公有方法。后面是形参表的class对象,参数个数可变 Method getNameMethod = userClass.getMethod("setName", String.class); Method getNameMethod = userClass.getMethod("getName"); User user = new User(1, "zhangsan", 20); //调用指定对象的该方法(无参),返回值是Object类型 String name = (String) getNameMethod.invoke(user); //调用指定对象的该方法(带参),后面是实参表,参数个数可变 setNameMethod.invoke(user, "lisi");
  • 调用方法时需要指定形参的class对象,所以方法的形参不能使用基本类型,要使用基本类型对应的包装类型。
  • getMethod()只能获取当前类的方法,不能获取从父类继承的方法,如果存在要获取从父类继承的方法的情况,可以新建一个私有方法,调用私有方法来获取
/** * 获取指定的方法 * * @param target 类的class对象 * @param methodName 方法名 * @param parameterTypes 形参的class对象 * @return Method * @throws NoSuchMethodException 在指定的类以及其父类中找不到指定的方法 */ private Method getMethod(Class<?> target, String methodName, Class<?>... parameterTypes) throws NoSuchMethodException { Method method = null; try { method = target.getMethod(methodName, parameterTypes); //可能是私有方法,需要取消权限检查 method.setAccessible(true); //当前类中没有指定的方法 } catch (NoSuchMethodException e) { //如果不是根类,则递归在父类中进行查找 if (target != Object.class) { method = getMethod(target.getSuperclass(), methodName, parameterTypes); } else { throw e; } } return method; }

使用反射动态创建、操作数组

//动态创建数组 Object obj = Array.newInstance(String.class, 10); //数组元素类型、元素个数,返回值是Object类型 String[] arr= (String[]) obj; //有需要的话可以强转 //动态操作数组。传入数组的元素类型是无法预测的,所以参数是Object类型 Object first = Array.get(obj, 0); //获取数组指定位置(index)上的元素,返回Object Array.set(obj, 0, "zhangsan"); //设置数组指定位置上的元素

反射常见的应用场景

  • 开发通用框架,比如spring 从配置文件中加载配置,动态创建对象、设置属性值。
  • 实现动态代理,动态创建代理对象;对原功能进行扩展、增强。
  • 实现注解。
     

反射的优点

  • 动态操作,可以根据传入的参数或从配置文件中读取的配置,动态创建对象、设置属性,十分灵活。

反射的缺点

  • 开销大,使用频率低时性能还行,频繁使用时严重影响性能
  • 通过反射可以操作类的私有成员,破坏了类的封装性,存在安全隐患

代理(proxy)

代理是在目标类的基础上进行扩展,增加一些额外的功能、处理,代替目标类进行工作。

代理可分为2种

  • 静态代理
  • 动态代理,常见的有jdk动态代理、cglib代理。aop就是使用动态代理实现的。

静态代理

//实现目标接口或继承目标类 public class UserDaoProxy implements UserDao{ private UserDao userDao; //把要代理的接口或类写作成员变量。声明为接口,则可以代理这个接口所有的实现类;声明为实现类,则只代理这个实现类 //注入目标对象 public UserDaoProxy(UserDao userDao) { this.userDao = userDao; } @Override public void addUser() { //..... //前增强 userDao.addUser(); //调用目标类的方法,如果是继承目标类,通过super来调用 //..... //后增强 } @Override public void deleteUser() { userDao.deleteUser(); //如果不需要增强,直接调用目标类的方法即可 } } UserDao userDao = new UserDaoImpl(); UserDaoProxy userDaoProxy = new UserDaoProxy(userDao); userDaoProxy.addUser();

静态代理的特点

  • 需要实现目标接口或者继承目标类
  • 可以代理接口、类
  • 可以设置每个方法的增强,很灵活;但要手动设置每个方法的增强,又很繁琐

jdk动态代理

import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; //不需要继承、实现目标接口、目标类 public class UserDaoProxyFactory { private UserDao userDao; //jdk动态代理代理的是接口,要声明为接口 //注入目标对象 public UserDaoProxyFactory(UserDao userDao) { this.userDao = userDao; } //获取代理,代理是目标接口的实例 public UserDao getProxyInstance(){ ClassLoader classLoader = userDao.getClass().getClassLoader(); //获取目标类的类加载器。 Class<?>[] interfaces = userDao.getClass().getInterfaces(); //获取目标类实现的所有接口的class对象,这也是为什么要声明为接口的原因 InvocationHandler invocationHandler = new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //后2个参数是目标方法、实参表 //..... //前增强 Object returnValue=method.invoke(userDao,args); //调用目标方法,传入目标对象、实参表 //.... //后增强 return returnValue; //返回目标方法的返回值,Object类型 } }; //创建代理,返回值是Object类型,需要强转 Object userDaoProxy = Proxy.newProxyInstance(classLoader, interfaces, invocationHandler); //强转为目标接口类型 return (UserDao)userDaoProxy; } } UserDaoImpl userDao = new UserDaoImpl(); //传入目标对象,创建代理,代理是目标接口类型 UserDao userDaoProxy = new UserDaoProxyFactory(userDao).getProxyInstance(); userDaoProxy.addUser();

看invoke()的参数,在编译时不能确定要增强的方法,运行时根据传入的参数动态进行增强,所以叫做动态代理,静态代理则在编译时就确定了要增强的方法。

是使用jdk自带的反射机制实现的动态代理,所以叫做jdk动态代理。 

动态代理、静态代理的区别

  • 静态代理在编译时就已经确定了要增强的方法
  • 静态代理代码侵入强,代码耦合度高,编码量大;动态代理对代码侵入小,编码量较小

jdk动态代理的特点

  • 基于jdk提供的反射机制实现,无需引入第三方依赖
  • 只能代理接口
  • 不需要实现接口、继承目标类
  • 通过代理调用的目标类的方法都会被增强,且所使用的增强完全一样,偏死板

cglib动态代理

需要添加cglib的依赖

<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.3.0</version> </dependency> //需要实现MethodInterceptor接口 public class UserDaoProxyFactory implements MethodInterceptor { private UserDao userDao; //可以是接口,也可以是类 //传入目标对象 public UserDaoProxyFactory(UserDao target) { this.userDao = target; } //拦截目标方法,进行增强 @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { //..... //前增强 Object returnValue = method.invoke(userDao, objects); //调用目标对象的方法 //..... //后增强 return returnValue; } //创建代理,代理是目标接口类型 public UserDao getProxyInstance(){ Enhancer en = new Enhancer(); //创建工具类对象 en.setSuperclass(userDao.getClass()); //设置基类(父类),即继承目标类 en.setCallback(this); //设置回调函数,此句代码是调用intercept()拦截目标方法,进行增强 return (UserDao) en.create(); //创建并返回代理对象。创建的对象是Object型,需要强转 } } UserDao userDao = new UserDaoImpl(); //传入目标对象,创建代理,代理是目标类型 UserDao userDaoProxy = new UserDaoProxyFactory(userDao).getProxyInstance(); userDaoProxy.addUser();

 
cglib动态代理的特点

  • 底层使用ASM(一个短小精悍的字节码操作框架)来操作字节码,对字节码进行增强,需要引入cglib的jar包。
  • 可以代理接口、类
  • 需要实现MethodInterceptor接口
  • 通过代理调用的目标类的方法都会被增强,且所使用的增强完全一样,偏死板

使用建议

  • 如果要对不同的方法做不同的增强,用静态代理;如果对每个方法的增强都一样,用动态代理。
  • 如果要代理接口,使用三种都行;如果要代理类,用静态代理、cglib动态代理。
    你可能想看:

    扫描二维码推送至手机访问。

    版权声明:本文由皇冠云发布,如需转载请注明出处。

    本文链接:https://www.idchg.com/info/24540.html

    分享给朋友:

    “Java 反射、代理java反射动态代理” 的相关文章

    美国服务器CN2线路速度解析:如何选择稳定高速的网络体验?

    随着互联网的快速发展,越来越多的企业和个人站长选择在美国部署服务器,以满足全球用户的需求。服务器的速度和稳定性始终是用户最关心的问题。尤其是在选择美国服务器时,CN2线路因其低延迟、高稳定性的特点,成为了许多用户的首选。美国服务器CN2线路的速度到底多少才算正常?本文将为您详细解析。一、什么是美国服...

    VPS主机如何选择?灵活性与性价比并存的最佳方案

    在当今信息化高速发展的时代,VPS主机成为了许多企业和个人用户的热门选择。那么,什么是VPS主机呢?它是通过虚拟化技术在一台物理服务器上创建的多个独立服务器。每个虚拟专用服务器(VPS)都具备自己的操作系统、CPU、内存和存储空间,用户可以像管理独立服务器一样灵活配置和控制自己的VPS,真是个便利的...

    检查自己的IP地址:全面掌握网络安全与故障排查技巧

    检查自己的IP地址的重要性 我常常感受到,了解自己的IP地址是使用互联网时不可或缺的一部分。无论是日常上网还是进行复杂的网络设置,IP地址都扮演着关键角色。所以,搞清楚自己的IP地址,真的非常重要。 那么,什么是IP地址呢?简单来说,IP地址是分配给每台连接到互联网设备的唯一标识符。它就像我们的家庭...

    国内到东京快还是首尔快网络速度对比分析

    引言 在这个数字化快速发展的时代,网络速度对我们生活的影响越来越显著。很多时候,我们的工作、学习和娱乐都离不开稳定的网络连接。尤其是当我们考虑访问国外网站或进行国际交流时,网络速度的重要性更是无法忽视。今天,我想带大家探讨国内到东京和首尔的网络速度比较,看看这两个城市的网络表现究竟有何不同。 为什么...

    深入了解ICMP协议及其在网络管理中的应用

    ICMP(Internet Control Message Protocol,互联网控制消息协议)是TCP/IP协议族中的一种重要网络协议。我们可以把ICMP想象成网络中的信使,它主要负责在网络中传递控制消息和错误报告。这种功能对于维护网络的正常运作至关重要,让网络管理员能够及时发现并处理问题。IC...

    台湾VPS:选择高效、安全、性价比优越的虚拟专用服务器

    在当今互联网时代,VPS(虚拟专用服务器)成为了很多用户在选择网络服务时的热门选择。在我看来,VPS的定义可以说是一个虚拟化的服务器,用户能够以相对较低的成本获得类似于物理服务器的管理权和资源使用权。VPS通常被用于搭建网站、管理应用程序、进行数据存储等多种场景,尤其是对资源有较高要求的用户而言,它...