动态代理类的字节码在程序运行时由Java反射机制动态生成,无需程序员手工编写它的源代码。 动态代理类不仅简化了编程工作,而且提高了软件系统的可扩展性,因为使用它可以生成任意类型的动态代理类 动态代理方式: 1.jdk提供的:java.lang.reflect包下面的Proxy类和InvocationHandler接口提供了生成动态代理类的能力 2.第三方架包提供的(CGLib代理)
案例:模拟在增删查学生时进行日志输出学生类Student.java
public class Student { private long id; private String name; public Student(long id, String name) { super(); this.id = id; this.name = name; } public Student() { super(); } public long getId() { return id; } public void setId(long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Student [id=" + id + ", name=" + name + "]"; } }日志类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提供的动态生成代理类的方式: 创建类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