Java如何生成@Mapper、@FeignClient等注解实现?是否运行时生成类?
Java 接口动态实现:@Mapper、@FeignClient 原理与通用方案
一、@Mapper(MyBatis)与@FeignClient的实现类生成时机
两者都不会生成物理的.class文件,而是在运行时通过动态代理机制生成代理实例,来承接接口方法的具体逻辑。
1. @Mapper(MyBatis)的实现逻辑
MyBatis在应用启动时,通过MapperScannerConfigurer或MapperFactoryBean扫描所有带@Mapper注解的接口,然后用JDK动态代理为每个接口生成代理实例。当你调用Mapper接口的方法时,代理类会拦截调用,解析方法上的注解(或XML中的SQL配置),执行对应的JDBC操作,最后封装结果返回。
比如调用userMapper.selectById(1)时,代理类会把这个方法映射为对应的查询SQL,执行后将结果封装为User对象返回。
2. @FeignClient(Spring Cloud Feign)的实现逻辑
Spring Cloud Feign在应用启动阶段,扫描所有标注@FeignClient的接口,通过**Spring动态代理(默认JDK动态代理,可配置为CGLIB)**生成代理类。代理类会把接口方法的调用转换成HTTP请求:
- 从
@FeignClient的name/url属性确定目标服务地址 - 解析
@GetMapping/@PostMapping等注解确定请求路径和方法 - 自动封装请求参数、发送HTTP请求、转换响应结果
二、Java运行时为接口生成实现类的通用方案
1. JDK动态代理
仅适用于代理接口,基于java.lang.reflect.Proxy和InvocationHandler实现:
public class ProxyHandler implements InvocationHandler { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 这里实现接口方法的具体逻辑 System.out.println("触发方法:" + method.getName()); return "默认返回值"; } } // 生成代理实例 MyInterface proxy = (MyInterface) Proxy.newProxyInstance( MyInterface.class.getClassLoader(), new Class[]{MyInterface.class}, new ProxyHandler() );
2. CGLIB动态代理
可代理类(无接口也能生效),基于ASM字节码库生成目标类的子类:
public class CglibInterceptor implements MethodInterceptor { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { // 方法执行前逻辑 System.out.println("方法执行前"); Object result = proxy.invokeSuper(obj, args); // 方法执行后逻辑 System.out.println("方法执行后"); return result; } } // 生成代理实例 Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(MyClass.class); enhancer.setCallback(new CglibInterceptor()); MyClass proxy = (MyClass) enhancer.create();
Spring、MyBatis等框架在需要代理类时会优先使用CGLIB。
3. 字节码直接生成(ASM/Javassist)
通过操作字节码直接生成类,灵活性最高但复杂度也高:
- ASM:底层字节码操作,需要熟悉Java字节码指令
- Javassist:提供上层API,支持用Java代码片段生成类
比如用Javassist生成接口实现类:
ClassPool pool = ClassPool.getDefault(); // 创建实现类 CtClass implClass = pool.makeClass("com.example.MyInterfaceImpl"); CtClass interfaceClass = pool.get("com.example.MyInterface"); implClass.addInterface(interfaceClass); // 添加方法实现 CtMethod method = CtMethod.make( "public String sayHello() { return \"Hello from Javassist\"; }", implClass ); implClass.addMethod(method); // 加载类并创建实例 Class<?> clazz = implClass.toClass(); MyInterface instance = (MyInterface) clazz.getDeclaredConstructor().newInstance();
4. 框架封装的动态代理
很多框架已经封装了动态代理逻辑,直接使用即可:
- Spring AOP:通过
@Aspect定义切面,自动为目标类生成代理 - MyBatis:自动为Mapper接口生成代理实例,无需手动处理
总结
- @Mapper和@FeignClient都是在运行时生成代理实例,不会生成物理的.class文件
- Java运行时生成接口实现类的核心是动态代理或字节码操作,常用方案包括JDK动态代理、CGLIB、ASM/Javassist,以及各类框架封装的现成方案
内容的提问来源于stack exchange,提问作者daniel-lopez
相关产品推荐
相关产品推荐

