Spring AOP注解开发详解

1. Spring中AOP的术语

  • Joinpoint(连接点) : 连接点是指那些被拦截到的方法。
  • Pointcut(切入点) : 切入点是指我们要对哪些Joinpoint进行拦截的定义。
  • Advice(通知/增强) : 通知是指拦截到Joinpoint之后所要做的事情。通知的类型包括:前置通知,后置通知,异常通知,最终通知,环绕通知。
  • Introduction(引介) : 引介是一种特殊的通知,在不修改类代码的前提下, 可以在运行期为类动态地添加方法或Field。
  • Target(目标对象) : 代理的目标对象。
  • Weaving(织入) : 指把增强应用到目标对象来创建新的代理对象的过程。spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入。
  • Proxy(代理): 一个类被AOP织入增强后,就产生一个结果代理类。
  • Aspect(切面) : 是切入点和通知(引介)的结合。

2. Spring AOP 注解驱动开发入门案例

需求:在执行service方法时输出执行日志。(除了业务层外,表现层和持久层也可以实现)

代码如下:

  • 实体类
/**
 * @author hao
*/
public class User implements Serializable {
	private String id;
	private String username;
	private String password;
	private String email;
	private Date birthday;
	private String gender;
	private String mobile;
	private String nickname;
}
  • 业务层接口
/**
 * @author hao
*/
public interface UserService {
	/**
	* 保存用户
	* @param user
	*/
	void save(User user);
}
  • 业务层实现类
/**
 * @author hao
*/
@Service("userService")
public class UserServiceImpl implements UserService{
	@Override
	public void save(User user) {
		System.out.println("保存用:"+user);
	}
}
  • 日志工具类
/**
 * @author hao
*/
@Component
@Aspect
public class LogUtil {
	/**
	* 通用切入点表达式
	*/
	@Pointcut("execution(* com.test.service.impl.*.*(..))")
	private void pt1(){}
	
	/**
	* 前置通知
	*/
	@Before("pt1()")
	public void beforeLog(){
		System.out.println("执行切入点方法前记录日志");
	}
	
	/**
	* 后置通知
	*/
	@AfterReturning("pt1()")
	public void afterReturningLog(){
		System.out.println("正常执行切入点方法后记录日志");
	}
	
	/**
	* 异常通知
	*/
	@AfterThrowing("pt1()")
	public void afterThrowingLog(){
		System.out.println("执行切入点方法产生异常后记录日志");
	}
	
	/**
	* 最终通知
	*/
	@After("pt1()")
	public void afterLog(){
		System.out.println("无论切入点方法执行是否有异常都记录日志");
	}
	
	/**
	* 环绕通知
	*/
	@Around("pt1()")
	public Object arountPrintLog(ProceedingJoinPoint pjp){
		//1.定义返回值
		Object rtValue = null;
		try{
			//前置通知
			System.out.println("执行切入点方法前记录日志");
			//2.获取方法执行所需的参数
			Object[] args = pjp.getArgs();
			//3.执行切入点方法
			rtValue = pjp.proceed(args);
			//后置通知
			System.out.println("正常执行切入点方法后记录日志");
		}catch (Throwable t){
			//异常通知
			System.out.println("执行切入点方法产生异常后记录日志");
		}finally {
			//最终通知
			System.out.println("无论切入点方法执行是否有异常都记录日志");
		}
		return rtValue;
	}
}
  • 配置类
/**
 * @author hao
*/
@Configuration
@ComponentScan("com.test")
@EnableAspectJAutoProxy
public class SpringConfiguration {
}
  • 测试类
/**
* @author hao
*/
public class SpringAOPTest {
	public static void main(String[] args) {
		//1.获取容器
		AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
		//2.获取bean对象
		UserService userService = ac.getBean("userService",UserService.class);
		//3.准备数据
		User user = new User();
		user.setId("1");
		user.setUsername("test");
		user.setNickname("泰斯特");
		//4.执行方法
		userService.save(user);
	}
}

3. AOP常用注解分析

3.1 @EnableAspectJAutoProxy

3.1.1 源码

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
	/**
	* Indicate whether subclass‐based (CGLIB) proxies are to be created as opposed
	* to standard Java interface‐based proxies. The default is {@code false}.
	*/
	boolean proxyTargetClass() default false;
	/**
	* Indicate that the proxy should be exposed by the AOP framework as a {@code ThreadLocal}
	* for retrieval via the {@link org.springframework.aop.framework.AopContext} class.
	* Off by default, i.e. no guarantees that {@code AopContext} access will work.
	* @since 4.3.1
	*/
	boolean exposeProxy() default false;
}

3.1.2 说明

  • 作用:
    表示开启spring对aop的注解支持。它有两个属性,分别是指定采用的代理方式和是否暴露代理对象,通过AopContext可以进行访问。
  • 属性:
    proxyTargetClass:指定是否采用cglib进行代理。默认值是false,表示使用jdk的代理。
    exposeProxy:指定是否暴露代理对象,通过AopContext可以进行访问。
  • 使用场景:
    当我们注解驱动开发时,在需要使用aop实现某些功能的情况下,都需要用到此注解。

3.1.3 示例

/**
* @author hao
*/
@Configuration
@ComponentScan("com.itheima")
@EnableAspectJAutoProxy
public class SpringConfiguration {
}

3.2 @Aspect

3.2.1 源码

/**
* Aspect declaration
*
* @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Aspect {
	/**
	* Per clause expression, defaults to singleton aspect
	* <p/>
	* Valid values are "" (singleton), "perthis(...)", etc
	*/
	public String value() default "";
}

3.2.2 说明

  • 作用:声明当前类是一个切面类。
  • 属性:
    value: 默认我们的切面类应该为单例的。当切面类为一个多例类时,指定预处理的切入点表达式。用法是 perthis(切入点表达式),它支持指定切入点表达式,或者是用@Pointcut修饰的方法名称(要求全限定方法名)
  • 使用场景:此注解也是一个注解驱动开发aop的必备注解。

3.2.3 示例

/**
* @author hao
*/
@Component
@Scope("prototype")  //注意:通常情况下我们的切面类是不需要多例的。
@Aspect(value="execution(* com.test.service.impl.*.*(..))")
public class LogUtil {
	/**
	* 用于配置当前方法是一个前置通知
	*/
	@Before("execution(* com.itheima.service.impl.*.*(..))")
	public void printLog(){
		System.out.println("执行打印日志的功能");
	}
}

3.3 @Pointcut

3.3.1 源码

/**
* Pointcut declaration
*
* @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Pointcut {
	/**
	* The pointcut expression
	* We allow "" as default for abstract pointcut
	*/
	String value() default "";
	/**
	* When compiling without debug info, or when interpreting pointcuts at runtime,
	* the names of any arguments used in the pointcut are not available.
	* Under these circumstances only, it is necessary to provide the arg names in
	* the annotation ‐ these MUST duplicate the names used in the annotated method.
	* Format is a simple comma‐separated list.
	*/
	String argNames() default "";
}

3.3.2 说明

  • 作用:此注解是用于指定切入点表达式的。
  • 属性:
    value : 用于指定切入点表达式。
    argNames : 用于指定切入点表达式的参数。参数可以是execution中的,也可以是args中的。通常情况下不使用此属性也可以获得切入点方法参数。
  • 使用场景:
    在实际开发中,当我们的多个通知需要执行,同时在增强规则确定的情况下,就可以把切入点表达式通用化。此注解就是代替xml中的aop:pointcut标签,实现切入点表达式的通用化。

3.3.3 示例

@Component
@Aspect
public class LogUtil {
	/**
	* 通用切入点表达式
	* 在value属性的中使用了&&符号,表示并且的关系。
	* &&符号后面的args和execution一样,都是切入点表达式支持的关键字,表示匹配参数。指定的内容可以是全限定类名,或者是名称。当指定参数名称时,要求与方法中形参名称相同。
	* argNames属性,是定义参数的名称,该名称必须和args关键字中的名称一致。
	*/
	@Pointcut(value = "execution(* com.test.service.impl.*.*
	(com.test.domain.User))&& args(user)",argNames = "user")
	private void pt1(User user){}
}

3.4 @Before

3.4.1 源码

/**
 * Before advice
 *  * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Before {
	/**
	* The pointcut expression where to bind the advice
	*/
	String value();
	
	/**
	* When compiling without debug info, or when interpreting pointcuts at runtime,
	* the names of any arguments used in the advice declaration are not available.
	* Under these circumstances only, it is necessary to provide the arg names in
	* the annotation ‐ these MUST duplicate the names used in the annotated method.
	* Format is a simple comma‐separated list.
	*/
	String argNames() default "";
}

3.4.2 说明

  • 作用:被此注解修饰的方法称为前置通知。前置通知是在切入点方法之前执行。
  • 属性:
    value : 用于指定切入点表达式,可以是表达式,也可以是表达式的引用。
    argNames : 用于指定切入点表达式参数的名称。它要求和切入点表达式中的参数名称一致。通常不指定也可以获取切入点方法的参数内容。
  • 使用场景:
    在实际开发中,我们需要对切入点方法执行之前进行增强, 此时就用到了前置通知。在通知(增强的方法)中需要获取切入点方法中的参数进行处理时,就要配合切入点表达式参数来使用。

3.4.3 示例

/**
* 前置通知
*/
@Before(value = "pt1(user)",argNames = "user")
public void beforeLog(User user){
	System.out.println("执行切入点方法前记录日志"+user);
}

3.5 @AfterReturning

3.5.1 源码

/**
* After returning advice
* @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AfterReturning {
	/**
	* The pointcut expression where to bind the advice
	*/
	String value() default "";
	
	/**
	* The pointcut expression where to bind the advice, overrides "value" when specified
	*/
	String pointcut() default "";
	
	/**
	* The name of the argument in the advice signature to bind the returned value to
	*/
	String returning() default "";
	
	/**
	* When compiling without debug info, or when interpreting pointcuts at runtime,
	* the names of any arguments used in the advice declaration are not available.
	* Under these circumstances only, it is necessary to provide the arg names in
	* the annotation ‐ these MUST duplicate the names used in the annotated method.
	* Format is a simple comma‐separated list.
	*/
	String argNames() default "";
}

3.5.2 说明

  • 作用:用于配置后置通知,后置通知的执行是在切入点方法正常执行之后执行。需要注意的是,由于基于注解配置时,spring创建通知方法的拦截器链时,后置通知在最终通知之后,所以会先执行@After注解修饰的方法。
  • 属性:
    value : 用于指定切入点表达式,可以是表达式,也可以是表达式的引用。
    pointcut : 它的作用和value是一样的。
    returning : 指定切入点方法返回值的变量名称。它必须和切入点方法返回值名称一致。
    argNames : 用于指定切入点表达式参数的名称。它要求和切入点表达式中的参数名称
    一致。通常不指定也可以获取切入点方法的参数内容。
  • 使用场景:
    此注解是用于配置后置增强切入点方法。被此注解修饰方法会在切入点方法正常执行之后执行。在我们实际开发中,像提交事务,记录访问日志,统计方法执行效率等等都可以利用后置通知实现。

3.5.3 示例

// 切入点方法:
@Override
public User findById(String id) {
	System.out.println("切入点方法开始执行。。。");
	User user = new User();
	user.setId(id);
	user.setUsername("heima");
	user.setNickname("黑马小王子");
	return user;
}

/**
* 后置通知
*/
@AfterReturning(value = "execution(* com.test.service.impl.*.*
(..))&&args(param)",returning = "user")
public void afterReturningLog(String param,Object user){
	System.out.println("正常执行切入点方法后记录日志,切入点方法的参数是:"+param);
	System.out.println("正常执行切入点方法后记录日志,切入点方法的返回值是:"+user);
}

3.6 @AfterThrowing

3.6.1 源码

/**
* After throwing advice
* @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AfterThrowing {
	/**
	* The pointcut expression where to bind the advice
	*/
	String value() default "";
	
	/**
	* The pointcut expression where to bind the advice, overrides "value" when specified
	*/
	String pointcut() default "";
	
	/**
	* The name of the argument in the advice signature to bind the thrown exception to
	*/
	String throwing() default "";
	
	/**
	* When compiling without debug info, or when interpreting pointcuts at runtime,
	* the names of any arguments used in the advice declaration are not available.
	* Under these circumstances only, it is necessary to provide the arg names in
	* the annotation ‐ these MUST duplicate the names used in the annotated method.
	* Format is a simple comma‐separated list.
	*/
	String argNames() default "";
}

3.6.2 说明

  • 作用:用于配置异常通知。
  • 属性:
    value : 用于指定切入点表达式,可以是表达式,也可以是表达式的引用。
    pointcut : 它的作用和value是一样的。
    throwing: 指定切入点方法执行产生异常时的异常对象变量名称,它必须和异常变量名称一致。
    argNames : 用于指定切入点表达式参数的名称。它要求和切入点表达式中的参数名称一致。通常不指定也可以获取切入点方法的参数内容。
  • 使用场景:
    此注解修饰的方法执行时机是在切入点方法执行产生异常之后执行。

3.6.3 示例

// 切入点方法:
@Override
public User findById(String id) {
	System.out.println("切入点方法开始执行。。。");
	User user = new User();
	user.setId(id);
	user.setUsername("heima");
	user.setNickname("黑马小王子");
	int i=1/0;
	return user;
}

/**
* 异常通知
*/
@AfterThrowing(value = "execution(* com.test.service.impl.*.*
(..))&&args(param)",throwing = "e")
public void afterThrowingLog(String param,Throwable e){
	System.out.println("执行切入点方法产生异常后记录日志,切入点方法的参数是:"+param);
	System.out.println("执行切入点方法产生异常后记录日志,切入点方法的异常是:"+e);
}

3.7 @After

3.7.1 源码

/**
 * After finally advice
 * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface After {
	/**
	* The pointcut expression where to bind the advice
	*/
	String value();
	
	/**
	* When compiling without debug info, or when interpreting pointcuts at runtime,
	* the names of any arguments used in the advice declaration are not available.
	* Under these circumstances only, it is necessary to provide the arg names in
	* the annotation ‐ these MUST duplicate the names used in the annotated method.
	* Format is a simple comma‐separated list.
	*/
	String argNames() default "";
}

3.7.2 说明

  • 作用:用于指定最终通知。
  • 属性:
    value : 用于指定切入点表达式,可以是表达式,也可以是表达式的引用。
    argNames : 用于指定切入点表达式参数的名称。它要求和切入点表达式中的参数名称
    一致。通常不指定也可以获取切入点方法的参数内容。
  • 使用场景:最终通知的执行时机是在切入点方法执行完成之后执行,无论切入点方法执行是否产生异常最终通知都会执行。所以被此注解修饰的方法,通常都是做一些清理操作。

3.7.3 示例

/**
* 最终通知
*/
@After(value = "execution(* com.test.service.impl.*.*(..))")
public void afterLog(){
	System.out.println("无论切入点方法执行是否有异常都记录日志");
}

3.8 @Around

3.8.1 源码

/**
* Around advice
* @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Around {
	/**
	* The pointcut expression where to bind the advice
	*/
	String value();
	
	/**
	* When compiling without debug info, or when interpreting pointcuts at runtime,
	* the names of any arguments used in the advice declaration are not available.
	* Under these circumstances only, it is necessary to provide the arg names in
	* the annotation ‐ these MUST duplicate the names used in the annotated method.
	* Format is a simple comma‐separated list.
	*/
	String argNames() default "";
}

3.8.2 说明

  • 作用:用于指定环绕通知。
  • 属性:
    value: 用于指定切入点表达式,可以是表达式,也可以是表达式的引用。
    argNames: 用于指定切入点表达式参数的名称。它要求和切入点表达式中的参数名称
    一致。通常不指定也可以获取切入点方法的参数内容。
  • 使用场景:环绕通知有别于前面介绍的四种通知类型。它不是指定增强方法执行时机的,而是
    spring为我们提供的一种可以通过编码的方式手动控制增强方法何时执行的机制。

3.8.3 示例

/**
* 环绕通知
*/
@Around("execution(* com.test.service.impl.*.*(..))")
public Object arountPrintLog(ProceedingJoinPoint pjp){
	//1.定义返回值
	Object rtValue = null;
	try{
		//前置通知
		System.out.println("执行切入点方法前记录日志");
		//获取方法执行所需的参数
		Object[] args = pjp.getArgs();
		//执行切入点方法
		rtValue = pjp.proceed(args);
		//后置通知
		System.out.println("正常执行切入点方法后记录日志");
	}catch (Throwable t){
		//异常通知
		System.out.println("执行切入点方法产生异常后记录日志");
	}finally {
		//最终通知
		System.out.println("无论切入点方法执行是否有异常都记录日志");
	}
	return rtValue;
}

3.9 @DeclareParents

3.9.1 源码

/**
* Declare parents mixin annotation
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface DeclareParents {
	/**
	* The target types expression
	*/
	String value();
	
	/**
	* Optional class defining default implementation
	* of interface members (equivalent to defining
	* a set of interface member ITDs for the
	* public methods of the interface).
	*/
	Class defaultImpl() default DeclareParents.class;

}

3.9.2 说明

  • 作用:用于给被增强的类提供新的方法。(实现新的接口)
  • 属性:
    value : 用于指定目标类型的表达式。当在全限定类名后面跟上+时,表示当前类及其子类。
    defaultImpl : 指定提供方法或者字段的默认实现类。
  • 使用场景:
    当我们已经完成了一个项目某个阶段的开发,此时需要对已完成的某个类加入新方法,我们首先想到的是写一个接口,然后让这些需要新方法的类实现此接口。但如果目标类非常复杂,改动的话可能非常麻烦。此时就可以使用此注解,然后建一个代理类,同时代理该类和目标类。

3.9.3 示例

/**
* 业务层接口
* @author hao
*/
public interface UserService {
	/**
	* 模拟保存用户
	* @param user
	*/
	void saveUser(User user);
}

/**
* @author hao
*/
@Service("userService")
public class UserServiceImpl implements UserService {
	@Override
	public void saveUser(User user) {
		System.out.println("执行了保存用户" + user);
	}
}

/**
* 需要加入的新方法
* @author hao
*/
public interface ValidateService {
	boolean checkUser(User user);
}


/**
* @author hao
*/
public class ValidateServiceImpl implements ValidateService {
	@Override
	public boolean checkUser(User user) {
		if(user.getNickname().contains("孙子")){
			return false;
		}
		return true;
	}
}

/**
* 记录日志的工具类
* @author hao
*/
@Component
@Aspect
public class LogUtil {
	@DeclareParents(value ="com.test.service.UserService+",defaultImpl = ValidateServiceImpl.class)
	private ValidateService validateService;
	
	/**
	* 用于配置当前方法是一个前置通知
	*/
	@Before(value = "com.test.pointcuts.MyPointcut.pointcut1() && args(user) && this(validateService)")
	public void printLog(User user,ValidateService validateService){
		//第二种触发方式
		boolean check = validateService.checkUser(user);
		if(check) {
			System.out.println("执行打印日志的功能");
		}else {
			throw new IllegalStateException("名称非法");
		}
	}
}

/**
* spring核心配置类
* @author hao
*/
@Configuration
@ComponentScan("com.test")
@EnableAspectJAutoProxy
public class SpringConfiguration {
}

/**
* 测试类
* 有两种触发方式:
* 第一种触发方式:在使用时自行强转新引入接口类型,然后调用方法。例如:测试类中的代码
* 第二种触发方式:在通知类中,使用this关键字,引入新目标类对象,调用方法触发。例如:切面类
* @author hao
*/
public class SpringPointcutTest {
	public static void main(String[] args) {
		//1.创建容器
		AnnotationConfigApplicationContext ac = new
		AnnotationConfigApplicationContext(SpringConfiguration.class);
		//2.获取对象
		UserService userService = ac.getBean("userService",UserService.class);
		//3.执行方法
		User user = new User();
		user.setId("1");
		user.setUsername("test");
		user.setNickname("孙子1");
		userService.saveUser(user);
	}
}

3.10 @EnableLoadTimeWeaving

3.10.1 源码

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(LoadTimeWeavingConfiguration.class)
public @interface EnableLoadTimeWeaving {
	/**
	* Whether AspectJ weaving should be enabled.
	*/
	AspectJWeaving aspectjWeaving() default AspectJWeaving.AUTODETECT;
	
	/**
	* AspectJ weaving enablement options.
	*/
	enum AspectJWeaving {
		/**
		* Switches on Spring‐based AspectJ load‐time weaving.
		*/
		ENABLED,
		
		/**
		* Switches off Spring‐based AspectJ load‐time weaving (even if a
		* "META‐INF/aop.xml" resource is present on the classpath).
		*/
		DISABLED,
		
		/**
		* Switches on AspectJ load‐time weaving if a "META‐INF/aop.xml" resource
		* is present in the classpath. If there is no such resource,then AspectJ
		* load‐time weaving will be switched off.
		*/
		AUTODETECT;
	}
}

3.10.2 说明

  • 作用:用于切换不同场景下实现增强。
  • 属性:
    aspectjWeaving:是否开启LTW的支持。ENABLED 开启LTW;DISABLED 不开启LTW;
    AUTODETECT 如果类路径下能读取到META‐INF/aop.xml文件,则开启LTW,否则关闭。
  • 使用场景:
    在Java 语言中,从织入切面的方式上来看,存在三种织入方式:编译期织入、类加载期织入和运行期织入。编译期织入是指在Java编译期,采用特殊的编译器,将切面织入到Java类中;而类加载期织入则指通过特殊的类加载器,在类字节码加载到JVM时,织入切面;运行期织入则是采用CGLib工具或JDK动态代理进行切面的织入。AspectJ提供了两种切面织入方式,第一种通过特殊编译器,在编译期将AspectJ语言编写的切面类织入到Java类中,可以通过一个Ant或Maven任务来完成这个操作;第二种方式是类加载期织入,也简称为LTW(Load Time Weaving)

3.10.3 示例

/**
* 切面类
* @author hao
*/
//@Component
@Aspect
public class LoadTimeWeavingAspect {
	/**
	* 增强方法
	* @param pjp
	* @return
	* @throws Throwable
	*/
	@Around("pointcut()")
	public Object profile(ProceedingJoinPoint pjp) throws Throwable {
		//1.创建秒表对象
		StopWatch sw = new StopWatch(getClass().getSimpleName());
		try {
			//2.记录执行
			sw.start(pjp.getSignature().getName());
			//3.调用切入点方法并返回
			return pjp.proceed();
		} finally {
			//4.停止计时
			sw.stop();
			//5.输出
			System.out.println(sw.prettyPrint());
		}
	}
	/**
	* 切入点表达式
	*/
	@Pointcut("execution(* com.test.service.impl.*.*(..))")
		public void pointcut() {
	}
}


/**
* 配置类
* @author hao
*/
@Configuration
@ComponentScan("com.itheima")
//@EnableAspectJAutoProxy
@EnableLoadTimeWeaving(aspectjWeaving = EnableLoadTimeWeaving.AspectJWeaving.ENABLED)
public class SpringConfiguration {
}

4. AOP注解执行过程

    1. 加载@EnableAspectJAutoproxy注解
    1. 解析切入点表达式
    1. 解析通知注解
    • 3.1 初始化通知注解的Map。在执行容器初始化创建时,spring把和通知相关的注解都放到一个受保护的内部类中了。
    • 3.2 构建通知的拦截器链
    1. 执行方法。spring在初始化容器时已经把要执行的通知都存入了一个集合中,接下来当执行切入点方法时,spring会按照通知的类型,顺序调用。

5. 切入点表达式总结

5.1 概念

  • 概念:指的是遵循特定的语法用于捕获每一个种类的可使用连接点的语法。
  • 作用:用于对符合语法格式的连接点进行增强。

5.2 按用途分类

主要的种类:

  • 方法执行:execution(MethodSignature)
  • 方法调用:call(MethodSignature)
  • 构造器执行:execution(ConstructorSignature)
  • 构造器调用:call(ConstructorSignature)
  • 类初始化:staticinitialization(TypeSignature)
  • 属性读操作:get(FieldSignature)
  • 属性写操作:set(FieldSignature)
  • 例外处理执行:handler(TypeSignature)
  • 对象初始化:initialization(ConstructorSignature)
  • 对象预先初始化:preinitialization(ConstructorSignature)

5.3 切入点表达式关键字

支持的AspectJ切入点指示符如下:

  • execution:用于匹配方法执行的连接点;
  • within:用于匹配指定类型内的方法执行;
  • this:用于匹配当前AOP代理对象类型的执行方法;注意是AOP代理对象的类型匹配,这样就可能包括引入接口类型匹配;
  • target:用于匹配当前目标对象类型的执行方法;注意是目标对象的类型匹配,这样就不包括引入接口类型匹配;
  • args:用于匹配当前执行的方法传入的参数为指定类型的执行方法;
  • @within:用于匹配所以持有指定注解类型内的方法;
  • @target:用于匹配当前目标对象类型的执行方法,其中目标对象持有指定的注解;
  • @args:用于匹配当前执行的方法传入的参数持有指定注解的执行;
  • @annotation:用于匹配当前执行方法持有指定注解的方法;
  • bean:Spring AOP扩展的,用于匹配特定名称的Bean对象的 执行方法;
  • reference pointcut:表示引用其他命名切入点,只支持@ApectJ风格,Schema风格不支持。

5.4 切入点表达式通配符

AspectJ类型匹配的通配符:

  • *:匹配任何数量字符;
  • .. :重复匹配任何数量字符,如在类型模式中匹配任何数量子包;而在方法参数模式中匹配任何数量参数。
  • +:匹配指定类型的子类型,仅作为后缀放在类型模式后边。

说明:
java.lang.String :匹配String类型;
java.*.String : 匹配java包下的任何“一级子包”下的String类型,如匹配java.lang.String,但不匹配java.lang.ss.String
java..* : 匹配java包及任何子包下的任何类型,如匹配java.lang.String、java.lang.annotation.Annotation
java.lang.*ing : 匹配任何java.lang包下的以ing结尾的类型;
java.lang.Number+ : 匹配java.lang包下的任何Number的自类型,如匹配java.lang.Integer,也匹配java.math.BigInteger

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/570128.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

在Redux Toolkit中使用redux-persist进行状态持久化

在 Redux Toolkit 中使用 redux-persist 持久化插件的步骤如下: 安装依赖 npm install redux-persist配置 persistConfig 在 Redux store 配置文件中(例如 store.js)&#xff0c;导入必要的模块并配置持久化选项: import { combineReducers } from redux; import { persist…

Jmeter插件技术:性能测试中服务端资源监控

性能测试过程中我们需要不断的监测服务端资源的使用情况&#xff0c;例如CPU、内存、I/O等。 Jmeter的插件技术可以很好的实时监控到服务器资源的运行情况&#xff0c;并以图形化的方式展示出来&#xff0c;非常方便我们性能测试分析。 操作步骤&#xff1a; 1、安装插件管理…

主打国产算力 广州市通用人工智能公共算力中心项目签约

4月9日&#xff0c;第十届广州国际投资年会期间&#xff0c;企商在线&#xff08;北京&#xff09;数据技术股份有限公司与广州市增城区政府就“广州市通用人工智能公共算力中心”项目进行签约。 该项目由广州市增城区人民政府发起&#xff0c;企商在线承建。项目拟建成中国最…

后端工程师——Java工程师如何准备面试

在国内,Java 程序员是后端开发工程师中最大的一部分群体,其市场需求量也是居高不下,C++ 程序员也是热门岗位之一,此二者的比较也常是热点话题,例如新学者常困惑的问题之一 —— 后端开发学 Java 好还是学 C++ 好。读完本文后,我们可以从自身情况、未来的发展,岗位需求量…

【JVM系列】关于静态块、静态属性、构造块、构造方法的执行顺序

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

Java算法 空间换时间(找重复)

一、算法示例 1、题目&#xff1a;题目&#xff1a;0-999的数组中&#xff0c;添加一个重复的元素&#xff0c;打乱后&#xff0c;找出这个重复元素 代码示例&#xff1a; package com.zw.study.algorithm; import java.util.*; public class XorTest {public static void mai…

Vue报错 Cannot read properties of undefined (reading ‘websiteDomains‘) 解决办法

浏览器控制台如下报错&#xff1a; Unchecked runtime.lastError: The message port closed before a response was received. Uncaught (in promise) TypeError: Cannot read properties of undefined (reading websiteDomains) at xl-content.js:1:100558 此问题困扰了…

可持续发展:制造铝制饮料罐要消耗多少资源?

铝制饮料罐是人们经常使用的日常用品&#xff0c;无论是在购物、午休还是在自动售货机前选择喝什么的时候&#xff0c;很少有人会想知道装他们喝的饮料的罐子到底是如何制成的&#xff0c;或者这些铝罐的原材料是如何进出的。 虽然有化学品和一些合金进入铝饮料罐制造过程或成为…

【VSCode调试技巧】Pytorch分布式训练调试

最近遇到个头疼的问题&#xff0c;对于单机多卡的训练脚本&#xff0c;不知道如何使用VSCode进行Debug。 解决方案&#xff1a; 1、找到控制分布式训练的启动脚本&#xff0c;在自己的虚拟环境的/lib/python3.9/site-packages/torch/distributed/launch.py中 2、配置launch.…

【Qt常用控件】—— 输入类控件

目录 1.1 Line Edit 1.2 Text Edit 1.3 Combo Box 1.4 Spin Box 1.5 Date Edit & Time Edit 1.6 Dial 1.7 Slider 1.1 Line Edit QLineEdit是Qt中的一个控件&#xff0c;用于 接收和显示单行文本输入。 核心属性 属性 说明 text 输⼊框中的⽂本 inputMask 输⼊…

Science Robotics 美国斯坦福大学研制了外行星洞穴探测机器人

月球和火星上的悬崖、洞穴和熔岩管已被确定为具有地质和天体生物学研究理想地点。由于其隔绝特性&#xff0c;这些洞穴提供了相对稳定的条件&#xff0c;可以促进矿物质沉淀和微生物生长。在火星上&#xff0c;这些古老的地下环境与火星表面可能适合居住时几乎没有变化&#xf…

JavaEE 初阶篇-深入了解网络通信相关的基本概念(三次握手建立连接、四次挥手断开连接)

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 网络通信概述 1.1 基本的通信架构 2.0 网络通信三要素 3.0 网络通信三要素 - IP 地址 3.1 查询 IP 地址 3.2 IP 地址由谁供应&#xff1f; 3.3 IP 域名 3.4 IP 分…

大模型接口管理和分发系统One API

老苏就职于一家专注于音视频实时交互技术和智能算法的创新企业。公司通过提供全面的 SDK 和解决方案&#xff0c;助力用户轻松实现实时音视频通话和消息传递等功能。尽管公司网站上有详细的文档中心&#xff0c;但在实际开发中&#xff0c;仍面临大量咨询工作。 鉴于此&#x…

知识图谱嵌入领域的重要研究:编辑基于语言模型的知识图谱嵌入

今天&#xff0c;向大家介绍一篇在知识图谱嵌入领域具有重要意义的研究论文——Editing Language Model-based Knowledge Graph Embeddings。这项工作由浙江大学和腾讯公司的研究人员联合完成&#xff0c;为我们在动态更新知识图谱嵌入方面提供了新的视角和方法。 研究背景 在…

Linux安装MongoDB超详细

Linux端安装 我们从MonDB官网下载Linux端的安装包&#xff0c;建议下载4.0版本 打开虚拟机&#xff0c;在虚拟机上安装传输工具lrzsz,将下载好的.tgz包拖到虚拟机当中&#xff0c;拖到/usr/local/mongoDB目录下&#xff0c; [rootserver ~]# yum install -y lrzsz [rootser…

如何使用 Vercel 托管静态网站

今天向大家介绍 Vercel 托管静态网站的几种方式&#xff0c;不熟悉 Vercel 的伙伴可以看一下之前的文章&#xff1a;Vercel: 开发者免费的网站托管平台 Github 部署 打开 Vercel 登录界面&#xff0c;推荐使用 GitHub账号 授权登录。 来到控制台界面&#xff0c;点击 Add New …

四川古力未来科技抖音小店:科技新宠,购物新体验

在当下数字化、智能化的时代&#xff0c;电商平台如雨后春笋般涌现&#xff0c;其中不乏一些富有创新精神和实力雄厚的科技企业。四川古力未来科技有限公司就是其中的佼佼者&#xff0c;其抖音小店更是凭借其独特的魅力和优质的服务&#xff0c;赢得了广大消费者的青睐。 一、科…

6步教你APP广告高效变现,收益翻倍秘诀大揭秘!

移动应用广告变现最佳实践与策略指南 在移动应用市场中&#xff0c;广告变现已成为开发者和公司获取收益的重要途径。然而&#xff0c;如何在保证用户体验的同时&#xff0c;实现广告收入的最大化&#xff0c;成为了众多开发者和公司面临的挑战。本文将为您介绍一些最佳的实践…

Seal^_^【送书活动第2期】——《Flink入门与实战》

Seal^_^【送书活动第2期】——《Flink入门与实战》 一、参与方式二、本期推荐图书2.1 作者简介2.2 编辑推荐2.3 前 言2.4 本书特点2.5 内容简介2.6 本书适用读者2.7 书籍目录 三、正版购买 一、参与方式 评论&#xff1a;"掌握Flink&#xff0c;驭大数据&#xff0c;实战…

nginx配置https及wss

环境说明 服务器的是centos7 nginx版本nginx/1.20.1 springboot2.7.12 nginx安装教程点击这里 微信小程序wss配置 如果您的业务是开发微信小程序&#xff0c; 请先进行如下配置。 boot集成websocket maven <dependency><groupId>org.springframework.boot<…