慕课网_《探秘Spring AOP》学习总结
时间:2017年09月03日星期日
说明:本文部分内容均来自慕课网。@慕课网:http://www.imooc.com
教学源码:https://github.com/zccodere/s...
学习源码:https://github.com/zccodere/s...
第一章:课程介绍
1-1 面向切面
课程章节
概览 AOP使用 AOP原理 AOP开源运用 课程实战 课程总结
面向切面编程是一种编程范式
编程范式概览
面向过程编程 面向对象编程 面向函数编程(函数式编程) 事件驱动编程(GUI开发中比较常见) 面向切面编程
AOP是什么
是一种编程范式,不是编程语言 解决特定问题,不能解决所有问题 是OOP的补充,不是替代
AOP的初衷
DRY:Don’t Repeat Yourself代码重复性问题
SOC:Separation of Concerns关注点分离
-水平分离:展示层->服务层->持久层
-垂直分离:模块划分(订单、库存等)
-切面分离:分离功能性需求与非功能性需求使用AOP的好处
集中处理某一关注点/横切逻辑 可以很方便地添加/删除关注点 侵入性少,增强代码可读性及可维护性
AOP的应用场景
权限控制 缓存控制 事务控制 审计日志 性能监控 分布式追踪 异常处理
支持AOP的编程语言
Java .NET C/C++ Ruby Python PHP …
1-2 简单案例
案例背景
产品管理的服务 产品添加、删除的操作只能管理员才能进行 普通实现VS AOP实现
创建一个名为springaopguide的maven项目pom如下
完成后的项目结构如下

代码编写
1.编写Product类
package com.myimooc.springaopguide.domain;
/**
* @title 产品领域模型
* @describe 产品实体对象
* @author zc
* @version 1.0 2017-09-03
*/
public class Product {
private Long id;
private String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}2.编写CurrentUserHolder类
package com.myimooc.springaopguide.security;
/**
* @title 获取用户信息
* @describe 模拟用户的切换,将用户信息存入当前线程
* @author zc
* @version 1.0 2017-09-03
*/
public class CurrentUserHolder {
private static final ThreadLocal<String> holder = new ThreadLocal<>();
public static String get(){
return holder.get() == null ? "unkown" : holder.get();
}
public static void set(String user){
holder.set(user);
}
}3.编写AdminOnly类
package com.myimooc.springaopguide.security;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @title 管理员权限注解
* @describe 被该注解声明的方法需要管理员权限
* @author zc
* @version 1.0 2017-09-03
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AdminOnly {
}4.编写SecurityAspect类
package com.myimooc.springaopguide.security;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.myimooc.springaopguide.service.AuthService;
/**
* @title 权限校验切面类
* @describe
* @author zc
* @version 1.0 2017-09-03
*/
// 声明为一个切面
@Aspect
@Component
public class SecurityAspect {
@Autowired
private AuthService authService;
// 使用要拦截标注有AdminOnly的注解进行操作
@Pointcut("@annotation(AdminOnly)")
public void adminOnly(){
}
@Before("adminOnly()")
public void check(){
authService.checkAccess();
}
}5.编写AuthService类
package com.myimooc.springaopguide.service;
import java.util.Objects;
import org.springframework.stereotype.Service;
import com.myimooc.springaopguide.security.CurrentUserHolder;
/**
* @title 权限校验类
* @describe 对用户权限进行校验
* @author zc
* @version 1.0 2017-09-03
*/
@Service
public class AuthService {
public void checkAccess(){
String user = CurrentUserHolder.get();
if(!Objects.equals("admin", user)){
throw new RuntimeException("operation not allow");
}
}
}6.编写ProductService类
package com.myimooc.springaopguide.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.myimooc.springaopguide.domain.Product;
/**
* @title 产品服务类
* @describe 产品相关业务服务-传统方式实现权限校验
* @author zc
* @version 1.0 2017-09-03
*/
@Service
public class ProductService {
@Autowired
private AuthService AuthService;
public void insert(Product product){
AuthService.checkAccess();
System.out.println("insert product");
}
public void delete(Long id){
AuthService.checkAccess();
System.out.println("delete product");
}
}7.编写ProductServiceAop类
package com.myimooc.springaopguide.service;
import org.springframework.stereotype.Service;
import com.myimooc.springaopguide.domain.Product;
import com.myimooc.springaopguide.security.AdminOnly;
/**
* @title 产品服务类
* @describe 产品相关业务服务-AOP方式实现权限校验
* @author zc
* @version 1.0 2017-09-03
*/
@Service
public class ProductServiceAop {
@AdminOnly
public void insert(Product product){
System.out.println("insert product");
}
@AdminOnly
public void delete(Long id){
System.out.println("delete product");
}
}8.编写AopGuideApplicationTests类
package com.myimooc.springaopguide;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import com.myimooc.springaopguide.security.CurrentUserHolder;
import com.myimooc.springaopguide.service.ProductService;
import com.myimooc.springaopguide.service.ProductServiceAop;
/**
* @title 单元测试类
* @describe 测试权限校验服务是否生效
* @author zc
* @version 1.0 2017-09-03
*/
@RunWith(SpringRunner.class)
@SpringBootTest
public class AopGuideApplicationTests {
@Autowired
private ProductService productService;
@Test(expected = Exception.class)
public void annoInsertTest(){
CurrentUserHolder.set("tom");
productService.delete(1L);
}
@Test
public void adminInsertTest(){
CurrentUserHolder.set("admin");
productService.delete(1L);
}
@Autowired
private ProductServiceAop productServiceAop;
@Test(expected = Exception.class)
public void annoInsertAopTest(){
CurrentUserHolder.set("tom");
productServiceAop.delete(1L);
}
@Test
public void adminInsertAopTest(){
CurrentUserHolder.set("admin");
productServiceAop.delete(1L);
}
}第二章:使用详解
2-1 本节内容
Spring AOP使用方式
XML配置+Pointcut expression【不推荐使用方式】 注解方式+ Pointcut expression【推荐使用该方式】
Aspectj注解
@Aspect:用于声明当前类是一个切面 @Pointcut:用于描述在哪些类、哪些方法上执行切面的代码 Advice:描述想要在这些方法执行的什么时机进行拦截
本章内容
Pointcut express:切面表达式 5种Advice:建言的五种细分怎么使用
2-2 切面表达式
切面表达式
1.designators(指示器)
execution()
描述通过什么样的方式去匹配哪些类、哪些方法
2.wildcards(通配符)
* .. +
使用通配符进行描述
3.operators(运算符)
&& || !
使用运算符进行多条件的判断Designators(指示器)
匹配方法 execution() 匹配注解 @target() @args() @within() @annotation() 匹配包/类型 @within() 匹配对象 this() bean() target() 匹配参数 args()
Wildcards(通配符)
* 匹配任意数量的字符 + 匹配指定类及其子类 .. 一般用于匹配任意参数的子包或参数
Operators(运算符)
&& 与操作符 || 或操作符 ! 非操作符
2-3 匹配包类
// 匹配 ProductServiceAop 类里面的所有方法
@Pointcut("within(com.myimooc.springaopguide.service.ProductServiceAop)")
public void matchType(){}
// 匹配 com.myimooc.springaopguide.service 包及子包下所有类的方法
@Pointcut("within(com.myimooc.springaopguide.service..*)")
public void matchPackage(){}2-4 匹配对象
// 匹配AOP对象的目标对象为指定类型的方法,即DemoDao的aop代理对象的方法
@Pointcut("this(com.myimooc.springaopguide.dao.DemoDao)")
public void testDemo(){}
// 匹配实现IDao接口的目标对象(而不是aop代理后的对象)的方法,这里即DemoDao的方法
@Pointcut("target(com.myimooc.springaopguide.dao.IDao)")
public void targetDemo(){}
// 匹配所有以Service结尾的bean里面的方法
@Pointcut("bean(*Service)")
public void beanDemo(){}2-5 匹配参数
// 匹配任何以find开头而且只有一个Long参数的方法
@Pointcut("execution(* *..find*(Long))")
public void argsDemo1(){}
// 匹配任何只有一个Long参数的方法
@Pointcut("args(Long)")
public void argsDemo2(){}
// 匹配任何以find开头而且第一个参数为Long型的方法
@Pointcut("execution(* *..find*(Long,..))")
public void argsDemo3(){}
// 匹配第一个参数为Long型的方法
@Pointcut("args(Long,..))")
public void argsDemo4(){}2-6 匹配注解
// 匹配方法标注有AdminOnly的注解的方法
@Pointcut("@annotation(com.myimooc.springaopguide.security.AdminOnly)")
public void annoDemo(){}
// 匹配标注有Beta的类底下的方法,要求的annotation的RetentionPolicy级别为CLASS
@Pointcut("@within(com.google.common.annotations.Beta)")
public void annoWithDemo(){}
// 匹配标注有Repository的类底下的方法,要求的RetentionPolicy级别为RUNTIME
@Pointcut("@target(org.springframework.stereotype.Repository)")
public void annoTargetDemo(){}
// 匹配传入的参数类标注有Repository注解的方法
@Pointcut("@args(org.springframework.stereotype.Repository)")
public void annoArgsDemo(){}2-7 匹配方法
execution()格式
execution(
modifier-pattern? // 修饰符匹配
ret-type-pattern // 返回值匹配
declaring-type-pattern? // 描述值包名
name-pattern(param-pattern) // 方法名匹配(参数匹配)
throws-pattern?// 抛出异常匹配
)execution()实例
// 匹配 使用public修饰符 任意返回值 在com.myimooc.springaopguide.service包及子下
// 以Service结尾的类 任意方法(任意参数)
@Pointcut("execution(public * com.myimooc.springaopguide.service..*Service.*(..))")
public void matchCondition(){}2-8 建言注解
5中Advice(建言)注解
@Before,前置通知 @After(finally),后置通知,方法执行完之后 @AfterReturning,返回通知,成功执行之后 @AfterThrowing,异常通知,抛出异常之后 @Around,环绕通知
5中Advice(建言)实例
// 定义切点,拦截使用NeedSecured注解修饰的方法
@Pointcut("@within(com.myimooc.demo.security.NeedSecured)")
public void annoTargetVsWithinDemo(){}
// 使用NeedSecured注解修饰 且 在com.myimooc包下的方法
@Before("annoTargetVsWithinDemo() && within(com.myimooc..*)")
public void beforeDemo(){
System.out.println("被拦截方法执行之前执行");
}
@After("annoTargetVsWithinDemo() && within(com.myimooc..*)")
public void afterDemo(){
System.out.println("被拦截方法执行之后执行");
}
@AfterReturning("annoTargetVsWithinDemo() && within(com.myimooc..*)")
public void afterReturning(){
System.out.println("代码成功之后执行");
}
@AfterThrowing("annoTargetVsWithinDemo() && within(com.myimooc..*)")
public void afterThrowing(){
System.out.println("代码执行抛出异常之后执行");
}
@Around("annoTargetVsWithinDemo() && within(com.myimooc..*)")
public Object aroundDemo(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("相当于@Before");
try{
Object result = pjp.proceed(pjp.getArgs());
System.out.println("相当于@AfterReturning");
return result;
}catch (Throwable throwable) {
System.out.println("相当于@AfterThrowing");
throw throwable;
}finally {
System.out.println("相当于@After");
}
}Advice中的参数及结果绑定
@Before("annoTargetVsWithinDemo() && within(com.myimooc..*) && args(userId)")
public void beforeWithArgs(JoinPoint joinPoint,Long userId){
System.out.println("被拦截方法执行之前执行,args:"+userId);
}
@AfterReturning(value="annoTargetVsWithinDemo() && within(com.myimooc..*)",returning="returnValue")
public void getResult(Object returnValue){
if(returnValue != null){
System.out.println("代码成功之后执行,result:"+returnValue);
}
}第三章:实现原理
3-1 本节内容
上节回顾
Pointcut expression的组成部分 各种designators的区别 5中advice及参数、结果绑定
实现原理
概述 设计:代理模式、责任链模式 实现:JDK实现、cglib实现
3-2 原理概述
原理概述:植入的时机
1.编译期(AspectJ) 2.类加载时(Aspectj 5+) 3.运行时(Spring AOP)【本节课讲解内容】
运行时值入
运行时织入是怎么实现的 从静态代理到动态代理 基于接口代理与基于继承代理
3-3 代理模式
代理AOP对象
Caller:调用方 Proxy:AOP代理对象 Target:目标对象
代理模式类图

客户端通过接口来引用目标对象 代理对象把真正的方法委托目标对象来执行,自己执行额外的逻辑
代码编写
1.编写Subject类
package com.myimooc.myproxydemo.pattern;
/**
* @title 代理对象接口
* @describe
* @author zc
* @version 1.0 2017-09-13
*/
public interface Subject {
void request();
}2.编写RealSubject类
package com.myimooc.myproxydemo.pattern;
/**
* @title 目标对象
* @describe 实现了Subject接口
* @author zc
* @version 1.0 2017-09-13
*/
public class RealSubject implements Subject{
@Override
public void request() {
System.out.println("real subject execute request");
}
}3.编写Proxy类
package com.myimooc.myproxydemo.pattern;
/**
* @title 代理对象
* @describe 同样也实现了Subject接口
* @author zc
* @version 1.0 2017-09-13
*/
public class Proxy implements Subject{
// 需要引用目标对象
private RealSubject realSubject;
// 强制必须传入目标对象
public Proxy(RealSubject realSubject) {
this.realSubject = realSubject;
}
@Override
public void request() {
// 在目标对象方法执行之前做一些额外的事情
System.out.println("before");
try{
// 代理对象不会做真实的业务逻辑,还是委托给真实的目标对象执行
realSubject.request();
}catch (Exception e) {
System.out.println("ex:"+e.getMessage());
throw e;
}finally {
// 在目标对象方法执行之后做一些额外的事情
System.out.println("after");
}
}
}4.编写Client类
package com.myimooc.myproxydemo.pattern;
/**
* @title 客户端
* @describe 测试代理模式
* @author zc
* @version 1.0 2017-09-13
*/
public class Client {
public static void main(String[] args) {
Subject subject = new Proxy(new RealSubject());
subject.request();
}
}3-4 JDK代理
静态代理与动态代理
静态代理的缺点:每当需要代理的方法越多的时候,重复的逻辑就越多 动态代理的两类实现:基于接口代理与基于继承代理 两类实现的代表技术:JDK代理与Cglib代理
JDK实现要点
类:java.lang.reflect.Proxy 接口:InvocationHandler 只能基于接口进行动态代理
代码编写
1.编写JdkSubject类
package com.myimooc.myproxydemo.jdkimpl;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import com.myimooc.myproxydemo.pattern.RealSubject;
/**
* @title 动态代理类
* @describe 相当于AOP的aspect
* @author zc
* @version 1.0 2017-09-13
*/
public class JdkSubject implements InvocationHandler{
// 同样需要引入目标对象
private RealSubject realSubject;
public JdkSubject(RealSubject realSubject) {
this.realSubject = realSubject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 在目标对象方法执行之前做一些额外的事情
System.out.println("before");
Object result = null;
try{
// 代理对象不会做真实的业务逻辑,还是委托给真实的目标对象执行
result = method.invoke(realSubject, args);
}catch (Exception e) {
System.out.println("ex:"+e.getMessage());
throw e;
}finally {
// 在目标对象方法执行之后做一些额外的事情
System.out.println("after");
}
return result;
}
}2.编写Client类
package com.myimooc.myproxydemo.jdkimpl;
import java.lang.reflect.Proxy;
import com.myimooc.myproxydemo.pattern.RealSubject;
import com.myimooc.myproxydemo.pattern.Subject;
/**
* @title 动态代理类
* @describe JDK实现动态代理测试类
* @author zc
* @version 1.0 2017-09-13
*/
public class Client {
public static void main(String[] args) {
Subject subject = (Subject) Proxy.newProxyInstance(Client.class.getClassLoader(),
new Class[]{Subject.class}, new JdkSubject(new RealSubject()));
subject.request();
}
}3-5 JDK解析
JDK代理源码解析
Proxy.newProxyInstance(首先,调用该方法) getProxyClass0、ProxyClassFactory、ProxyGenerator(然后,分别调用方法,生成字节码) newInstance(最后,利用反射根据字节码生成实例)
3-6 Cglib代理
代码编写
1.编写DemoMethodInterceptor类
package com.myimooc.myproxydemo.cglib;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
/**
* @title 需要植入的代码类
* @describe 需要实现MethodInterceptorj接口
* @author zc
* @version 1.0 2017-09-13
*/
public class DemoMethodInterceptor implements MethodInterceptor{
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("before in cglib");
Object result = null;
try{
// 代理类调用父类的方法
proxy.invokeSuper(obj, args);
}catch (Exception e) {
System.out.println("ex:"+e.getMessage());
throw e;
}finally {
// 在目标对象方法执行之后做一些额外的事情
System.out.println("after in cglib");
}
return result;
}
}2.编写Client类
package com.myimooc.myproxydemo.cglib;
import com.myimooc.myproxydemo.pattern.RealSubject;
import com.myimooc.myproxydemo.pattern.Subject;
import net.sf.cglib.proxy.Enhancer;
/**
* @title 动态代理类
* @describe Cglib实现动态代理测试类
* @author zc
* @version 1.0 2017-09-13
*/
public class Client {
public static void main(String[] args) {
// 实例化Enhancer对象
Enhancer enhancer = new Enhancer();
// 设置需要代理的对象
enhancer.setSuperclass(RealSubject.class);
// 设置需要植入的代码
enhancer.setCallback(new DemoMethodInterceptor());
// 生成代理类
Subject subject = (Subject)enhancer.create();
subject.request();
}
}JDK与Cglib代理对比
JDK只能针对有接口的类的接口方法进行动态代理 Cglib基于继承来实现代理,无法对static、final类进行代理 Cglib基于继承来实现代理,无法对private、static方法进行代理
3-7 Spring选择
Spring创建代理bean时序图

SpringAOP对两种实现的选择
如果目标对象实现了接口,则默认采用JDK动态代理 如果目标对象没有实现接口,则采用Cglib进行动态代理 如果目标对象实现了接口,但设置强制cglib代理,则使用cglib代理 在SpringBoot中,通过@EnableAspectJAutoProxy(proxyTargetClass=true)设置
3-8 链式调用
当多个AOP作用到同一个目标对象时,采用责任链模式
责任链模式类图

代码编写
1.编写Handler类
package com.myimooc.myproxydemo.chain;
/**
* @title 责任链模式
* @describe 抽象接口
* @author zc
* @version 1.0 2017-09-13
*/
public abstract class Handler {
// 后继Handler,是否有类进行处理
private Handler sucessor;
// 对外暴露
public void execute(){
handleProcess();
if(sucessor != null){
sucessor.execute();
}
}
// 由子类实现
protected abstract void handleProcess();
public Handler getSucessor() {
return sucessor;
}
public void setSucessor(Handler sucessor) {
this.sucessor = sucessor;
}
}2.编写Client类
package com.myimooc.myproxydemo.chain;
/**
* @title 责任链模式
* @describe 测试类
* @author zc
* @version 1.0 2017-09-13
*/
public class Client {
static class HandlerA extends Handler{
@Override
protected void handleProcess() {
System.out.println("handle by a");
}
}
static class HandlerB extends Handler{
@Override
protected void handleProcess() {
System.out.println("handle by b");
}
}
static class HandlerC extends Handler{
@Override
protected void handleProcess() {
System.out.println("handle by c");
}
}
public static void main(String[] args) {
HandlerA handlerA = new HandlerA();
HandlerB HandlerB = new HandlerB();
HandlerC HandlerC = new HandlerC();
// 设置链接关系
handlerA.setSucessor(HandlerB);
HandlerB.setSucessor(HandlerC);
handlerA.execute();
}
}3.编写Chain类
package com.myimooc.myproxydemo.chain;
import java.util.List;
/**
* @title 责任链模式
* @describe 封装链式关系
* @author zc
* @version 1.0 2017-09-13
*/
public class Chain {
private List<ChainHandler> handlers;
private int index = 0;
public Chain(List<ChainHandler> handlers){
this.handlers = handlers;
}
public void proceed(){
if(index >= handlers.size()){
return;
}
handlers.get(index++).execute(this);
}
}4.编写ChainHandler类
package com.myimooc.myproxydemo.chain;
/**
* @title 责任链模式
* @describe 对Handler进行封装
* @author zc
* @version 1.0 2017-09-13
*/
public abstract class ChainHandler {
public void execute(Chain chain){
handleProcess();
chain.proceed();
}
// 由子类实现
protected abstract void handleProcess();
}5.编写ChainClient类
package com.myimooc.myproxydemo.chain;
import java.util.Arrays;
import java.util.List;
/**
* @title 责任链模式
* @describe 有顺序的链式调用测试类
* @author zc
* @version 1.0 2017-09-13
*/
public class ChainClient {
static class ChainHandlerA extends ChainHandler{
@Override
protected void handleProcess() {
System.out.println("handle by a");
}
}
static class ChainHandlerB extends ChainHandler{
@Override
protected void handleProcess() {
System.out.println("handle by b");
}
}
static class ChainHandlerC extends ChainHandler{
@Override
protected void handleProcess() {
System.out.println("handle by c");
}
}
public static void main(String[] args) {
// 声明链式调用顺序
List<ChainHandler> handlers = Arrays.asList(
new ChainHandlerA(),
new ChainHandlerB(),
new ChainHandlerC()
);
Chain chain = new Chain(handlers);
chain.proceed();
}
}第四章:代码解读
4-1 本节内容
上节回顾
静态代理与动态代理 JDK代理与Cglib代理区别及局限 代理模式与责任链模式
Spring AOP在开源项目里面的应用:三个例子
事务:@Transactional:Spring如何利用Transaction进行事务控制 安全:@PreAuthorize:Spring Security如何利用PreAuthorize进行安全控制 缓存:@Cacheable:Spring Cache如何利用Cacheable进行缓存控制
通过案例来讲解,源码可到我的github地址查看
第五章:实战案例
5-1 案例背景
实战案例背景
商家产品管理系统 记录产品修改的操作记录 什么人在什么时间修改了哪些产品的哪些字段修改为什么值
实现思路
利用aspect去拦截增删改方法 利用反射获取对象的新旧值 利用@Around的advice去记录操作记录
5-2 案例实现
创建名为mydatalog的maven项目pom如下
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.myimooc</groupId>
<artifactId>mydatalog</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>mydatalog</name>
<url>http://maven.apache.org</url>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.2.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency> -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.36</version>
</dependency>
</dependencies>
</project>完成后的项目结构图如下

受篇幅限制,源码请到我的github地址查看
第六章:课程总结
6-1 课程总结
要点清单
AOP的适用范围及优劣势 AOP的概念及Spring切面表达式 AOP的实现原理及运用
使用SpringAOP的注意事项
不宜把重要的业务逻辑放到AOP中处理 无法拦截static、final、private方法 无法拦截内部方法调用
课程小结
合理利用面向切面编程提高代码质量 掌握SpringAOP概念及实现原理 了解AOP的优缺点及SpringAOP的使用局限