AOP
AOP:面向切面编程
OOP:面向对象编程
面向切面编程:基于OOP基础之上的编程思想
指在程序运行期间,将某段代码动态地切入到指定方法的指定位置进行运行的这种编程方式,面向切面编程;
AOP专业名词
AOP使用步骤
1.导包
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
2.写配置文件
1)将目标类和封装了通知方法(在目标方法前后执行的方法)加入到ioc容器中
2)告诉spring哪个是切面
@Aspect 注解在切面类上
3)告诉spring切面类里面的方法都是何时何地运行
@Before在目标方法之前运行—前置通知
@After在目标方法运行结束之后运行—后置通知
@AfterReturning在目标方法正常返回之后—返回通知
@AfterThrowing在目标方法抛出异常之后运行 —异常通知
-
@Arround环绕—环绕通知,Before–》环绕前–》执行方法–》环绕后–》After
/** * @Around:环绕:是Spring中强大的通知 * @Around:环绕:动态代理 * try{ * //前置通知 * method.invoke(obj,args); * //返回通知 * }catch(e){ * //异常通知 * }finally{ * //后置通知 * } * 四合一通知就是环绕通知: * 环绕通知中有一个参数:ProceedingJoinPoint pjp * pjp.proceed()就是利用反射调用目标方法即可,就是method.invoke(obj,args) */
4)开启基于注解的AOP模式:aop名称空间
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
- 切入点表达式execution(访问权限符 返回值类型 方法签名)
@Before("execution(public int com.atguigu.impl.MyMathCalculator.add(int,int))")
如果要执行所有方法,把具体的方法add用 * 代替
@Before("execution(public int com.atguigu.impl.MyMathCalculator.*(int,int))")
3.测试
注意从IOC容器中拿到目标对象,如果想要使用类型,一定用他的接口类型,不要用它本类
AOP细节
1.底层就是动态代理,容器中保存的组件是他的代理对象
2.接口一般不加入容器中,不过加了容器也不会创建对象
3.如果没有接口,则用cglib创建本类的代理对象,但是得导入cglib的jar包
4.切入点表达式
固定写法 execution(访问权限符 返回值类型 方法签名)
通配符 :1)匹配一个或多个字符
2)匹配任意一个参数
3)如果放在路径下,只能匹配一层路径
…:
1)匹配任意多个参数和任意多个类型
2)可以匹配任意路径
最模糊的:execution(* .*(…))千万别写
最精确的:execution(“public int com.LLLLxr.ser.UserServiceImpl.add(User,int)”)
通配符&&:
execution(com.LLLLxr.ser.UserServiceImpl.add(User,int))&&execution( * . *(User,int))
同时满足两种表达式才行
通配符||:或,同上
com.LLLLxr.ser.UserServiceImpl.add(User user,int id))
1) com.LLLLxr.ser.User*.add(User user,int id))//匹配以User开头的,后面带什么都行的
2)com.LLLLxr.ser.User*.add(User user,*))//匹配任意一个参数
3)com.LLLLxr.*.UserServiceImpl.add(User user,int id))//匹配一层路径
4)execution(* com.LLLLxr.ser.UserServiceImpl.*(..))//可以修饰public(不过这个可以省略,一般写*是省略返回值类型)
..:1)com.LLLLxr.ser.UserServiceImpl.*(..))//匹配任意多个参数和任意多个类型
2) com..UserServiceImpl.add(User user,int id))
5.如何在通知方法运行的时候,拿到目标方法的详细信息
1)只需要为通知方法的参数列表上写一个参数:JoinPoint joinPoint
@Before("execution(* com.LLLLxr.ser.UserServiceImpl.*(..))")
public void before(JoinPoint joinPoint){
//获取目标方法运行的使用参数
Object[] args = joinPoint.getArgs();
//获取方法签名,方法签名有很多方法可以调用
Signature signature = joinPoint.getSignature();
String name = signature.getName();
System.out.println("========方法执行前========");
}
6.告诉Spring用result来接收返回值,exception用来接收异常
returning=“result”,throwing=“exception”
@Before(value="execution(* com.LLLLxr.ser.UserServiceImpl.*(..))",returning="result",throwing="exception" )
public void before(Object result,Exception exception){
System.out.println("========方法执行前========");
}
7.抽取可重用的切入点表达式
1)随便盛名一个没有实现的空方法
2)加入@PointCut注解
3)如下
@Component
@Aspect //标注这个类是一个切面
public class AnnotationPointCut {
@Pointcut("execution(* com.LLLLxr.ser.UserServiceImpl.*(..))")
public void hahaMyPoint(){
}
@Before(value =" hahaMyPoint()")
public void before(){
System.out.println("========方法执行前========");
}
}
贴模板代码
- 切面类
@Component
@Aspect //标注这个类是一个切面
public class AnnotationPointCut {
@Before("execution(* com.LLLLxr.ser.UserServiceImpl.*(..))")
public void before(){
System.out.println("========方法执行前========");
}
}
public interface UserService {
public void add();
public void delete();
public void update();
public void select();
}
import org.springframework.stereotype.Service;
@Service("userService")
public class UserServiceImpl implements UserService{
@Override
public void add() {
System.out.println("增加了一个用户");
}
@Override
public void delete() {
System.out.println("删除了一个用户");
}
@Override
public void update() {
System.out.println("修改了一个用户");
}
@Override
public void select() {
System.out.println("查询了一个用户");
}
}
- 配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--扫描包下的注解-->
<context:component-scan base-package="com.LLLLxr"></context:component-scan>
<!--开启aop注解模式-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
AOP使用场景
1.AOP加日志保存到数据库中
2.AOP做权限验证(环绕通知)
3.AOP做安全检查
4.AOP事务控制