AOP的概念
Aspect:切面,指的是横切多个类的一种模块。在Spring中,切面用的就是普通的类(xml或者带@Aspect注解配置):
Joint point: 连接点,表示要横切的方法。
Advice:建议,一个切面在特定的连接点执行的方法。建议有::等
PointCut:切入点,能匹配上连接点的那些方法,Advice和切点表达式有关,它会在任何匹配的连接点执行。Spring使用AspectJ的切入点表达式来表示连接点
Introduction:引入,声明额外的方法或者属性,比如让bean实现一个接口
Target Object: 指已经被一个或者多个切面建议过的对象
Aop proxy:Aop代理,采用JDK代理或者CGLIB代理
Weaving:织入,织入切面的过程,可以在编译,加载,或者运行时进行织入,对于Spring AOP来说,是在运行时起作用的。
Advice的种类:
Before advice :连接点运行之前运行
After returning advice:连接点正常运行之后进行,比如一个方法正常return并且没有抛出异常
After throwing advice:抛出异常之后运行
After(finally) advice:不论是连接点是正常结束还是抛异常结束都会执行
Aroud advice:在方法调用之前和之后运行,是一个非常强大的advice,能够自定义方法的行为
添加aspectjweaver依赖
SpringBoot的AOP是默认开启的,不需要加注解@EnableAspectJAutoProxy
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
定义切面和切入点@Aspect表示该类是一个切面
package com.example.springboot.config;
import com.example.springboot.aspect.Demo;
import javafx.beans.property.Property;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.PropertyValue;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import java.lang.reflect.AnnotatedElement;
/**
* 定义切面
*/
@Aspect
@Component
public class DemoAspect {
//定义一个切入点@annotation定义在方法上
@Pointcut("@annotation(com.example.springboot.aspect.Demo)"
+"||@within(com.example.springboot.aspect.Demo)")//@within定义类注解的切入点
public void PinCut(){
}
//定义切点二
@Pointcut("within(com.example.springboot.service..*)")
public void PinCut2(){
}
@Around("PinCut()")
public Object Aroud(ProceedingJoinPoint point) throws Throwable {
MethodSignature signature = (MethodSignature) point.getSignature();
Demo demo;
//获取方法上的注解
demo = AnnotationUtils.findAnnotation(signature.getMethod(), Demo.class);
if (demo==null){
//注解在类上时调用该方法
demo=AnnotationUtils.findAnnotation(signature.getDeclaringType(),Demo.class);
}
Object[] args = point.getArgs();
//BeanWapper bean的包装器 可以进行bean的数据绑定
BeanWrapper wrapper = new BeanWrapperImpl(args[0]);
//使用时需要先判断该bean是否有该属性
if (wrapper.isWritableProperty("pwd")){
wrapper.setPropertyValue(new PropertyValue("pwd","123456"));
}
if (wrapper.isWritableProperty("name")){
wrapper.setPropertyValue("name","小明");
}
System.out.println(demo.value());
System.out.println("环绕通知");
return point.proceed();
}
@Before("PinCut2()")
public void befor(){
System.out.println("方法执行前通知");
}
}
切入点标识符
execution: 匹配连接点
//表示service包下的所有方法
execution(* com.example.springboot.service.*.*(..))
within: 某个类里面
//表示service包以及子包下的所有方法 单独针对mapper不生效
@Pointcut("within(com.example.springboot.service..*)")
this: 指定AOP代理类的类型
//this(com.xyz.service.AccountService):代理实现了AccountService接口
target:指定目标对象的类型
//target(com.xyz.service.AccountService):目标对象实现了AccoutService接口
args: 指定参数的类型
//args(java.io.Serializable):表示传递的参数是Serializable
bean:指定特定的bean名称,可以使用通配符(Spring自带的)
//bean(tradeService):bean的名字,可以使用通配符
@target: 带有指定注解的类型
//@target(org.springframework.transaction.annotation.Transactional):目标对象有@Transactional注解
@args: 指定运行时传的参数带有指定的注解
//@args(com.xyz.security.Classified):有一个参数,并且传递的类型有@Classfied注解
@within: 匹配使用指定注解的类
// 作用在类上
@annotation:指定方法所应用的注解
// 作用在方法上
切入点表达式可以用&&,||,!来组合使用
@Pointcut("@annotation(com.example.springboot.aspect.Demo)"
+"||@within(com.example.springboot.aspect.Demo)")
定义一个注解
package com.example.springboot.aspect;
import java.lang.annotation.*;
/**
* 定义Demo注解
*/
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Demo {
public String value() default "first";
}
package com.example.springboot.utils;
import com.example.springboot.aspect.Demo;
import com.example.springboot.mapper.UserBaseInfoMapper;
import com.example.springboot.pojo.UserBaseInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
@Component
public class AspectDemo {
@Demo(value = "second")
public void test(UserBaseInfo userBaseInfo){
System.out.println("改方法执行了");
System.out.println(userBaseInfo);
}
}
UserBaseInfo{id=8, name='大力', username='15526054511', pwd='123456', status='1'}
second
环绕通知
改方法执行了
UserBaseInfo{id=8, name='小明', username='15526054511', pwd='666666', status='1'}