动态代理就是无侵入式的给代码增加新的功能,通过接口保证后面的对象和代理需要实现同一个接口,接口中就是被代理的所有方法,代理里面就是对象要被代理的方法。
因为一个对象觉得自己身上的功能太多,就会将一部分功能代理出去,对象中什么方法想要被代理,在代理中必须有对应的方法,一般会定义一个接口,接口中的方法是想要被代理的方法
首先是有一个需要被代理的对象,然后这个对象中有哪个方法想要被代理,创建一个接口,接口中的方法就是这个对象想要被代理的方法,在这个对象中需要实现这个接口,然后重写这几个方法。这时候就需要一个代理类,这个代理类,这个代理类主要是用到里面的代理方法,这个方法的作用是给一个对象创建一个代理,将需要被代理的对象传入其中,然后返回的是代理的对象。在这个方法里面,需要调用一个方法,就是java.util.reflect.Proxy
类里面的一个方法,由于这个方法是静态方法,所以可以直接使用类名进行调用,public static Object newProxyInstance(ClassLoader loader, Class>[] interfaces, InvocationHandler h)
在这个方法中,第一个参数是指定哪一个类加载器去加载生成的代理类,第二个参数是指定的接口,第三个参数是用于指定生成的代理对象要干什么,一般创建代理对象不会调用到第三个参数,后面只有通过代理对象进行方法回调的时候才会调用第三个参数,在执行这个第三个参数时,会重写invoke方法进行代理方法的实现,传入invoke方法有三个参数,第一个参数是传入代理的对象,第二个参数是传入需要运行的就是原本对象的方法,第三个参数就是传入这个方法的参数。在返回的时候,返回的是传入这个方法的invoke方法传回的值。
public class BigStar implements Star {
private String name;
public BigStar() {
}
public BigStar(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(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 + "跳舞");
}
}
/**
* 我们可以将所有想要被代理的方法定义在接口当中
*/
public interface Star {
// 唱歌
String sing(String name);
// 跳舞
void dance();
}
public class ProxyUtil {
/**
* 这个方法的作用:给明星的一个对象创建一个代理
* @param bigStar 被代理的明星对象
* @return 给明星创建的代理
*/
public static Star createProxy(BigStar bigStar){
/**
* java.util.reflect.Proxy类:提供了为对象产生代理对象的方法
* public static Object newProxyInstance(ClassLoader loader, Class>[] interfaces, InvocationHandler h)
* 参数一:用于指定用哪个类加载器去加载生成的代理类
* 参数二:指定接口,就是这个接口用于指定生成的代理有哪些方法
* 参数三:用于指定生成的代理对象要干什么
*/
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 {
/**
* 参数一:代理的对象
* 参数二:要运行的方法
* 参数三:调用方法时传递的实参
*/
if("sing".equals(method.getName())){
System.out.println("给话筒");
} else if ("dance".equals(method.getName())) {
System.out.println("准备场地");
}
return method.invoke(bigStar, args);
}
}
);
return star;
}
}
public class Test {
public static void main(String[] args) {
BigStar bigStar = new BigStar("坤哥");
Star prosy = ProxyUtil.createProxy(bigStar);
String res = prosy.sing("鸡你太美");
System.out.println(res);
// ProxyUtil.createProxy(bigStar).dance();
}
}
反射就是可以在运行时分析类和执行类的能力,通过反射可以获得任意一个类的属性和方法,还可以调用这些类的属性和方法。比如下面这张图片。
在反射中,我们可以使用三种方式调用类的字节码文件,分别是Class.forName(全类名),类名.class,对象.getClass();在这三种调用类的字节码的过程中,第一种一般市用在类加载过程中的,第二种经常作为参数进行调用,第三种一般是创建对象之后,然后使用对象名进行调用。当我们获取到类的字节码文件之后,就可以通过类的字节码文件去调用类中的构造方法,成员变量和成员方法以及他们相应的信息。构造方法(Constructor),使用getConstructors
可以获取到所有的公共的构造方法,使用getDeclaredConstructors
可以获取所有的构造方法,使用不加s的可以用来获取单个的构造方法,但是需要传入构造方法中传入的参数类型,比如Stirng就需要传入String.class
,int就需要传入int.class
,Declared
是用来控制是否查找公共方法,当我们获取到某个对象之后,可以使用getModifiers
获取修饰这个对象的权限修饰符(但是一般获取到的都是数字比如下图),使用getParameters
获取这个对象中所有传入参数的类型,然后使用newInstance
去构造这个对象,但是一般private
修饰的方法会报错,需要使用setAccessible(true)
消除掉当前方法的修饰权限。
反射虽然给与了我们在运行时分析类和操作类的能力,但是也增加了安全性问题,可以通过setAccessible(true)方法设置对象可被强制访问,可以破坏单例的封装性
/**
* Java反射
* 反射允许对类的成员变量,成员方法,构造方法的信息进行编程访问
* 可以获取到这三者的权限修饰符,名字,返回值类型。可以对获取到的属性赋值,或者获取已经有的值。
* 可以获取到成员方法返回的值,还可以获取到方法抛出的异常,方法上的注解
* 可以运行获取出来的方法
*
* 首先要获取.class字节码文件
* 获取class对象的三种方式:
* 1. Class.forName("全类名"); <源代码阶段使用>
* 2. 类名.class <类加载阶段使用>
* 3. 对象.getClass(); <运行阶段使用>
*/
public class ReflectDemo1 {
public static void main(String[] args) throws ClassNotFoundException {
Student student = new Student();
// 最常见的一种
System.out.println(Class.forName("oop.reflectdemo.reflect.Student"));
// 一般当作参数进行传递
System.out.println(Student.class);
// 当只有这个类的对象才可以使用
System.out.println(student.getClass());
}
}
/**
* Java反射
* 常见的获取构造方法,成员变量,成员方法的方法
* Java中Constructor描述构造方法,Field描述成员变量,Method描述成员方法
*/
public class ReflectDemo2 {
public static void main(String[] args) throws Exception {
// 获取字节码文件对象
Class clazz = Class.forName("oop.reflectdemo.reflect.Student");
// 获取所有公共的的构造方法
Constructor[] constructor = clazz.getConstructors();
for (Constructor constructor1 : constructor) {
System.out.println(constructor1);
}
// 获取所有的构造方法
Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
for (Constructor declaredConstructor : declaredConstructors) {
System.out.println(declaredConstructor);
}
/**
* 获取单个构造方法
* 后面括号里面需输入这个构造方法的传入值类型
* 一般getConstructor只能获取到public修饰的方法,而要获取到所有权限修饰的方法必须用getDeclaredConstructor;
*
*/
clazz.getConstructor(); // 获取无参构造
clazz.getConstructor(String.class); // 获取传入参数为String类型的构造方法
Constructor declaredConstructor = clazz.getDeclaredConstructor(String.class, int.class);// 获取传入参数为int的构造方法
// 获取修饰这个对象的权限修饰符
int modifiers = declaredConstructor.getModifiers();
System.out.println(modifiers);
// 获取这个对象中所有传入参数的类型
declaredConstructor.getParameters();
declaredConstructor.setAccessible(true);
Student tom = (Student) declaredConstructor.newInstance("Tom", 21);
System.out.println(tom);
/**
* 操作成员变量
*/
// 获取public修饰的所有的成员变量
Field[] fields = clazz.getFields();
for (Field field : fields) {
System.out.println(field);
}
// 获取所有成员变量
Field[] declaredField = clazz.getDeclaredFields();
for (Field field : declaredField) {
System.out.println(field);
}
// 返回单个的成员变量对象
Field name = clazz.getDeclaredField("name");
System.out.println(name);
// 获取权限修饰符
int modifiers1 = name.getModifiers();
System.out.println(modifiers1);
// 获取名字
String name1 = name.getName();
System.out.println(name1);
// 获取类型
Class> type = name.getType();
System.out.println(type);
// 获取成员变量记录的值
Student s = new Student("Tom", 32);
name.setAccessible(true);
Object value = name.get(s);
System.out.println(value);
// 修改对象里面的值
name.set(s,"Jerry");
System.out.println(s.getName());
/**
* 操作成员方法
*/
// 获取所有的公共方法
Method[] methods = clazz.getMethods();
// 获取所有的方法
Method[] declaredMethods = clazz.getDeclaredMethods();
/**
* 获取方法进行调用
*/
// 获取单个方法, 传入方法名,后面跟着方法传入的参数类型
Method eat = clazz.getMethod("eat", String.class);
// 获取方法的权限修饰符
int modifiers2 = eat.getModifiers();
// 获取方法的形参
Parameter[] parameters = eat.getParameters();
// 获取方法抛出的异常
Class>[] exceptionTypes = eat.getExceptionTypes();
// 方法运行
Student student = new Student();
eat.setAccessible(true);
eat.invoke(student,"汉堡");
}
}