动态代理
特点:
无侵入式的给代码增加额外的功能。
interface Star
package proxy;
/**
* 在接口中 定义 想要 被代理 的方法
*/
public interface Star {
//唱歌
public abstract String sing(String name);
//跳舞
public abstract void song();
}
class BigStar
package proxy;
/**
* JavaBean 要实现 代理的 接口
*/
public class BigStar implements Star{
private String name;
public BigStar(String name) {
this.name = name;
}
//唱歌
@Override
public String sing(String name){
System.out.println(this.name + "正在唱" + name);
return "谢谢";
}
//跳舞
@Override
public void song(){
System.out.println(this.name + "正在跳舞");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class ProxyUtil
package proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 创建一个 代理
*/
public class ProxyUtil {
/**
* 方法作用:给一个明星对象, 创建一个代理
* @param bigStar 被代理的明星对象 JavaBean
* @return 给 JavaBean 创建的代理
*/
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 {
/*
* Object proxy 参数1:代理的对象 (一般用不到)
* Method method 参数2:要运行的方法
* Object[] args 参数3:调用方法时,传递的实参
*/
if ("sing".equals(method.getName())) {
System.out.println("准备话筒");
} else if ("song".equals(method.getName())) {
System.out.println("准备场地");
}
//代码形式:调用需要 被代理的对象(JavaBean) 的方法
return method.invoke(bigStar, args);
}
}
);
return star;
}
}
class Test
package proxy;
public class Test {
public static void main(String[] args) {
/**
* 1. 获取代理的对象
* 2. 调用代理的(唱歌/跳舞)方法
*/
//获取代理的对象
BigStar bigStar = new BigStar("G.E.M");
Star proxy = ProxyUtil.createProxy(bigStar);
//调用代理的(唱歌)方法
String singName = proxy.sing("倒数");
System.out.println(singName);
//调用代理的(跳舞)方法
proxy.song();
}
}
运行结果
准备话筒
G.E.M正在唱倒数
谢谢
准备场地
G.E.M正在跳舞
提问:
- Java提供了什么
API
帮我们创建代理? newProxyInstance
方法在创建代理时,需要接收几个参数,每个参数的含义是是什么?- 通过
invokehandler
的invoke
方法指定代理干的事,这个invoke
会被谁调用?要接收那几个参数?
反射
反射允许对成员变量,成员方法和构造方法的信息进行编程访问
- 获取
class
对象的三种方式:
package reflection.demo1;
public class reflectDemo1 {
public static void main(String[] args) throws ClassNotFoundException {
/*
* 获取class对象的三种方式:
* 1. Class.forName("全类名"),即相对路径
* 2. 类名.class
* 3. 对象.getClass()
*/
//1. 最为常用
Class clazz1 = Class.forName("reflection.demo1.Student");
//2. 更多的是当作参数进行传递
Class clazz2 = Student.class;
//3. 前提条件,需要有对象
Student student = new Student("张三", 18);
Class clazz3 = student.getClass();
System.out.println(clazz1 == clazz2);
System.out.println(clazz2 == clazz3);
}
}
- 利用反射获取构造方法
JavaBean
中的某个私有构造方法:
public class Student {
private String name;
private int age;
public Student(){
}
public Student(String name) {
this.name = name;
}
protected Student(int age){
this.age = age;
}
private Student(String name, int age){
this.name = name;
this.age = age;
}
}
类reflectDemo2
测试:
package reflection.demo2;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class reflectDemo2 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//1. 获取 class 字节码文件对象
Class clazz = Class.forName("reflection.demo2.Student");
//2. 获取构造方法,我们以Constructor类中用于创建对象的方法 讲解
Constructor con1 = clazz.getDeclaredConstructor(String.class, int.class);
// System.out.println(con1);
// int modifiers = con1.getModifiers();
// System.out.println("con1对应的构造方法的修饰属性是:" + modifiers); // 2
//暴力反射:临时取消权限校验
con1.setAccessible(true);
//使用Constructor类用于创建对象
Student stu = (Student) con1.newInstance("张三", 18);
System.out.println(stu);
}
}
- 利用反射获取成员变量
JavaBean
中的成员变量:
public class Student {
private String name;
private int age;
public String gender;
}
package reflection.demo3;
import java.lang.reflect.Field;
public class reflectDemo3 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
//获取 class 字节码文件对象
Class clazz = Class.forName("reflection.demo3.Student");
//获取 成员变量(选择单个私有成员变量讲解)
Field name = clazz.getDeclaredField("name");
System.out.println(name); // private java.lang.String reflection.demo3.Student.name
//获取成员变量的 数据类型
Class<?> type = name.getType();
System.out.println(type); // class java.lang.String
//获取成员变量记录的 值
Student student = new Student("张三", 18, "男");
//因为 name 属性是私有的,所以需要取消临时校验
name.setAccessible(true);
Object nameValue = name.get(student);
System.out.println(nameValue); // 张三
//修改对象里面记录的 值
name.set(student, "李四");
System.out.println(student); // Student{name='李四', age=18, gender='男'}
}
}
- 利用反射获取成员方法:
JavaBean
中的成员方法:
public class Student {
public void sleep(){
System.out.println("睡觉");
}
private void eat(String something) {
System.out.println("在吃" + something);
}
}
类reflectDemo4
中的测试:
package reflection.demo4;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class reflectDemo4 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
//获取 class 字节码 文件对象
Class clazz = Class.forName("reflection.demo4.Student");
//获取 成员方法
//1. 获取所有的公共方法对象 (包含父类中的..)
// Method[] methods = clazz.getMethods();
// for (Method method : methods) {
// System.out.println(method);
// }
//2. 获取所有的成员方法对象(不能获取父类的..)
// Method[] declaredMethods = clazz.getDeclaredMethods();
// for (Method declaredMethod : declaredMethods) {
// System.out.println(declaredMethod);
// }
//方法运行(获取私有的单个成员方法作为讲解)
Method eatMethod = clazz.getDeclaredMethod("eat", String.class);
Student student = new Student();
eatMethod.setAccessible(true);
/**
* 参数一:表示方法的调用者(即,JavaBean)
* 参数二:表示在调用方法的时候传递的实际参数(即,JavaBean中,方法需要的实际参数)
*/
eatMethod.invoke(student, "汉堡包");
}
}
- 练习
- 对于任意一个对象,都可以把对象所有的字段名和值,保存到文件中去(此时需要用到运行阶段的方式获取反射)。
假设有两个JavaBean对象:Teacher
和Student
类reflectDemo5
中的测试:
package reflection.demo5;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Field;
public class reflectDemo5 {
public static void main(String[] args) throws IllegalAccessException, IOException {
Student student = new Student("张三", 23, "男", "睡觉");
Teacher teacher = new Teacher("李四", "1000");
saveObject(teacher);
}
//把对象里面所有的 成员变量名 和 值 保存到本地文件中
private static void saveObject(Object object) throws IllegalAccessException, IOException {
//1. 获取字节码文件的对象
Class clazz = object.getClass();
//2. 创建IO流 (一定要注意路径名:项目的相对路径)
BufferedWriter bw = new BufferedWriter(new FileWriter("proxy\\src\\main\\java\\reflection\\demo5\\a.txt"));
//3. 获取所有成员变量
Field[] declaredFields = clazz.getDeclaredFields();
for (Field declaredField : declaredFields) {
declaredField.setAccessible(true);
//获取成员变量的名字
String fieldName = declaredField.getName();
//获取成员变量的值
Object value = declaredField.get(object);
bw.write(fieldName + "=" + value);
bw.newLine();
}
bw.close();
}
}
- 反射可以跟配置文件结合的方式,动态的创建对象,并调用方法。(优点:在测试时,不需要改测试代码,直接修改配置文件的信息,即可动态测试不同的对象所对应的方法)
prop.properties
:
classname=reflection.demo6.Teacher
method=teach
两个JavaBean对象:Student 和 Teacher
public class Student {
public Student() {
}
public void study() {
System.out.println("学生正在学习");
}
}
public class Teacher {
public Teacher() {
}
public void teach() {
System.out.println("老师正在教书");
}
}
类reflectDemo6
中的测试:
package reflection.demo6;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;
/**
* 测试:使用配置文件
*/
public class reflectDemo6 {
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//1. 读取配置文件中的信息,Properties类读取配置文件时需要 输入流
Properties pro = new Properties();
FileInputStream fis = new FileInputStream("proxy\\prop.properties");
pro.load(fis);
fis.close();
// System.out.println(pro); // {classname=reflection.demo6.Student, method=study}
//2. 获取全类名和方法名
String className = (String) pro.get("classname");
String methodName = (String) pro.get("method");
//3. 利用 反射 创建对象 并 运行对象
Class clazz = Class.forName(className);
// 获取构造方法(用来实例化对象)
Constructor con = clazz.getDeclaredConstructor();
Object object = con.newInstance();
// 获取成员方法并运行
Method method = clazz.getDeclaredMethod(methodName);
method.invoke(object);
}
}