不明觉厉的spring(3)---Aop
2013-10-14 22:35
381 查看
aop的定义
将程序中的交叉业务逻辑提取出来,称之为切面。将这些切面动态织入到目标对象,然后生成一个代理对象的过程aop的核心概念
Aspect(切面):指横切性关注点的抽象即为切面,它与类相似,只是两者的关注点不一样,类是对物体特征的抽象,而切面横切性关注点的抽象.joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点,实际上joinpoint还可以是field或类构造器)
Pointcut(切入点):所谓切入点是指我们要对那些joinpoint进行拦截的定义.
Advice(通知):所谓通知是指拦截到joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知
Target(目标对象):代理的目标对象
Weave(织入):指将aspects应用到target对象并导致proxy对象创建的过程称为织入.
Introduction(引入):在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field.
Proxy(代理对象)代理对象,指切面织入目标对象之后形成的对象
aop的实现原理
可以通过操作字节码,比如asm,Javassist,SERP等也可使用用动态代理模式。
Spring AOP采用动态代理的过程:
(1) 将切面使用动态代理的方式动态织入到目标对象(被代理类),形成一个代理对象;
(2) 目标对象如果没有实现代理接口,那么Spring会采用CGLib来生成代理对象,该代理对象是目标对象的子类;
(3) 目标对象如果是final类,并且也没实现代理接口,就不能运用AOP。
可以参考我的另一篇博文:/article/1620004.html
Spring的通知
Spring的通知类型(1) MethodBeforeAdvice
全名:前置通知
在方法调用之前,做处理。
不能够改变返回值
不能够改变目标方法的流程,也不能中断流程的处理过程(除非抛出异常)
(2) AfterReturningAdvice
全名:后置通知
在方法调用之后,做处理。
不能够改变返回值
不能够改变目标方法的流程,也不能中断流程的处理过程(除非抛出异常)
(3) MethodInterceptor
全名:环绕通知
在方法调用之前以及之后,做处理。
可以改变返回值,也可以改变流程。
(4) ThrowsAdvice
全名:异常通知
在方法抛出异常后,做处理。
当该通知处理完异常后,会简单地将异常再次抛出给目标调用方法。
(5)After
全名:最终通知
无论一个方法是如何结束的,最终通知都会运行。
使用spring的aop编程
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" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd"> <aop:aspectj-autoproxy/><!-- 启动对aop注解的支持 --> </beans>注意:spring3.1不再把那些依赖的第三方jar包打包进来所以我们在引入org.springframework.aop-3.1.1.RELEASE.jar之外,还需要自己下载第三方依赖包:
spectjrt.jar,aspectjweaver.jar,以及aopalliance.jar,如果有需要可以联系我
使用到的jar包,jdk1.6
切面编程代码:
package spring.senssic.aop; 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 MyAop { // 切入点,其中的为匹配为匹配所有函数 @Pointcut("execution(* *(..))") private void anyMethod() { }// 声明一个切入点,不会使用到,只是一个标注点,返回空,参数空 @Before(value = "anyMethod() && args(userName)") // 可以获取到参数为userName并使用 // 定义前置通知 public void doAccessCheck(String userName) { System.out.println("我是方法执行前调用的前置通知。\n参数名字:" + userName); } @AfterReturning(pointcut = "anyMethod()", returning = "revalue") // 可以获取到返回值为revalue并使用 // 定义后置通知 public void doReturnCheck(String revalue) { System.out.println("我是方法执行后调用的后置通知。\n方法返回值:" + revalue); } @AfterThrowing(pointcut = "anyMethod()", throwing = "ex") // 定义例外通知 public void doExceptionAction(Exception ex) { System.out.println("我是方法抛出例外时调用的例外通知。\n抛出的例外:"); ex.printStackTrace(); } @After(value = "anyMethod()") // 定义最终通知 public void doReleaseAction() { System.out.println("无论方法怎样,我都会被执行,方法参数为:"); } @Around("anyMethod()") // 环绕通知 public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable { System.out .println("我是环绕通知,可以再方法执行前和执行后运行,也可以确定此方法什么时候执行,如何执行,是否执行,我的功能最强大"); // 通知的第一个参数必须是ProceedingJoinPoint类型,当调用proceed()方法会导致后面的连接的的方法的执行 // startwacth System.out.println("我第一个执行"); Object retval = pjp.proceed(); System.out.println("我最后执行"); // stopwacth return retval; } }
要织入的类:
package spring.senssic.temp; public class AopTo { public String doString(String name) { return "我是一个方法,我的参数为:" + name; } }
xml的配置文件:
<?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" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd"> <aop:aspectj-autoproxy/><!-- 启动对aop注解的支持 --> <context:annotation-config></context:annotation-config> <bean id="myaop" class="spring.senssic.aop.MyAop"></bean> <bean id="aopto" class="spring.senssic.temp.AopTo"></bean> </beans>测试类:
package spring.senssic.test; import org.junit.BeforeClass; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import spring.senssic.temp.AopTo; import spring.senssic.temp.B; import spring.senssic.temp.C; import spring.senssic.temp.D; public class STest { private static B b; private static C c; private static D d; private static AopTo aTo; @BeforeClass public static void TWork() { ApplicationContext act = new ClassPathXmlApplicationContext("beans.xml"); /* * b = (B) act.getBean("classb"); c = (C) act.getBean("c"); C c2 = (C) * act.getBean("c"); d = (D) act.getBean("d"); System.out.println(c == * c2); */ aTo = (AopTo) act.getBean("aopto"); } @Test public void test() { System.out.println(aTo.doString("senssic")); // System.out.println(c.toString()); } }
测试结果:
2013-10-14 20:41:30 org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@471e30: startup date [Mon Oct 14 20:41:30 CST 2013]; root of context hierarchy
2013-10-14 20:41:31 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [beans.xml]
2013-10-14 20:41:33 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
信息: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1f66cff: defining beans [org.springframework.aop.config.internalAutoProxyCreator,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,myaop,aopto,org.springframework.context.annotation.ConfigurationClassPostProcessor$ImportAwareBeanPostProcessor#0];
root of factory hierarchy
我是方法执行前调用的前置通知。
参数名字:senssic
我是环绕通知,可以再方法执行前和执行后运行,也可以确定此方法什么时候执行,如何执行,是否执行,我的功能最强大
我第一个执行
我是方法执行后调用的后置通知。
方法返回值:我是一个方法,我的参数为:senssic
无论方法怎样,我都会被执行,方法参数为:
我最后执行
我是一个方法,我的参数为:senssic
其中关于execution的匹配问题
Spring AOP 用户可能会经常使用 execution切入点指示符。执行表达式的格式如下:execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern)
throws-pattern?)
除了返回类型模式(上面代码片断中的ret-type-pattern),
名字模式和参数模式以外, 所有的部分都是可选的。返回类型模式决定了方法的返回类型必须依次匹配一个连接点。
*--->它代表了匹配任意的返回类型。 一个全限定的类型名将只会匹配返回给定类型的方法。
名字模式匹配的是方法名。 你可以使用*通配符作为所有或者部分命名模式。
参数模式稍微有点复杂:()匹配了一个不接受任何参数的方法, 而(..)匹配了一个接受任意数量参数的方法
(零或者更多)。 模式(*)匹配了一个接受一个任何类型的参数的方法。 模式(*,String)匹配了一个接受两个参数的方法
第一个可以是任意类型, 第二个则必须是String类型。
除了返回类型模式(上面代码片断中的ret-type-pattern),
名字模式和参数模式以外, 所有的部分都是可选的。返回类型模式决定了方法的返回类型必须依次匹配一个连接点。
你会使用的最频繁的返回类型模式是*,它代表了匹配任意的返回类型。
一个全限定的类型名将只会匹配返回给定类型的方法。名字模式匹配的是方法名。
你可以使用*通配符作为所有或者部分命名模式。 参数模式稍微有点复杂:()匹配了一个不接受任何参数的方法
, 而(..)匹配了一个接受任意数量参数的方法(零或者更多)。 模式(*)匹配了一个接受一个任何类型的参数的方法。
模式(*,String)匹配了一个接受两个参数的方法,第一个可以是任意类型, 第二个则必须是String类型。
下面给出一些通用切入点表达式的例子。
任意公共方法的执行:
execution(public * *(..))
任何一个名字以“set”开始的方法的执行:
execution(* set*(..))
AccountService接口定义的任意方法的执行:
execution(* com.xyz.service.AccountService.*(..))
在service包中定义的任意方法的执行:
execution(* com.xyz.service.*.*(..))
在service包或其子包中定义的任意方法的执行:
execution(* com.xyz.service..*.*(..))
在service包中的任意连接点(在Spring AOP中只是方法执行):
2.使用xml的方式配置
切面编程代码:package spring.senssic.aop; import org.aspectj.lang.ProceedingJoinPoint; public class Myaop2 { // 定义前置通知 public void doAccessCheck() { System.out.println("我是方法执行前调用的前置通知。"); } // 定义后置通知 public void doReturnCheck(String revalue) { System.out.println("我是方法执行后调用的后置通知。\n方法返回值:" + revalue); } // 定义例外通知 public void doExceptionAction(Exception ex) { System.out.println("我是方法抛出例外时调用的例外通知。\n抛出的例外:"); ex.printStackTrace(); } // 定义最终通知 public void doReleaseAction() { System.out.println("无论方法怎样,我都会被执行,方法参数为:"); } // 环绕通知 public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable { System.out .println("我是环绕通知,可以再方法执行前和执行后运行,也可以确定此方法什么时候执行,如何执行,是否执行,我的功能最强大"); // 通知的第一个参数必须是ProceedingJoinPoint类型,当调用proceed()方法会导致后面的连接的的方法的执行 // startwacth System.out.println("我第一个执行"); Object retval = pjp.proceed(); System.out.println("我最后执行"); // stopwacth return retval; } }
要织入的java
package spring.senssic.temp; public class AopTo { public String doString(String name) { return "我是一个方法,我的参数为:" + name; } }
xml配置文件
<?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" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd"> <aop:aspectj-autoproxy/><!-- 启动对aop注解的支持 --> <context:annotation-config></context:annotation-config> <bean id="myaop" class="spring.senssic.aop.Myaop2"></bean> <bean id="aopto" class="spring.senssic.temp.AopTo"></bean> <aop:config> <aop:aspect id="my" ref="myaop"> <aop:pointcut expression="execution(* *(..))" id="mycut"/> <aop:before pointcut-ref="mycut" method="doAccessCheck"/> <aop:after-returning pointcut-ref="mycut" method="doReturnCheck" returning="revalue"/> <aop:after-throwing pointcut-ref="mycut" method="doExceptionAction" throwing="ex"/> <aop:after pointcut-ref="mycut" method="doReleaseAction"/> <aop:around pointcut-ref="mycut" method="doBasicProfiling"/> </aop:aspect> </aop:config> </beans>
引入(Introduction)
引入(在AspectJ中被称为inter-type声明)使得一个切面可以定义被通知对象实现给定的接口, 并且可以为那些对象提供具体的实现。使用@DeclareParents注解来定义引入。这个注解用来定义匹配的类型 拥有一个新的父类(所以有了这个名字)。比如,给定一个接口UsageTracked, 和接口的具体实现DefaultUsageTracked类, 接下来的切面声明了所有的service接口的实现都实现了UsageTracked接口。 (比如为了通过JMX输出统计信息)。
@Aspect public class myIntroduction{ @DeclareParents(value="srping.senssic.service.*+",//所有的service下的类或接口都将实现UsageTracked接口 defaultImpl=spring.senssic.service.impl.DefaultUsageTracked.class)//UsageTracked接口实现类 public static UsageTracked mixin;//UsageTracked接口 @Before(value = "execution(* *(..)) && args(userName)")//匹配切入点 public void doAccessCheck(UsageTracked userName) { System.out.println("我是方法执行前调用的前置通知。\n参数名字:" + userName.toString()); } }
实现的接口通过被注解的字段类型来决定。@DeclareParents注解的 value属性是一个AspectJ的类型模式:- 任何匹配类型的bean都会实现 UsageTracked接口。请注意,在上面的前置通知的例子中,service beans 可以直接用作UsageTracked接口的实现。如果需要编程式的来访问一个bean, 你可以这样写:
UsageTracked usageTracked = (UsageTracked) context.getBean("myService");
相关文章推荐
- 利用Spring AOP 更新memcached 缓存策略的实现(二)
- springboot aop 自定义注解方式实现一套完善的日志记录(完整源码)
- Spring-aop: Error Cannot convert value of type [$Proxy...] to required type
- Spring全家桶(八)AOP核心思想与AspectJ 5种类型通知
- SpringBoot项目中使用AOP
- 5.8 把输出日志的实例改成用Spring的AOP来实现
- Spring AOP 注解配置
- spring aop实现类似代理类和类中的方法(注解实现)
- 《Spring 系列》- AOP-XML方式
- Spring入门(三)— AOP注解、jdbc模板、事务
- Spring Aop详解
- spring的AOP配置
- Spring 在XML中声明切面/AOP
- 关于 Spring AOP 中,包含参数的成员方法的调用,出现 NoSuchMethodException 的研究
- Spring实现AOP的4种方式(转)
- spring中实现aop的例子及说明
- SPRING AOP
- 使用Spring的注解方式实现AOP
- spring AOP小例子
- aop 注解 开启spring自带的事务