淄博网站制作公司推广工业设计师

张小明 2025/12/29 19:15:12
淄博网站制作公司推广,工业设计师,杭州谷歌seo公司,江苏城乡建设部网站首页一、前言 自从有了AI、大模型、DeepSeek、豆包、GPT......#xff0c;就再也没写过技术文章了。毕竟#xff0c;在它们面前写什么内容都是多余的。我甚至问过AI“AI 时代写技术博客还有意义吗”这个问题#xff0c;它给出了如下结论#xff1a; 结论#xff1a;AI 时代就再也没写过技术文章了。毕竟在它们面前写什么内容都是多余的。我甚至问过AI“AI 时代写技术博客还有意义吗”这个问题它给出了如下结论结论AI 时代技术博客不仅有意义反而变得 更重要 了。 虽然 AI 能快速生成内容但真正的价值恰恰在于人类的独特思考和实践经验 这是 AI 无法完全复制的 极值而 AI 只能产出 平均值。行动建议不要犹豫是否开始而是思考如何将 AI 变成你的创作助手。 本周就选定一个小而精的技术主题尝试 人类构思 AI 辅助 的创作模式体验效率与质量的双重提升。记住在 AI 时代最有价值的不是 写什么而是 你为什么这样写 的独特视角。总感觉它在一本正经的胡说八道但又拿不出来证据偶尔还会被它的“心灵鸡汤”戳中。有时候想想也是AI 是很牛逼但它写不出我深夜改 bug 改到脱发的崩溃AI 是很万能但它写不出一个 bug 是 bug两个 bug 是 feature的玄学代码AI 是能给方案但它给不出在办公室用底层原理驯服测试小姐姐的那种拿捏感。以上便是今天这篇博文的引子字里行间表达了作者对故乡的思念之情对童年时光的怀念之情爱国之情对小日子的痛恨之情等等。二、背景问MyBatis为什么写一个public interface UserMapper接口类就能访问数据库问OpenFeign为什么写一个public interface UserFeignClient接口类就能发送HTTP请求此时你一脸懵逼的说我平常项目就是这么开发的接口会调用xml中我写好的sql接口会调用我注解中的url地址。遗憾的是要是面试时你这么答面试官大概率直接给你打零分 —— 他要的不是 “怎么用”而是 “为什么能这么用”。要搞懂这些问题的核心就绕不开 “动态代理”—— 这正是面试官想考察的底层思维咱们今天就掰烂了揉碎了说说“动态代理的那些事儿”。接下来咱们不背八股直接手搓一个类 OpenFeign 的 “MyHttp”把动态代理扒明白。三、手搓一个“MyHttp”我们要实现的东西暂且叫做“MyHttp”他的目标就是像OpenFeign一样定义一个接口就能发送HTTP请求不需要任何配置和任何实现类。我们首先来看常规调用HTTP接口的代码大概长下面这个样子private static RegisterResponse registerUser(RegisterUserRequest requestParam) throws IOException, ParseException { try (CloseableHttpClient httpClient HttpClients.createDefault()) { //构建POST请求 HttpPost httpPost new HttpPost(http://localhost:8080/api/user/register); //设置请求头 httpPost.setHeader(Content-Type, ContentType.APPLICATION_JSON.toString()); //将请求参数序列化为JSON字符串 String requestJson OBJECT_MAPPER.writeValueAsString(requestParam); HttpEntity requestEntity new StringEntity(requestJson, ContentType.APPLICATION_JSON); httpPost.setEntity(requestEntity); //执行请求获取响应 try (CloseableHttpResponse response httpClient.execute(httpPost)) { //解析响应实体 HttpEntity responseEntity response.getEntity(); if (responseEntity null) { throw new RuntimeException(注册接口返回空响应); } //将响应JSON字符串反序列化为实体类 String responseJson EntityUtils.toString(responseEntity); return OBJECT_MAPPER.readValue(responseJson, RegisterResponse.class); } } }“MyHttp”的目标是这个样子HttpClient(baseUrlhttp://localhost:8080/api) public interface UserHttp { HttpPost(url /user/register) RegisterResponse registerUser(RegisterUserRequest requestParam); }看起来是不是很清爽接下来我们基于动态代理一步一步实现它。四、先搞几个核心注解HttpClient放到接口类上表示这是一个基于“MyHttp”的接口放一个属性baseUrl定义这个接口下的所有HTTP调用的url根路径Retention(RetentionPolicy.RUNTIME) Target(ElementType.TYPE) public interface HttpClient { String baseUrl() default ; }当然如果你愿意还可以扩展其它属性比如你想设置连接超时时间、读超时时间再比如你的baseUrl是动态的或者是个地址列表要负载均衡去调用等等Retention(RetentionPolicy.RUNTIME) Target(ElementType.TYPE) public interface HttpClient { String baseUrl() default ; int connectTimeout() default -1; int readTimeout() default -1;Class? extends BaseUrlSource baseUrlSource() default BaseUrlSource.class; } public interface BaseUrlSource { ListString getBaseUrls(); }HttpPost放在接口方法上表示这个方法具体要调用哪个接口报文头怎么设置超时参数等等Retention(RetentionPolicy.RUNTIME) Target({ElementType.METHOD}) public interface HttpPost { String url() default ; String contentType() default MediaType.APPLICATION_JSON_VALUE; int connectTimeout() default -1; int readTimeout() default -1; }当然如果你愿意仍然可以扩展很多很多属性但这不是本文重点。五、再实现一下InvocationHandler简单说一下InvocationHandler 是 JDK 动态代理的 “调用处理器”当我们通过Proxy.newProxyInstanceJDK 动态代理的核心方法作用是 “绑定接口和代理逻辑生成最终可用的代理对象”的方式生成对象并调用目标方法时JVM 会自动将调用转发到 InvocationHandler 的 invoke 方法由该方法完成最终的方法执行 自定义增强逻辑。翻译成人话用 InvocationHandler 把 “被代理的接口” 包一层生成一个 “代理对象”之后调用接口方法时其实是在调代理对象的方法自然就会走进 invoke 里咱们写的逻辑。以下例子中我们便在invoke方法中拿到了被代理的接口类和接口方法这时候我们就能拿到所有注解进而根据注解信息组装HTTP报文并发送请求。/** * HTTP动态代理处理器拦截接口方法调用自动发送HTTP请求 */ public class HttpInvocationHandler implements InvocationHandler { // JSON序列化工具全局复用 private static final ObjectMapper OBJECT_MAPPER new ObjectMapper(); // HttpClient客户端 private static final CloseableHttpClient HTTP_CLIENT HttpClients.createDefault(); // 目标接口的Class对象用于解析注解 private final Class? targetInterface; public HttpInvocationHandler(Class? targetInterface) { this.targetInterface targetInterface; } Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 解析接口级HttpClient注解获取基础URL HttpClient httpClientAnnotation targetInterface.getAnnotation(HttpClient.class); String baseUrl httpClientAnnotation.baseUrl(); // 解析方法级HttpPost注解获取子路径 HttpPost httpPostAnnotation method.getAnnotation(HttpPost.class); String subUrl httpPostAnnotation.url(); String contentType httpPostAnnotation.contentType(); // 拼接完整请求URL String fullUrl baseUrl subUrl; //处理请求参数 String requestJson OBJECT_MAPPER.writeValueAsString(args[0]); //发送HTTP POST请求 HttpPost httpPost new HttpPost(fullUrl); // 设置请求头JSON格式 httpPost.setHeader(Content-Type, contentType); // 设置请求体 HttpEntity requestEntity new StringEntity(requestJson); httpPost.setEntity(requestEntity); // 执行请求并获取响应 try (var response HTTP_CLIENT.execute(httpPost)) { HttpEntity responseEntity response.getEntity(); // 解析响应JSON String responseJson EntityUtils.toString(responseEntity); //响应结果反序列化为方法返回类型 Type returnType method.getGenericReturnType(); return OBJECT_MAPPER.readValue(responseJson, OBJECT_MAPPER.constructType(returnType)); } } }六、再写个代理工厂现在我们只差如何创建代理对象了这也是最后一步这时候我们用到了Proxy.newProxyInstance。这个方法你可以想象成被代理对象通过Proxy.newProxyInstance的方式与代理对象绑定了起来这样当被代理对象的方法被调用时实际就变成了代理对象在帮你调用那么就会进入代理对象的invoke方法从而执行我们的增强逻辑。/** * HTTP代理工厂封装动态代理对象的创建逻辑 */ public class HttpProxyFactory { /** * 创建HTTP接口的代理对象 * param interfaceClass 目标接口Class如UserHttp.class * return 接口代理对象 * param T 接口类型 */ public static T T createProxy(ClassT interfaceClass) { // 创建自定义InvocationHandler HttpInvocationHandler handler new HttpInvocationHandler(interfaceClass); // 生成动态代理对象 return (T) Proxy.newProxyInstance( interfaceClass.getClassLoader(), new Class?[]{interfaceClass}, handler ); } }七、核心逻辑串一串进度条走到这里核心逻辑基本梳理完了咱们先简单总结一下整体流程接口注解定义 → 代理工厂创建代理对象 → 调用接口方法触发 invoke → 解析注解组装 HTTP 请求 → 响应反序列化返回。八、测试一下RestController RequestMapping(/demo) public class DemoController { PostMapping(/register) public RegisterResponse register(RequestBody RegisterUserRequest request) { //创建UserHttp接口的代理对象 UserHttp userHttp HttpProxyFactory.createProxy(UserHttp.class); // 调用接口方法底层自动发送HTTP请求 return userHttp.registerUser(request); } }至此上面的实现已经能跑通但总觉得还缺点什么九、还缺点什么有人说你怎么通过UserHttp userHttp HttpProxyFactory.createProxy(UserHttp.class);的方式才能调用我平常项目里都是这样就能调用了Autowired private UserHttp userHttp;这里就涉及到Spring 的 FactoryBean 接口和注解扫描注册器并不是本文重点但还是给大家补全这个 “实战最后一公里”。首先要实现FactoryBean Spring 的 “特殊 Bean 工厂”专门用来创建 “不是简单 new 出来” 的 Bean比如咱们的动态代理对象/** * 自定义FactoryBean生成HTTP接口的动态代理对象 * param T 目标接口类型如UserHttp */ public class HttpProxyFactoryBeanT implements FactoryBeanT { // 目标接口的Class对象 private ClassT interfaceClass; // 构造器注入接口类型 public HttpProxyFactoryBean(ClassT interfaceClass) { this.interfaceClass interfaceClass; } /** * 创建Bean实例返回动态代理对象 */ Override Nullable public T getObject() throws Exception { // 调用之前的动态代理工厂生成代理对象 return HttpProxyFactory.createProxy(interfaceClass); } /** * 返回Bean的类型接口类型 */ Override public Class? getObjectType() { return interfaceClass; } /** * 单例模式代理对象复用 */ Override public boolean isSingleton() { return true; } }然后实现ImportBeanDefinitionRegistrar扫描所有标记了HttpClient的接口自动注册为 Spring Beanpublic class HttpProxyBeanRegistrar implements ImportBeanDefinitionRegistrar { Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { //创建扫描器只扫描标记HttpClient的接口 ClassPathScanningCandidateComponentProvider scanner new ClassPathScanningCandidateComponentProvider(false); scanner.addIncludeFilter(new AnnotationTypeFilter(HttpClient.class)); //扫描指定包需替换为实际包名 String basePackage com.demo.http; scanner.findCandidateComponents(basePackage).forEach(beanDefinition - { try { //获取接口的Class对象 String className beanDefinition.getBeanClassName(); Class? interfaceClass ClassUtils.forName(className, ClassUtils.getDefaultClassLoader());//构建BeanDefinition指定Bean类型为HttpProxyFactoryBean BeanDefinitionBuilder builder BeanDefinitionBuilder.genericBeanDefinition(HttpProxyFactoryBean.class); //构造器注入接口Class对象 builder.addConstructorArgValue(interfaceClass); //注册BeanBean名称默认用接口类名首字母小写如userHttp registry.registerBeanDefinition(ClassUtils.getShortNameAsProperty(interfaceClass), builder.getBeanDefinition()); } catch (ClassNotFoundException e) { throw new RuntimeException(扫描HTTP接口失败 e.getMessage(), e); } }); } }最后别忘了关键的一步增加一个配置类导入自定义bean注册器Configuration Import(HttpProxyBeanRegistrar.class) public class HttpProxyAutoConfiguration { }十、再测试一下RestController RequestMapping(/demo) public class DemoController { //自动注入UserHttp接口 Autowired private UserHttp userHttp; PostMapping(/register) public RegisterResponse register(RequestBody RegisterUserRequest request) { // 调用接口方法底层自动发送HTTP请求 return userHttp.registerUser(request); } }这个代码是不是就非常有感觉了。有了这一套面试时被问 “OpenFeign 为什么能直接注入接口用”你不光能说清动态代理还能说清 Spring 是怎么管理这些代理 Bean 的直接碾压八股文选手。十一、结语手搓的意义不止于 “会用”好像没啥可说的了用AI生成一段吧写到这咱们的 “MyHttp” 就彻底跑通了 —— 从注解定义到动态代理拦截再到 Spring 自动注入核心逻辑和 OpenFeign、MyBatis 的接口代理思想完全一致。 可能有人会说“有现成的框架用为啥还要手搓” 答案很简单 面试时“会用” 只能拿及格分“懂原理 能手搓” 才能拿 Offer 工作中遇到框架适配问题时底层原理才是你解决问题的底气。 就像面试官问 “OpenFeign 为什么能直接调用接口”你要是能把今天这一套手搓逻辑讲清楚再对比一下 JDK 动态代理和 CGLIB 的区别 他大概率会觉得 “这小子是真懂不是背八股”。以此表达作者对故乡的思念之情对童年时光的怀念之情爱国之情对小日子的痛恨之情等等。
版权声明:本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

金融公司网站规划方案高性能wordpress

第一章:Open-AutoGLM 2.0 云手机Open-AutoGLM 2.0 云手机是新一代基于云端的人工智能自动化平台,专为移动应用场景设计。该系统融合了大语言模型与自动化控制技术,能够在虚拟安卓环境中实现智能操作、任务调度与数据交互,广泛适用…

张小明 2025/12/29 19:15:11 网站建设

美食网站怎么做一个新手怎么做电商运营

终极指南:用KH Coder轻松搞定专业级文本分析 【免费下载链接】khcoder KH Coder: for Quantitative Content Analysis or Text Mining 项目地址: https://gitcode.com/gh_mirrors/kh/khcoder 你是否曾面对海量文本数据感到无从下手?想要从客户反馈…

张小明 2025/12/29 19:14:36 网站建设

云网站建站网红营销成功案例

在亚马逊平台上,A页面正在成为品牌差异化竞争的核心战场,这一工具已从简单的图文展示,演变为集智能创作、交互体验与数据优化于一体的品牌中枢系统,每一次功能升级,都标志着电商沟通从“信息告知”向“价值感知”的深度…

张小明 2025/12/29 19:14:01 网站建设

手机里面的网站怎么制作国外做蒸汽锅炉的网站

GitHub托管PyTorch项目最佳实践:结合镜像提升协作效率 在深度学习项目的开发过程中,团队最常遇到的不是模型调参失败,而是“在我机器上明明能跑”的环境问题。尤其当项目涉及 GPU 加速、CUDA 版本依赖和复杂 Python 包管理时,新成…

张小明 2025/12/29 19:13:28 网站建设

专门建立网站的公司吗seo刷关键词排名工具

在工业生产、商业运营及家庭生活中,设备安装是保障设备正常运转、发挥使用价值的核心环节。小到家庭净水器、空调,大到工厂生产线设备、商业中央空调,每一项设备的安装质量都直接影响后续使用体验、安全性能与使用寿命。尤其在不同地域环境下…

张小明 2025/12/29 19:12:55 网站建设