首页 > IT职业 > IT图书 > 慕课网《探秘Spring AOP》学习总结
慕课网《探秘Spring AOP》学习总结
时间:2017年09月03日星期日
说明:本文部分内容均来自慕课网。@慕课网:http://www.imooc.com
教学源码:https://github.com/zccodere/study-imooc
学习源码:https://github.com/zccodere/study-imooc 第一章:课程介绍 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如下
4.0.0 com.myimooc springaopguide 0.0.1-SNAPSHOT jar springaop http://maven.apache.org org.springframework.boot spring-boot-starter-parent 1.5.1.RELEASE <!-- lookup parent from repository -->UTF-8 UTF-8 1.8 org.springframework.boot spring-boot-starter-aop org.springframework.boot spring-boot-starter-test test org.springframework.boot spring-boot-maven-plugin 完成后的项目结构如下

代码编写
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 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类
2.编写RealSubject类
3.编写Proxy类
4.编写Client类
受篇幅限制,源码请到我的github地址查看
3-4 JDK代理静态代理与动态代理
静态代理的缺点:每当需要代理的方法越多的时候,重复的逻辑就越多
动态代理的两类实现:基于接口代理与基于继承代理
两类实现的代表技术:JDK代理与Cglib代理 JDK实现要点
类:java.lang.reflect.Proxy
接口:InvocationHandler
只能基于接口进行动态代理 代码编写
1.编写JdkSubject类
2.编写Client类
受篇幅限制,源码请到我的github地址查看
3-5 JDK解析JDK代理源码解析
Proxy.newProxyInstance(首先,调用该方法)
getProxyClass0、ProxyClassFactory、ProxyGenerator(然后,分别调用方法,生成字节码)
newInstance(最后,利用反射根据字节码生成实例) 3-6 Cglib代理 代码编写
1.编写DemoMethodInterceptor类
2.编写Client类
受篇幅限制,源码请到我的github地址查看
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类
2.编写Client类
3.编写Chain类
4.编写ChainHandler类
5.编写ChainClient类
受篇幅限制,源码请到我的github地址查看
第四章:代码解读 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如下
4.0.0 com.myimooc mydatalog 0.0.1-SNAPSHOT jar mydatalog http://maven.apache.org org.springframework.boot spring-boot-starter-parent 1.5.2.RELEASE <!-- lookup parent from repository -->UTF-8 UTF-8 1.8 org.springframework.boot spring-boot-starter-aop org.springframework.boot spring-boot-starter-data-jpa <!--
org.springframework.boot
spring-boot-starter-data-mongodb
-->mysql mysql-connector-java org.springframework.boot spring-boot-starter-test test commons-beanutils commons-beanutils com.alibaba fastjson 1.2.36 完成后的项目结构图如下

受篇幅限制,源码请到我的github地址查看
第六章:课程总结 6-1 课程总结要点清单
AOP的适用范围及优劣势
AOP的概念及Spring切面表达式
AOP的实现原理及运用 使用SpringAOP的注意事项
不宜把重要的业务逻辑放到AOP中处理
无法拦截static、final、private方法
无法拦截内部方法调用 课程小结
合理利用面向切面编程提高代码质量
掌握SpringAOP概念及实现原理
了解AOP的优缺点及SpringAOP的使用局限 来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/1817/viewspace-2808096/ ,如需转载,请注明出处,否则将追究法律责任。