1 编程范式
1.1 面向过程
1.2 面向对象
1.3 面向切面编程
1.4 函数式编程
1.5 事件驱动编程
2 什么是面向切面编程
2.1 是一种编程范式,而不是一种编程语言
2.2 解决一些特定的问题
2.3 作为面向对象编程的一种补充
3 AOP产生的初衷
3.1 解决代码重复性问题 Don't Repeat Yourself
3.2 解决关注点分离问题 Separation of Concerns
3.2.1 水平分离(技术上划分)
控制层 -> 服务层 -> 持久层
3.2.2 垂直分离(模块上划分)
模块划分
3.2.3 切面分离(功能上划分)
分离功能性需求和非功能性需求
4 使用AOP的好处
4.1 集中处理某一个关注点、横切逻辑
4.2 可以很方便地添加、删除关注点
4.3 侵入性减少,增强代码可读性和可维护性
5 AOP的使用场景
5.1 权限控制
5.2 缓存控制
5.3 事物控制
5.4 审计日志
5.5 性能监控
5.6 分布式追踪
5.7 异常处理
6 利用传统方法实现权限验证
6.1 创建一个springBoot项目
下载地址:点击前往
6.2 新建一个Product实体类
package cn.test.demo.base_demo.entity; /**
* @author 王杨帅
* @create 2018-04-29 17:11
* @desc 商品实体类
**/
public class Product {
private Integer id;
private String name; public Product() {
} public Product(Integer id, String name) {
this.id = id;
this.name = name;
} public Integer getId() {
return id;
} public void setId(Integer id) {
this.id = id;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} @Override
public String toString() {
return "Product{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
Product.java
6.3 新建一个权限模拟类
该类主要用来设定用户和获取用户
package cn.test.demo.base_demo.security; /**
* @author 王杨帅
* @create 2018-04-29 17:15
* @desc 模拟用户登录,该类可以设定用户和获取用户
**/
public class CurrentSetHolder {
private final static ThreadLocal<String> holder = new ThreadLocal<>(); /**
* 获取用户
* @return
*/
public static String get() {
return holder.get() == null ? "unknown" : holder.get();
} /**
* 设定用户
* @param user
*/
public static void set(String user) {
holder.set(user);
}
}
CurrentSetHolder.java
6.4 新建一个权限校验类
该类主要用于判断当前用户是否是“admin”用户
package cn.test.demo.base_demo.service; import cn.test.demo.base_demo.security.CurrentSetHolder;
import org.springframework.stereotype.Component; /**
* @author 王杨帅
* @create 2018-04-29 17:19
* @desc 权限校验服务类
**/
@Component
public class AuthService { /**
* 权限检查,如果用户不是 “admin" 就会报错
*/
public void checkAccess() {
String user = CurrentSetHolder.get();
if (!"admin".equals(user)) {
throw new RuntimeException("operation not allow.");
}
}
}
AuthService.java
6.5 新建ProductService类
该类主要实现对Product的一些操作
package cn.test.demo.base_demo.service; import cn.test.demo.base_demo.entity.Product;
import cn.test.demo.base_demo.security.AdminOnly;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; /**
* @author 王杨帅
* @create 2018-04-29 17:14
* @desc 商品服务层
**/
@Service
@Slf4j
public class ProductService { private final String className = getClass().getName(); /**
* 依赖注入权限校验服务
*/
@Autowired
private AuthService authService; public void insert(Product product) {
authService.checkAccess(); // 利用权限校验对象进行权限校验,不是 ”admin" 就会报错
log.info("===/" + className + "/insert===新增数据");
} @AdminOnly
public void delete(Integer id) {
// authService.checkAccess(); // 利用权限校验对象进行权限校验
log.info("===/" + className + "/delete===删除数据");
}
}
ProductService.java
6.6 创建一个服务层测试类
在调用ProductService的insert方法前对用户进行设定
package cn.test.demo.base_demo.service; import cn.test.demo.base_demo.entity.Product;
import cn.test.demo.base_demo.security.CurrentSetHolder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.omg.CORBA.Current;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner; import static org.junit.Assert.*; @RunWith(SpringRunner.class)
@SpringBootTest
public class ProductServiceTest { /**
* 依赖注入权限
*/
@Autowired
private ProductService productService; /**
* productService.insert(product) 方法中有权限验证,如果不是“admin"用户就会报错
*/
@Test
public void annonInsert() {
CurrentSetHolder.set("bob"); // 设置用户
Product product = new Product();
productService.insert(product);
} /**
* productService.insert(product) 方法中有权限验证,如果不是“admin"用户就会报错
*/
@Test
public void adminInsert() {
CurrentSetHolder.set("admin"); // 设置用户
Product product = new Product();
productService.insert(product);
} @Test
public void delete() throws Exception {
} }
ProductServiceTest.java
7 利用AOP实现权限验证
7.1 新建一个springBoot项目
下载地址:点击前往
7.2 创建一个Product实体类
package cn.test.demo.base_demo.entity; /**
* @author 王杨帅
* @create 2018-04-29 17:11
* @desc 商品实体类
**/
public class Product {
private Integer id;
private String name; public Product() {
} public Product(Integer id, String name) {
this.id = id;
this.name = name;
} public Integer getId() {
return id;
} public void setId(Integer id) {
this.id = id;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} @Override
public String toString() {
return "Product{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
Product.java
7.3 创建一个AdminOnly注解
package cn.test.demo.base_demo.security; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AdminOnly {
}
AdminOnly.java
7.4 新建一个权限模拟类
该类主要用来设置用户和获取用户
package cn.test.demo.base_demo.security; /**
* @author 王杨帅
* @create 2018-04-29 17:15
* @desc 模拟用户登录,该类可以设定用户和获取用户
**/
public class CurrentSetHolder {
private final static ThreadLocal<String> holder = new ThreadLocal<>(); /**
* 获取用户
* @return
*/
public static String get() {
return holder.get() == null ? "unknown" : holder.get();
} /**
* 设定用户
* @param user
*/
public static void set(String user) {
holder.set(user);
}
}
CurrentSetHolder.java
7.5 创建一个切面类
该类主要对所有使用了@AdminOnly注解的方法进行权限校验
package cn.test.demo.base_demo.security; import cn.test.demo.base_demo.service.AuthService;
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; /**
* @author 王杨帅
* @create 2018-04-29 17:37
* @desc 权限检查的AOP类
**/
@Aspect
@Component
public class SecurityAspect {
@Autowired
AuthService authService; @Pointcut("@annotation(AdminOnly)")
public void adminOnly(){ } @Before("adminOnly()")
public void check(){
authService.checkAccess();
}
}
SecurityAspect.java
7.6 新建一个ProductService服务类
该类的delete方法用了@AdminOnly注解,所以delete方法会进行权限校验
package cn.test.demo.base_demo.service; import cn.test.demo.base_demo.entity.Product;
import cn.test.demo.base_demo.security.AdminOnly;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; /**
* @author 王杨帅
* @create 2018-04-29 17:14
* @desc 商品服务层
**/
@Service
@Slf4j
public class ProductService { private final String className = getClass().getName(); /**
* 依赖注入权限校验服务
*/
@Autowired
private AuthService authService; public void insert(Product product) {
authService.checkAccess(); // 利用权限校验对象进行权限校验,不是 ”admin" 就会报错
log.info("===/" + className + "/insert===新增数据");
} @AdminOnly
public void delete(Integer id) {
// authService.checkAccess(); // 利用权限校验对象进行权限校验
log.info("===/" + className + "/delete===删除数据");
}
}
ProductService.java
7.7 创建一个测试类
productService.delete(id) 方法中有权限验证,如果不是“admin"用户就会报错【利用AOP实现的】
package cn.test.demo.base_demo.service; import cn.test.demo.base_demo.entity.Product;
import cn.test.demo.base_demo.security.CurrentSetHolder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.omg.CORBA.Current;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner; import static org.junit.Assert.*; @RunWith(SpringRunner.class)
@SpringBootTest
public class ProductServiceTest { /**
* 依赖注入权限
*/
@Autowired
private ProductService productService; /**
* productService.insert(product) 方法中有权限验证,如果不是“admin"用户就会报错【利用传统方法实现的】
*/
@Test
public void annonInsert() {
CurrentSetHolder.set("bob"); // 设置用户
Product product = new Product();
productService.insert(product);
} /**
* productService.insert(product) 方法中有权限验证,如果不是“admin"用户就会报错【利用传统方法实现的】
*/
@Test
public void adminInsert() {
CurrentSetHolder.set("admin"); // 设置用户
Product product = new Product();
productService.insert(product);
} /**
* productService.delete(id) 方法中有权限验证,如果不是“admin"用户就会报错【利用AOP实现的】
*/
@Test
public void annoDelete() throws Exception {
CurrentSetHolder.set("peter");
productService.delete(3);
} /**
* productService.delete(id) 方法中有权限验证,如果不是“admin"用户就会报错【利用AOP实现的】
*/
@Test
public void adminDelete() {
CurrentSetHolder.set("admin");
productService.delete(4);
} }
ProductServiceTest.java