面试题答案
一键面试为何通常使用接口定义被代理对象的方法
- 接口的灵活性:
- 接口允许多个类实现同一个接口,在动态代理场景下,不同的实现类可以共享同一套代理逻辑。例如,有
UserService
接口,UserServiceImpl1
和UserServiceImpl2
都实现了该接口,通过动态代理可以为这两个不同的实现类提供相同的事务管理、日志记录等代理功能。 - 而抽象类只能被一个具体类继承,一旦继承,具体类就被限制在该抽象类的体系内,不利于代码的复用和扩展。
- 接口允许多个类实现同一个接口,在动态代理场景下,不同的实现类可以共享同一套代理逻辑。例如,有
- 符合代理模式的设计理念:
- 代理模式强调为其他对象提供一种代理以控制对这个对象的访问。接口定义了一组方法的契约,代理对象和被代理对象通过实现相同的接口来保证方法签名的一致性,从而可以在运行时动态地将代理对象替换被代理对象,实现对被代理对象方法的控制访问。
- 抽象类虽然也能定义一些抽象方法,但它可能包含一些具体实现,这与代理模式强调的纯粹的方法契约和代理控制访问的理念不完全契合。
- Java动态代理机制的限制:
- Java的
java.lang.reflect.Proxy
类创建动态代理对象时,要求被代理对象必须实现至少一个接口。这是因为Proxy
类通过实现被代理对象所实现的接口来生成代理类,从而实现对方法的拦截和代理操作。如果使用抽象类,由于Proxy
类不能继承抽象类(Java不支持多继承),所以无法直接基于抽象类创建动态代理。
- Java的
使用抽象类实现动态代理的技术难点及解决办法
- 技术难点:
- 多继承限制:Java不支持多继承,
Proxy
类不能同时继承抽象类和Proxy
父类,导致无法直接基于抽象类创建代理类。 - 方法契约不一致:抽象类可能包含具体方法实现,不同子类对抽象类的具体实现可能有差异,难以像接口那样形成统一的方法契约,给代理逻辑的统一处理带来困难。
- 多继承限制:Java不支持多继承,
- 解决办法:
- 使用CGLIB库:CGLIB(Code Generation Library)是一个强大的,高性能,高质量的Code生成类库,可以在运行期扩展Java类与实现Java接口。它采用继承的方式,为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,织入横切逻辑。通过CGLIB,可以基于抽象类创建代理类,解决了Java多继承限制的问题。
- 规范抽象类设计:在设计抽象类时,尽量减少具体方法的实现,使抽象类更接近接口的功能,定义统一的抽象方法契约。这样在使用CGLIB创建代理时,代理逻辑可以更统一地处理抽象类及其子类的方法调用。例如,在抽象类中只定义抽象方法,将具体实现留给子类,然后在代理逻辑中针对这些抽象方法进行统一的增强处理。