- 新增 IoC 容器学习模块 - 新增 AOP 切面编程学习模块 - 新增 MyBatis 集成学习模块 - 新增事务管理学习模块 - 新增用户/产品/订单 CRUD - 新增 7 个交互式学习页面 - 集成性能监控切面
159 lines
9.4 KiB
HTML
159 lines
9.4 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>AOP 切面学习 - Spring Boot</title>
|
||
<style>
|
||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #f5f5f5; }
|
||
.container { max-width: 1400px; margin: 0 auto; padding: 20px; }
|
||
|
||
.header { background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); color: white; padding: 30px 20px; text-align: center; margin-bottom: 20px; border-radius: 10px; }
|
||
.header h1 { font-size: 2em; }
|
||
|
||
.nav { display: flex; gap: 10px; margin-bottom: 20px; flex-wrap: wrap; justify-content: center; }
|
||
.nav a { padding: 10px 20px; background: white; border-radius: 20px; text-decoration: none; color: #333; font-size: 0.9em; }
|
||
.nav a:hover, .nav a.active { background: #f5576c; color: white; }
|
||
|
||
.card { background: white; border-radius: 10px; padding: 20px; margin-bottom: 20px; box-shadow: 0 2px 10px rgba(0,0,0,0.08); }
|
||
.card h3 { color: #f5576c; margin-bottom: 15px; border-bottom: 2px solid #eee; padding-bottom: 10px; }
|
||
|
||
.advice-box { background: #f8f9fa; border-radius: 8px; padding: 15px; margin: 10px 0; border-left: 4px solid; }
|
||
.advice-before { border-color: #28a745; }
|
||
.advice-after { border-color: #6c757d; }
|
||
.advice-returning { border-color: #17a2b8; }
|
||
.advice-throwing { border-color: #dc3545; }
|
||
.advice-around { border-color: #ffc107; }
|
||
|
||
.btn { padding: 10px 20px; background: #f5576c; color: white; border: none; border-radius: 5px; cursor: pointer; margin: 5px; }
|
||
.btn:hover { background: #e0465b; }
|
||
|
||
.result { background: #1e1e1e; color: #d4d4d4; padding: 15px; border-radius: 5px; margin-top: 10px; font-family: monospace; font-size: 0.9em; overflow-x: auto; max-height: 400px; overflow-y: auto; }
|
||
|
||
.code-block { background: #f4f4f4; padding: 15px; border-radius: 5px; font-family: monospace; font-size: 0.9em; overflow-x: auto; margin: 10px 0; }
|
||
|
||
.flow-diagram { display: flex; align-items: center; justify-content: center; flex-wrap: wrap; gap: 10px; padding: 20px; background: #f8f9fa; border-radius: 10px; margin: 15px 0; }
|
||
.flow-step { padding: 10px 20px; background: white; border-radius: 20px; border: 2px solid #ddd; }
|
||
.flow-arrow { font-size: 1.5em; color: #999; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="container">
|
||
<div class="header">
|
||
<h1>🔪 AOP 切面编程</h1>
|
||
<p>Aspect Oriented Programming - 面向切面编程</p>
|
||
</div>
|
||
|
||
<div class="nav">
|
||
<a href="index.html">🏠 首页</a>
|
||
<a href="ioc.html">📦 IoC</a>
|
||
<a href="aop.html" class="active">🔪 AOP</a>
|
||
<a href="mybatis.html">💾 MyBatis</a>
|
||
<a href="transaction.html">🔄 事务</a>
|
||
<a href="users.html">👥 用户</a>
|
||
<a href="api.html">🔌 API</a>
|
||
</div>
|
||
|
||
<div class="card">
|
||
<h3>📚 核心概念</h3>
|
||
<p><strong>AOP (面向切面编程)</strong>:将横切关注点(日志、权限、事务等)从业务逻辑中分离出来,实现模块化。</p>
|
||
<div class="flow-diagram">
|
||
<div class="flow-step">目标方法</div>
|
||
<div class="flow-arrow">→</div>
|
||
<div class="flow-step">@Before</div>
|
||
<div class="flow-arrow">→</div>
|
||
<div class="flow-step">@Around (前)</div>
|
||
<div class="flow-arrow">→</div>
|
||
<div class="flow-step">方法执行</div>
|
||
<div class="flow-arrow">→</div>
|
||
<div class="flow-step">@Around (后)</div>
|
||
<div class="flow-arrow">→</div>
|
||
<div class="flow-step">@AfterReturning</div>
|
||
<div class="flow-arrow">→</div>
|
||
<div class="flow-step">@After</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="card">
|
||
<h3>🔔 五种通知类型</h3>
|
||
|
||
<div class="advice-box advice-before">
|
||
<h4>@Before - 前置通知</h4>
|
||
<p>方法执行前触发,可用于参数校验、日志记录</p>
|
||
<div class="code-block">@Before("execution(* com.example.service..*.*(..))")<br>public void before(JoinPoint jp) {<br> log.info("即将执行: " + jp.getSignature().getName());<br>}</div>
|
||
</div>
|
||
|
||
<div class="advice-box advice-after">
|
||
<h4>@After - 后置通知</h4>
|
||
<p>方法执行后触发(无论是否异常),可用于资源释放</p>
|
||
<div class="code-block">@After("execution(* com.example.service..*.*(..))")<br>public void after(JoinPoint jp) {<br> log.info("执行完成: " + jp.getSignature().getName());<br>}</div>
|
||
</div>
|
||
|
||
<div class="advice-box advice-returning">
|
||
<h4>@AfterReturning - 返回通知</h4>
|
||
<p>方法成功返回后触发,可获取返回值</p>
|
||
<div class="code-block">@AfterReturning(pointcut="...", returning="result")<br>public void afterReturning(Object result) {<br> log.info("返回结果: " + result);<br>}</div>
|
||
</div>
|
||
|
||
<div class="advice-box advice-throwing">
|
||
<h4>@AfterThrowing - 异常通知</h4>
|
||
<p>方法抛出异常后触发,可用于异常处理</p>
|
||
<div class="code-block">@AfterThrowing(pointcut="...", throwing="ex")<br>public void afterThrowing(Exception ex) {<br> log.error("发生异常: " + ex.getMessage());<br>}</div>
|
||
</div>
|
||
|
||
<div class="advice-box advice-around">
|
||
<h4>@Around - 环绕通知</h4>
|
||
<p>完全控制方法执行,可决定是否执行目标方法</p>
|
||
<div class="code-block">@Around("execution(* com.example.controller..*.*(..))")<br>public Object around(ProceedingJoinPoint pjp) throws Throwable {<br> long start = System.currentTimeMillis();<br> Object result = pjp.proceed(); // 执行目标方法<br> long cost = System.currentTimeMillis() - start;<br> log.info("耗时: " + cost + "ms");<br> return result;<br>}</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="card">
|
||
<h3>🧪 在线测试</h3>
|
||
<p>调用下面的 API,然后查看控制台日志观察 AOP 执行顺序</p>
|
||
<button class="btn" onclick="testAop()">测试正常执行</button>
|
||
<button class="btn" onclick="testAopError()">测试异常通知</button>
|
||
<div id="testResult" class="result"></div>
|
||
</div>
|
||
|
||
<div class="card">
|
||
<h3>📝 切入点表达式</h3>
|
||
<table style="width:100%;border-collapse:collapse;">
|
||
<tr style="background:#f8f9fa;"><th style="padding:10px;text-align:left;">表达式</th><th style="padding:10px;text-align:left;">含义</th></tr>
|
||
<tr><td style="padding:10px;border-bottom:1px solid #eee;">execution(* *(..))</td><td style="padding:10px;border-bottom:1px solid #eee;">匹配所有方法</td></tr>
|
||
<tr><td style="padding:10px;border-bottom:1px solid #eee;">execution(* com.example.service.*.*(..))</td><td style="padding:10px;border-bottom:1px solid #eee;">service包下所有方法</td></tr>
|
||
<tr><td style="padding:10px;border-bottom:1px solid #eee;">execution(* com.example.service..*.*(..))</td><td style="padding:10px;border-bottom:1px solid #eee;">service包及子包所有方法</td></tr>
|
||
<tr><td style="padding:10px;border-bottom:1px solid #eee;">execution(public * *(..))</td><td style="padding:10px;border-bottom:1px solid #eee;">所有public方法</td></tr>
|
||
<tr><td style="padding:10px;border-bottom:1px solid #eee;">@annotation(org.springframework.transaction.annotation.Transactional)</td><td style="padding:10px;border-bottom:1px solid #eee;">带@Transactional的方法</td></tr>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
async function testAop() {
|
||
const result = document.getElementById('testResult');
|
||
result.textContent = '测试中...';
|
||
try {
|
||
const res = await fetch('/api/learning/aop/test?message=HelloAOP');
|
||
const data = await res.json();
|
||
result.innerHTML = `<strong>✅ 测试成功</strong>\n\n${JSON.stringify(data, null, 2)}\n\n💡 提示: 查看服务器控制台,观察 AOP 通知执行顺序`;
|
||
} catch (e) {
|
||
result.textContent = '测试失败: ' + e.message;
|
||
}
|
||
}
|
||
|
||
async function testAopError() {
|
||
const result = document.getElementById('testResult');
|
||
result.textContent = '测试中...';
|
||
try {
|
||
const res = await fetch('/api/learning/aop/test-error?error=true');
|
||
const data = await res.json();
|
||
result.innerHTML = `<strong>✅ 正常返回</strong>\n\n${JSON.stringify(data, null, 2)}`;
|
||
} catch (e) {
|
||
result.innerHTML = `<strong>❌ 触发异常(预期行为)</strong>\n\n异常信息: ${e.message}\n\n💡 提示: 查看服务器控制台,观察 @AfterThrowing 通知`;
|
||
}
|
||
}
|
||
</script>
|
||
</body>
|
||
</html> |