文章目录
动态代理
直观的讲:
比如需要增加功能,进入源代码进行修改叫:侵入式修改,可能会导致原有的代码无法运行
在不修改原有的代码并添加新的功能,就需要寻找一个代理来执行添加的新功能:无侵入式修改代码,添加新的功能
最终,调用者需要调用对象中的方法时就要先去调用代理中的方法,代理再去调用对象
代理
- 一类对象执行一类方法,对象可以通过代理转移自身的部分职责
- 对象有什么方法想要被代理,代理就需要有相对的方法
- 需要代理的对象得提供相对应的接口,接口内带有需要被代理的方法,对象和代理需要实现同一个接口
创建一个代理对象
java.lanf.reflect.Proxy
类:提供了为代理产生对象的方法
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
// 参数1:用于指定用那个类加载器,来加载生成的代理类
// 参数2:指定接口,这些接口用于指定生成的代理存在那些方法
// 参数3:用于指定生成的代理需要干那些事情
类加载器:把当前类加载到内存的
动态代理的例子
-
创建一个需要被代理的类:一个大明星只需要唱歌跳舞,场地准备和收取门票的事情交给负责代理的经纪公司
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("正在唱歌:"+name); return "唱完了"; } @Override public void dance() { System.out.println("正在跳舞"); } public String getName() { return name; } public void setName(String name) { this.name = name; } }
-
负责代理的经纪公司需要派出一名经纪人,这个经纪人要求一样会唱歌跳舞,所以需要一个有着唱歌跳舞方法的接口,这个接口是代理对象制作代理方法的依据
public interface Star { public String sing(String name); public void dance(); }
-
代理公司给出了一个代理对象,对象内有一个静态方法,从此用户需要通过先调用代理对象的代理方法才能运行有新功能的 大明星对象
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * 类的作用: * 创建一个代理 */ public class ProxyUtil { // 方法的作用 给BigStar对象创建一个代理 /** * * 形参: 被代理的对象 * @return 方法的返回值是代理的对象,返回接口即可 */ // 1. 方法是获取代理的对象的,用户想要获取BigStar对象就要ProxyUtil.createProxy(BigStar),返回的值就是代理的对象:代理对象 = ProxyUtil.createProxy(BigStar); // 2. 再用代理的对象调用BigStar里的sing方法:代理对象.sing(“name"); public static Star createProxy(BigStar bigStar) { Star star = (Star) Proxy.newProxyInstance // 参数1:用于指定用那个类加载器,来加载生成的代理类 :当前的类 (ProxyUtil.class.getClassLoader(), // 参数2:指定接口,这些接口用于指定生成的代理存在那些方法 new Class[]{Star.class}, // 参数3:用于指定生成的代理需要干那些事情 //InvocationHandler 是一个接口,再形参中要写他的实现类(匿名内部类) new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 要添加的新的功能写再这里 /** invoke方法的三个参数的作用分别是 proxy: 被代理的对象 method: 要运行的方法 args: 调用要运行的方法时传递的实参 */ // 反射获取到的方法名是字符串,做一个判断,如果调用的sing方法 if ("sing".equals(method.getName())) { System.out.println("这里是新增的方法1:准备话筒,收取门票"); }else if ("dance".equals(method.getName())) { System.out.println("这里是新增的方法1:准备话筒,收取门票"); } // 返回被代理对象BigDance的方法的输出结构 return method.invoke(bigStar,args); } } ); return star; } }
-
用户调取 对象
public class Text { public static void main(String[] args) { BigStar bigStar = new BigStar("cxk"); Star proxy = ProxyUtil.createProxy(bigStar); proxy.sing("及你太忙"); proxy.dance(); } }
#反射
反射允许对封装类的字段(成员变量 field)、方法(成员方法 method)和构造函数(构造方法 constructor)的信息进行编程访问
获取class 字节码对象
获取class对象一共有三种方法
1. Class.forName("全类名"); // 全类名:包名.类名 // 常用
2. 类名.class; // 多作为参数传递
3. 对象.getClass(); // 当已经创建了该类的对象时 使用
###获取构造方法Constructor
Class类中用于获取构造方法 的方法
Constructor<>[] getConstructors(); 返回所有 公共构造方法 的对象的数组 只能获取public
Constructor<>[] getDeclaredConstructors(); 返回所有 构造方法 对象的数组
Constructor<T> getConstructor(Class<?>...parameterTypes); 返回单个公共构造方法对象 只能获取public
Constructor<T> getDeclaredConstructor(Class<?>...parameterTypes); 返回单个构造方法对象
// 参数:和需要获取的构造方法的参数保持一致
比如我需要获取的类里面有构造方法
public student(String name);
那获取就需要
Class clazz = Class.forName("student");
Constructor con = clazz.getDeclaredConstructor(String.class);
Constructor 类中用于创建对象的方法
T newInstance(Object... initargs): 根据指定的构造方法创建对象
setAccessible(boolean flag): 设置为true,表示取消访问检查(取消权限)
获取了构造方法后
获取权限修饰符
private student(String name);
///
Class clazz = Class.forName("student");
Constructor con = clazz.getDeclaredConstructor(String.class);
// 获取权限修饰符
int modifiers = con4.getModifi
获取名字
就是类名
获取形参
private student(String name);
///
Class clazz = Class.forName("student");
Constructor con = clazz.getDeclaredConstructor(String.class);
// 获取构造方法的参数(一般用在私有身上)
Parameter[] parameters = con.getParameters();
创建对象
private student(String name);
///
Class clazz = Class.forName("student");
Constructor con = clazz.getDeclaredConstructor(String.class);
// 利用构造方法创建对象(创建共有私有都可以用)
//创建私有要有额外步骤
con.setAccessible(true); // 暴力反射:临时取消权限检验
student student =(student)con.newInstance("zhangsan") // 传参要和构造方法一致
获取字段(成员变量)
Class类中用于获取成员变量的方法
Field[] getFields(): 返回所有公共成员变量对象的数组
Field[] getDeclaredFields(): 返回所有成员变量对象的数组
Field getField(String name): 返回单个公共成员变量对象
Field getDeclaredField(String name):返回单个成员变量对象
Field 类中用于创建对象的方法
void set(Object obj,Object value):赋值
Object get(Object obj):获取值
获取名字
// 获取成员变量
class student {public String name; public int age; }
///
Class clazz = Class.forName("student");
// 获取全部
Field[] fields = clazz.getDeclaredFields();
// 获取单一的
Field field = clazz.getDeclaredField(age);
获取权限修饰符
class student {public String name; public int age; }
///
Class clazz = Class.forName("student");
Field field = clazz.getDeclaredField(age);
// 获取权限修饰符
int modifiers = field.getModifiers();
获取数据类型
class student {public String name; public int age; }
///
Class clazz = Class.forName("student");
Field field = clazz.getDeclaredField(age);
// 获取数据类型
Class<?> type = field.getType();
成员变量的值
获取值和对象有关(已经被创建并赋值了)
class student {public String name; public int age; } // 有构造方法
student s = new student("张三",19);
//
Class clazz = Class.forName("student");
Field field = clazz.getDeclaredField(age);
// 如果变量是私有的,则需要
field.setAccessible(true);
int value = (int)field.get(s); // 此处抛出一个异常
修改对象的值
class student {public String name; public int age; } // 有构造方法
student s = new student("张三",19);
//
Class clazz = Class.forName("student");
Field field = clazz.getDeclaredField(age);
// 修改对象的值
field.set(s,23); // 传入对象和要修改的值,这里19--> 23
获取成员方法
Class类中用于获取成员方法的方法
Method[] getMethods();返回所有公共成员方法对象的数组
Method[] getDeclaredMethods(); 返回所有成员方法对象的数组
Method getMethod(String name,Class<?>...parameterTypes);返回单个公共成员方法的对象
Method getDeclaredMethod(String name,Class<?>...parameterTypes);返回单个成员方法的对象
// 参数1:方法的名字
// 参数2:方法的参数的类型(方法重载)
Method类中用于创建对象的方法
Object invoke(Object obj,Object...args):运行方法
/*
参数1: 用obj对象调用该方法
参数2: 调用方法的传递参数(如果没有就不写)
返回值: 方法的返回值(没有则返回null)
*/
获取方法
class student{
// get、set、tostring方法都有
.....
public void sleep() {
System.out.println("睡觉");
}
public void eat (String something) {
System.out.println("在吃"+ something);
}
.....
}
Class clazz = Class.forName("student");
// 获取所有方法(getMethods 会获取到父类的所有public方法)
Method[] methods= clazz.getDeclaredMethods(); // 不能获取父类的
// 获取单个方法
Method method= clazz.getDeclaredMethod("eat",String.class);
获取方法修饰符
class student{
// get、set、tostring方法都有
.....
public void sleep() {
System.out.println("睡觉");
}
public void eat (String something) {
System.out.println("在吃"+ something);
}
.....
}
Class clazz = Class.forName("student");
Method method= clazz.getDeclaredMethod("eat",String.class);
// 获取方法的修饰符
int modifiers = method.getModifiers();
获取方法的名字
class student{
// get、set、tostring方法都有
.....
public void sleep() {
System.out.println("睡觉");
}
public void eat (String something) {
System.out.println("在吃"+ something);
}
.....
}
Class clazz = Class.forName("student");
Method method= clazz.getDeclaredMethod("eat",String.class);
// 获取方法名字
String name = m.getName();
获取方法的形参
class student{
// get、set、tostring方法都有
.....
public void sleep() {
System.out.println("睡觉");
}
public void eat (String something) {
System.out.println("在吃"+ something);
}
.....
}
Class clazz = Class.forName("student");
Method method= clazz.getDeclaredMethod("eat",String.class);
//获取方法的形参
Parameter[] parameters = method.getParameters();
// getParameterCount 获取参数数量
// getParameterTypes 获取参数类型
获取方法抛出的异常
class student{
// get、set、tostring方法都有
.....
public void sleep() {
System.out.println("睡觉");
}
public void eat (String something) throw IOException,NullPointException{
System.out.println("在吃"+ something);
}
.....
}
Class clazz = Class.forName("student");
Method method= clazz.getDeclaredMethod("eat",String.class);
// 获取方法抛出的异常
Class<?>[] exceptionTypes = method.getExceptionTypes();
方法运行并获取返回值(有就获取到了)
class student{
// get、set、tostring方法都有
.....
public void sleep() {
System.out.println("睡觉");
}
public String eat (String something) {
System.out.println("在吃"+ something);
return "好吃";
}
.....
}
Class clazz = Class.forName("student");
Method method= clazz.getDeclaredMethod("eat",String.class);
// 运行方法
// 参数1: 用obj对象调用该方法
// 参数2: 调用方法的传递参数(如果没有就不写)
// 方法的返回值,void就不写
// 创建一个新对象
student s = new student();
// 调用eat 方法
// 参数1表示方法的调用者,参数2方法传入的实际参数
String result = (String)method.invoke(s,"汉堡")
反射的作用
- 获取一个类里的所有信息,获取后再执行其他的业务逻辑
- 结合配置文件,动态创建对象并调用方法