动态代理类的字节码在程序运行时由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