本篇文章整理的内容来源于: 反射原理
使用动态代理能够无侵入式的给方法增强功能.
所谓无侵入式,指的是在原有代码的基础上,不需要修改原始类的源代码,就可以通过代理来增强其功能。通过动态代理,我们可以在代理对象的方法执行前后插入额外的逻辑,例如日志记录、性能监测、事务管理等。这样可以在不修改原始类的情况下,通过代理对象对方法进行增强,达到扩展功能的目的。
动态代理利用了反射机制,在运行时动态地生成代理类和代理对象,从而实现对原始对象的包装。通过调用代理对象的方法,实际上是调用了被代理对象的方法,并在方法执行前后执行额外的逻辑。
如何为Java对象创建一个代理对象呢?
我们可以使用java.lang.reflect.Proxy
类:提供了为对象产生代理对象的方法
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
参数一:用于指定用哪个类加载器,去加载生成的代理类
参数二:指定接口,这些接口用于指定生成的代理长什么,也就是有哪些方法
参数三:用来指定生成的代理对象要干什么事情
package com.xxxflower.demo1;
public class BigStar implements Star {
private String name;
public BigStar() {
}
public BigStar(String name) {
this.name = name;
}
//唱歌
@Override
public String sing(String name){
System.out.println(this.name + "正在唱" + name);
return "谢谢";
}
//跳舞
@Override
public void dance(){
System.out.println(this.name + "正在跳舞");
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
public String toString() {
return "BigStar{name = " + name + "}";
}
}
package com.xxxflower.demo1;
public interface Star {
//我们可以把所有想要被代理的方法定义在接口当中
//唱歌
public abstract String sing(String name);
//跳舞
public abstract void dance();
}
package com.xxxflower.demo1;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/*
*
* 类的作用:
* 创建一个代理
*
* */
public class ProxyUtil {
/*
*
* 方法的作用:
* 给一个明星的对象,创建一个代理
*
* 形参:
* 被代理的明星对象
*
* 返回值:
* 给明星创建的代理
*
*
*
* 需求:
* 外面的人想要大明星唱一首歌
* 1. 获取代理的对象
* 代理对象 = ProxyUtil.createProxy(大明星的对象);
* 2. 再调用代理的唱歌方法
* 代理对象.唱歌的方法("只因你太美");
* */
public static Star createProxy(BigStar bigStar){
Star star = (Star) Proxy.newProxyInstance(
ProxyUtil.class.getClassLoader(),//参数一:用于指定用哪个类加载器,去加载生成的代理类
new Class[]{Star.class},//参数二:指定接口,这些接口用于指定生成的代理长什么,也就是有哪些方法
//参数三:用来指定生成的代理对象要干什么事情
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/*
* 参数一:代理的对象
* 参数二:要运行的方法 sing
* 参数三:调用sing方法时,传递的实参
* */
if("sing".equals(method.getName())){
System.out.println("准备话筒,收钱");
}else if("dance".equals(method.getName())){
System.out.println("准备场地,收钱");
}
//去找大明星开始唱歌或者跳舞
//代码的表现形式:调用大明星里面唱歌或者跳舞的方法
return method.invoke(bigStar,args);
}
}
);
return star;
}
}
package com.xxxflower.demo1;
public class Test {
public static void main(String[] args) {
/*
需求:
外面的人想要大明星唱一首歌
1. 获取代理的对象
代理对象 = ProxyUtil.createProxy(大明星的对象);
2. 再调用代理的唱歌方法
代理对象.唱歌的方法("只因你太美");
*/
//1. 获取代理的对象
BigStar bigStar = new BigStar("鸡哥");
Star proxy = ProxyUtil.createProxy(bigStar);
//2. 调用唱歌的方法
String result = proxy.sing("只因你太美");
System.out.println(result);
}
}
程序执行结果:
Java中的反射(Reflection),是指在运行时动态地获取类的信息,以及动态地调用对象的方法和属性。简而言之,就是程序在运行时可以动态地获取类的信息并对其进行操作。
Java反射机制允许程序在运行时检查和操作类、接口、字段、方法以及构造方法等元素,例如:
反射机制为Java编程提供了更大的灵活性和扩展性,尤其在框架设计、动态代理、JavaBean操作等方面得到广泛应用。但是由于反射会牺牲一定的性能,因此在性能要求较高的场景下,应该谨慎使用反射。
反射都是从class字节码文件中获取的内容。因此如果我们想使用反射,就必须首先获取class字节码文件的对象.
获取字节码文件对象的三种方式如下
我们可以通过Class这个类里面的静态方法forName(“全类名”)来获取.示例:
注意: 全类名 = 包名 + 类名
Class clazz1 = Class.forName("com.xxxflower.reflectdemo.Student");
源代码阶段获取 — > 先把Student加载到内存中,再获取字节码文件的对象
clazz
就表示Student
这个类的字节码文件对象。即Student.class
这个文件加载到内存之后,产生的字节码文件对象.
通过 类名.class 示例如下:
Class clazz2 = Student.class;
class文件在硬盘中是唯一的.
所以,当这个文件加载到内存之后产生的对象也是唯一的.
示例:
Student s = new Student();
Class clazz3 = s.getClass();
此处注意几个概念:
字节码文件
:通过.java
文件编译之后的.class
文件(真实存在的)
字节码文件对象
:当.class
文件加载到内存之后,虚拟机自动创建出来的对象.这个对象里面至少包含了:构造方法,成员变量,成员方法。
我们反射所获取的就是字节码文件对象
.
方法名 | 说明 |
---|---|
Constructor<?>[] getConstructors() | 获得所有的构造(只能public修饰) |
Constructor<?>[] getDeclaredConstructors() | 获得所有的构造(包含private修饰) |
Constructor getConstructor(Class<?>… parameterTypes) | 获取指定构造(只能public修饰) |
Constructor getDeclaredConstructor(Class<?>… parameterTypes) | 获取指定构造(包含private修饰) |
代码示例:
public class ReflectDemo2 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
//1.获得整体(class字节码文件对象)
Class clazz = Class.forName("com.itheima.reflectdemo.Student");
//2.获取构造方法对象
//获取所有构造方法(public)
Constructor[] constructors1 = clazz.getConstructors();
for (Constructor constructor : constructors1) {
System.out.println(constructor);
}
System.out.println("=======================");
//获取所有构造(带私有的)
Constructor[] constructors2 = clazz.getDeclaredConstructors();
for (Constructor constructor : constructors2) {
System.out.println(constructor);
}
System.out.println("=======================");
//获取指定的空参构造
Constructor con1 = clazz.getConstructor();
System.out.println(con1);
Constructor con2 = clazz.getConstructor(String.class,int.class);
System.out.println(con2);
System.out.println("=======================");
//获取指定的构造(所有构造都可以获取到,包括public包括private)
Constructor con3 = clazz.getDeclaredConstructor();
System.out.println(con3);
//了解 System.out.println(con3 == con1);
//每一次获取构造方法对象的时候,都会新new一个。
Constructor con4 = clazz.getDeclaredConstructor(String.class);
System.out.println(con4);
}
}
示例:
//1.获取整体的字节码文件对象
Class clazz = Class.forName("com.itheima.a02reflectdemo1.Student");
//2.获取空参的构造方法
Constructor con = clazz.getConstructor();
//3.利用空参构造方法创建对象
Student stu = (Student) con.newInstance();
System.out.println(stu);
示例:
//1.获取整体的字节码文件对象
Class clazz = Class.forName("com.itheima.a02reflectdemo1.Student");
//2.获取有参构造方法
Constructor con = clazz.getDeclaredConstructor(String.class, int.class);
//3.临时修改构造方法的访问权限(暴力反射)
con.setAccessible(true);
//4.直接创建对象
Student stu = (Student) con.newInstance("zhangsan", 23);
System.out.println(stu);
方法名 | 说明 |
---|---|
Field[] getFields() | 返回所有成员变量对象的数组(只能拿public的) |
Field[] getDeclaredFields() | 返回所有成员变量对象的数组,存在就能拿到 |
Field getField(String name) | 返回单个成员变量对象(只能拿public的) |
Field getDeclaredField(String name) | 返回单个成员变量对象,存在就能拿到 |
方法名 | 说明 |
---|---|
Method[] getMethods() | 返回所有成员方法对象的数组(只能拿public的) |
Method[] getDeclaredMethods() | 返回所有成员方法对象的数组,存在就能拿到 |
Method getMethod(String name, Class<?>… parameterTypes) | 返回单个成员方法对象(只能拿public的) |
Method getDeclaredMethod(String name, Class<?>… parameterTypes) | 返回单个成员方法对象,存在就能拿到 |