动态代理
代理模式
代理模式是指为目标对象提供一个代理对象,外部对目标对象的访问通过代理委托进行以达到控制访问的目的。
如果我们想要对A类进行操作,但不能直接访问A类完成操作,这时可通过一个中介类B来访问。那么我们就称A类为委托类,B类为代理类。实际上,代理类并不能靠自己实现真正的服务,而是通过调用委托类的相关方法来完成我们需要的操作。委托对象也可以称为被代理对象。
对代理类和委托类可以这样理解:比如一个明星,他平时需要完成一些工作,但是一般工作的相关琐事明星并不会自己完成,他就会交给中介或者是代理来完成,他只需要完成相应的本职就好。
动态代理vs静态代理
在静态代理中,代理类需要我们自己先自行定义好,它在程序运行之前就已经编译完成了。但是动态代理并不需要我们事先定义代理类,他可根据我们在java代码中的指示动态生成。动态代理的优势在于可以很方便地对代理类中的所有函数进行统一的管理
动态代理的写法
下面代码为将方法调用信息写入一个文件中
首先定义一个接口:
package experience2;
import java.io.IOException;
public interface Speakable {
public void setAge(int age) throws IOException;
public void setName(String name) throws IOException;
public void setGender(String gender) throws IOException;
}
在定义一个该接口的实现类
package experience2;
public class Person implements Speakable {
private int age;
private String name;
private String gender;
public Person() {
}
public Person(int age, String name, String gender) {
this.age = age;
this.name = name;
this.gender = gender;
}
public int getAge() {
return age;
}
@Override
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
@Override
public void setName(String name) {
this.name = name;
}
public String getGender() {
return gender;
}
@Override
public void setGender(String gender) {
this.gender = gender;
}
}
再定义一个InvocationHandler接口的实现类
package experience2.dynamicproxy;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
public class PersonInvocationHandler implements InvocationHandler {
//被代理的对象
private Object target;
private Date date = new Date();
private SimpleDateFormat dateFormat= new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
public PersonInvocationHandler(Object target) throws FileNotFoundException {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//通过反射调用目标对象的方法
method.invoke(this.target,args);
FileWriter des = new FileWriter("D:\学习QAQ\advanced java\src\experience2\dynamicproxy\DynamicLogs.txt",true);
des.write("时间:" + dateFormat.format(date) + ";方法名称:"+ method.getName()+"参数:" + Arrays.toString(args) + "\n");
des.close();
return null;
}
}
invoke
invoke(Object proxy, Method method, Object[] args)该方法为回调方法
第一个参数:当前代理对象,或者说通过该参数获得这个参数拿到当前的代理对象
第二个参数:有外面的调用者决定当前的调用方法
第三个参数:将外面调用者的方法参数由Object数组传进来
最后是一个启动类
package experience2.dynamicproxy;
import experience2.Person;
import experience2.Speakable;
import java.lang.reflect.Proxy;
public class Bootstrap {
public static void main(String[] args) throws Exception {
Person person = new Person();
Speakable speakable = (Speakable) Proxy.newProxyInstance(
Speakable.class.getClassLoader(),new Class[]{Speakable.class},new PersonInvocationHandler(person)
);
speakable.setAge(19);
speakable.setName("小明");
speakable.setGender("男");
}
}
newProxyInstance
newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler handler)
第一个参数:用于指定一个类加载器,一般用于指定当前类,通过
当前类的类名.class.getClassLoader()
第二个参数:用于指定生成的代理是什么,注意格式为数组
第三个参数:用来指定生成的代理对象要干什么,由invoke()决定
反射
在反射中,将类中所有的内容都可以看成为对象,即java中万物皆对象的思想
作用
基本作用:可以得到一个类的全部成分然后操作
可以破坏封装性
最重要的用途:适合做java框架,基本上主流的框架都会基于反射设计出一些通用的功能
获取类对象
而为了解剖一个类,就必须先要获取该类的字节码文件对象,所以就要先获取该文件的Class类型对象
方法一:先给该对象创建一个实例,在通过该实例.getClass获取类对象
Student student = new Student(); Class stClazz1 = student.getClass();
方法二:直接通过该类.class获取
Class stClazz2 = Student.class;
方法三:通过该类的路径获取
Class stClazz3 = Class.forName('该类带包名类名的路径');
注意三种方式获取的类对象都是相同的
ps:一般推荐第三种方式来获取类对象
获取构造方法
方法:
//声明一个Constructor类型的数组
Constructor<>[] conStus;
//获取全部构造器,但只能获取public修饰的
//clazz为Student的类对象
conStus = stClazz.getConstructors();
//获取全部构造器,只要存在就能拿到
consStus = stClazz.getDeclaredConstructors();
Constructor<>[] conStu;
//获取某个构造器,只能获取public修饰的
conStu = stClazz.getConsturctor(Class<?>……parameterTypes);
//获取某个构造器,只要存在就能拿到
conStu = stClazz.getDeclaredConsturctor();
不推荐使用以上的第一个和第三个方法
作用:
初始化对象返回
Constructor提供的方法:
T newInstance(Object…initargs)该方法通过构造器对象.newInstance调用并传入参数,完成对象的初始化并返回
public void setSccessible(boolean flag),该方法可完成暴力反射,设置为true,表示禁止检查访问控制
获取成员变量
方法:
public Field[] getFields( ) 获取类的全部成员变量,但只能获取public修饰的
public Field[] getDeclaredFields( ) 获取类的全部成员变量
public Field getField(String name) 获取类的某个成员变量,只能获取public修饰的
public Field getDeclaredField(String name) 获取类的某个成员变量
void set(Object obj, Object value),obj:要设置的字段所在的对象 value:要为字段设置的值, 赋值
object get(Object obj) 取值
public void setAccessible(boolean flag) 暴力反射,设置为true,表示禁止检查访问控制
作用:
赋值、取值
获取成员方法
Method[ ] getMethods( ) 获取类的全部成员方法,只能获取public修饰的
Method[ ] getDeclaredMethods( ) 获取类的全部成员方法
Method getMethod(String name,class<?>…parameterTypes) 获取类的某个成员方法,只能获取public修饰的
Method[ ] getDeclaredMethods(String name,class<?>…parameterTypes) 获取类的某个成员方法
public Object invoke(Object object,Object…args) 触发某个对象的该方法执行
public void setAccessible(boolean flag) 暴力反射,设置为true,表示禁止检查访问控制