前言
切面很多时候都可以用到,多数据源动态切换,AOP编程检查权限是否开启,AOP编程检查网络状态是否可用,AOP检查用户登录,AOP用户行为统计,AOP日志等。当为一个系统添加新的功能的时候又不能修改原来的代码 ,这个时候就可以使用aop技术。当为多个类添加相同的功能的时候就可以使用aop技术,当为业务方法增加事务或者日志输出的时候可以使用aop技术。
AOP
所谓AOP是面向切面编程。即:在不改变原来代码的基础上,实现功能的扩展(增加)。
切面
切面:由切点、前置通知、后置通知这三部分织成的一个横向的平面。
① 切点
前面已经说到,AOP就是:在不改变原来代码的基础上,实现功能的扩展(增加)。
首先我们要清楚明白我们要对哪个功能方法进行扩展,比如说我们对a()方法进行功能扩展。我们把a()方法称为:切点。
② 前置通知
在切点之前执行的扩展方法。
③ 后置通知
在切点之后执行的扩展方法。
一张图简单的解释AOP思想
用法
数据库数据为以下:
1.添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2、编写UserServiceImpl类
package com.dalaoyang.serviceImpl;
import ch.qos.logback.core.db.dialect.DBUtil;
import com.dalaoyang.config.DataSource;
import com.dalaoyang.dao.UserMapper;
import com.dalaoyang.entity.User;
import com.dalaoyang.utils.DbUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Arrays;
import java.util.List;
@Service
public class UserServiceImpl {
@Autowired
UserMapper userMapper;
public String getUser(){
User user =userMapper.findUserByUsername("dalaoyang");
return user!=null ? "dalaoyang"+"的密码是:"+user.getUser_password():"不存在用户名为"+"dalaoyang"+"的用户";
}
public String setUser(){
User user =userMapper.findUserByUsername("xiaoli");
return user!=null ? "xiaoli"+"的密码是:"+user.getUser_password():"不存在用户名为"+"xiaoli"+"的用户";
}
public String ALLUser(){
List<User> userList =userMapper.getUserList();
return userList.toString() ;
}
@DataSource(DbUtil.DB_MASTER2)
public String findUser(){
User user =userMapper.findUserByUsername("xiaoxiongmao");
return user!=null ? "xiaoxiongmao"+"的密码是:"+user.getUser_password():"不存在用户名为"+"xiaoxiongmao"+"的用户";
}
}
3、编写普通方法AOP类
package com.dalaoyang.config;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
@Aspect
@Component
public class AOPAspect {
@Pointcut(value = "execution(* com.dalaoyang.serviceImpl.UserServiceImpl.get*(..))") //注意这里是全文件匹配函数
public void getPoint() {
}
@Pointcut(value = "execution(* com.dalaoyang.serviceImpl.UserServiceImpl.set*(..))") //注意这里是全文件匹配函数
public void setPoint() {
}
@Before("getPoint()")
public void getPointAcc1(){
System.out.println("BeforeGetPoint");
}
@After("getPoint()")
public void getPointAcc2(){
System.out.println("AfterGetPoint");
}
@Before("setPoint()")
public void setPointAcc1(){
System.out.println("BeforeSetPoint");
}
@After("setPoint()")
public void setPointAcc2(){
System.out.println("AfterSetPoint");
}
@Around("getPoint()")
public Object getDoAround(ProceedingJoinPoint pjp){
ThreadLocal<Long> startTime = new ThreadLocal<>();
startTime.set(System.currentTimeMillis());
System.out.println("我是环绕通知执行");
Object obj;
try{
obj = pjp.proceed(); //启动目标方法执行
System.out.println("执行返回值 : " + obj);
Signature s = pjp.getSignature();
System.out.println(pjp.getSignature().getName()+"方法执行耗时: " +
(System.currentTimeMillis() - startTime.get()));
} catch (Throwable throwable) {
obj=throwable.toString();
}
return obj;
}
@Around("setPoint()")
public Object setDoAround(ProceedingJoinPoint pjp){
ThreadLocal<Long> startTime = new ThreadLocal<>();
startTime.set(System.currentTimeMillis());
System.out.println("我是环绕通知执行");
Object obj;
try{
obj = pjp.proceed(); //启动目标方法执行
System.out.println("执行返回值 : " + obj);
Signature s = pjp.getSignature();
System.out.println(pjp.getSignature().getName()+"方法执行耗时: " +
(System.currentTimeMillis() - startTime.get()));
}catch (Throwable throwable) {
obj=throwable.toString();
}
return obj;
}
/**
* 执行完请求可以做的
* @param result
* @throws Throwable
*/
@AfterReturning(returning = "result", pointcut = "getPoint()||setPoint()")
public void doAfterReturning(Object result){
System.out.println("大家好,我是@AfterReturning,他们都秀完了,该我上场了"+result);
}
}
4、测试代码
package com.dalaoyang;
import com.dalaoyang.serviceImpl.UserServiceImpl;
import com.dalaoyang.utils.DbUtil;
import com.dalaoyang.utils.DbUtilA;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
@SpringBootTest
public class SpringbootMybatisApplicationTests {
@Resource
private UserServiceImpl userService;
@Test
public void contextLoads() {
System.out.println(userService.getUser());
}
@Test
public void setContextLoads() {
System.out.println(userService.setUser());
}
}
5、测试结果
我是环绕通知执行
BeforeGetPoint
2022-01-21 13:18:13.287 INFO 3316 — [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting…
2022-01-21 13:18:13.870 INFO 3316 — [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2022-01-21 13:18:13.886 DEBUG 3316 — [ main] c.d.dao.UserMapper.findUserByUsername : Preparing: SELECT * FROM user WHERE user_name=?
2022-01-21 13:18:13.925 DEBUG 3316 — [ main] c.d.dao.UserMapper.findUserByUsername : > Parameters: dalaoyang(String)
2022-01-21 13:18:13.957 DEBUG 3316 — [ main] c.d.dao.UserMapper.findUserByUsername : <== Total: 1
大家好,我是@AfterReturning,他们都秀完了,该我上场了dalaoyang的密码是:1111
AfterGetPoint
执行返回值 : dalaoyang的密码是:1111
getUser方法执行耗时: 733
dalaoyang的密码是:1111
2022-01-21 13:18:14.087 INFO 3316 — [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated…
2022-01-21 13:18:14.142 INFO 3316 — [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
Process finished with exit code 0
6、编写自定义注解
DataSource
package com.dalaoyang.config;
import com.dalaoyang.utils.DbUtil;
import java.lang.annotation.*;
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DataSource {
String value() default DbUtil.DB_MASTER1;
}
DbUtil
package com.dalaoyang.utils;
public class DbUtil {
/**数据库ds_master1**/
public static final String DB_MASTER1 = "ds_master1";
/**数据库ds_master2**/
public static final String DB_MASTER2 = "ds_master2";
}
7、编写自定义注解方法AOP类
package com.dalaoyang.config;
import com.dalaoyang.utils.DbUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
@Aspect
@Component
public class AOPAspectAnnotation {
@Pointcut(value = "@annotation(com.dalaoyang.config.DataSource)") //注意这里是全文件匹配函数
public void getPoint() {
}
@Before("getPoint()")
public void setPointAcc1(){
System.out.println("BeforeGetPoint");
}
@Around("getPoint()")
public Object getDoAround(ProceedingJoinPoint pjp){
ThreadLocal<Long> startTime = new ThreadLocal<>();
startTime.set(System.currentTimeMillis());
System.out.println("我是环绕通知执行");
Object obj;
try{
DataSource dataSource = null;
MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
Method method = methodSignature.getMethod();
dataSource = method.getAnnotation(DataSource.class);
if (dataSource==null){
System.out.println("空值");
System.out.println(DbUtil.DB_MASTER1);
obj = pjp.proceed();
System.out.println("执行返回值 : " + obj);
}else {
System.out.println(dataSource.value());
obj = pjp.proceed();
System.out.println("执行返回值 : " + obj);
}
System.out.println(pjp.getSignature().getName()+"方法执行耗时: " +
(System.currentTimeMillis() - startTime.get()));
} catch (Throwable throwable) {
obj=throwable.toString();
}
return obj;
}
@After("getPoint()")
public void setPointAcc2(){
System.out.println("AfterGetPoint");
}
/**
* 执行完请求可以做的
* @param result
* @throws Throwable
*/
@AfterReturning(returning = "result", pointcut = "getPoint()")
public void doAfterReturning(Object result){
System.out.println("大家好,我是@AfterReturning,他们都秀完了,该我上场了"+result);
}
}
8、测试代码
package com.dalaoyang;
import com.dalaoyang.serviceImpl.UserServiceImpl;
import com.dalaoyang.utils.DbUtil;
import com.dalaoyang.utils.DbUtilA;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
@SpringBootTest
public class SpringbootMybatisApplicationTests {
@Resource
private UserServiceImpl userService;
@Test
public void get1ContextLoads() {
System.out.println(userService.findUser());
}
}
9、测试结果
我是环绕通知执行
ds_master2
BeforeGetPoint
2022-01-21 14:31:41.909 INFO 9072 — [main] com.zaxxer.hikari.HikariDataSource :
HikariPool-1 - Starting…
2022-01-21 14:31:42.366 INFO 9072 — [main] com.zaxxer.hikari.HikariDataSource :
HikariPool-1 - Start completed.
2022-01-21 14:31:42.384 DEBUG 9072 — [main] c.d.dao.UserMapper.findUserByUsername : =>
Preparing: SELECT * FROM user WHERE user_name=?
2022-01-21 14:31:42.444 DEBUG 9072 — [main] c.d.dao.UserMapper.findUserByUsername : =>
Parameters: xiaoxiongmao(String)
2022-01-21 14:31:42.478 DEBUG 9072 — [main] c.d.dao.UserMapper.findUserByUsername :
<== Total: 1
大家好,我是@AfterReturning,他们都秀完了,该我上场了xiaoxiongmao的密码是:3333
AfterGetPoint
执行返回值 : xiaoxiongmao的密码是:3333
findUser方法执行耗时: 630
xiaoxiongmao的密码是:3333
10、项目代码
AOP项目链接,方便更加切实了解,在idea导入maven项目即可。
提取码:rs8m