Spring-通过注解实现的AOP

来源:互联网 发布:java 项目引用新技术 编辑:程序博客网 时间:2024/04/29 06:12

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.xsdhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsdhttp://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@Componentpublic 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@Componentpublic 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.");}}
<完>
0 0
原创粉丝点击