反射
本章重点:
1.获取Class实例
2.创建运行时类对象
3.调用运行时类的指定结构
4.代理模式-静态代理和动态代理
1.反射机制的概述
1.1 概述
**Reflection(反射):**它是动态语言的关键,反射机制允许程序在执行期间借助反射API取得任何类的内部结构(信息),并能直接操作任意对象的内部属性和方法
动态性:java程序执行有两个过程:编译 和 执行。动态性说的就是程序编辑器并不能确定我们需要使用哪些对象,只有在实际执行的时候才能确定。
理解:
加载完类之后,在堆内存的方法区产生一个Class类型的对象(一个类对应一个Class对象),这个对象包含了完整的类的结构信息,我们可以通过它获取类的结构
**正常方式:**通过包-----》类-----》获取对象
**反射方式:**对象------》类------》包
1.2动态语言和静态语言
1.**动态语言:**是一类在运行时可以改变其结构的语言,通俗的解释:就是在运行时根据某些条件改变自身结构。代表:c#,js,PHP,Python
2.**静态语言:**和动态语言是相对的,运行时结构是不可变的就是静态语言。代表:Java,C,C++
1.3反射的应用
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时判断任意一个类所具有的成员变量和方法
- 在运行时获取泛型信息
- 在运行时调用任意一个对象的成员变量和方法
- 在运行时处理注解
- 生成动态代理
1.4Class类(相关ApI)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RhSEu71r-1614243179531)(E:\笔记图片\image-20210217084903096.png)]
- java.lang.Class:代表一个类
- java.lang.reflect.Method:代表类的方法
- java.lang.reflect.Field:代表类的成员变量
- java.lang.reflect.Construction:代表类的构造器
1.5反射功能演示
package org.wdit.unit15;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class ReflectionTest {
//反射前对于Person的操作
@Test
public void test(){
//1.创建Person对象
Person person=new Person();
//2.通过对象调用属性和方法
person.age=30;
person.show();
//person.name();
//在Person类的外部,不可以通过对象调用其私有属性和方法
}
//使用反射,对Person进行操作
@Test
public void test2() throws Exception {
//1.通过反射的方式创建对象
//获取Class对象
Class c=Person.class;
//通过class对象获取对应类的构造器
Constructor constructor = c.getConstructor(String.class, int.class);
//通过构造器创建对象
//Object o = constructornewInstance("哈哈",28);
Person person= (Person)constructor.newInstance("哈哈",28);
// System.out.println(o);
//2.通过反射方式调用对象的指定属性和方法
Field name = c.getDeclaredField("name");
name.setAccessible(true);
name.set(person,"100");
System.out.println(person);
//方法
Method method = c.getDeclaredMethod("show");
method.invoke(person);
Method method1 = c.getDeclaredMethod("showNation", String.class);
String nation =(String) method1.invoke(person, "中国");
System.out.println(nation);
}
}
1.6反射和封装
1.通过new的方式和反射的方式都可以创建对象,开发中到底用哪个?
从代码量和难易程度,建议使用new关键字,那什么时候使用反射的方式呢?前面说过反射的特征:动态性。实现动态性需要使用反射。
2.反射机制和面向对象的封装二者不是相互矛盾的?
不矛盾。表面看二者是有一点矛盾,但是其实二者应用领域不同,作用不同
首先:封装是用来解决编码层面,在面向对象我们讲解封装的时候,是这样解释的:隐藏对象的属性和功能实现细节,仅对外提供公共的访问方式,作用是提高安全性,独立性,复用性。
私有方法都会被本类中的公有方法调用
封装起到了提示作用。我们的方法,成员被定义成私有的,就是给程序员一个提示:这个方法你调用不方便,我提供的公共方法里调用了这个私有方法,而且功能比这个还多,比这个还好,你去调用共有的。
而反射为了解决应用层面。当我们需要使用某个对象,但又不清楚该对象内部结构时。就需要借助反射机制,动态地产生用户需求的对象。这才是反射真实的用途和存在的意义。
1.7Class类详解
类的加载过程
程序经过Javac.exe命令编译以后,会生成一个或多个字节码文件(.class结尾),接着我们使用Java.exe命令对某个字节码文件进行解释运行,相当于把某个字节码文件加载到内存中,加载的过程称为类的加载。加载到内存中的类,我们称之为Class实例对象。但是该类的对象我们不能直接用类名去表示,所以我们就在类名后添加了一个属性.class
来表示类名.class
是一个Class的一个对象。
2.获取Class实例对象的四种方式
package org.wdit.unit15.Demo2;
import org.wdit.unit15.Demo1.Person;
/**
* 获取Class对象的四种方式
* 常用第三种
*/
public class ReflectionDemo {
public static void main(String[] args) throws Exception {
//1.通过.class获得
Class<Person> personClass = Person.class;
System.out.println(personClass);
//2.通过运行时类的对象获取
Person person=new Person();
Class aClass = person.getClass();
System.out.println(aClass);
//3.调用Class的静态方法:forName()
Class aClass1 = Class.forName("org.wdit.unit15.Demo1.Person");
System.out.println(aClass1);
//4.使用类加载器
ClassLoader classLoader = ReflectionDemo.class.getClassLoader();
Class aClass2 = classLoader.loadClass("org.wdit.unit15.Demo1.Person");
System.out.println(aClass2);
}
}
常用的是第三种:第三种是运行时才能知道是否有错 ,它能更好地体现动态性,反射主要关注的是动态性,动态性强调的是运行时的表现
3.Class实例可以获取哪些结构
package org.wdit.unit15.Demo3;
import java.io.Serializable;
import java.lang.annotation.ElementType;
/**
*Class实例可以是哪些结构
*/
public class ReflectionDemo {
public static void main(String[] args) {
Class c = Object.class;
Class serializableClass = Serializable.class;
//主要数据类型和维度不同,他们就是不同的Class实例
Class aClass = int[].class;
Class aClass1 = int[][].class;
Class elementTypeClass = ElementType.class;//枚举
Class overrideClass = Override.class;//注解
Class<Integer> integerClass = int.class;
Class<Void> voidClass = void.class;
Class<Class> classClass = Class.class;
}
}
4.类的加载和ClassLoader理解
当程序主动使用某个类时,如果该类还没被加载到内存中,则系统会通过三步来对类进行初始化
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yqeRVDVq-1614243179533)(E:\笔记图片\image-20210217155420362.png)]
加载:
将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口(即引用地址)。所有需要访问和使用类数据只能通过这个Class对象。这个加载的过程需要类的加载器参与。
链接:
将java类的二进制代码合并到JVM的运行状态之中的过程。
- 验证:确保加载的类信息符合JVM规范,例如:以cafe开头,没有安全方面的问题
- 准备:正式为类变量(static)分配内存并设置类变量默认初始值阶段,这些内存都将在方法区中进行分配。
- 解析:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程。
初始化:
- 执行类构造器
<clinit>()
方法的过程。类构造器<clinit>()
:方法是由编译期自动收集类中所有变量的赋值动作和静态代码块中的语句合并产生的。(类构造器是构造类信息的,不是构造该类对象的的构造器)。 - 当初始化一个类时,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化。
- 虚拟机会保证一个类的()方法在多线程环境中被正确加锁和同步。
类的加载器的作用:
- 类加载的作用:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区运行时数据结构,然后再堆内存中生成一个代表这个类的java.lang.Class对象,作为方法区数据的访问入口。
- 类缓存:标准的javaSE类加载器可以按照要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间。JVM垃圾回收器可以回收这些Class对象。
加载器的分类:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jaEWMqJk-1614243179534)(E:\JAVA上课笔记\img\image-20210224104406802.png)]
加载器演示:
package org.wdit.unit15.Demo4;
/**
* 加载器演示案例
*/
public class ClassLoaderDemo {
public static void main(String[] args) {
ClassLoader classLoader = ClassLoaderDemo.class.getClassLoader();
System.out.println(classLoader);//系统类加载器
ClassLoader parent = classLoader.getParent();
System.out.println(parent);//扩展类加载器
ClassLoader parent1 = parent.getParent();
System.out.println(parent1);//引导类加载器
}
}
5.使用ClassLoader读取配置文件
package org.wdit.unit15.Demo5;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.Properties;
/**
* 读取配置文件
*/
public class ReadProperties {
public static void main(String[] args) throws Exception {
//方式1:
Properties properties=new Properties();
/*FileInputStream fis=new FileInputStream("jdbc.properties");
properties.load(fis);
String username = properties.getProperty("username");
String password = properties.getProperty("password");
System.out.println(username+"----"+password);*/
//读取配置文件方式2
ClassLoader classLoader = ReadProperties.class.getClassLoader();
InputStream resourceAsStream = classLoader.getResourceAsStream("jdbc.properties");
properties.load(resourceAsStream);
String username = properties.getProperty("username");
String password = properties.getProperty("password");
System.out.println(username+"----"+password);
}
}
6.创建运行时类的对象
package org.wdit.unit15.Demo6;
import org.wdit.unit15.Demo1.Person;
import java.lang.reflect.InvocationTargetException;
/*
* 通过反射创建对应类运行时类对象*/
public class NewInstanceDemo {
public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
Class<Person> personClass = Person.class;
Person person = personClass.newInstance();
Person person1 = Person.class.getConstructor().newInstance();
}
}
注意:
1.反射方式获取类对象,其实它的底层也是调用该类的无参构造,如果该类没有无参构造,则会报错
2.无参构造的访问权限必须满足当前类的访问权限
3.框架创建对象一般都是使用newInstance(),属性的操作可以在创建完对象后再去调用其他方法,这就是为什么前面学习面向对象时,建议大家手动给出无参构造的原因。
7.反射功能体现
package org.wdit.unit15.Demo7;
import java.util.Random;
/*
* 反射功能体会
*/
public class NewInstanceDemo {
public static void main(String[] args) throws Exception {
for(int i=0;i<100;i++){
//产生一个随机数0~2
int num=new Random().nextInt(3);
//System.out.println(num);
String classpath=null;
switch (num){
case 0:
classpath="java.lang.object";
break;
case 1:
classpath="java.util.Date";
break;
case 2:
classpath="org.wdit.unit15.Demo1.Person";
break;
}
Object obj = getInstance(classpath);
System.out.println(obj);
}
}
public static Object getInstance(String classpath) throws Exception {
Class<?> c = Class.forName(classpath);
return c.newInstance();
}
}
普通获取对象的方式:给什么对象,用什么对象
反射方式获取对象:我们需要什么对象,就给我们创建什么对象
优点:不必提前创建对象,当程序需要某个对象时,我们在去创建,可以有效节省系统资源。
8.通过反射获取复杂类的内部结构
1.先准备一个类
package org.wdit.unit15.Demo8;
import java.io.Serializable;
public class Person <T>implements Serializable {
private char gender;
public double weight;
//私有方法
private void breath(){
System.out.println("呼吸");
}
//公有方法
public void eat(){
System.out.println("吃东西");
}
}
2.接口和注解
package org.wdit.unit15.Demo8;
public interface MyInterface {
void info();
}
package org.wdit.unit15.Demo8;
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
/*
* @Documented:类和方法的Annotation默认是不出现在JavaDoc中
* 如果使用该注解@dOCUMENTED修饰,则Annotation就会出现在JavaDoc中*/
@Documented
@Target({ElementType.TYPE, ElementType.METHOD,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String value()default "hello";
}
3.子类
package org.wdit.unit15.Demo8;
@MyAnnotation
public class Teacher extends Person<String>implements Comparable<String>,MyInterface {
private String name;
int age;
public int id;
public Teacher(){
}
private Teacher(String name){
this.name=name;
}
Teacher (String name,int age){
this.name=name;
this.age=age;
}
//私有方法,有返回值,有参数
private String show(String nation){
System.out.println("我的国籍是:"+nation);
return nation;
}
//私有静态的方法
private static void showInfo(){
System.out.println("我是一个私有的静态方法");
}
//公有方法:有返回值,有参数,有异常抛出
@MyAnnotation
public String display(String gender,int age)throws NullPointerException{
return gender+age;
}
@Override
public int compareTo(String o) {
return 0;
}
@Override
public void info() {
System.out.println("我是一个人");
}
@Override
public String toString() {
return "Teacher{" +
"name='" + name + '\'' +
", age=" + age +
", id=" + id +
'}';
}
}
4.测试类
package org.wdit.unit15.Demo9;
import org.junit.jupiter.api.Test;
import org.wdit.unit15.Demo8.Teacher;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;
public class Demo {
@Test
public void test(){
Class<Teacher> teacherClass = Teacher.class;
//获取属性结构
//getFields():获取当前运行时类及其父类声明为public访问权限的属性
Field[] fields = teacherClass.getFields();
for(Field field:fields){
System.out.println(field);
}
System.out.println("**************************");
//getDeclaredFields():获取当前运行时类中声名的所有属性,不包含父类属性
Field[] declaredFields = teacherClass.getDeclaredFields();
for(Field field:declaredFields){
System.out.println(field );
}
}
/*
* 获取属性的内部结构:权限修饰符,数据类型 变量名 属性值*/
@Test
public void test2(){
//1.万恶之源
Class<Teacher>teacherClass=Teacher.class;
//2.获取属性对象数组
Field[] fields = teacherClass.getDeclaredFields();
//3.遍历
for(Field field:fields){
//变量名
String fieldName = field.getName();
System.out.println(fieldName+"\t");
//访问权限
int modifiers = field.getModifiers();//对应的值可以通过Modifier类查看
System.out.print(modifiers+"\t");
//数据类型
Class type = field.getType();
System.out.println(type+"\t");
}
}
/*
* 运行时类的方法结构*/
@Test
public void test3(){
Class teacherClass = Teacher.class;
//getMethods():获取当前运行时类及其父类声明为public访问权限的方法
Method[] methods = teacherClass.getMethods();
for (Method method:methods){
System.out.println(method);
}
System.out.println("**************");
//getDeclaredMethods():获取当前运行时类中声名的所有方法,不包含父类方法
Method[] methods1 = teacherClass.getDeclaredMethods();
for(Method method:methods1){
System.out.println(method);
}
}
/*
* 获取方法的内部结构
* @xxxx:注释:可以有多个
* 权限修饰符 返回值类型 方法名(参数列表) throws 异常*/
@Test
public void test4(){
Class<Teacher> teacherClass = Teacher.class;
Method[] methods = teacherClass.getDeclaredMethods();
for(Method method:methods){
//获取方法的注解(Runtime)
Annotation[] annotations = method.getAnnotations();
for(Annotation annotation:annotations){
System.out.print(annotation);
}
//权限修饰符
int modifiers = method.getModifiers();
System.out.print("\t"+modifiers);
//返回值类型
System.out.print("\t"+method.getReturnType());
//方法名
System.out.print("\t"+method.getName());
//参数列表
System.out.print("(");
Class[] parameterTypes = method.getParameterTypes();
if(!(parameterTypes==null&¶meterTypes.length==0)){
for (int i=0;i<parameterTypes.length;i++){
System.out.print("args_"+i+" "+parameterTypes[i].getName()+",");
}
}
System.out.println(")");
//异常
Class[] exceptionTypes = method.getExceptionTypes();
if(exceptionTypes.length>0){
System.out.print("throws ");
for(int i=0;i<exceptionTypes.length;i++){
System.out.print(exceptionTypes[i].getName()+",");
}
}
System.out.println();
}
}
/*
*获取当前运行时类的构造器
* */
@Test
public void test5(){
Class<Teacher> teacherClass = Teacher.class;
//getConstructors():获取当前运行时类中声名的public构造方法
Constructor[] constructors = teacherClass.getConstructors();
for(Constructor constructor:constructors){
System.out.println(constructor);
}
System.out.println("**********");
//getDeclaredConstructors():获取当前运行时类中声名的所有构造方法
Constructor[] declaredConstructors = teacherClass.getDeclaredConstructors();
for(Constructor constructor:declaredConstructors){
System.out.println(constructor);
}
}
/*
* 获取运行时类的父类及父类的泛型*/
@Test
public void test6(){
Class<Teacher> teacherClass = Teacher.class;
//获取运行时类的父类
Class superclass = teacherClass.getSuperclass();
System.out.println(superclass);
//获取运行时类父类的泛型
Type genericSuperclass = teacherClass.getGenericSuperclass();
ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;//参数化类型
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
System.out.println(actualTypeArguments[0]);
}
//获取运行时类实现的接口,所在的包,注解等
@Test
public void test7(){
Class teacherClass = Teacher.class;
//实现的接口
Class[] interfaces = teacherClass.getInterfaces();
for(Class c:interfaces){
System.out.println(c);
}
//获取父类实现的接口
Class[] interfaces1 = teacherClass.getSuperclass().getInterfaces();
for(Class c:interfaces1){
System.out.println(c);
}
System.out.println("********");
//获取运行时类所在的包
Package aPackage = teacherClass.getPackage();
System.out.println(aPackage);
//获取运行时类所包含的注解
Annotation[] annotations = teacherClass.getAnnotations();
for(Annotation annotation:annotations){
System.out.println(annotation);
}
}
}
后期框架用到的知识:
1.获取父类的泛型:
举例:
在后期框架搭建中,我们经常需要将方法向上抽取到父类中
BaseDao
interface BaseDao<T>{
T findUserById(int id);
}
BaseDaoImpl
class BaseDaoImpl<T>implements BaseDao<T> {
@Override
public T findUserById(int id) {
//具体实现省略
return null;
}
}
UserDaoImpl
class UserDaoImpl extends BaseDaoImpl<User>{
}
在这个案例中,框架替我们创建对象时,并不知道T.class指的具体是哪个类,我们就需要通过反射机制,通过子类去确定和获取父类的泛型
DaoTest
public class DaoTest {
public static void main(String[] args) {
Class<UserDaoImpl> userDaoClass = UserDaoImpl.class;
Type genericSuperclass = userDaoClass.getGenericSuperclass();
System.out.println(genericSuperclass);
}
}
2.获取类实现的接口:动态代理中使用(后面讲)
3.获取运行时类声名的注解:框架中使用
9.调用运行时类的指定结构
案例1:获取指定属性-2种方式
package org.wdit.unit15.Demo11; import org.junit.jupiter.api.Test; import org.wdit.unit15.Demo8.Teacher; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; /* * 调用运行时类的指定结构*/ public class Demo { /*调用属性*/ @Test public void test() throws Exception { Class teacherClass = Teacher.class; //Object o = teacherClass.newInstance(); //创建运行时类对象 Teacher teacher= (Teacher)teacherClass.getConstructor().newInstance(); //获取指定属性 Field id = teacherClass.getField("id"); //设置属性 /* * set():参数1:指明设置那个对象的属性,参数2赋值*/ id.set(teacher,100); System.out.println(teacher); } @Test public void test2() throws Exception { Class teacherClass = Teacher.class; //Object o = teacherClass.newInstance(); //创建运行时类对象 Teacher teacher= (Teacher)teacherClass.getConstructor().newInstance(); Field age = teacherClass.getDeclaredField("age"); //设置属性可访问 age.setAccessible(true); age.set(teacher,10); System.out.println(teacher); }}
案例2:获取指定的非静态方法和静态方法------重点
/*调用静态和非静态方法------------重点*/
@Test
public void test3() throws Exception {
//Class实例对象
Class teacherClass = Teacher.class;
//Teacher的实例对象
Teacher teacher=(Teacher)teacherClass.getConstructor().newInstance();
//获取指定方法
Method show = teacherClass.getDeclaredMethod("show", String.class);
show.setAccessible(true);
String nation =(String) show.invoke(teacher, "中国");
System.out.println(nation);
System.out.println("************************");
//静态方法
Method showInfo = teacherClass.getDeclaredMethod("showInfo");
showInfo.setAccessible(true);
showInfo.invoke(teacher);
showInfo.invoke(Teacher.class);
showInfo.invoke(null);//可以不指定对象,该方法对象是通过Teacher.class获得,JVM就可以找到对应的静态方法
}
案例3:获取运行时类的指定构造器
/*调用指定的构造器*/
@Test
public void test4() throws Exception {
//Class实例对象
Class teacherClass = Teacher.class;
Constructor constructor = teacherClass.getDeclaredConstructor(String.class);
constructor.setAccessible(true);
Teacher teacher= (Teacher)constructor.newInstance("刘德华");
System.out.println(teacher);
Constructor constructor1 = teacherClass.getDeclaredConstructor(String.class,int.class);
constructor1.setAccessible(true);
Teacher teacher1= (Teacher)constructor1.newInstance("张惠妹",28);
System.out.println(teacher1);
}
10.练习:
10.1获取Class实例的三种常见方式
方式1: 类名.class;该种方式不常用
方式2: 对象名.getClass()
方式3: Class.forName("classpath")
方式4: 使用类加载器
类名.class.getClassLoader();
classLoader.loadClass("org.wdit.unit15.Demo1.Person");
10.2简述:什么是Class类?
Class类可以表示类的结构信息,Class类的实例对应着加载到内存中的一个运行时类,平时我们使用类名调用静态方法所使用的一个类名就是Class的一个实例, 通过这个实例我们可以获取类的完整结构:
属性,方法,构造器,实现的接口,所在的包,它的父类…
10.3创建Class对应运行时类的对象的通用方法,用代码如何实现?如果能实现,运行时类构造器需要满足哪些要求?
JDK 8:class1.newInstance();
JDK 11:class1.getDeclaredConstructor().newInstance;
必须满足的要求:
1.必须有无参
2.无参构造的访问权限必须满足该方法的权限
10.4读取配置文件的两种方式:
//方式1:
Properties properties=new Properties();
FileInputStream fis=new FileInputStream("jdbc.properties");
properties.load(fis);
String username = properties.getProperty("username");
String password = properties.getProperty("password");
System.out.println(username+"----"+password);
//读取配置文件方式2
ClassLoader classLoader = ReadProperties.class.getClassLoader();
InputStream resourceAsStream = classLoader.getResourceAsStream("jdbc.properties");
properties.load(resourceAsStream);
String username = properties.getProperty("username");
String password = properties.getProperty("password");
System.out.println(username+"----"+password);
}
10.5如何调用指定方法
例
class Teacher{
public void show(){
System.out.println("Teacher show")
}
}
答:
//非静态方法。要调用该必须有对象
Class class1=Teacher.class;
Teacher teacher = (Teacher)class1.getDeclaredConstructor().newInstance();
//通过Class实例获取方法
Method method = class1.getDeclaredMethod("show");
//确保method的可访问性
method.setAccessible(true);
//调用方法
show.invoke(teacher);
11.反射的应用-动态代理
后面讲Spring框架会讲它的两大核心技术:IOC容器 和 AOP切面编程(动态代理技术)
11.1代理模式
概述:
代理模式是java开发中使用较多的一种设计模式。代理模式就是为其他对象提供一种代理 以控制对这个对象的访问。使用代理将对象包装起来,然后用代理对象取代原始对象,任何对原始对象的调用都要通过代理,代理对象决定是否 和 何时将方法调用转到原始对象上。
图解:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-06q0QU02-1614243179536)(E:\JAVA上课笔记\img\image-20210224214953071.png)]
代码
package org.wdit.unit15.Demo12;
public class ProxyTest {
public static void main(String[] args) {
NetServer netServer=new NetServer();
ProxyNetServer proxyNetServer=new ProxyNetServer(netServer);
proxyNetServer.netPlay();
}
}
interface Internet{
void netPlay();
}
//被代理类
class NetServer implements Internet{
@Override
public void netPlay() {
System.out.println("网上冲浪");
}
}
//代理类
class ProxyNetServer implements Internet{
private NetServer netServer;
public ProxyNetServer(NetServer netServer){
this.netServer=netServer;
}
@Override
public void netPlay() {
System.out.println("上网前的一些检查;网络是否通畅,等");
netServer.netPlay();
}
}
从这个案例我们看出,代理类中的方法相当于是对被代理类中方法的增强。
应用场景:
- 安全代理:屏蔽对真实角色的访问
- 远程代理:通过代理类处理远程方法调用
- 延迟加载:先加载轻量级的代理对象,真正需要再去加载真实对象。比如说较大视频或图片。
分类:
静态代理:静态定义代理类
动态代理:动态生成代理类
11.2动态代理
上一个案例讲解的代理模式属于静态代理,因为代理类和被代理类都在编译期已经确定,不利于程序的扩展。同时,每一个代理只能服务一个被代理类,这样一来,程序必然产生过多的代理类,造成结构冗余。最好的办法是可不可以通过一个代理类完成全部的代理功能,即定义一个通用的代理类。但是这种通用的代理类无法在编译期就定义好,只能设法在运行时根据加载的类动态的创建 代理类对象:代理模式中代理类和被代理类都实现了相同的接口,我们只需知道加载类都实现了哪些接口,就可以动态的创建对应的代理类对象。
应用场景:
- 调试
- 远程方法调用
11.3动态代 理演示案例
案例:
package org.wdit.unit15.Demo13;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/*动态演示案例*/
public class ProxyTest {
public static void main(String[] args) {
SuperMan superMan=new SuperMan();
Human proxyInstance = (Human)ProxyFactory.getProxyInstance(superMan);
System.out.println(proxyInstance.think());
proxyInstance.eat("烤肉");
Teacher teacher=new Teacher();
Human proxyTeacher=(Human)ProxyFactory.getProxyInstance(teacher);
System.out.println(proxyTeacher.think());
proxyTeacher.eat("香菇");
}
}
interface Human{
String think();
void eat(String food);
}
//被代理类
class SuperMan implements Human{
@Override
public String think() {
return "无敌真寂寞";
}
@Override
public void eat(String food) {
System.out.println("吃"+food);
}
}
/*分析:
1.如何根据加载到内存中的被代理类,动态的创建一个代理类对象
* 2.当通过代理类调用方法时;如何动态的调用被代理类中同名的方法
*/
class ProxyFactory{
/*根据被代理类对象生产一个代理类对象
@param obj 被代理类对象
@return 代理类对象
*/
public static Object getProxyInstance(Object obj) {
/*
*newProxyInstance()
* 参数1:类加载器
* 参数2:被代理类实现的接口
* 参数3:InvocationHandler接口-------解决问题2的关键
* */
MyInvocationHandler myInvocationHandler=new MyInvocationHandler();
myInvocationHandler.bind(obj);
return Proxy.newProxyInstance(
obj.getClass().getClassLoader(),
obj.getClass().getInterfaces(),
myInvocationHandler
);
}
}
class MyInvocationHandler implements InvocationHandler{
private Object object;//被代理类对象
public void bind(Object object){
this.object=object;
}
/*当通过代理类调用同名方法时就会自动调用该方法*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("检查......");
Object returnValue = method.invoke(object, args);
return returnValue;
}
}
class Teacher implements Human{
@Override
public String think() {
return "想问题";
}
@Override
public void eat(String food) {
System.out.println("不爱吃"+food);
}
}
图解:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ik2W46vX-1614243179538)(E:\JAVA上课笔记\img\image-20210224231231127.png)]
11.4AOP和动态代理
图解:
MyInvocationHandler myInvocationHandler=new MyInvocationHandler();
myInvocationHandler.bind(obj);
return Proxy.newProxyInstance(
obj.getClass().getClassLoader(),
obj.getClass().getInterfaces(),
myInvocationHandler
);
}
}
class MyInvocationHandler implements InvocationHandler{
private Object object;//被代理类对象
public void bind(Object object){
this.object=object;
}
/当通过代理类调用同名方法时就会自动调用该方法/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(“检查…”);
Object returnValue = method.invoke(object, args);
return returnValue;
}
}
class Teacher implements Human{
@Override
public String think() {
return "想问题";
}
@Override
public void eat(String food) {
System.out.println("不爱吃"+food);
}
}
<font color='cornflowerblue'>图解:</font>
[外链图片转存中...(img-Ik2W46vX-1614243179538)]
#### 11.4AOP和动态代理
**图解:**
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kSQ43NAl-1614243179539)(E:\JAVA上课笔记\img\image-20210224230808616.png)]