【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版本以后提供了动态代理,允许开发者在运行期创建接口的代理实例,而且只 ...
随机推荐
- 返回零长度的数组或集合,而不是null
返回零长度的数组或集合,而不是null 像下面的方法并不少见: private final List<Cheese> cheesesInStock = ...; /** * @retu ...
- H - F(x)
#include <iostream> #include <algorithm> #include <cstring> #include <cstdio> ...
- idea | SpringBoot项目热加载
第一步: 在pom文件添加依赖: <dependency> <groupId>org.springframework.boot</groupId> <arti ...
- 洛谷P4095||bzoj3163 [HEOI2013]Eden 的新背包问题
https://www.luogu.org/problemnew/show/P4095 不太会.. 网上有神奇的做法: 第一种其实是暴力(复杂度3e8...)然而可以A.考虑多重背包,发现没有办法快速 ...
- 117 Populating Next Right Pointers in Each Node II 每个节点的右向指针 II
这是“每个节点的右向指针”问题的进阶.如果给定的树可以是任何二叉树,该怎么办?你以前的解决方案仍然有效吗?注意: 你只能使用恒定的空间.例如,给定以下二叉树, 1 / ...
- html 手机端click 事件延迟问题(fastclick.js使用方法)
下载地址: fastclick.js 为什么存在延迟? 从点击屏幕上的元素到触发元素的 click 事件,移动浏览器会有大约 300 毫秒的等待时间.为什么这么设计呢? 因为它想看看你是不是要进行双击 ...
- display flex在部分低级android中的支付宝窗口表现
display flex用在移动端布局 当该元素是inline元素如span的时候回出现无宽高的情况,需要增加display:block: 他的子元素如果是inline元素那么也同样会出现这个问题,需 ...
- mysql实现计数器
本文转自:https://blog.csdn.net/stevendbaguo/article/details/70889449 如果是在非常高的并发之下,还是建议用内存数据库redis去实现计数的功 ...
- 浅析 var that = this;
在阅读别人的代码时,发现别人写的代码中有这么一句:var that = this;,这代表什么意思呢?经过一番查阅,才明白是这么回事. 在JavaScript中,this代表的是当前对象. var t ...
- Apache Kafka框架学习
背景介绍 消息队列的比较 kafka框架介绍 术语解释 文件存储 可靠性保证 高吞吐量实现 负载均衡 应用场景 背景介绍: kafka是由Apache软件基金会维护的一个开源流处理平台,由scala和 ...