Update: build artifacts and classes
This commit is contained in:
@@ -1,2 +1,10 @@
|
||||
server.port=8082
|
||||
spring.application.name=springboot-demo
|
||||
|
||||
# 学习友好:默认只保护 /api/secure/**
|
||||
learning.auth.enabled=true
|
||||
learning.auth.jwt.secret=demo-learning-secret-key-demo-learning-secret-key
|
||||
learning.auth.jwt.expiration=86400000
|
||||
|
||||
# 避免默认生成密码干扰学习输出
|
||||
spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration
|
||||
|
||||
BIN
target/classes/com/example/demo/common/ApiResponse.class
Normal file
BIN
target/classes/com/example/demo/common/ApiResponse.class
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
target/classes/com/example/demo/dto/UserRequest.class
Normal file
BIN
target/classes/com/example/demo/dto/UserRequest.class
Normal file
Binary file not shown.
BIN
target/classes/com/example/demo/dto/auth/LoginRequest.class
Normal file
BIN
target/classes/com/example/demo/dto/auth/LoginRequest.class
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
target/classes/com/example/demo/security/LearningJwtFilter.class
Normal file
BIN
target/classes/com/example/demo/security/LearningJwtFilter.class
Normal file
Binary file not shown.
BIN
target/classes/com/example/demo/security/LearningJwtUtil.class
Normal file
BIN
target/classes/com/example/demo/security/LearningJwtUtil.class
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -28,6 +28,8 @@
|
||||
.nav { margin-bottom: 20px; }
|
||||
.nav a { margin-right: 15px; color: #6DB33F; text-decoration: none; }
|
||||
.nav a:hover { text-decoration: underline; }
|
||||
.lab { background:#fff7e6; border-left:4px solid #fa8c16; padding:15px; border-radius:8px; margin:15px 0; }
|
||||
.lab h4 { color:#ad6800; margin-bottom:8px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -38,11 +40,24 @@
|
||||
</div>
|
||||
|
||||
<h1>🔪 AOP 切面编程</h1>
|
||||
|
||||
<div class="lab">
|
||||
<h4>🧪 实验任务卡(AOP)</h4>
|
||||
<label style="display:block;margin-bottom:8px;"><input id="aopTaskDone" type="checkbox" onchange="toggleAopTaskDone(this)"> 本任务我已经完成</label>
|
||||
<ul style="padding-left:20px;line-height:1.8;">
|
||||
<li>目标:观察同一请求如何触发 Before/After/Around 通知</li>
|
||||
<li>步骤1:调用用户接口 <code>/api/users</code></li>
|
||||
<li>步骤2:回到本页点击“刷新统计数据”</li>
|
||||
<li>预期:统计里能看到 Controller/Service 方法耗时累积</li>
|
||||
<li>常见坑:只看页面不看控制台,容易错过切面日志</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h3>📊 实时性能统计</h3>
|
||||
<p>AOP 自动统计所有 Controller 和 Service 方法的执行时间</p>
|
||||
<button class="btn btn-primary" onclick="loadStats()">刷新统计数据</button>
|
||||
<button class="btn btn-info" onclick="demoValidationError()">演示校验失败</button>
|
||||
<div class="result-box" id="statsResult">点击按钮查看...</div>
|
||||
</div>
|
||||
|
||||
@@ -167,11 +182,41 @@ execution(* com.example.demo.service.*.save*(..))
|
||||
<p style="margin-top: 30px;"><a href="/">← 返回学习中心</a></p>
|
||||
|
||||
<script>
|
||||
const AOP_TASK_KEY = 'task.aop.done';
|
||||
|
||||
function toggleAopTaskDone(el) {
|
||||
localStorage.setItem(AOP_TASK_KEY, el.checked ? '1' : '0');
|
||||
}
|
||||
|
||||
function initAopTaskState() {
|
||||
const done = localStorage.getItem(AOP_TASK_KEY) === '1';
|
||||
const checkbox = document.getElementById('aopTaskDone');
|
||||
if (checkbox) checkbox.checked = done;
|
||||
}
|
||||
|
||||
async function demoValidationError() {
|
||||
const box = document.getElementById('statsResult');
|
||||
box.textContent = '发送错误示例请求中...';
|
||||
try {
|
||||
const res = await fetch('/api/users', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ name: '', email: 'bad', age: 999 })
|
||||
});
|
||||
const data = await res.json();
|
||||
box.textContent = JSON.stringify(data, null, 2);
|
||||
} catch (e) {
|
||||
box.textContent = '错误: ' + e.message;
|
||||
}
|
||||
}
|
||||
|
||||
async function loadStats() {
|
||||
const res = await fetch('/aop/stats');
|
||||
const data = await res.json();
|
||||
document.getElementById('statsResult').textContent = JSON.stringify(data, null, 2);
|
||||
}
|
||||
|
||||
initAopTaskState();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -30,6 +30,8 @@
|
||||
.nav { margin-bottom: 20px; }
|
||||
.nav a { margin-right: 15px; color: #6DB33F; text-decoration: none; }
|
||||
.nav a:hover { text-decoration: underline; }
|
||||
.lab { background:#fff7e6; border-left:4px solid #fa8c16; padding:15px; border-radius:8px; margin:15px 0; }
|
||||
.lab h4 { color:#ad6800; margin-bottom:8px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -40,6 +42,18 @@
|
||||
</div>
|
||||
|
||||
<h1>📡 Spring 事件机制</h1>
|
||||
|
||||
<div class="lab">
|
||||
<h4>🧪 实验任务卡(事件)</h4>
|
||||
<label style="display:block;margin-bottom:8px;"><input id="eventTaskDone" type="checkbox" onchange="toggleEventTaskDone(this)"> 本任务我已经完成</label>
|
||||
<ul style="padding-left:20px;line-height:1.8;">
|
||||
<li>目标:体验发布者与监听者解耦</li>
|
||||
<li>步骤1:输入 userId/userName,点击“发布登录事件”</li>
|
||||
<li>步骤2:重复发布不同用户,比较返回结果</li>
|
||||
<li>预期:接口立即返回;监听处理在日志中可观察</li>
|
||||
<li>常见坑:把事件当同步 RPC,忽略异步监听特性</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h3>🎉 事件发布演示</h3>
|
||||
@@ -48,6 +62,7 @@
|
||||
<input type="text" id="userName" placeholder="用户名" value="张三" style="padding: 10px; border: 1px solid #ddd; border-radius: 4px; width: 150px;">
|
||||
<input type="number" id="userId" placeholder="用户ID" value="1" style="padding: 10px; border: 1px solid #ddd; border-radius: 4px; width: 100px;">
|
||||
<button class="btn btn-primary" onclick="publishEvent()">发布登录事件</button>
|
||||
<button class="btn btn-warning" onclick="demoEventError()">演示参数错误</button>
|
||||
</div>
|
||||
<div class="result-box" id="eventResult">等待事件发布...</div>
|
||||
</div>
|
||||
@@ -187,6 +202,29 @@ public class AopEventController {
|
||||
<p style="margin-top: 30px;"><a href="/">← 返回学习中心</a></p>
|
||||
|
||||
<script>
|
||||
const EVENT_TASK_KEY = 'task.event.done';
|
||||
|
||||
function toggleEventTaskDone(el) {
|
||||
localStorage.setItem(EVENT_TASK_KEY, el.checked ? '1' : '0');
|
||||
}
|
||||
|
||||
function initEventTaskState() {
|
||||
const done = localStorage.getItem(EVENT_TASK_KEY) === '1';
|
||||
const checkbox = document.getElementById('eventTaskDone');
|
||||
if (checkbox) checkbox.checked = done;
|
||||
}
|
||||
|
||||
async function demoEventError() {
|
||||
const resultBox = document.getElementById('eventResult');
|
||||
try {
|
||||
const res = await fetch('/aop/event/publish?userName=', { method: 'POST' });
|
||||
const data = await res.json();
|
||||
resultBox.textContent = JSON.stringify(data, null, 2);
|
||||
} catch (e) {
|
||||
resultBox.textContent = '错误: ' + e.message;
|
||||
}
|
||||
}
|
||||
|
||||
async function publishEvent() {
|
||||
const userId = document.getElementById('userId').value;
|
||||
const userName = document.getElementById('userName').value;
|
||||
@@ -208,6 +246,8 @@ public class AopEventController {
|
||||
document.getElementById('eventResult').textContent = '错误: ' + e.message;
|
||||
}
|
||||
}
|
||||
|
||||
initEventTaskState();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -68,6 +68,10 @@
|
||||
<h4>📡 事件机制</h4>
|
||||
<p>发布/订阅模式、解耦业务逻辑</p>
|
||||
</a>
|
||||
<a href="/learn" class="feature-item">
|
||||
<h4>🔐 鉴权演示(学习用)</h4>
|
||||
<p>最小 JWT 流程:登录、携带 Token、访问受保护接口</p>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -143,7 +143,8 @@ public class UserController {
|
||||
// 加载用户列表
|
||||
async function loadUsers() {
|
||||
const res = await fetch('/api/users');
|
||||
const users = await res.json();
|
||||
const payload = await res.json();
|
||||
const users = payload.data || [];
|
||||
const tbody = document.querySelector('#userTable tbody');
|
||||
tbody.innerHTML = users.map(u => `
|
||||
<tr>
|
||||
@@ -196,7 +197,7 @@ public class UserController {
|
||||
await fetch(`/api/users/${id}`, {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ ...user, id: parseInt(id) })
|
||||
body: JSON.stringify(user)
|
||||
});
|
||||
} else {
|
||||
await fetch('/api/users', {
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -1,3 +0,0 @@
|
||||
artifactId=demo
|
||||
groupId=com.example
|
||||
version=0.0.1-SNAPSHOT
|
||||
@@ -1,15 +1,25 @@
|
||||
com/example/demo/controller/auth/SecureDemoController.class
|
||||
com/example/demo/exception/ResourceNotFoundException.class
|
||||
com/example/demo/event/UserEventPublisher.class
|
||||
com/example/demo/exception/GlobalExceptionHandler.class
|
||||
com/example/demo/dto/auth/LoginRequest.class
|
||||
com/example/demo/DemoApplication.class
|
||||
com/example/demo/controller/PageController.class
|
||||
com/example/demo/dto/UserRequest.class
|
||||
com/example/demo/security/LearningJwtUtil.class
|
||||
com/example/demo/security/LearningSecurityConfig.class
|
||||
com/example/demo/model/UserEvent$Type.class
|
||||
com/example/demo/model/User.class
|
||||
com/example/demo/service/UserService.class
|
||||
com/example/demo/aop/PerformanceAspect.class
|
||||
com/example/demo/aop/RateLimited.class
|
||||
com/example/demo/model/UserEvent.class
|
||||
com/example/demo/aop/LoggingAspect.class
|
||||
com/example/demo/event/UserEventPublisher.class
|
||||
com/example/demo/controller/auth/LearningAuthController.class
|
||||
com/example/demo/common/ApiResponse.class
|
||||
com/example/demo/controller/AopEventController.class
|
||||
com/example/demo/controller/LearnController.class
|
||||
com/example/demo/DemoApplication.class
|
||||
com/example/demo/event/UserEventListener.class
|
||||
com/example/demo/controller/PageController.class
|
||||
com/example/demo/controller/UserController.class
|
||||
com/example/demo/model/UserEvent$Type.class
|
||||
com/example/demo/model/User.class
|
||||
com/example/demo/aop/RateLimitAspect.class
|
||||
com/example/demo/service/UserService.class
|
||||
com/example/demo/aop/PerformanceAspect.class
|
||||
com/example/demo/security/LearningJwtFilter.class
|
||||
|
||||
@@ -1,14 +1,24 @@
|
||||
/home/llm/projects/springboot-demo/src/main/java/com/example/demo/security/LearningSecurityConfig.java
|
||||
/home/llm/projects/springboot-demo/src/main/java/com/example/demo/controller/auth/SecureDemoController.java
|
||||
/home/llm/projects/springboot-demo/src/main/java/com/example/demo/controller/UserController.java
|
||||
/home/llm/projects/springboot-demo/src/main/java/com/example/demo/model/User.java
|
||||
/home/llm/projects/springboot-demo/src/main/java/com/example/demo/aop/LoggingAspect.java
|
||||
/home/llm/projects/springboot-demo/src/main/java/com/example/demo/security/LearningJwtUtil.java
|
||||
/home/llm/projects/springboot-demo/src/main/java/com/example/demo/controller/PageController.java
|
||||
/home/llm/projects/springboot-demo/src/main/java/com/example/demo/aop/RateLimited.java
|
||||
/home/llm/projects/springboot-demo/src/main/java/com/example/demo/aop/PerformanceAspect.java
|
||||
/home/llm/projects/springboot-demo/src/main/java/com/example/demo/service/UserService.java
|
||||
/home/llm/projects/springboot-demo/src/main/java/com/example/demo/exception/ResourceNotFoundException.java
|
||||
/home/llm/projects/springboot-demo/src/main/java/com/example/demo/controller/AopEventController.java
|
||||
/home/llm/projects/springboot-demo/src/main/java/com/example/demo/exception/GlobalExceptionHandler.java
|
||||
/home/llm/projects/springboot-demo/src/main/java/com/example/demo/event/UserEventPublisher.java
|
||||
/home/llm/projects/springboot-demo/src/main/java/com/example/demo/dto/auth/LoginRequest.java
|
||||
/home/llm/projects/springboot-demo/src/main/java/com/example/demo/dto/UserRequest.java
|
||||
/home/llm/projects/springboot-demo/src/main/java/com/example/demo/common/ApiResponse.java
|
||||
/home/llm/projects/springboot-demo/src/main/java/com/example/demo/security/LearningJwtFilter.java
|
||||
/home/llm/projects/springboot-demo/src/main/java/com/example/demo/DemoApplication.java
|
||||
/home/llm/projects/springboot-demo/src/main/java/com/example/demo/event/UserEventListener.java
|
||||
/home/llm/projects/springboot-demo/src/main/java/com/example/demo/controller/LearnController.java
|
||||
/home/llm/projects/springboot-demo/src/main/java/com/example/demo/controller/auth/LearningAuthController.java
|
||||
/home/llm/projects/springboot-demo/src/main/java/com/example/demo/model/UserEvent.java
|
||||
/home/llm/projects/springboot-demo/src/main/java/com/example/demo/aop/RateLimitAspect.java
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
com/example/demo/controller/UserControllerTest.class
|
||||
com/example/demo/controller/AuthFlowTest.class
|
||||
@@ -0,0 +1,2 @@
|
||||
/home/llm/projects/springboot-demo/src/test/java/com/example/demo/controller/AuthFlowTest.java
|
||||
/home/llm/projects/springboot-demo/src/test/java/com/example/demo/controller/UserControllerTest.java
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,4 @@
|
||||
-------------------------------------------------------------------------------
|
||||
Test set: com.example.demo.controller.AuthFlowTest
|
||||
-------------------------------------------------------------------------------
|
||||
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.666 s -- in com.example.demo.controller.AuthFlowTest
|
||||
@@ -0,0 +1,4 @@
|
||||
-------------------------------------------------------------------------------
|
||||
Test set: com.example.demo.controller.UserControllerTest
|
||||
-------------------------------------------------------------------------------
|
||||
Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 24.99 s -- in com.example.demo.controller.UserControllerTest
|
||||
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user