2.动态代理——jdk提供的方式

动态代理类的字节码在程序运行时由Java反射机制动态生成,无需程序员手工编写它的源代码。
动态代理类不仅简化了编程工作,而且提高了软件系统的可扩展性,因为使用它可以生成任意类型的动态代理类

动态代理方式:
1.jdk提供的:java.lang.reflect包下面的Proxy类和InvocationHandler接口提供了生成动态代理类的能力
2.第三方架包提供的(CGLib代理)


案例:模拟在增删查学生时进行日志输出

学生类Student.java

public class Student {
	private long id;
	private String name;
	//无参 有参构造器
	//get set
}

日志类Logger.java

public class Logger {
	public void log(String msg) {
		System.out.println("log:"+msg);
	}
}

接口类IStudentService.java

public interface IStudentService {
	void save(Student student);
	void delete(long id);
	Student find(long id);
}

实现接口模拟进行具体的曾删改查操作StudentServiceImpl.java

public class StudentServiceImpl implements IStudentService{
	private Logger logger=new Logger();
	@Override
	public void save(Student student) {
		logger.log("sava开始执行");
		System.out.println("sava执行完成,数据插入数据库");
	}

	@Override
	public void delete(long id) {
		logger.log("delete开始执行");
		System.out.println("delete执行完成,数据已删除");
	}

	@Override
	public Student find(long id) {
		logger.log("find开始执行");
		System.out.println("find执行完成,数据已查到");
		Student stu = new Student(1,"tom");
		return stu;
	}
}

测试类:

public class Test {
	public static void main(String[] args) {
		IStudentService service=new StudentServiceImpl();
		service.save(new Student(2, "jack"));
		System.out.println("---------------");
		service.find(1L);
		System.out.println("---------------");
		service.delete(1L);
	}
}

在这里插入图片描述
上面的情况虽然实现了日志输出功能,但是没有采用代理,所以比较麻烦


采用jdk提供的动态生成代理类的方式:
//主要学习参数 loader、interfaces、 h
//loader参数:目标类的加载器
//interfaces参数:目标类所实现的所有接口
//h参数:InvocationHandler接口的实现类对象
Proxy.newProxyInstance(loader, interfaces, h);



创建类MyHandler 实现InvocationHandler,重写里面的方法invoke

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

public class MyHandler implements InvocationHandler{
	private Object target;//目标对象
	private Logger logger=new Logger();//初始化日志,现在写死,将来可以通过注入的方式注入@Component

	public MyHandler(Object target) {//通过构造器将目标对象引入
		this.target=target;
	}
	
	// proxy 参数:将来动态生成的代理类
	// method 参数:将来代理对象调用的方法
	// args 参数:将来代理对象调用方法所传的参数
	// 将来指的是运行的时候,所以称为动态生成
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		//日志打印在执行之前
		logger.log(method.getName()+"方法马上执行");
		//真正执行的功能,由目标类去执行
		Object result = method.invoke(target, args);
		//日志打印在执行之后
		//logger.log(method.getName()+"方法执行完成");
		return result;
	}
}

测试:

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

public class Test {
	public static void main(String[] args) {
		IStudentService target=new StudentServiceImpl();
		//获取目标类Class类型对象
		Class<? > c = target.getClass();
		//获取目标类的类加载器
		ClassLoader loader = c.getClassLoader();
		//获取目标类所实现的所有接口
		Class<?>[] interfaces = c.getInterfaces();
		//处理器
		InvocationHandler h=new MyHandler(target);//将目标对象传里面
		//代表代理类需要的内容
		//loader参数:目标类的加载器
		//interfaces参数:目标类所实现的所有接口
		//h参数:InvocationHandler接口的实现类对象
		IStudentService proxy=(IStudentService) Proxy.newProxyInstance(loader, interfaces, h);
		proxy.save(new Student(1,"tom"));
		System.out.println("-----------------");
		proxy.find(1);
		System.out.println("-----------------");
		proxy.delete(1);
		System.out.println("-----------------");
		System.out.println(target==proxy);//判断代理类的地址和目标类的地址是否一致,false(不一致)
		System.out.println("-----------------");
		System.out.println(target.toString());
		System.out.println("-----------------");
		System.out.println(proxy.toString());//不光自己定义的,类中默认的方法也会被代理
		System.out.println("-----------------");
		System.out.println(target.getClass());//真实地址
		System.out.println(proxy.getClass());//代理地址,因为代理类是动态生成的,不存在的类
	}
}

在这里插入图片描述

步骤:
1.Proxy.newProxyInstance(loader, interfaces, h);生成代理对象proxy
2.往Handler的invoke方法中传入代理对象proxy
3.通过代理类调用代理类中的方法如:proxy.save(new Student(1,“tom”));,
将调用的方法通过反射机制,传给invoke中的参数Method
4.将method对应要传入的参数值赋值给invoke中的参数args
5.运行执行方法


jdk代理流程

在这里插入图片描述

动态代理jdk方式要求:
	1.目标类和代理类实现共同的接口
	2.代理类继承目标类
代理对象代理目标对象哪些方法?
	正常是目标对象中`所有的方法`,但后期学习aop的时候可以指定代理哪些方法
哪些目标对象的方法不能被代理?
	由`final修饰的类不能够被代理`,因为final修饰的方法不能被重写
代理主要是针对方法之前和方法之后完成一些嵌入功能,例如日志输出,权限认证,事务管理...

假如目标类没有实现任何接口,这种情况不能使用jdk代理方式--->CGLib
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值