Java在AI时代的崛起:从传统机器学习到AIGC的全栈解决方案

Java在AI时代的崛起:从传统机器学习到AIGC的全栈解决方案
个人名片

🎓作者简介:java领域优质创作者
🌐个人主页码农阿豪
📞工作室:新空间代码工作室(提供各种软件服务)
💌个人邮箱:[[email protected]]
📱个人微信:15279484656
🌐个人导航网站www.forff.top
💡座右铭:总有人要赢。为什么不能是我呢?
  • 专栏导航:
码农阿豪系列专栏导航
面试专栏:收集了java相关高频面试题,面试实战总结🍻🎉🖥️
Spring5系列专栏:整理了Spring5重要知识点与实战演练,有案例可直接使用🚀🔧💻
Redis专栏:Redis从零到一学习分享,经验总结,案例实战💐📝💡
全栈系列专栏:海纳百川有容乃大,可能你想要的东西里面都有🤸🌱🚀

目录

Java在AI时代的崛起:从传统机器学习到AIGC的全栈解决方案

在人工智能浪潮席卷全球的今天,Python凭借其丰富的AI生态系统成为了机器学习和深度学习的首选语言。然而,作为企业级应用开发的王者,Java在AI领域的表现同样不容小觑。本文将深入探讨Java在AI生态中的定位、技术栈以及在AIGC时代的机遇与挑战。

一、Java AI生态概览:多样化的技术选择

Java在AI领域的技术栈可以用"百花齐放"来形容,从传统机器学习到现代深度学习,从自然语言处理到计算机视觉,Java都有相应的解决方案。

1.1 深度学习框架:接轨主流AI技术

Deep Java Library (DJL) - 统一的深度学习接口

DJL是Amazon开源的Java深度学习库,其最大的优势在于提供了统一的API来操作不同的深度学习后端。

// DJL示例:使用预训练模型进行图像分类importai.djl.Application;importai.djl.Model;importai.djl.inference.Predictor;importai.djl.modality.Classifications;importai.djl.modality.cv.Image;importai.djl.modality.cv.ImageFactory;importai.djl.repository.zoo.Criteria;importai.djl.repository.zoo.ModelZoo;importai.djl.repository.zoo.ZooModel;publicclassImageClassificationExample{publicstaticvoidmain(String[] args)throwsException{// 加载预训练的ResNet模型Criteria<Image,Classifications> criteria =Criteria.builder().optApplication(Application.CV.IMAGE_CLASSIFICATION).setTypes(Image.class,Classifications.class).optFilter("layer","50").optEngine("PyTorch").build();try(ZooModel<Image,Classifications> model =ModelZoo.loadModel(criteria)){try(Predictor<Image,Classifications> predictor = model.newPredictor()){Image image =ImageFactory.getInstance().fromUrl("https://example.com/cat.jpg");Classifications classifications = predictor.predict(image);System.out.println("预测结果:"); classifications.items().forEach(classification ->System.out.printf("%s: %.2f%%\n", classification.getClassName(), classification.getProbability()*100));}}}}
Deeplearning4j - 企业级深度学习解决方案

DL4J专为Java生态系统设计,特别适合需要与现有Java应用集成的场景。

// DL4J示例:构建简单的神经网络importorg.deeplearning4j.nn.conf.MultiLayerConfiguration;importorg.deeplearning4j.nn.conf.NeuralNetConfiguration;importorg.deeplearning4j.nn.conf.layers.DenseLayer;importorg.deeplearning4j.nn.conf.layers.OutputLayer;importorg.deeplearning4j.nn.multilayer.MultiLayerNetwork;importorg.deeplearning4j.nn.weights.WeightInit;importorg.nd4j.linalg.activations.Activation;importorg.nd4j.linalg.lossfunctions.LossFunctions;publicclassSimpleNeuralNetwork{publicstaticvoidmain(String[] args){// 构建神经网络配置MultiLayerConfiguration conf =newNeuralNetConfiguration.Builder().seed(123).weightInit(WeightInit.XAVIER).updater(neworg.nd4j.linalg.learning.config.Adam(0.001)).list().layer(0,newDenseLayer.Builder().nIn(784)// 输入层大小.nOut(128)// 隐藏层大小.activation(Activation.RELU).build()).layer(1,newOutputLayer.Builder(LossFunctions.LossFunction.NEGATIVELOGLIKELIHOOD).nIn(128).nOut(10)// 输出类别数.activation(Activation.SOFTMAX).build()).build();MultiLayerNetwork model =newMultiLayerNetwork(conf); model.init();System.out.println("神经网络构建完成,参数数量: "+ model.numParams());}}

1.2 传统机器学习框架:成熟稳定的选择

Weka - 学术界的宠儿

Weka提供了丰富的机器学习算法和可视化工具,既可以通过GUI操作,也可以编程调用。

// Weka示例:使用决策树进行分类importweka.classifiers.trees.J48;importweka.core.Instances;importweka.core.converters.ConverterUtils.DataSource;importweka.filters.Filter;importweka.filters.unsupervised.attribute.Remove;publicclassWekaClassificationExample{publicstaticvoidmain(String[] args)throwsException{// 加载数据集DataSource source =newDataSource("path/to/dataset.arff");Instances dataset = source.getDataSet();// 设置类别属性(最后一列)if(dataset.classIndex()==-1){ dataset.setClassIndex(dataset.numAttributes()-1);}// 创建决策树分类器J48 tree =newJ48(); tree.setUnpruned(true);// 不剪枝// 训练模型 tree.buildClassifier(dataset);// 输出决策树System.out.println("决策树模型:");System.out.println(tree);// 10折交叉验证weka.classifiers.Evaluation eval =newweka.classifiers.Evaluation(dataset); eval.crossValidateModel(tree, dataset,10,newjava.util.Random(1));System.out.println("交叉验证结果:");System.out.println("准确率: "+ eval.pctCorrect()+"%");System.out.println("召回率: "+ eval.weightedRecall());System.out.println("F1-Score: "+ eval.weightedFMeasure());}}
Smile - 现代化的机器学习库

Smile提供了高性能的机器学习算法实现,API设计简洁现代。

// Smile示例:随机森林分类importsmile.classification.RandomForest;importsmile.data.DataFrame;importsmile.data.formula.Formula;importsmile.io.Read;importsmile.validation.CrossValidation;publicclassSmileRandomForestExample{publicstaticvoidmain(String[] args)throwsException{// 读取数据DataFrame data =Read.csv("path/to/data.csv");// 定义公式(目标变量 ~ 特征变量)Formula formula =Formula.lhs("target");// 训练随机森林RandomForest model =RandomForest.fit(formula, data,100);// 100棵树// 交叉验证double[] accuracy =CrossValidation.classification(10, formula, data,(f, x)->RandomForest.fit(f, x,100));System.out.printf("随机森林交叉验证准确率: %.2f%% (+/- %.2f%%)\n",smile.math.MathEx.mean(accuracy)*100,smile.math.MathEx.sd(accuracy)*100);// 特征重要性double[] importance = model.importance();String[] features = data.names();System.out.println("特征重要性排序:");java.util.stream.IntStream.range(0, importance.length).boxed().sorted((i, j)->Double.compare(importance[j], importance[i])).limit(10).forEach(i ->System.out.printf("%s: %.4f\n", features[i], importance[i]));}}

1.3 自然语言处理框架:文本智能处理

Stanford CoreNLP - 功能全面的NLP工具包
// Stanford CoreNLP示例:文本分析管道importedu.stanford.nlp.pipeline.StanfordCoreNLP;importedu.stanford.nlp.pipeline.CoreDocument;importedu.stanford.nlp.ling.CoreAnnotations;importedu.stanford.nlp.sentiment.SentimentCoreAnnotations;importjava.util.Properties;publicclassCoreNLPExample{publicstaticvoidmain(String[] args){// 配置处理管道Properties props =newProperties(); props.setProperty("annotators","tokenize,ssplit,pos,lemma,ner,sentiment"); props.setProperty("coref.algorithm","neural");StanfordCoreNLP pipeline =newStanfordCoreNLP(props);// 待分析文本String text ="Java在人工智能领域展现出强大的潜力。"+"企业级应用中,Java的稳定性和可维护性是重要优势。";// 创建文档并进行注释CoreDocument document = pipeline.processToCoreDocument(text);// 输出分析结果System.out.println("=== 句子分析 ==="); document.sentences().forEach(sentence ->{System.out.println("句子: "+ sentence.text());System.out.println("情感: "+ sentence.sentiment());System.out.println("词汇分析:"); sentence.tokens().forEach(token ->{System.out.printf(" %s [词性: %s, 词元: %s]\n", token.word(), token.get(CoreAnnotations.PartOfSpeechAnnotation.class), token.lemma());});System.out.println("命名实体:"); sentence.entityMentions().forEach(entityMention ->{System.out.printf(" %s: %s\n", entityMention.text(), entityMention.entityType());});System.out.println();});}}

1.4 大数据AI框架:处理海量数据的利器

Apache Spark MLlib - 分布式机器学习
// Spark MLlib示例:大规模文本分类importorg.apache.spark.sql.SparkSession;importorg.apache.spark.sql.Dataset;importorg.apache.spark.sql.Row;importorg.apache.spark.ml.Pipeline;importorg.apache.spark.ml.PipelineStage;importorg.apache.spark.ml.classification.LogisticRegression;importorg.apache.spark.ml.feature.HashingTF;importorg.apache.spark.ml.feature.Tokenizer;importorg.apache.spark.ml.feature.StopWordsRemover;publicclassSparkMLTextClassification{publicstaticvoidmain(String[] args){SparkSession spark =SparkSession.builder().appName("TextClassification").master("local[*]").getOrCreate();// 创建示例数据Dataset<Row> training = spark.createDataFrame(Arrays.asList(newJavaBean("Java是一门优秀的编程语言",1.0),newJavaBean("Python在AI领域很流行",1.0),newJavaBean("这个产品质量很差",0.0),newJavaBean("服务态度需要改进",0.0)),JavaBean.class);// 构建机器学习管道Tokenizer tokenizer =newTokenizer().setInputCol("text").setOutputCol("words");StopWordsRemover remover =newStopWordsRemover().setInputCol("words").setOutputCol("filtered");HashingTF hashingTF =newHashingTF().setNumFeatures(1000).setInputCol("filtered").setOutputCol("features");LogisticRegression lr =newLogisticRegression().setMaxIter(10).setRegParam(0.001);Pipeline pipeline =newPipeline().setStages(newPipelineStage[]{tokenizer, remover, hashingTF, lr});// 训练模型org.apache.spark.ml.PipelineModel model = pipeline.fit(training);// 创建测试数据并预测Dataset<Row> test = spark.createDataFrame(Arrays.asList(newJavaBean("Java开发效率很高",0.0),newJavaBean("这个软件有严重问题",0.0)),JavaBean.class);Dataset<Row> predictions = model.transform(test); predictions.select("text","label","prediction","probability").show(false); spark.stop();}publicstaticclassJavaBean{privateString text;privateDouble label;publicJavaBean(String text,Double label){this.text = text;this.label = label;}// getters and setterspublicStringgetText(){return text;}publicvoidsetText(String text){this.text = text;}publicDoublegetLabel(){return label;}publicvoidsetLabel(Double label){this.label = label;}}}

二、Java在AIGC时代的机遇与挑战

随着ChatGPT、GPT-4等大语言模型的爆火,AIGC(AI Generated Content)成为了AI领域的新热点。Java在这一波浪潮中面临着机遇与挑战并存的局面。

2.1 Spring AI:Java拥抱AIGC的新起点

Spring AI是Spring生态系统在AI领域的重要布局,它为Java开发者提供了友好的AIGC应用开发框架。

// Spring AI示例:构建聊天机器人importorg.springframework.ai.chat.ChatClient;importorg.springframework.ai.chat.ChatResponse;importorg.springframework.ai.chat.prompt.Prompt;importorg.springframework.ai.openai.OpenAiChatClient;importorg.springframework.boot.SpringApplication;importorg.springframework.boot.autoconfigure.SpringBootApplication;importorg.springframework.web.bind.annotation.*;@SpringBootApplication@RestControllerpublicclassSpringAiChatApplication{privatefinalChatClient chatClient;publicSpringAiChatApplication(ChatClient chatClient){this.chatClient = chatClient;}@PostMapping("/chat")publicChatResponsechat(@RequestBodyChatRequest request){Prompt prompt =newPrompt(request.getMessage());return chatClient.call(prompt);}@GetMapping("/generate")publicStringgenerate(@RequestParamString topic){String promptText =String.format("请为我写一篇关于'%s'的技术博客大纲,包含5个主要章节", topic);Prompt prompt =newPrompt(promptText);ChatResponse response = chatClient.call(prompt);return response.getResult().getOutput().getContent();}publicstaticvoidmain(String[] args){SpringApplication.run(SpringAiChatApplication.class, args);}staticclassChatRequest{privateString message;publicStringgetMessage(){return message;}publicvoidsetMessage(String message){this.message = message;}}}

配置文件示例:

# application.ymlspring:ai:openai:api-key: ${OPENAI_API_KEY}chat:options:model: gpt-3.5-turbo temperature:0.7max-tokens:1000server:port:8080logging:level:org.springframework.ai: DEBUG 

2.2 Java AIGC应用架构设计

基于Java技术栈构建AIGC应用的典型架构:

// AIGC服务抽象层设计publicinterfaceAigcService{/** * 文本生成 */GeneratedContentgenerateText(TextGenerationRequest request);/** * 图像生成 */GeneratedContentgenerateImage(ImageGenerationRequest request);/** * 代码生成 */GeneratedContentgenerateCode(CodeGenerationRequest request);/** * 流式文本生成 */Flux<String>generateTextStream(TextGenerationRequest request);}@Service@Slf4jpublicclassAigcServiceImplimplementsAigcService{privatefinalChatClient chatClient;privatefinalImageClient imageClient;privatefinalRedisTemplate<String,Object> redisTemplate;publicAigcServiceImpl(ChatClient chatClient,ImageClient imageClient,RedisTemplate<String,Object> redisTemplate){this.chatClient = chatClient;this.imageClient = imageClient;this.redisTemplate = redisTemplate;}@Override@Cacheable(value ="text-generation", key ="#request.hashCode()")publicGeneratedContentgenerateText(TextGenerationRequest request){ log.info("生成文本内容,prompt: {}", request.getPrompt());try{// 构建提示词Prompt prompt =buildPrompt(request);// 调用大语言模型ChatResponse response = chatClient.call(prompt);// 构建响应returnGeneratedContent.builder().content(response.getResult().getOutput().getContent()).type(ContentType.TEXT).model(request.getModel()).timestamp(System.currentTimeMillis()).usage(buildUsage(response)).build();}catch(Exception e){ log.error("文本生成失败", e);thrownewAigcException("文本生成失败: "+ e.getMessage());}}@OverridepublicFlux<String>generateTextStream(TextGenerationRequest request){returnFlux.create(sink ->{try{Prompt prompt =buildPrompt(request);// 流式调用 chatClient.stream(prompt).subscribe( response ->{String content = response.getResult().getOutput().getContent(); sink.next(content);}, error ->{ log.error("流式生成失败", error); sink.error(error);},()-> sink.complete());}catch(Exception e){ sink.error(e);}});}privatePromptbuildPrompt(TextGenerationRequest request){PromptTemplate template =newPromptTemplate(request.getTemplate());return template.create(request.getVariables());}privateUsagebuildUsage(ChatResponse response){returnUsage.builder().promptTokens(response.getMetadata().getUsage().getPromptTokens()).completionTokens(response.getMetadata().getUsage().getGenerationTokens()).totalTokens(response.getMetadata().getUsage().getTotalTokens()).build();}}

2.3 AIGC应用的微服务架构

// 网关层:统一入口和负载均衡@RestController@RequestMapping("/api/v1/aigc")@Slf4jpublicclassAigcGatewayController{privatefinalAigcOrchestrationService orchestrationService;privatefinalRateLimitService rateLimitService;@PostMapping("/generate")publicResponseEntity<GeneratedContent>generate(@RequestBodyGenerationRequest request,@RequestHeader("User-Id")String userId){// 限流检查if(!rateLimitService.isAllowed(userId)){returnResponseEntity.status(429).build();}// 路由到对应的服务GeneratedContent content = orchestrationService.generate(request);returnResponseEntity.ok(content);}@GetMapping("/generate/stream")publicResponseEntity<Flux<ServerSentEvent<String>>>generateStream(@RequestParamString prompt,@RequestHeader("User-Id")String userId){Flux<ServerSentEvent<String>> stream = orchestrationService .generateStream(prompt).map(content ->ServerSentEvent.<String>builder().data(content).build());returnResponseEntity.ok().header("Content-Type","text/event-stream").body(stream);}}// 编排服务:协调多个AI服务@ServicepublicclassAigcOrchestrationService{privatefinalMap<String,AigcService> aigcServices;privatefinalModelLoadBalancer loadBalancer;publicGeneratedContentgenerate(GenerationRequest request){// 根据请求类型选择合适的服务AigcService service =selectService(request.getType());// 选择最优模型String model = loadBalancer.selectBestModel(request); request.setModel(model);return service.generate(request);}privateAigcServiceselectService(ContentType type){returnswitch(type){case TEXT -> aigcServices.get("textService");case IMAGE -> aigcServices.get("imageService");case CODE -> aigcServices.get("codeService");default->thrownewUnsupportedOperationException("不支持的内容类型: "+ type);};}}

三、Java AI技术栈的优势与适用场景

3.1 企业级应用的天然优势

Java在AI领域的最大优势在于其在企业级应用中的成熟度和稳定性。对于需要将AI能力集成到现有业务系统中的企业来说,Java提供了无缝的集成体验。

高并发处理能力

// 异步处理大量AI请求@ServicepublicclassAsyncAigcService{@Async("aigcThreadPool")@CompletableFuture<GeneratedContent>publicCompletableFuture<GeneratedContent>generateAsync(GenerationRequest request){// 异步执行AI生成任务returnCompletableFuture.supplyAsync(()->{return aigcService.generate(request);});}publicList<GeneratedContent>batchGenerate(List<GenerationRequest> requests){List<CompletableFuture<GeneratedContent>> futures = requests.stream().map(this::generateAsync).collect(Collectors.toList());return futures.stream().map(CompletableFuture::join).collect(Collectors.toList());}}

完善的监控和运维体系

// AI服务监控@ComponentpublicclassAigcMetrics{privatefinalMeterRegistry meterRegistry;privatefinalCounter requestCounter;privatefinalTimer responseTimer;publicAigcMetrics(MeterRegistry meterRegistry){this.meterRegistry = meterRegistry;this.requestCounter =Counter.builder("aigc.requests").description("AIGC请求总数").register(meterRegistry);this.responseTimer =Timer.builder("aigc.response.time").description("AIGC响应时间").register(meterRegistry);}publicvoidrecordRequest(String model,String status){ requestCounter.increment(Tags.of("model", model,"status", status));}publicTimer.SamplestartTimer(){returnTimer.start(meterRegistry);}}

3.2 适用场景分析

最适合Java的AI场景:

  1. 企业级AI平台构建 - 需要高可用、高并发的AI服务平台
  2. 现有系统AI改造 - 为现有Java系统添加AI能力
  3. AI服务化部署 - 将AI模型包装为微服务
  4. 复杂业务逻辑AI应用 - 需要复杂业务规则的AI系统

不太适合的场景:

  1. 算法研究和原型开发 - Python更适合快速实验
  2. 纯深度学习模型训练 - GPU加速和数值计算优化不如Python
  3. 计算机视觉核心算法 - OpenCV-Python生态更完整

四、未来展望:Java AI生态的发展方向

4.1 技术发展趋势

  1. 更深度的云原生集成 - 与Kubernetes、Istio等云原生技术深度结合
  2. 边缘AI部署 - GraalVM Native Image支持边缘设备部署
  3. 企业级AI中台 - 构建统一的企业AI能力平台
  4. 多模态AI应用 - 文本、图像、语音的统一处理框架

4.2 生态完善路径

Java AI生态的完善需要在以下几个方面持续努力:

  1. 降低使用门槛 - 提供更多开箱即用的解决方案
  2. 性能优化 - 在数值计算和GPU加速方面追赶Python
  3. 社区建设 - 培养更多Java AI开发者和贡献者
  4. 标准化 - 建立Java AI框架的标准规范

结语

Java在AI时代并非被边缘化,而是在寻找自己的独特定位。虽然在算法研究和模型训练方面Python依然占据主导地位,但在企业级AI应用、AI服务化部署、以及将AI能力集成到现有业务系统方面,Java展现出了独特的优势。

随着AIGC技术的普及和企业数字化转型的深入,Java在AI领域的价值将进一步凸显。对于Java开发者来说,现在正是学习和掌握AI技术的最佳时机。通过合理利用Java的生态优势,结合现代AI技术,我们完全可以构建出高质量、高性能的AI应用系统。

未来的AI应用不仅需要强大的算法能力,更需要稳定可靠的工程实现。而这正是Java的强项所在。让我们拥抱AI时代,用Java构建更智能的未来!

Read more

Obsidian同步太折腾?试试坚果云官方插件:免WebDAV配置,支持Git级冲突合并

Obsidian同步太折腾?试试坚果云官方插件:免WebDAV配置,支持Git级冲突合并

Obsidian 作为本地 Markdown 笔记软件的王者,其“数据掌握在自己手中”的理念深受开发者喜爱。但作为一名多端用户,同步问题一直是最大的痛点。官方 Sync 服务太贵,WebDAV 配置繁琐且不仅容易断连,还经常遇到笔记冲突。 终于,大家催了无数遍的 Obsidian x 坚果云「官方同步插件 Nutstore Sync」 正式上架社区插件市场了! 这不仅仅是一个同步工具,更是一套完整的移动端解决方案。 为什么推荐这款官方插件? 1. 告别复杂的 WebDAV 配置(SSO单点登录) 以前配置 WebDAV,你需要生成应用密码、复制服务器地址、担心端口被封。 现在,安装 Nutstore Sync 后,直接点击“登录”,通过单点登录 授权,一键回调到 Obsidian,配置过程缩短到秒级,新手极其友好。

By Ne0inhk
什么是weblogic?一文带你了解

什么是weblogic?一文带你了解

Weblogic 简介 WebLogic 是 Oracle 公司开发的一款企业级 Java EE(Java Platform, Enterprise Edition)应用服务器,广泛用于构建、部署和管理分布式应用。它支持高可用性、可扩展性和安全性,适用于大型企业环境。WebLogic 提供了完整的 Java EE 标准实现,包括 Servlet、JSP、EJB、JMS 等技术,同时集成了多种管理工具和监控功能。 Weblogic 核心功能 * Java EE 支持:完全兼容 Java EE 标准,支持企业级应用开发。 * 集群与负载均衡:支持多服务器集群,提供高可用性和故障转移能力。 * 安全性:集成身份认证、授权和加密功能,保障企业数据安全。 * 管理控制台:提供基于 Web

By Ne0inhk

轻松实现Office在线编辑:基于Collabora的Web集成指南

引言 在Web项目中嵌入Office文档编辑功能可以显著提升用户体验。Collabora Online基于LibreOffice核心,提供开源解决方案,支持主流格式(DOCX/XLSX/PPTX等)的实时协作编辑。以下指南详细介绍了如何部署和集成Collabora,实现媲美Office 365的网页端编辑体验。 核心组件与原理 Collabora Online Development Edition (CODE) 服务端提供文档渲染与协作引擎(通过Docker部署),前端通过<iframe>嵌入编辑窗口。 WOPI协议 定义Web应用与Office服务间的通信标准,关键操作包括文件加载、保存回调和权限控制。 部署Collabora服务端 环境要求 Linux服务器(Ubuntu/CentOS)、Docker。 步骤 拉取Collabora镜像: docker pull collabora/code 启动容器: docker run -t -d -p 9980:9980

By Ne0inhk
【Java Web学习 | 第五篇】CSS(4) -盒子模型

【Java Web学习 | 第五篇】CSS(4) -盒子模型

🌈个人主页: Hygge_Code🔥热门专栏:从0开始学习Java | Linux学习| 计算机网络💫个人格言: “既然选择了远方,便不顾风雨兼程” 文章目录 * CSS盒子模型🥝 * 1. 什么是CSS盒子模型? * 2. 边框(border):盒子的"外衣"🍋‍🟩 * 边框的基本属性 * 单边边框设置 * 边框对盒子大小的影响 * 表格细线边框 * 3. 内边距(padding):内容与边框的缓冲带🍋‍🟩 * 内边距的基本用法 * 内边距对盒子大小的影响 * 内边距的实用技巧 * 内边距不影响盒子大小的特殊情况 * 4. 外边距(margin):盒子之间的距离🍋‍🟩 * 外边距的基本用法 * 外边距的典型应用:水平居中 * 外边距合并问题 * 清除默认内外边距🐦‍🔥 * 综合代码演示 * CSS美化三剑客:圆角边框、盒子阴影与文字阴影🥝 * 1. 圆角边框(border-radius):告别生

By Ne0inhk