【Java基础】java中的反射机制与动态代理
一、java中的反射机制
java反射的官方定义:在运行状态下,可以获取任意一个类的所有属性和方法,并且可通过某类任意一对象实例调用该类的所有方法。这种动态获取类的信息及动态调用类中方法的功能称为java的反射机制。
获取一个类的Class对象是应用反射机制的前提,获取Class对象的方式有如下三种:
instance.getClass(),这个是Object类里面的方法
Type.Class属性,任何的数据类型,基本数据类型或者抽象数据类型,都可以通过这种方式获取类
- Class.forName("类的全名"),Class类提供了这样一个方法,让我们通过类名来获取到对象类
1. 通过反射机制操作某个类的属性
- package net.xsoftlab.baike;
- import java.lang.reflect.Field;
- public class TestReflect {
- private String proprety = null;
- public static void main(String[] args) throws Exception {
- Class<?> clazz = Class.forName("net.xsoftlab.baike.TestReflect");
- Object obj = clazz.newInstance();
- // 可以直接对 private 的属性赋值
- Field field = clazz.getDeclaredField("proprety");
- field.setAccessible(true);
- field.set(obj, "Java反射机制");
- System.out.println(field.get(obj));
- }
- }
2. 通过反射机制调用某个类的方法
- package net.xsoftlab.baike;
- import java.lang.reflect.Method;
- public class TestReflect {
- public static void main(String[] args) throws Exception {
- Class<?> clazz = Class.forName("net.xsoftlab.baike.TestReflect");
- // 调用TestReflect类中的reflect1方法
- Method method = clazz.getMethod("reflect1");
- method.invoke(clazz.newInstance());
- // Java 反射机制 - 调用某个类的方法1.
- // 调用TestReflect的reflect2方法
- method = clazz.getMethod("reflect2", int.class, String.class);
- method.invoke(clazz.newInstance(), 20, "张三");
- // Java 反射机制 - 调用某个类的方法2.
- // age -> 20. name -> 张三
- }
- public void reflect1() {
- System.out.println("Java 反射机制 - 调用某个类的方法1.");
- }
- public void reflect2(int age, String name) {
- System.out.println("Java 反射机制 - 调用某个类的方法2.");
- System.out.println("age -> " + age + ". name -> " + name);
- }
- }
二、动态代理
2.1 动态代理AOP
java代理模式的定义:用一个对象来控制对另一个对象的访问,称控制对象为代理对象,被控制对象为被代理对象或真实对象。通过调用代理对象的方法来间接访问被代理对象中的同名方法,要保证二者具有同名方法通常将这些同名方法抽象成接口,由二者共同实现。
动态代理和静态代理的区别:静态代理必须要为每个真实类定义一个代理类,而动态代理中无需定义代理类,由java反射包中的Proxy类动态的创建代理对象或代理类。
在java的动态代理机制中,有一个类和接口至关重要,Proxy类用于动态创建代理类或代理对象,调度处理器接口InvocationHandler。每个动态代理对象必须与实现了InvocationHandler接口的实例关联,代理对象每个方法的内部都调用了此关联实例的invoke 方法,而invoke 方法的内部又利用反射机制调用真是对象的方法,从而实现了由代理对象访问真实对象的目的。
下面是实现动态代理的经典案例:
- import java.lang.reflect.InvocationHandler;
- import java.lang.reflect.Method;
- import java.lang.reflect.Proxy;
- //定义真实类和代理类共同的接口
- interface Subject {
- public void rent();
- public void hello(String str);
- }
- //定义真实类(被代理类
- class RealSubject implements Subject {
- @Override
- public void rent() {
- System.out.println("I want to rent my house");
- }
- @Override
- public void hello(String str) {
- System.out.println("hello: " + str);
- }
- }
- //定义继承了InvocationHandler接口的调度处理器
- public class MyInvocationHandler implements InvocationHandler {
- //要代理的真实对象
- private Object realObj;
- // 构造方法给要代理的真实对象赋值
- public MyInvocationHandler(Object realObj) {
- this.realObj = realObj;
- }
- /**
- * 执行动态代理对象的所有方法都会被替换为执行如下的invoke方法
- * 其中:
- * autoProxy:动态代理对象
- * method:代表正在执行的方法
- * args:由动态代理对象调用方法时传入的实参
- */
- @Override
- public Object invoke(Object autoProxy, Method method, Object[] args) throws Throwable {
- //在代理真实对象前我们可以添加一些自己的操作
- System.out.println("before rent invoke");
- //当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
- System.out.println("Method:" + method);
- method.invoke(realObj, args);
- //在代理真实对象后我们也可以添加一些自己的操作
- System.out.println("after rent invoke");
- return null;
- }
- }
- class Client {
- public static void main(String[] args) {
- //要代理的真实对象
- Subject realObj = new RealSubject();
- //要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法的
- InvocationHandler MyHandler = new MyInvocationHandler(realObj);
- /*通过Proxy类的newProxyInstance方法动态创建代理对象
- *public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
- * 它的三个参数
- * 第一个参数 指定动态代理类的类加载器
- * 第二个参数 指定动态代理类需实现的接口集合
- * 第三个参数 指定与该动态代理对象所关联的调度处理器实例
- */
- Subject autoProxy = (Subject) Proxy.newProxyInstance(MyHandler.getClass().getClassLoader(), realObj.getClass().getInterfaces(), MyHandler);
- System.out.println(autoProxy.getClass().getName());
- autoProxy.rent();
- autoProxy.hello("world");
- }
- }
输出如下:
- net.xsoftlab.baike.$Proxy0
- before rent invoke
- Method:public abstract void net.xsoftlab.baike.Subject.rent()
- I want to rent my house
- after rent invoke
- before rent invoke
- Method:public abstract void net.xsoftlab.baike.Subject.hello(java.lang.String)
- hello: world
- after rent invoke
总结:1.动态代理中无需定义代理类,代理对象是由Proxy类动态产生的。
2.代理对象对方法的访问都将转换成调度处理器对其invoke 方法的访问。
3.调度处理器的invoke 方法内部利用反射机制去调用真是对象的同名方法。
4.像上面这种动态代理在Spring中被称为AOP(Aspect Orient Programming,面向切面编程),AOP代理可替代目标对象,AOP代理包含了目标对象的全部方法。
2.2 AOP的应用
上面介绍了动态代理,接下来说下它的应用。在开发中如果要复用同一功能模块,可以通过复制该功能模块的代码来实现,但在开发中一般不会这样做,通常将该功能模块封装成一个方法,在需要的地方直接调用就可以了。
通过将公共模块封装成方法来调用虽然提高了复用减少了代码的冗余,但却提高了这三个代码段与方法的耦合。最理想的效果是:代码块一,代码块二,代码块三既可以执行蓝色区域的代码部分,又无需以硬编码的方式来直接调用蓝色区域的方法。这时就可以通过动态代理来实现这个功能。
接下来使用Proxy和InvocationHandler来实现当程序调用info()或run()方法时,系统可以“自动”将method1()和method2()两个方法插入info()和run()方法中执行。
首先提供一个Dog接口,在该接口中提供两个方法:
- import java.lang.reflect.InvocationHandler;
- import java.lang.reflect.Method;
- import java.lang.reflect.Proxy;
- interface Dog {
- //info()方法声明
- void info();
- //run()方法
- void run();
- }
- class GunDog implements Dog {
- //实现info方法,仅仅打印一个字符串
- @Override
- public void info() {
- System.out.println("我是一只猎狗");
- }
- //实现run()方法,仅仅打印一个字符串
- @Override
- public void run() {
- System.out.println("迅速奔跑");
- }
- }
- class DogUtil {
- public static void method1() {
- System.out.println("=====模拟第一个通用方法=====");
- }
- public static void method2() {
- System.out.println("=====模拟第二个通用方法=====");
- }
- }
- class MyInvocationHandler implements InvocationHandler {
- private Object target;
- public void setTarget(Object target) {
- this.target = target;
- }
- @Override
- public Object invoke(Object proxy, Method method, Object[] args)
- throws Throwable {
- //执行DogUtil对象中method1方法
- DogUtil.method1();
- //以target作为主调执行method方法
- Object result = method.invoke(target, args);
- //执行DogUtil对象中的method2方法
- DogUtil.method2();
- return result;
- }
- }
- class MyProxyFactory {
- public static Object getProxy(Object target) {
- MyInvocationHandler myInvocationHandler = new MyInvocationHandler();
- //设置需要代理的对象
- myInvocationHandler.setTarget(target);
- //创建并返回动态代理
- return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), myInvocationHandler);//使用指定的类加载器,需要实现的接口,自定义Handler
- }
- }
- public class TestAOP {
- public static void main(String[] args) {
- Dog dog = (Dog) MyProxyFactory.getProxy(new GunDog());
- dog.info();
- dog.run();
- }
- }
输出如下:
- =====模拟第一个通用方法=====
- 我是一只猎狗
- =====模拟第二个通用方法=====
- =====模拟第一个通用方法=====
- 迅速奔跑
- =====模拟第二个通用方法=====
下面这张图片对上面的程序流程进行了简单解释:
本篇博文参考了:【java】解析java类加载与反射机制 java的动态代理机制详解
【Java基础】java中的反射机制与动态代理的更多相关文章
- Java中的反射机制和动态代理
一.反射概述 反射机制指的是Java在运行时候有一种自观的能力,能够了解自身的情况为下一步做准备,其想表达的意思就是:在运行状态中,对于任意一个类,都能够获取到这个类的所有属性和方法:对于任意一个对象 ...
- Java反射机制以及动态代理
Java反射机制以及动态代理 Java反射机制 含义与功能 Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类 ...
- java反射机制与动态代理
在学习HadoopRPC时.用到了函数调用.函数调用都是採用的java的反射机制和动态代理来实现的,所以如今回想下java的反射和动态代理的相关知识. 一.反射 JAVA反射机制定义: JAVA反射机 ...
- Java基础系列 - 泛型和反射机制
package com.test5; import java.lang.reflect.Field; import java.lang.reflect.Method; /** * Java泛型和反射机 ...
- java.lang.Class<T> -- 反射机制及动态代理
Interface : Person package java_.lang_.component.bean; public interface Person { String area = " ...
- Java的反射机制和动态代理
介绍Java注解的时候,多次提到了Java的反射API.与javax.lang.model不同的是,通过反射API可以获取程序在运行时刻的内部结构.反射API中提供的动态代理也是非常强大的功能,可以原 ...
- java学习笔记13--反射机制与动态代理
本文地址:http://www.cnblogs.com/archimedes/p/java-study-note13.html,转载请注明源地址. Java的反射机制 在Java运行时环境中,对于任意 ...
- Spring AOP中的JDK和CGLib动态代理哪个效率更高?
一.背景 今天有小伙伴面试的时候被问到:Spring AOP中JDK 和 CGLib动态代理哪个效率更高? 二.基本概念 首先,我们知道Spring AOP的底层实现有两种方式:一种是JDK动态代理, ...
- Spring AOP动态代理实现,解决Spring Boot中无法正常启用JDK动态代理的问题
Spring AOP底层的动态代理实现有两种方式:一种是JDK动态代理,另一种是CGLib动态代理. JDK动态代理 JDK 1.3版本以后提供了动态代理,允许开发者在运行期创建接口的代理实例,而且只 ...
随机推荐
- C#:索引
1. 什么是索引 索引是一组get和set访问器,类似于属性的访问器. 2. 索引和属性 和属性一样,索引不用分配内存来存储 索引和属性都主要被用来访问其他数据成员,这些成员和它们关联,它们为这些成员 ...
- PAT甲级——1123 Is It a Complete AVL Tree (完全AVL树的判断)
嫌排版乱的话可以移步我的CSDN:https://blog.csdn.net/weixin_44385565/article/details/89390802 An AVL tree is a sel ...
- [题解]luogu_P2613有理数取余
#include<bits/stdc++.h> #define ll long long using namespace std; ; inline int read(){ ,fix=;c ...
- 洛谷P3603 || bzoj 4763 雪辉 && bzoj4812: [Ynoi2017]由乃打扑克
https://www.luogu.org/problemnew/show/P3603 https://www.lydsy.com/JudgeOnline/problem.php?id=4763 就是 ...
- ueditor单独调用图片上传
很多人在问ueditor,如何单独使用图片上传功能,但是网上没有一篇能用的文档,没办法,我刚好也需要这个功能,花了3天时间(本人水平太菜,哎)终于知道怎么处理了,发出来给大家共享: 效果如下: 页面效 ...
- 对shell的简单认识
shell是一个命令解释器: shell分为交互式shell和非交互式shell: 交互式shell就是命令行一问一答:非交互式shell是像shell文本那样,一次解析文本, 并未在命令行给我们作出 ...
- storm trident的filter和函数
目的:通过kafka输出的信息进行过滤,添加指定的字段后,进行打印 SentenceSpout: package Trident; import java.util.HashMap; import j ...
- mongodb备份、还原
1.mongodb 备份 mongodump 命令可以使用可用的选项的列表. 此命令将仅备份指定的数据库,在指定的路径 语法 描述 示例 mongodump --host HOST_NAME --p ...
- git的基本使用命令操作
Linux操作命令行: mkdir - 创建文件夹, cd - 切换文件路径 pwd - 显示文件路径 ls -ah - 可以查看隐藏的文件夹名(.git) cat 文件 ...
- MySQL常用命令和语句
1.常用SQL语句 1)常用函数/*type可取值为:MICROSECONDSECONDMINUTEHOURDAYWEEKMONTHQUARTERYEARSECOND_MICROSECONDMINUT ...