我们可以通过几个运用先前给出的公式完成3d图形绘制的例子来证明该公式的正确性:
# --- 1. 核心数学规则:透视投影 ---def project(x, y, z): """
本质公式:x' = x / z, y' = y / z
我们乘上一个系数 400 (视距 d),是为了让画面大一点,方便观察
"""
d = 400
x_2d = (x / z) * d
y_2d = (y / z) * d return x_2d, y_2d# --- 2. 定义 3D 数据 (8个顶点的 x, y, z) ---# 我们让前四个点的 z=2 (近),后四个点的 z=3 (远)vertices = [ # 前面的四个顶点 (z=2, 离眼睛近,看起来大)
[-1, -1, 2], [1, -1, 2], [1, 1, 2], [-1, 1, 2], # 后面的四个顶点 (z=3, 离眼睛远,看起来小)
[-1, -1, 3], [1, -1, 3], [1, 1, 3], [-1, 1, 3]
]# 定义哪些点需要连成线 (索引号)edges = [
(0,1), (1,2), (2,3), (3,0), # 连接前脸的4条边
(4,5), (5,6), (6,7), (7,4), # 连接后脸的4条边
(0,4), (1,5), (2,6), (3,7) # 连接前后脸的4条纵向边]# --- 3. 执行投影计算 ---# 将 3D 坐标转换成 2D 坐标points_2d = []for v in vertices:
p_2d = project(v[0], v[1], v[2])
points_2d.append(p_2d)# --- 4. 绘图部分 ---screen = turtle.Screen()
screen.title("2D转3D本质演示:静态立方体")
t = turtle.Turtle()
t.pensize(2)
t.speed(1) # 慢速绘图,观察过程for edge in edges:
start_idx = edge[0]
end_idx = edge[1]
# 移动到起点
t.up()
t.goto(points_2d[start_idx]) # 画线到终点
t.down()
t.goto(points_2d[end_idx])
t.hideturtle()print("绘制完成!观察近处的面(z=2)是否比远处的面(z=3)大?")
turtle.done()
这个例子演示了一个静态的立方体是如何绘制的,当然,有人会说只要打好点也能做到与程序类似的效果,那么我们在用一个动态的旋转立方体来证明公式的正确性:
import turtleimport mathimport time# --- 1. 核心数学规则:透视投影 ---def project(x, y, z, fov, viewer_distance): """
将 3D 坐标变换为 2D 坐标
本质公式:x' = x / z, y' = y / z
"""
# 这里的 z 需要加上 viewer_distance,防止物体就在眼睛上导致除以 0
factor = fov / (viewer_distance + z)
x_2d = x * factor
y_2d = y * factor return x_2d, y_2d# --- 2. 定义立方体的数据结构 ---# 8个顶点 (x, y, z)vertices = [
[-1, -1, 1], [1, -1, 1], [1, 1, 1], [-1, 1, 1],
[-1, -1, -1], [1, -1, -1], [1, 1, -1], [-1, 1, -1]
]# 12条棱 (连接顶点的索引)edges = [
(0,1), (1,2), (2,3), (3,0), # 前面
(4,5), (5,6), (6,7), (7,4), # 后面
(0,4), (1,5), (2,6), (3,7) # 连接前后的线]# --- 3. 设置画布 ---screen = turtle.Screen()
screen.bgcolor("black")
screen.setup(width=600, height=600)
screen.tracer(0) # 关闭自动刷新,手动控制动画t = turtle.Turtle()
t.ht() # 隐藏画笔图标t.color("#00FF00") # 极客绿t.pensize(2)# --- 4. 动画循环 ---angle = 0while True:
t.clear()
# 存储投影后的 2D 点
projected_points = []
# 每一帧都旋转一下坐标,让它动起来
angle += 0.02
for v in vertices: # 旋转矩阵(简单的绕 Y 轴和 X 轴旋转数学)
# 这一步是为了让数据“动”起来,不是投影的本质
x, y, z = v # 绕 Y 轴转
nx = x * math.cos(angle) - z * math.sin(angle)
nz = x * math.sin(angle) + z * math.cos(angle) # 绕 X 轴转
ny = y * math.cos(angle*0.7) - nz * math.sin(angle*0.7)
nz = y * math.sin(angle*0.7) + nz * math.cos(angle*0.7)
# --- 调用本质公式 ---
# fov(视距)设为 400,viewer_distance(物体离眼睛距离)设为 4
p2d = project(nx, ny, nz, 400, 4)
projected_points.append(p2d) # 绘制棱
for edge in edges:
p1 = projected_points[edge[0]]
p2 = projected_points[edge[1]]
t.up()
t.goto(p1)
t.down()
t.goto(p2)
screen.update() # 刷新屏幕
time.sleep(0.01)
public McpServerEndpointProvider serverEndpoint () { McpServerEndpointProvider serverEndpoint = McpServerEndpointProvider.builder() .name( "mcp-weather" ) .channel(McpChannel.STDIO) .build(); FunctionToolDesc weatherTool = new FunctionToolDesc ( "get_weather" ) .description( "获取指定城市的天气情况" ) .stringParamAdd( "location" , "根据用户提到的地点推测城市" ) .doHandle(map -> { return "24度" ; }); serverEndpoint.addTool( new MethodToolProvider (weatherTool));
public
class
McpServerTool
implements
ToolProvider
{
private
McpClientProvider
stdioToolProvider
=
McpClientProvider.builder()
.channel(McpChannel.STDIO)
//表示使用 stdio
.command(
"npx"
)
.args(
"-y"
,
"@gitee/mcp-gitee@latest"
)
.addEnvVar(
"GITEE_API_BASE"
,
"https://gitee.com/api/v5"
)
.addEnvVar(
"GITEE_ACCESS_TOKEN"
,
"<your personal access token>"
)
.build();
public
Collection<FunctionTool>
getTools
()
{
McpClientProvider clientProvider = McpClientProvider.builder() .url( "http://localhost:8080/mcp" ) .customize(spec -> { spec.capabilities(McpSchema.ClientCapabilities.builder().sampling().build()); spec.sampling(req -> Mono.just(McpSchema.CreateMessageResult.builder() zggren.com.content( new McpSchema .TextContent( "test" )) .build())); }) .build(); clientProvider.callToolAsText( "demo" , Utils.asMap( "a" , 1 )) .getContent(); } } //服务端 public class SamplingServerDemo { //可以注入 exchange(实现反向通讯:服务端向客户端请求) public Mono<McpSchema.CreateMessageResult> demo(McpAsyncServerExchange exchange) { // 服务端向客户端请求 AI 采样决策 return exchange.createMessage(McpSchema.CreateMessageRequest.builder() .messages(Collections.singletonList(McpSchema.PromptMessage.builder() .role(McpSchema.Role.USER) .content( new McpSchema .TextContent( "请帮我拆解这个任务..." )) .build()))
.channel(McpChannel.STREAMABLE) .url( "http://localhost:8080/mcp" ) .build(); //获取所有工具原语 clientProvider.getTools(); //获取所有模板原语 clientProvider.getResources(); //获取所有资源模板原语 clientProvider.getResourceTemplates();
import org.noear.solon.ai.chat.skill.SkillDesc; import org.noear.solon.ai.mcp.McpChannel; import org.noear.solon.ai.mcp.client.McpClientProvider; public class McpSkillDemo { public void test () { // 1. 创建 MCP 客户端,从远端获取工具生态 // 这里假设远端 Server 提供了如 "restart_server", "query_log" 等运维工具 McpClientProvider devopsMcpProvider = McpClientProvider.builder() .channel(McpChannel.STREAMABLE) .url( "http://devops-center:8080/mcp" ) .build();chengyiwl.com // 2. 使用 SkillDesc 将 MCP 工具集包装成一个“智能运维技能” Skill devopsSkill = SkillDesc.builder( "devops-skill" ) .description( "高级运维管理技能,支持服务器状态查询与故障处理" ) // 智能分发:只有当用户提问包含“服务器”、“重启”、“日志”时才激活此技能 .isSupported( "服务器" , "重启" , "日志" , "负载" ) // 动态指令:为技能注入特殊的 System Prompt 引导 .instruction(prompt -> { return "你现在是一名高级架构师。在执行重启操作前,请务必确认操作人的权限。" ; }) // 挂载工具:核心一步!直接将 MCP 获取的所有工具注入到该技能中 .toolAdd(devopsMcpProvider) // 钩子函数:当技能被挂载到会话时触发逻辑(如:记录审计日志) .onAttach(prompt -> { System.out.println( "检测到运维相关指令,DevOps 技能已就绪..." ); }) .build(); // 3. 应用技能:将技能交给 Agent ChatModel chatModel = ChatModel.of(apiUrl).build(); ReActAgent agent cqpryk.com = ReActAgent.of(chatModel) .defaultSkillAdd(devopsSkill) // 添加包装后的技能 .build(); // 此时 Agent 只有在聊到运维话题时,才会通过 MCP 协议去调用对应的远端工具
public class UserController {
@Autowired
private UserService userService; // 自动注入
@RequestMapping("/get")
@ResponseBody
public User getUser(@RequestParam("id") Long id) {
return userService.getUserById(id);
}
@RequestMapping("/detail/{id}")
@ResponseBody
public User getUserDetail(@PathVariable("id") Long id) {
return userService.getUserById(id);
}
@RequestMapping("/create")
@ResponseBody
public User createUser(@RequestBody User user) {
return userService.createUser(user);
}
}
@Before("com.example.service.*.*")
public void logBefore(Method method) {
System.out.println("开始执行: " + method.getName());
}
@After("com.example.service.*.*")
public void logAfter(Method method) {
System.out.println("执行完成: " + method.getName());
}
}
// 业务代码保持纯净
@Service
public class UserService {
public void registerUser(User user) {
userDao.save(user); // 只关注业务逻辑
@Before("com.example.service.*.*")
public void logBefore(Method method) {
System.out.println("开始执行:ttkq-qq.cn " + method.getName());
}
@After("com.example.service.*.*")
public void logAfter(Method method) {
System.out.println("执行完成: " + method.getName());
}
}
// 业务代码保持纯净
@Service
public class UserService {
public void registerUser(User user) {
userDao.save(user); // 只关注业务逻辑
}
}
日志、事务、权限校验这些横切关注点被抽离出来,业务代码变得清爽多了。
3. 约定优于配置 - 少写XML,多用注解
早期的Spring需要写大量XML配置:
<!-- 古老的Spring配置 -->
<bean id="userService" class="com.example.UserService">
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" class="com.example.UserDaoImpl"/>
现在只需要:
@Service
public class UserService {
@Autowired
private UserDao userDao;
}
一个注解就搞定了。这就是约定优于配置的威力。
4. 模板方法模式 - 封装样板代码
JDBC操作有大量的样板代码:
// 传统JDBC:50多行代码
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
try {lq2026.cn
conn = dataSource.getConnection();
stmt = conn.prepareStatement("SELECT * FROM users WHERE id = ?");
stmt.setLong(1, userId);basketzhibo.cn
rs = stmt.executeQuery();
if (rs.next()) {
return new User(rs);
}
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {spacexzhibo.cn
if (rs != null) try { rs.close(); } catch (SQLException e) {}
if (stmt != null) try { stmt.close(xzhibo.com.cn); } catch (SQLException e) {}
if (conn != null) try { conn.close(); } catch (SQLException e) {}
}
private Map<String, livesoccerzb.cn Object> singletonObjects = new ConcurrentHashMap<>();
// 二级缓存:早期Bean引用(用于解决循环依赖)
private Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>();
// 三级缓存:ObjectFactory工厂(用于延迟生成早期引用,支持AOP)
private Map<String, ObjectFactory<?>> singletonFactories = new ConcurrentHashMap<>();
// 正在创建的Bean集合(用于检测循环依赖)
private Set<String> 5gzbw.com singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>());
public SpringWindApplicationContext(Class<?> configClass) {
scanComponents(configClass); // 1. 扫描组件
createSingletonBeans(); sportsurge.cn // 2. 创建Bean
dependencyInjection(ttkqol.cn); ttkq.mo.cn // 3. 依赖注入
. 检查一级缓存singletonObjects → 未找到
3. 检查是否正在创建 → 不在,继续创建流程
4. 标记A正在创建:singletonsCurrentlyInCreation.add("beanA")
5. 实例化A:调用构造器,得到原始对象(此时尚未注入依赖)
6. **关键步骤**:将ObjectFactory放入三级缓存
singletonFactories.put("beanA", () -> getEarlyBeanReference("beanA", rawBeanA))
// 注意:此时放入的是工厂对象,不是Bean本身!
7. 开始为A注入依赖:doDependencyInjection(beanA)
8. 发现A依赖B,调用getBean("beanB")
// 创建B的过程(嵌套在A的创建过程中)
9. 检查一级缓存 → 未找到B
10. 标记B正在创建:singletonsCurrentlyInCreation.add("beanB")
11. 实例化B:得到原始对象
12. 将ObjectFactory放入三级缓存:
singletonFactories.put("beanB", () -> getEarlyBeanReference("beanB", rawBeanB))
13. 开始为B注入依赖:发现B依赖A,调用getBean("beanA")
// 循环依赖检测与解决(关键!)
14. 检查一级缓存 → 未找到A(因为A还没完成初始化)
15. 发现A在正在创建集合中:singletonsCurrentlyInCreation.contains("beanA") = true
16. 检查二级缓存earlySingletonObjects → 未找到
17. 检查三级缓存singletonFactories → 找到A的ObjectFactory ✓
18. **调用ObjectFactory.getObject(tiantianapp.org.cn)**
→ 内部调用getEarlyBeanReference("beanA", rawBeanA)
→ 如果A需要AOP代理,SmartInstantiationAwareBeanPostProcessor会在此创建代理
→ 返回A的早期引用(可能是原始对象,也可能是代理对象)
19. 将A的早期引用放入二级缓存:earlySingletonObjects.put("beanA", earlyA)
20. 从三级缓存移除:singletonFactories.remove("beanA")
21. 返回A的早期引用给B ✓
22. B完成依赖注入(注入了A的早期引用)
23. B执行前置处理器:applyBeanPostProcessorsBeforeInitialization
24. B执行@PostConstruct初始化方法
25. B执行后置处理器:applyBeanPostProcessorsAfterInitialization(AOP代理可能在此创建)
26. B完成创建,放入一级缓存:singletonObjects.put("beanB", beanB)
27. 清理B的二、三级缓存
28. 从正在创建集合移除:singletonsCurrentlyInCreation.remove("beanB")
29. getBean("beanB") 返回完整的B → A成功注入B
30. A完成依赖注入
31. A执行前置处理器
32. A执行@PostConstruct初始化方法
33. A执行后置处理器(如果A之前已在getEarlyBeanReference中创建了代理,这里会跳过)
34. A完成创建,放入一级缓存:singletonObjects.put("beanA", beanA)
// 遍历所有BeanPostProcessor ttzbapp.com.cn
for (BeanPostProcessor bp : beanPostProcessors) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp =
(SmartInstantiationAwareBeanPostProcessor) bp;
// AOP在这里提前创建代理,避免循环依赖时产生多个不同的代理
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
return exposedObject;
}
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/70048957/viewspace-3108992/ ,如需转载,请注明出处,否则将追究法律责任。