@AspectJ注解开发Spring AOP

本文介绍如何使用Spring AOP框架实现方法级别的切面编程。通过具体案例,详细讲解了AOP的基本概念,如连接点、通知类型等,并演示了如何创建切面、配置AOP及测试AOP功能。
  1. 准备

Spring是方法级别的AOP框架,所以我们以某个类的某个方法作为连接点,说白了就是拦截哪个方法,来织入(也就是插入)对应AOP通知。

实体类 :Role

package com.cmb.test;

public class Role {
    
	private Long id;
	private String name;
	private String note;
	
	public Role(){
		
	}
	
	public Role(Long id, String name, String note){
		this.id = id;
		this.name = name;
		this.note = note;
	}
	
	public Long getId(){
		return id;
	}
	public void setId(Long id){
		this.id = id;
	}
	
	public String getName(){
		return name;
	}
	public void setName(String name){
		this.name = name;
	}
	
	public String getNote(){
		return note;
	}
	
	public void setNote(String note){
		this.note = note;
	}
}

给出一个接口:RoleService

package com.cmb.test;

public interface RoleService {

	public void printRole(Role role);
	
}

再给出实现类:RoleServiceImpl

package com.cmb.test;

import org.springframework.stereotype.Component;

@Component
public class RoleServiceImpl implements RoleService {

	public void printRole(Role role) {
			System.out.println( "{id=" + role.getId()
					+ ",roleName = " + role.getName()
					+ ", note = " + role.getNote()
					);
	}

}

其中,RoleServiceImpl中的printRole方法会作为AOP的连接点,后续会为之生成动态代理对象,并且拦截这个方法,然后产生各种AOP通知方法

 

2. 创建切面

在Spring中用@Aspect注解了一个类,那么SpringIoC就会认为这个类是一个切面了。

RoleAspect:

package com.cmb.test;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class RoleAspect {

	@Pointcut("execution(* com.cmb.test.RoleServiceImpl.printRole(..))")
	public void print(){
		
	}
	
	@Before("print()")
	public void before(){
		System.out.println("before....");
	}
	
	@After("print()")
	public void after(){
		System.out.println("after....");
	}
	
	@AfterReturning("print()")
	public void afterReturning(){
		System.out.println("afterReturning....");
	}
	
	@AfterThrowing("print()")
	public void afterThrowing(){
		System.out.println("afterThrowing....");
	}
	
	/*@Around("print()")
	public void around(ProceedingJoinPoint jp){
		System.out.println("around before ...");
		try{
			jp.proceed();
		}catch(Throwable e){
			e.printStackTrace();
		}
		System.out.println("around after ...");
	}*/
	
}

 

注解

通知

备注

@Before

在被代理对象放入方法调用前调用

前置通知

@Around

将被代理对象的方法封装起来,并用环绕通知取代它

环绕通知,它将覆盖原有方法,但是允许你通过反射调用原有方法

@After

在被代理对象放入方法调用后调用

后置通知

@AfterReturning

在被代理对象放入方法正常返回后调用

返回通知,要求被代理对象的方法执行过程中没有发生异常

@AfterThrowing

在被代理对象放入方法抛出异常后调用

异常通知,要求被代理对象的方法执行过程中产生异常

 

此外,代码:

@Pointcut("execution(* com.cmb.test.RoleServiceImpl.printRole(..))")

  • execution:代表执行方法的时候会触发;
  • *:代表任意返回类型;
  • com.cmb.test.RoleServiceImpl:代表类的全限定名
  • printRole:被拦截方法
  • (..):任意的参数;

Spring AOP切面有两种写法:

1

@Before("execution(* com.cmb.test.RoleServiceImpl.printRole(..))")

@After("execution(* com.cmb.test.RoleServiceImpl.printRole(..))")

@AfterReturning("execution(* com.cmb.test.RoleServiceImpl.printRole(..))")

2

@Pointcut("execution(* com.cmb.test.RoleServiceImpl.printRole(..))")

@ Before(“print()”)

@After(“print()”)

其中第一种比较麻烦,需要把类的限定,写多次,本例中采用的是第二种写法,因此引入注解@Pointcut.

 

3. 测试AOP

配置Spring bean :AopConfig

package com.cmb.test;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@EnableAspectJAutoProxy
@ComponentScan("com.cmb.test")
public class AopConfig {

    @Bean
	public RoleAspect getRoleAspect(){
		return new RoleAspect();
	}

}

@EnableAspectJAutoProxy:这个注解代表启动AspectJ框架的自动代理,这个时候Spring才会生成动态代理对象,进而可以使用AOP, getRoleAspect()方法则会生成一个切面实例。

测试函数: GameMain2

package com.cmb.test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class GameMain2 {

	public static void main(String[] args) {

		ApplicationContext ctx = new AnnotationConfigApplicationContext(AopConfig.class);
		
		RoleService roleService = (RoleService) ctx.getBean(RoleService.class);
		
		Role role = new Role(1L, "role_name_1", "note_1");
		roleService.printRole(role);
		System.out.println("#######################################");
		
		//测试异常通知
		role = null;
		roleService.printRole(role);
		
	}

}

结果如下:

八月 17, 2018 9:31:57 上午 org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@300ffa5d: startup date [Fri Aug 17 09:31:57 CST 2018]; root of context hierarchy
before....
{id=1,roleName = role_name_1, note = note_1
after....
afterReturning....
#######################################
before....
after....
afterThrowing....
Exception in thread "main" java.lang.NullPointerException

显然切面的通知已经通过AOP织入到约定的流程中了,这个时候就可以使用AOP去处理一些需要切面的场景了。

4. 环绕通知

环绕通知是Spring AOP中最强大的通知,它可以同时实现前置通知和后置通知。它还可以保留调度被代理对象原有方法的功能。在切面RoleAspect中把最后一段代码注释去掉,运行,就是含有环绕通知的方法。

其中参数ProceedingJoinPoint,是Spring提供,可以反射连接点方法。

结果:

信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@300ffa5d: startup date [Fri Aug 17 10:09:45 CST 2018]; root of context hierarchy
around before ...
before....
{id=1,roleName = role_name_1, note = note_1
around after ...
after....
afterReturning....
#######################################
around before ...
before....
java.lang.NullPointerException
....

around after ...
after....
afterReturning....

可以看出环绕通知使用jp.proceed();后,会先调度前置通知,然后才是反射切点方法,最后才是后置通知和返回/异常通知。

 

参考文献:《javaEE互联网轻量级框架整合开发》

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值