反射的应用动态代理【Java】

写在前面的话:

  1. 参考资料:尚硅谷视频
  2. 本章内容:Java反射的实际应用动态代理
  3. IDE:eclipse
  4. JDK:Java8

目录

1.静态代理

2.动态代理 

2.1 动态代理基本概念

2.2 代码示例

2.3 动态代理讲解

2.3.1 创建实现InvocationHandler接口的类

2.3.2 获取代理类的对象 

2.3.3 使用代理类

3. 静态代理与动态代理的区别?


1.静态代理

静态代理:

  1. 每一个代理类只能为一个接口服务
  2. 在编译阶段,就确定了目标对象

 示例代码

测试类

package static_proxy;

public class TestProxy {

	public static void main(String[] args) {
		
		//创建一个学生
		Student student1 = new Student("张三","男",0);
		
		//创建一个学生
		Student student2 = new Student("小花","女",0);
		
		System.out.println(student1);
		System.out.println(student2);
		
		//创建一个代理类
		ProxyStudent proxyStudent = new ProxyStudent(new Teacher());
		
		//给student[张三]打分
		proxyStudent.giveStudentScore(student1, 78.5);
		
		//给student[小花]打分
		proxyStudent.giveStudentScore(student2, 89.2);
		
		//打印输出学生信息
		System.out.println(student1);
		System.out.println(student2);
		
	}
}

老师类

package static_proxy;

/*
 * 【被代理类】
 * 老师类 : 对学生进行打分
 */

public class Teacher implements GiveScore {

	@Override
	public void giveStudentScore(Student student,double score) {
		
		//设置学生分数
		student.setScore(score);
		System.out.println("给" + student.getName() + "分数:" + score);
	}

}

 代理学生类

package static_proxy;

/*
 * 【代理类】
 * 由老师指派的一些学生进行打分
 */

public class ProxyStudent implements GiveScore{
	
	GiveScore giveScore;

	public ProxyStudent(Teacher teacher) {
		super();
		this.giveScore = teacher;
	}

	@Override
	public void giveStudentScore(Student student,double score) {
		
		System.out.print("[代理学生]");
		giveScore.giveStudentScore(student, score);
	}

}

学生类

package static_proxy;

import java.util.Objects;

public class Student {

	private String name;//姓名
	private String sex;//性别
	private int id;//学号
	private double score;//分数
	private static int count;//学生个数
	
	/**
	 * 创建一个学生
	 * @param name 姓名
	 * @param sex     性别
	 * @param score  分数
	 */
	public Student(String name, String sex, double score) {
		super();
		count++;
		this.id = count;
		this.name = name;
		this.sex = sex;
		this.score = score;
	}
	
	public Student() {
		super();
		count++;
		this.id = count;
	}

	public String getName() {
		return name;
	}
	
	public void setName(String name) {
		this.name = name;
	}
	
	public String getSex() {
		return sex;
	}
	
	public void setSex(String sex) {
		this.sex = sex;
	}
	
	public int getId() {
		return id;
	}
	
	public void setId(int id) {
		this.id = id;
	}

	public double getScore() {
		return score;
	}
	
	public void setScore(double score) {
		this.score = score;
	}
	
	@Override
	public int hashCode() {
		return Objects.hash(id, name, score, sex);
	}
	
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Student other = (Student) obj;
		return id == other.id && Objects.equals(name, other.name) && score == other.score
				&& Objects.equals(sex, other.sex);
	}
	
	@Override
	public String toString() {
		return "学生[id=" + id + ",name=" + name + ", sex=" + sex +", score=" + score + "]";
	}
	
}

 接口:给出学生分数

package static_proxy;

/*
 * 功能:给学生打分
 */

public interface GiveScore {

	/**
	 * 给出学生分数
	 * @param student 给哪一个学生打分
	 * @param score 打分的多少
	 */
	public void giveStudentScore(Student student,double score);
	
}

通过代理,我们不需要创建老师这个对象,通过代理学生就可以完成对学生进行打分

效果截图:

2.动态代理 

2.1 动态代理基本概念

动态代理是指客户通过代理类来调用其它对象的方法,并且是在程序运行时根据需要动态创建目标类的代理对象。(动态代理是什么?

动态代理具体使用场合

  1. 调试
  2. 远程方法调用

 代理设计模式的原理: 

 使用一个代理将对象包装起来, 然后用该代理对象取代原始对象. 任何对原始对象的调用都要通过代理. 代理对象决定是否以及何时将方法调用转到原始对象上。

2.2 代码示例

上面的代码有些还可以利用:老师类学生类接口

ProxyInvocationHandler类:专门用来创建一个代理类【将获取代理类对象封装起一个方法

package dynamic_proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/*
 * 代理类
 */

//动态代理要实现接口:InvocationHandler
public class ProxyInvocationHandler implements InvocationHandler{
	
	//实现了接口的被代理类的对象的声明【在这里:老师类对象的声明】
	Object object;
	
	//创建代理类的对象
	public Object create(Object object) {
		
		this.object = object;//实例化被代理类的对象
		
		//返回代理类的实例化对象
		return Proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces(),this);
	}
	
	//重写接口中的方法
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		
		System.out.print("[代理类]");
		Object returnValue =  method.invoke(object,args);//调用object的方法
		
		//返回调用方法的返回值
		return returnValue;
	}
}

 TestProxy类:用来测试用的

package dynamic_proxy;

public class TestProxy {

	public static void main(String[] args) {
		
		//1.创建一个被代理类
		Teacher teacher = new Teacher();
		
		//2.创建一个实现了InvocationHandler接口的类的对象
		ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler();
		
		//3.创建一个代理类【该代理类同样实现了被代理类实现接口GiveScore】
		GiveScore proxy =  (GiveScore) proxyInvocationHandler.create(teacher);//填入的是被代理类
		
		//4.创建一个学生【被代理类要操作的对象】
		Student student = new Student("张三","男",0);
		System.out.println(student);
		
		//5.通过代理类可以调用方法【调用的方法与被代理类一致】
		proxy.giveStudentScore(student, 78.5);
		System.out.println(student);
		
	}
}

2.3 动态代理讲解

2.3.1 创建实现InvocationHandler接口的类

InvocationHandler 接口要实现动态代理,这是代码中重要的一部分

2.3.2 获取代理类的对象 

获取代理类的对象

2.3.3 使用代理类

主要分为5个步骤:

  1. 创建一个被代理类
  2. 创建一个实现了InvocationHandler接口的类的对象【相当于代理类仓库
  3. .创建一个代理类【该代理类同样实现了被代理类实现接口GiveScore】
  4. 创建一个学生【被代理类要操作的对象】
  5. 通过代理类可以调用方法调用的方法与被代理类一致

效果截图:

3. 静态代理与动态代理的区别?

区别:

  1. 静态代理中每一个代理类只能为一个接口服务,否则容易出错
  2. 静态代理如果一些被代理类实现的接口不同,需要另外创建代理类【与第1点类似】
  3. 动态代理只需要创建一个实现了InvocationHandler接口的类的对象
  4. 动态代理通过实现了InvocationHandler接口的类的对象 获取不同的代理类(即无需另外去创建代理类)
  5. 总的来说,动态代理必须创建一个实现了InvocationHandler接口的类的对象!

代码示例(通过代码来深入了解动态代理的好处)

新创建一个接口:功能是布置作业

package dynamic_proxy;

/*
 * 布置作业
 */

public interface Homework {

	/**
	 * 布置作业
	 * @param content 作业内容
	 * @param student 具体哪一个学生
	 */
	public void assignHomework(Student student,String content);
	
}

 再创建一个Machine类:通过机器实现布置作业

package dynamic_proxy;

/*
 * 再创建一个被代理类
 */

//实现接口Homework
public class Machine implements Homework{

	@Override
	public void assignHomework(Student student,String content) {
		
		System.out.println("机器给" + student.getName() + "布置的作业为:" + content);
		
	}
}

从这里开始,将分为动态代理静态代理两部分来讲解 

 如果是动态代理的话,直接获取代理类即可

 动态代理完整源码:

package dynamic_proxy;

public class TestProxy {

	public static void main(String[] args) {
		
		//1.创建一个被代理类
		Teacher teacher = new Teacher();
		
		//2.创建一个实现了InvocationHandler接口的类的对象
		ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler();
		
		//3.创建一个代理类【该代理类同样实现了被代理类实现接口GiveScore】
		GiveScore proxy =  (GiveScore) proxyInvocationHandler.create(teacher);//填入的是被代理类
		
		//4.创建一个学生【被代理类要操作的对象】
		Student student = new Student("张三","男",0);
		System.out.println(student);
		
		//5.通过代理类可以调用方法【调用的方法与被代理类一致】
		proxy.giveStudentScore(student, 78.5);
		System.out.println(student);
		
		/*
		 * 再增加一个被代理类
		 */
		Student student2 = new Student("小红","女",0);
		Machine machine = new Machine();
		System.out.println(student2);
		
		//获取代理类
		Homework proxy1 =  (Homework) proxyInvocationHandler.create(machine);
		
		proxy1.assignHomework(student2, "背古诗");
		
	}
}

 效果截图:

静态代理 :需要返回代理类,进行添加相应的构造器(参数:被代理类的对象的声明)

 添加构造器,被代理类的实现接口

 添加了新的实现接口,需要重写相应的重写方法

 在一个代理类中修改后,会出现一定的问题。

  1. 比如你代理的是Teacher,那么该代理是可以访问assignHomework方法,这会导致空指针异常
  2. 这样,该代理类就不合理。我们应该创建2个不同的代理类 这就是静态代理是一个代理类只能访问一个接口的原因

所以在使用代理类的时候,需要特别注意一些代码,要将其注释掉

静态代理完整代码:

package static_proxy;

public class TestProxy {

	public static void main(String[] args) {
		
		//创建一个学生
		Student student1 = new Student("张三","男",0);
		
		//创建一个学生
		Student student2 = new Student("小花","女",0);
		
		System.out.println(student1);
		System.out.println(student2);
		
		//创建一个代理类
		ProxyStudent proxyStudent = new ProxyStudent(new Teacher());
		ProxyStudent proxyStudent2 = new ProxyStudent(new Machine());
		
		//给student[张三]打分
		proxyStudent.giveStudentScore(student1, 78.5);
//		proxyStudent.assignHomework(student1,"背书");
		
		//给student[小花]打分
//		proxyStudent2.giveStudentScore(student2, 89.2);
		proxyStudent2.assignHomework(student1,"写字");
		
		//打印输出学生信息
		System.out.println(student1);
		System.out.println(student2);
		
	}
}

效果截图:

 完


【最后】附上项目结构图:

静态代理:

 动态代理

posted @ 2022-04-19 00:04  辰梦starDream  阅读(1)  评论(0编辑  收藏  举报  来源