第一章 动态代理
1.1 概念
1
| 代理就是被代理者没有能力或者不愿意去完成某件事情,需要找个人代替自己去完成这件事,动态代理就是用来对业务功能(方法)进行代理的。
|
1
| 动态代理,提供了一个代理的对象,有了代理对象后,当访问某个方法时,会被代理对象拦截(拦截后可以对方法进行前增强、后增强【代理对象不会破坏原有方法的代码】)
|
1.2 好处
1 2 3 4
| 非常的灵活,支持任意接口类型的实现类对象做代理,也可以直接为接本身做代理。 可以为被代理对象的所有方法做代理。 可以在不改变方法源码的情况下,实现对方法功能的增强。 不仅简化了编程工作、提高了软件系统的可扩展性,同时也提高了开发效率。
|
1.3 动态代理的特点
1 2 3 4
| 1.动态的创建.class文件 2.动态的加载.class文件到内存中(创建Class对象) 3.Proxy类是java中定义好的专门创建代理对象的类,该类需要一个"类加载器" 4.对程序员来讲,不需要书写代理类
|
1.4 动态代理的代码实现格式
1
| 代理对象 = Proxy.newProxyInstance(类加载器 , 父接口 , 处理器)
|
1 2 3
| 类加载器: 动态的加载.class文件 父接口 : 代理类和被代理类需要拥有共同的父接口 处理器: 代理对象拦截了方法后,对方法进行前增强、后增强,是由处理器来书写逻辑
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| 代理对象 = Proxy.newProxyInstance( 类.class.getClassLoader(), 被代理类.class.getInterfaces(), new InvocationHandler(){ public Object invoke( Object 代理对象, Method 被拦截的方法对象 , Object[] 方法中的实参 ){ } } )
|
1.5 代码实现
接口
1 2 3 4 5 6 7 8 9 10
| public interface Human { void dance(float money); void sing(float money); void eat(); }
|
实现类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class SpringBrother implements Human { @Override public void dance(float money) { System.out.println("拿到" + money + "钱,开跳...."); } @Override public void sing(float money) { System.out.println("拿到" + money + "钱,开唱...."); } @Override public void eat() { System.out.println("吃地沟油盖饭..."); } }
|
测试类1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
|
public class MyCreateProxyFactory {
private MyCreateProxyFactory() { }
public static Object getProxyInstance(Object target) { System.out.println("创建代理对象...");
return Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("开始执行代理逻辑"); Object res = method.invoke(target, args); System.out.println("结束执行代理逻辑"); return res; } } ); } }
public class Test { public static void main(String[] args) { LDH ldh = new LDH(); Star proxy = (Star) MyCreateProxyFactory.getProxyInstance(ldh);
proxy.sing(); proxy.dance(); } }
|
逐行解释三个参数:
target.getClass().getClassLoader()
- 类加载器:Java 需要知道用哪个类加载器去动态生成代理类的字节码。
- 这里直接用目标对象的类加载器,保证代理类和目标类在同一个环境中。
target.getClass().getInterfaces()
- 接口数组:JDK 动态代理要求目标对象必须实现至少一个接口。代理对象会实现相同的接口,这样代理对象就能被当作接口类型来使用。
- 如果你传一个没有实现任何接口的普通类(比如
Person 但没有 implements 任何东西),这里会报错。
new InvocationHandler() { ... }
- 调用处理器:这是代理逻辑的核心。当有人调用代理对象的任何方法时,实际都会执行这个
invoke 方法。
invoke 的三个参数:
proxy:代理对象本身(一般用不到)
method:当前被调用的方法对象(比如 sayHello)
args:调用该方法时传入的参数数组
- 方法体中:
- 你可以在
method.invoke(target, args) 前后做各种增强操作(日志、校验、缓存等)。
method.invoke(target, args) 就是真正去执行目标对象的方法。
- 最后返回结果。
第二章 动态代理练习
1 2 3 4 5 6
| public interface A {
public void a1(int a); public double a2(double d);
}
|
1 2 3 4 5 6 7 8 9 10 11 12
| public class AZi implements A { @Override public void a1(int a) { System.out.println("AZi.a1方法得到的实参是:"+a); }
@Override public double a2(double d) { System.out.println("AZi.a2方法得到的实参是:"+d+",即将返回的是:"+d*2); return d*2; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
|
public class MyCreateProxyFactory2 { private MyCreateProxyFactory2(){
}
public static Object getProxyInstance(Object target){ return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), (p,m,a)->{ Class<?> ac = target.getClass(); String name = ac.getName(); String mname = m.getName(); System.out.println("即将执行: " + name + " 的 " + mname + " 方法,原始参数是什么: " + Arrays.toString( a)); if("a1".equals(mname)){ a[0] = (int)(Integer.parseInt(a[0].toString())*0.3); }else if("a2".equals(mname)){ a[0] = Double.parseDouble(a[0].toString())*0.3; Object invoke = m.invoke(target, a); return 2*Double.parseDouble(invoke.toString()); } Object invoke = m.invoke(target, a); return invoke;
}); } }
|
1 2 3 4 5 6 7 8 9 10 11
| public class Test { public static void main(String[] args) { AZi aZi = new AZi(); A o = (A) MyCreateProxyFactory2.getProxyInstance(aZi);
double v = o.a2(200.0); System.out.println(o.toString()); System.out.println(v); } }
|
第三章 动态代理练习
3.1 需求
3.2 接口
1 2 3 4 5 6 7 8 9 10 11 12 13
|
public interface UserService { String login(String loginName , String passWord) ; void selectUsers(); boolean deleteUsers(); void updateUsers(); }
|
3.3 接口实现类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| public class UserServiceImpl implements UserService{ @Override public String login(String loginName, String passWord) { try { Thread.sleep(1000); } catch (Exception e) { e.printStackTrace(); } if("admin".equals(loginName) && "1234".equals(passWord)) { return "登录成功"; } return "登录名和密码可能有毛病";
}
@Override public void selectUsers() { System.out.println("查询了100个用户数据!"); try { Thread.sleep(2000); } catch (Exception e) { e.printStackTrace(); } }
@Override public boolean deleteUsers() { try { System.out.println("删除100个用户数据!"); Thread.sleep(500); return true; } catch (Exception e) { e.printStackTrace(); return false; } }
@Override public void updateUsers() { try { System.out.println("修改100个用户数据!"); Thread.sleep(2500); } catch (Exception e) { e.printStackTrace(); } } }
|
3.4 创建代理对象的工具类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
|
public class ProxyUtil {
public static <T> T getProxy(T obj) { return (T)Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { long startTimer = System .currentTimeMillis(); Object result = method.invoke(obj, args);
long endTimer = System.currentTimeMillis(); System.out.println(method.getName() + "方法耗时:" + (endTimer - startTimer) / 1000.0 + "s");
return result; } }); } }
|
3.5 测试类
1 2 3 4 5 6 7 8 9 10 11
| public class Test { public static void main(String[] args) { UserService userService = ProxyUtil.getProxy(new UserServiceImpl()); System.out.println(userService.login("admin", "1234")); System.out.println(userService.deleteUsers()); userService.selectUsers(); userService.updateUsers(); } }
|
使用CGLIB工具
概念
cglib(Code Generation Library)是一个基于 Java 字节码技术的代码生成库,主要用于在运行时动态生成类和方法,实现类的增强或代理功能。它广泛应用于 AOP(面向切面编程)、ORM(对象关系映射)等场景,是许多 Java 框架(如 Spring、Hibernate)的底层依赖之一。
核心原理
cglib 的核心能力是通过继承目标类的方式生成代理类(子类),并在子类中重写父类的方法,从而在方法执行前后插入增强逻辑(如日志、事务控制等)。
与 JDK 动态代理(基于接口生成代理类)不同,cglib 不要求目标类实现接口,因此可以代理没有接口的类。
主要应用场景
- AOP 增强:当目标对象没有实现接口时,Spring AOP 会默认使用 cglib 生成代理类,实现方法的前置、后置等切面逻辑。
- ORM 懒加载:Hibernate 等 ORM 框架使用 cglib 生成实体类的代理,实现数据的延迟加载(仅在真正使用数据时才从数据库查询)。
- 动态类扩展:在运行时为类动态添加方法或修改行为,无需手动编写代码。
与 JDK 动态代理的区别
| 特性 |
JDK 动态代理 |
cglib |
| 依赖条件 |
目标类必须实现接口 |
无接口要求(基于继承) |
| 代理方式 |
生成接口的实现类 |
生成目标类的子类 |
| 局限性 |
无法代理无接口的类 |
无法代理 final 类 / 方法(因无法继承重写) |
| 底层依赖 |
JDK 原生 API |
依赖 ASM 字节码操作框架 |
代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| public class B { public void b1(int b){ System.out.println("B类的b1,实参="+b); }
public Double b2(Double b){ System.out.println("B类的b2,实参="+b); return b*2; } }
public class TestB { public static void main(String[] args) { B b = new B(); B proxy = getProxy(b); proxy.b1(100); Double v = proxy.b2(200.0); System.out.println(v); }
public static B getProxy(B b) {
Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(B.class); MethodInterceptor mi = ( mb, method, args, methodProxy )->{ String name = method.getDeclaringClass().getName(); String name1 = method.getName(); System.out.println("即将执行的是"+name+"类中的"+name1+"方法,原始实参是:"+ Arrays.toString(args)); Object o = methodProxy.invokeSuper(mb, args); System.out.println("方法执行完毕,返回值是:"+o); return o; }; enhancer.setCallback(mi); return (B)enhancer.create(); }
}
|
注意事项
cglib 生成的代理类是目标类的子类,因此目标类不能被声明为final,否则会抛出异常。
目标类中的final方法无法被代理(因子类不能重写 final 方法)。
由于涉及字节码生成,cglib 的初始化开销通常比 JDK 动态代理略大,但运行时性能在现代 JVM 中差异不大。
由于jdk9新增了模块化技术,因此使用cglib生成代理对象的时候,需要给类添加如下的 vm options 参数:
1
| --add-opens java.base/java.lang=ALL-UNNAMED
|
总之,cglib 是 Java 生态中实现动态代理的重要工具,尤其在需要代理无接口类的场景中发挥关键作用。