首页 文章 精选 留言 我的

精选列表

搜索[协同工作],共10000篇文章
优秀的个人博客,低调大师

禅道 MCP 发布,协同项目管理与工程研发,提升研发效率

禅道 MCP 正式发布!项目源码已上线GitHub,支持用户在 Claude、Cursor、CodeBuddy 等AI助手中统一调用。只需通过自然语言,即可完成从需求到代码的融合,全面提升研发管理体验。 一、禅道MCP是什么? 禅道MCP是基于MCP模型上下文协议实现的桥接代理服务。可将禅道API2.0等遵循OpenAPI规范的REST接口,自动转为MCP标准工具,供Claude、Cursor、CodeBuddy等AI助手统一调用,实现跟禅道数据的相互调用(可以从禅道中获取数据,也可以更新禅道中的数据)。 适用版本:开源版21.7.8、企业版12.7、旗舰版7.7、IPD版4.7及以上版本。 本项目基于上游openapi-mcp-server修改扩展而来,采用MIT开源许可协议发布。 用户在保留原项目开源授权与版权规范的前提下,可自由使用、修改与分发。 二、核心特性 自动转换能力:从OpenAPI/Swagger文档自动生成MCP工具,无需人工编写适配逻辑,适配所有遵循该规范的REST API。 传输协议支持:同时兼容Streamable HTTP和SSE(Server-Sent Events),兼顾兼容(HTTP)和实时性(SSE),适配不同AI客户端的通信需求。 链路追踪:内置OpenTelemetry链路追踪和指标收集,能监控服务调用链路、收集运行指标,方便问题排查和服务优化。 多服务代理:单个禅道MCP实例可同时代理多个不同的API服务,不仅支持禅道API,还能适配其他遵循OpenAPI规范的系统API,扩展性强。 跨平台部署:支持Linux、macOS、Windows主流系统,部署灵活。 三、快速开始 (一)配置MCP服务(四选一即可) 1.Windows用户配置方式 1)下载包 【AMD 64位】 【AMR 64位】 2)解压包(以AMD-64位为例) 将下载的包解压到目录 D:\zentao-mcp 3)修改MCP配置 复制配置模板:copy D:\zentao-mcp\config.example.yaml D:\zentao-mcp\config.yaml 修改配置文件:D:\zentao-mcp\config.yaml schema_url: "D:/zentao-mcp/docs/zentao-openapi.json" # 更新为实际文件路径 base_url: "https://禅道域名/api.php/v2" # 修改您的禅道访问域名 4)启动MCP服务命令 在cmd命令行执行启动命令: D:\zentao-mcp\bin\zentao-mcp-windows-amd64.exe -config D:\zentao-mcp\config.yaml 2.Linux用户配置方式 1)下载包 AMD-64位:curl -k -L -O https://pkg.zentao.net/zentao-mcp/1.0.1/zentao-mcp-linux-amd64.tar.gz ARM-64位:curl -k -L -O https://pkg.zentao.net/zentao-mcp/1.0.1/zentao-mcp-linux-arm64.tar.gz 2)解压包(以AMD-64位为例) 建目录:mkdir -p /opt/zentao-mcp 解压包:tar -zxvf zentao-mcp-linux-amd64.tar.gz -C /opt/zentao-mcp 3)修改MCP配置 复制配置模板:cp /opt/zentao-mcp/config.example.yaml /opt/zentao-mcp/config.yaml 修改配置文件:/opt/zentao-mcp/config.yaml schema_url: "/opt/zentao-mcp/docs/zentao-openapi.json" # 更新为实际文件路径 base_url: "https://禅道域名/api.php/v2" # 修改您的禅道访问域名 4)启动MCP服务命令 /opt/zentao-mcp/bin/zentao-mcp-linux-amd64 -config /opt/zentao-mcp/config.yaml 3.Mac用户配置方式 1)下载包 AMD-64位:curl -k -L -O https://pkg.zentao.net/zentao-mcp/1.0.1/zentao-mcp-darwin-amd64.tar.gz ARM-64位:curl -k -L -O https://pkg.zentao.net/zentao-mcp/1.0.1/zentao-mcp-darwin-arm64.tar.gz 2)解压包 建目录:mkdir /opt/zentao-mcp 解压包:tar -zxvf zentao-mcp-darwin-amd64.tar.gz -C /opt/zentao-mcp 3)修改MCP配置 复制配置模板:cp /opt/zentao-mcp/config.example.yaml /opt/zentao-mcp/config.yaml 修改配置文件:/opt/zentao-mcp/config.yaml schema_url: "/opt/zentao-mcp/docs/zentao-openapi.json" # 更新为实际文件路径 base_url: "https://禅道域名/api.php/v2" # 修改您的禅道访问域名 4)启动MCP服务命令 /opt/zentao-mcp/bin/zentao-mcp-darwin-amd64 -config /opt/zentao-mcp/config.yaml 4.源代码启动(面向开发者) 1)克隆代码 git clone https://github.com/easysoft/zentao-mcp.git 2)启动项目 进入项目:cd zentao-mcp 下载依赖:go mod tidy 启动命令:go build -o zentao-mcp ./cmd/app(或使用task dev) (二)配置MCP客户端(AI助手) 1.调用禅道API V2接口获取Token curl -X POST "http://您的禅道域名/api.php/v2/user/login" \ -H "Content-Type: application/json" \ -d '{"account":"用户名","password":"密码"}' 2.在AI助手中配置MCP(示例) { "mcpServers": { "zentao": { "disabled": false, "type": "mcp", "url": "http://127.0.0.1:9090/zentao/mcp", "timeout": 60000, "headers": { "token": "禅道API V2 Token", # 禅道API V2要求header中必须传token “Authorization”: "" } }, "gitfox": { "disabled": false, "type": "sse", "url": "http://127.0.0.1:9090/gitfox/sse", "timeout": 60000, "headers": { "Authorization": "GitFox Token" } } } } 3.在CodeBuddy配置禅道MCP服务后的效果: 四、场景示例 1.创建产品 ▼通过CodeBuddy,在禅道中创建一个产品: ▼在禅道中查看产品是否创建成功: 2.创建需求 ▼通过CodeBuddy,为禅道中的产品创建一个需求: ▼在禅道产品下确认需求是否产生: 3.创建代码库 ▼通过CodeBuddy,根据禅道需求,在GitFox创建代码库: ▼在GitFox上确认是否有代码库生成: 4.生成代码并推送至仓库 ▼通过CodeBuddy,在GitFox代码库生成一份脚手架代码: ▼在GitFox确认代码是否存在: 五、相关链接 禅道API手册 GitFox介绍 项目源码

优秀的个人博客,低调大师

.net core 工作流 WorkFlow 流程

WikeFlow官网:http://www.wikesoft.com WikeFlow2.0帮助文档:http://wikeflowhelp.wikesoft.com/ WikeFlow2.0简介 为什么我们要自已开发流程引擎? .net 平台下面有一些三方的流程引擎,这些流程引擎很多都不好用或者是很难满足用户的业务需求,功能臃肿导致学习难度大,条件限制多导致集成难度大或者根本就没办法集成,无法使用客户自己的权限系统跑流程,针对于行业现状,我们才决定自己研发流程引擎。 1、WikeFlow2.0几乎能满足各行各业的流程审批业务场景,流程引擎最大的优点:小巧,灵活,高度可扩展性,集成容易。 2、WikeFlow2.0的核心团队具有10年以上开发经验,具有非常丰富的流程引擎相关经验。 3、WikeFlow2.0总结了国内外的流程引擎,取长补短,WikeFlow2.0一直也在收集客户的反馈并不断进化。 4、WikeFlow2.0经过公司多个项目的实际运用中不断修改并完善后,我们决定独立成一个产品,产品具体很强的实践经验。 5、WikeFlow2.0有两个版本:支持.Net FrameWork的版本和支持.net Core 的版本,后期可能会支持Java(WikeFlow For Java版本) 6、WikeFlow2.0并不是对WikeFlow的升级,而是完全的重构,如你的项目在使用WikeFlow,需要自行调整代码。 7、WikeFlow2.0的流程审批是支持WinForm程序的,你只需要将每个节点的待办路径设置为你的Winform窗体就可以了。 如果你需要在Winform程序里面进行流程设计,,则在Winform程序里面加一个WebBrower,将流程设计的Url加入到WebBrower。 WikeFlow2.0 主要功能 1、流程审批,一般的业务审批 2、流程会签,一个任务节点多人审批 3、流程驳回,流程不同意 4、流程转办,流程从一个人转交给另外的人处理 5、流程传阅,将流程传递给其他的人查阅 6、流程撤回,提交后可以撤回 7、流程附件,支持节点附件 8、流程审批中修改业务数据 9、流程审批中业务数据权限设 WikeFlow2.0 系统框架 WikeFlow2.0同时有支持.net framework和.net core 的两个版本,WikeFlow for java正在开发中。 后端框架 .net framework asp.net Mvc5和asp.net Webform ORM:EntityFrameWork6.0 后端框架 .net core .net core Mvc ORM:EFCore/Sqlsugar MVC前端框架: bootstrap3,jqGrid,jQuery,layer,jsPlumb等。 Vue3前端框架: Vue3、Element plus、Axios、Vite等。 容易集成: WikeFlow2集成使用方便,如何集成到你的项目中,请查看《如何集成WikeFlow2.0》 WikeFlow2.0 多数据库支持 同时支持多数据库,MySql,Oracle,SQLServer,达梦、人大金仓。 .net framework版本只需要修改Web.config里面的数据库连接字符串就可以实现不同数据库之间的轻松切换。 .net core版本只需要修改StartUp.cs里面的ConfigureServices中的代码就可以实现不同数据库之间的轻松切换。

优秀的个人博客,低调大师

它是如何工作的?

松哥最近辅导了几个小伙伴秋招,有小伙伴在面小红书时遇到这个问题,这个问题想回答全面还是有些挑战,松哥结合之前的一篇旧文和大伙一起来聊聊。 一 无状态登录 1.1 什么是有状态 有状态服务,即服务端需要记录每次会话的客户端信息,从而识别客户端身份,根据用户身份进行请求的处理,典型的设计如 Tomcat 中的 Session。例如登录:用户登录后,我们把用户的信息保存在服务端 session 中,并且给用户一个 cookie 值,记录对应的 session,然后下次请求,用户携带 cookie 值来(这一步有浏览器自动完成),我们就能识别到对应 session,从而找到用户的信息。这种方式目前来看最方便,但是也有一些缺陷,如下: 服务端保存大量数据,增加服务端压力 服务端保存用户状态,不支持集群化部署 1.2 什么是无状态 微服务集群中的每个服务,对外提供的都使用 RESTful 风格的接口。而 RESTful 风格的一个最重要的规范就是:服务的无状态性,即: 服务端不保存任何客户端请求者信息 客户端的每次请求必须具备自描述信息,通过这些信息识别客户端身份 那么这种无状态性有哪些好处呢? 客户端请求不依赖服务端的信息,多次请求不需要必须访问到同一台服务器 服务端的集群和状态对客户端透明 服务端可以任意的迁移和伸缩(可以方便的进行集群化部署) 减小服务端存储压力 1.3 如何实现无状态 无状态登录的流程: 首先客户端发送账户名/密码到服务端进行认证 认证通过后,服务端将用户信息加密并且编码成一个 token(一般是 JWT),返回给客户端 以后客户端每次发送请求,都需要携带认证的 token 服务端对客户端发送来的 token 进行解密,判断是否有效,并且获取用户登录信息 二 JWT 2.1 JWT 简介 JWT,全称是 Json Web Token, 是一种 JSON 风格的轻量级的授权和身份认证规范,可实现无状态、分布式的 Web 应用授权: JWT 作为一种规范,并没有和某一种语言绑定在一起,常用的 Java 实现是 GitHub 上的开源项目 jjwt,地址如下:https://github.com/jwtk/jjwt 2.2 JWT 数据格式 JWT 包含三部分数据: Header:头部,通常头部有两部分信息: 声明类型,这里是JWT 加密算法,自定义 我们会对头部进行 Base64Url 编码(可解码),得到第一部分数据。 Payload:载荷,就是有效数据,在官方文档中(RFC7519),这里给了7个示例信息: iss (issuer):表示签发人 exp (expiration time):表示token过期时间 sub (subject):主题 aud (audience):受众 nbf (Not Before):生效时间 iat (Issued At):签发时间 jti (JWT ID):编号 这部分也会采用 Base64Url 编码,得到第二部分数据。 Signature:签名,是整个数据的认证信息。一般根据前两步的数据,再加上服务的的密钥secret(密钥保存在服务端,不能泄露给客户端),通过 Header 中配置的加密算法生成。用于验证整个数据完整和可靠性。 生成的数据格式如下图: 注意,这里的数据通过 . 隔开成了三部分,分别对应前面提到的三部分,另外,这里数据是不换行的,图片换行只是为了展示方便而已。 2.3 JWT 交互流程 流程图: 步骤翻译: 应用程序或客户端向授权服务器请求授权 获取到授权后,授权服务器会向应用程序返回访问令牌 应用程序使用访问令牌来访问受保护资源(如 API) 因为 JWT 签发的 token 中已经包含了用户的身份信息,并且每次请求都会携带,这样服务的就无需保存用户信息,甚至无需去数据库查询,这样就完全符合了 RESTful 的无状态规范。 2.4 JWT 存在的问题 说了这么多,JWT 也不是天衣无缝,由客户端维护登录状态带来的一些问题在这里依然存在,举例如下: 续签问题,这是被很多人诟病的问题之一,传统的 cookie+session 的方案天然的支持续签,但是 jwt 由于服务端不保存用户状态,因此很难完美解决续签问题,如果引入 redis,虽然可以解决问题,但是 jwt 也变得不伦不类了。 注销问题,由于服务端不再保存用户信息,所以一般可以通过修改 secret 来实现注销,服务端 secret 修改后,已经颁发的未过期的 token 就会认证失败,进而实现注销,不过毕竟没有传统的注销方便。 密码重置,密码重置后,原本的 token 依然可以访问系统,这时候也需要强制修改 secret。 基于第 2 点和第 3 点,一般建议不同用户取不同 secret。 对于上面提到的这些问题,在目前具体的项目实践中,更多的是通过引入 Redis 来解决这些问题。 三 实战 说了这么久,接下来我们就来看看这个东西到底要怎么用? 3.1 环境搭建 首先我们来创建一个 Spring Boot 项目,创建时需要添加 Spring Security 依赖,创建完成后,添加 jjwt 依赖,完整的 pom.xml 文件如下: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> </dependency> 然后在项目中创建一个简单的 User 对象实现 UserDetails 接口,如下: public class User implements UserDetails { private String username; private String password; private List<GrantedAuthority> authorities; public String getUsername() { return username; } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return true; } //省略getter/setter } 这个就是我们的用户对象,先放着备用,再创建一个 HelloController,内容如下: @RestController public class HelloController { @GetMapping("/hello") public String hello() { return "hello jwt !"; } @GetMapping("/admin") public String admin() { return "hello admin !"; } } HelloController 很简单,这里有两个接口,设计是 /hello 接口可以被具有 user 角色的用户访问,而 /admin 接口则可以被具有 admin 角色的用户访问。 3.2 JWT 配置 先准备一个 JWT 操作的工具类: public class JwtUtils { public static final Integer EXPIRE_TIME = 7 * 24 * 60 * 60; public static final Integer REDIS_TOKEN_EXPIRE_TIME = 30 * 60; public static final String TOKEN_ISSUER = "javaboy"; /** * 生成 JWT 字符串 * * @return */ public static String createJWT() throws IOException, NoSuchAlgorithmException, InvalidKeySpecException { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); String jws = Jwts.builder() //设置头部信息(相当于 JWT 字符串第一部分的内容) .header().add("type", "JWT") .and() //相当于 JWT 字符串第二部分的内容 //签名密钥 .signWith(getPrivateKey()) //设置过期时间 .expiration(java.util.Date.from(java.time.Instant.now().plusSeconds(EXPIRE_TIME))) //设置用户名 .subject(authentication.getName()) //设置用户权限,相当于把用户权限转为字符串 admin,user,xxx .claim("authorities", authentication.getAuthorities().stream().map(a -> a.getAuthority()).collect(Collectors.joining(","))) //令牌签发时间 .issuedAt(new Date()) .issuer(TOKEN_ISSUER) .compact(); return jws; } public static PrivateKey getPrivateKey() throws IOException, NoSuchAlgorithmException, InvalidKeySpecException { ClassPathResource resource = new ClassPathResource("private_key.pem"); byte[] privateKeyBytes = FileCopyUtils.copyToByteArray(resource.getInputStream()); String key = new String(privateKeyBytes, StandardCharsets.UTF_8); String privateStr = key .replace("-----BEGIN PRIVATE KEY-----", "") .replaceAll(System.lineSeparator(), "") .replace("-----END PRIVATE KEY-----", ""); byte[] privateKeyDecodedBytes = Base64.getDecoder().decode(privateStr); PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyDecodedBytes); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PrivateKey privateKey = keyFactory.generatePrivate(keySpec); return privateKey; } public static RSAPublicKey getPublicKey() throws IOException, NoSuchAlgorithmException, InvalidKeySpecException { ClassPathResource resource = new ClassPathResource("public_key.pem"); byte[] publicKeyBytes = FileCopyUtils.copyToByteArray(resource.getInputStream()); String key = new String(publicKeyBytes, StandardCharsets.UTF_8); String publicStr = key .replace("-----BEGIN PUBLIC KEY-----", "") .replaceAll(System.lineSeparator(), "") .replace("-----END PUBLIC KEY-----", ""); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); byte[] publicKeyBase64Bytes = Base64.getDecoder().decode(publicStr); X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(publicKeyBase64Bytes); PublicKey rsaPublicKey = keyFactory.generatePublic(publicKeySpec); return (RSAPublicKey) rsaPublicKey; } public static Authentication verifyToken(String token) { try { Claims claims = Jwts.parser() .verifyWith(getPublicKey()).build() .parseSignedClaims(token).getPayload(); String username = claims.getSubject(); // user,admin,xxx String authorities = claims.get("authorities", String.class); // 解析用户权限 List<SimpleGrantedAuthority> list = Arrays.stream(authorities.split(",")).map(s -> new SimpleGrantedAuthority(s)).collect(Collectors.toList()); return UsernamePasswordAuthenticationToken.authenticated(username, null, list); } catch (Exception e) { //令牌校验失败 return null; } } } 接下来,在用户登录成功的时候,生成 JWT 字符串,并存入到 Redis 中,如下: @Bean SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http.authorizeHttpRequests(a -> a.anyRequest().authenticated()) .csrf(c -> c.disable()) .sessionManagement(s -> s.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .formLogin(f -> f .successHandler((req, resp, auth) -> { //登录成功,生成 JWT 字符串 try { String token = JwtUtils.createJWT(); //将 JWT 字符串保存到 redis 中 redisTemplate.opsForValue().set(auth.getName() + ":" + token, auth.getName()); // 设置过期时间 redisTemplate.expire(auth.getName() + ":" + token, JwtUtils.REDIS_TOKEN_EXPIRE_TIME, TimeUnit.SECONDS); resp.setContentType("application/json;charset=utf-8"); RespBean respBean = new RespBean(200, token, null); resp.getWriter().write(new ObjectMapper().writeValueAsString(respBean)); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); } catch (InvalidKeySpecException e) { throw new RuntimeException(e); } }) .permitAll()) .logout(l -> l.logoutSuccessHandler((req, resp, auth) -> { // 删除 redis 中的 token String key = auth.getName() + ":" + req.getHeader("Authorization").replace("Bearer ", ""); System.out.println("key = " + key); redisTemplate.delete(key); resp.getWriter().write("logout success"); }).logoutUrl("/logout").permitAll()); http.addFilterAfter(new JwtFilter(redisTemplate), SecurityContextHolderFilter.class); return http.build(); } 最后再来一个校验的过滤器,在用法发送请求的时候,校验 JWT 字符串,如下: public class JwtFilter extends GenericFilterBean { StringRedisTemplate redisTemplate; public JwtFilter(StringRedisTemplate redisTemplate) { this.redisTemplate = redisTemplate; } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) { RespBean result = null; HttpServletRequest req = (HttpServletRequest) servletRequest; HttpServletResponse resp = (HttpServletResponse) servletResponse; resp.setContentType("application/json;charset=utf-8"); try { //如果是登录请求,则直接放行 if (req.getRequestURI().equals("/login")) { filterChain.doFilter(servletRequest, servletResponse); return; } //获取请求头中的token String token = req.getHeader("Authorization"); if (token == null) { result = new RespBean(500, "令牌为空,请求失败", null); PrintWriter out = resp.getWriter(); out.write(new ObjectMapper().writeValueAsString(result)); } else { //验证token token = token.replace("Bearer ", ""); //验证token是否有效 //校验通过,则返回认证后的 Auhtentication 对象,否则返回 null Authentication authentication = JwtUtils.verifyToken(token); String s = redisTemplate.opsForValue().get(authentication.getName() + ":" + token); if (s == null) { // 令牌失效 result = new RespBean(501, "令牌失效,请重新登录", null); resp.getWriter().write(new ObjectMapper().writeValueAsString(result)); } else { //令牌有效,检查令牌过期时间,如果临近过期,则为令牌续期 Long expire = redisTemplate.getExpire(authentication.getName() + ":" + token); if (expire != null && expire < 10 * 60) { //说明令牌还有 10 分钟过期,则重新设置新的过期时间 redisTemplate.expire(authentication.getName() + ":" + token, Duration.ofSeconds(JwtUtils.REDIS_TOKEN_EXPIRE_TIME)); } SecurityContextHolder.getContext().setAuthentication(authentication); filterChain.doFilter(servletRequest, servletResponse); } } } catch (Exception e) { } finally { // 清除上下文 SecurityContextHolder.clearContext(); } } } 关于这个过滤器,我说如下几点: 首先从请求头中提取出 Authorization 字段,这个字段对应的 value 就是用户的 token。 将提取出来的 token 字符串调用 JwtUtils.verifyToken 方法进行验证,将验证结果放到当前的 SecurityContext 中,然后执行过滤链使请求继续执行下去。 3.3 Spring Security 配置 最后,在 Spring Security 中配置这个过滤器。 http.addFilterAfter(new JwtFilter(redisTemplate), SecurityContextHolderFilter.class); 搞定! 3.4 测试 做完这些之后,我们的环境就算完全搭建起来了,接下来启动项目然后在 POSTMAN 中进行测试,如下: 登录成功后返回的字符串就是经过 base64url 转码的 token,一共有三部分,通过一个 . 隔开,我们可以对第一个 . 之前的字符串进行解码,即 Header,如下: 再对两个 . 之间的字符解码,即 payload: 可以看到,我们设置信息,由于 base64 并不是加密方案,只是一种编码方案,因此,不建议将敏感的用户信息放到 token 中。 接下来再去访问 /hello 接口,注意认证方式选择 Bearer Token,Token 值为刚刚获取到的值,如下: 可以看到,访问成功。 四 总结 这就是 JWT 结合 Spring Security 的一个简单用法。

资源下载

更多资源
腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册