Spring-通过注解实现的AOP

1.若实现AOP则需要在原来核心包基础上加入四个jar包。
aopalliance,aspectjweaver,aop,aspects
新版本的Spring不会将Spring自身开发以外的依赖包继承发布。
所以AOP需要的包,需要自己额外下载:
aopalliance-1.0.jar(最近更新是2004年)
下载地址:http://sourceforge.net/projects/aopalliance/files/ 

aspectjweaver的包
http://mvnrepository.com/artifact/org.aspectj/aspectjweaver
2.基本概念:
aspect 切面 横切关注点,被模块化的特殊对象。比如专业做验证的对象,专业最log的对象都被称为切面。
advice 通知 切面必须要完成的工作。切面里面的方法,可以理解为对实际功能的通知。
比如数据库写操作,那么验证advice的作用是对数据库写操作进行验证。
target:被通知的对象,就是实际的功能体。
proxy:跟动态代理对象相同,将切面混合到功能体后的整体被称为proxy,即具有原来的功能,又加入了advice的功能。
joinpoint:连接点 相对于实际功能体的某个特定位置 比如功能体方法调用前,调用后,或者抛出异常后等等。
pointcut:切点, 每个函数有多个连接点,那么AOP如何去定位到连接点?答案是Pointcut对象。
3.在配置文件中加入aop的命名空间
xmlns:aop="http://www.springframework.org/schema/aop"
4.通过注解方式实现AOP步骤
1)指定注解的扫描范围<context:component-scan base-package="spring2"></context:component-scan>

2)声明AOP注解有效<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

5.切面优先级
一个方法被多个切面处理时,可以通过@Order(x)注解来调整执行优先级,参数的数值越小优先级越高。

6.切面定义举例:
1)配置文件
<?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/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd">
	<context:component-scan base-package="spring2"></context:component-scan>
	<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

2)LoggingAspect类
@Aspect //声明切面对象
@Component //让注解扫描时称为有效对象
public class LoggingAspect {
	//前置advice,通过@Before声明,其中该处指定的函数为setName,
	//也可以用*代替所有函数,*代替任意的返回值,或者参数。
	//比如public * spring2.*.*(String)等等
	//函数的参数 JoinPoint 是可选的。
	@Before("execution(public void spring2.Person.setName(String))")
	public void beforeMethod(JoinPoint joinPoint){
		String methodName = joinPoint.getSignature().getName();
		List<Object> args = Arrays.asList(joinPoint.getArgs());
		System.out.println(methodName + " method begins with:" + args);
	}
	
	//后置Advice,在函数体执行之后相当于Finnaly 在 @AfterReturning 之后
	//不管是否抛出异常都会执行,并且在抛出异常前,即函数结束时调用
	@After("execution(public void spring2.Person.setName(String))")
	public void afterMethod(JoinPoint joinPoint){
		String methodName = joinPoint.getSignature().getName();
		System.out.println(methodName + " method ends.");
	}
	
	//在函数返回之后,在@After之前,关于After和AfterReturning的关系,可以参照下面AroundAdvice的说明。
	@AfterReturning(value="execution(public void spring2.Person.setName(String))", returning="result")
	public void afterReturning(JoinPoint joinPoint, Object result){ //参数result是由注解指定
		String methodName = joinPoint.getSignature().getName();
		System.out.println(methodName + " returning." + result);
	}
	
	//在函数抛出异常之后,同AfterReturning,可以获取抛出的异常,注意若参数指定子类异常时,只能截获指定的异常
	@AfterThrowing(value="execution(public void spring2.Person.setName(String))", throwing="ex")
	public void afterThrowing(JoinPoint joinPoint, Exception ex){//ex是注解中指定的变量名
		String methodName = joinPoint.getSignature().getName();
		System.out.println(methodName + " Throwing Exception." + ex);
	}
}
功能最全,但是不如上面四种更有针对性的Around通知
@Aspect
@Component
public class LoggingAspect {
	//环绕通知,相当于整个动态代理,需要参数ProceedingJoinPoint,参数可以决定是否执行被代理的方法。
	//aroud方法的返回值是被代理对象的返回值
	@Around("execution(public String test.Person.getName())")
	public Object aroundMethod(ProceedingJoinPoint pjp){
		Object result = null;
		String methodName = pjp.getSignature().getName();
		try {
			
			System.out.println(methodName + " Before."); //在proceed()位置前的代码相当于前置通知代码
			result = pjp.proceed(); //执行真正的被代理实体,如果不调用该方法,则实体被屏蔽
			System.out.println(methodName + " AfterReturning."); //在proceed()位置后的代码相当于AfterReturning通知代码
		} catch (Throwable e) {
			System.out.println(methodName + " AfterThrowing.");// 在这个位置的代码相当于AfterThrowing通知的代码
			e.printStackTrace();
		} 
		System.out.println(methodName + " After."); //在这个位置的代码相当于After通知的代码
		return result;
	}
}
3)测试函数
在Person类上面使用@Component注解后,由于在配置文件中配置了自动扫描的路径包,Ioc容器会自动扫描发现,并生成器bean对象。
直接通过getBean即可获取Bean对象。
aspect注解也是同样的道理。
public class Main {
	public static void main(String[] args) throws Exception
	{
		ApplicationContext ctx = new ClassPathXmlApplicationContext("annotation.xml");
		Person person = ctx.getBean("person", Person.class);
		person.setName("high");
	}
}
7.简化切点描述
上面例子可以看出,切点比较长,想多次使用时,重复的拷贝比较麻烦。可以通过以下方式简化连接点定义。
value="execution(public void spring2.Person.setName(String))"
@Aspect
@Component
public class LoggingAspect {
	//声明一个切点,在定义其他通知时直接使用方法名来引用当前的切点即可。
	@Pointcut("execution(public void test.Person.setName(String)))")
	public void getPointCut(){}
	
	@Before("getPointCut()")
	public void beforeMethod(JoinPoint joinPoint){
		String methodName = joinPoint.getSignature().getName();
		
		System.out.println(methodName + " begins.");
	}
}
<完>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值