2

时间: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如下

完成后的项目结构如下

clipboard.png

代码编写

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:目标对象

代理模式类图

clipboard.png

客户端通过接口来引用目标对象
代理对象把真正的方法委托目标对象来执行,自己执行额外的逻辑

代码编写

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时序图

clipboard.png

SpringAOP对两种实现的选择

如果目标对象实现了接口,则默认采用JDK动态代理
如果目标对象没有实现接口,则采用Cglib进行动态代理
如果目标对象实现了接口,但设置强制cglib代理,则使用cglib代理
在SpringBoot中,通过@EnableAspectJAutoProxy(proxyTargetClass=true)设置

3-8 链式调用

当多个AOP作用到同一个目标对象时,采用责任链模式

责任链模式类图

clipboard.png

代码编写

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的使用局限

妙手空空
1.3k 声望368 粉丝

博观而约取,厚积而薄发