@H_419_2@2、AOP术语@H_404_6@横切关注点(cross-cutuing concern):散布在应用中多处的功能。@H_404_6@@H_404_6@切面(aspect) : 横切关注点模块化为特殊的类。切面是通知和切点的结合。@H_404_6@通知(advice):定义了切面是什么以及何时使用。
Spring切面可以应用5种类型的通知:
前置通知(Before):在目标方法被调用之前调用通知功能;
后置通知(After):在目标方法完成之后调用通知,此时不会关心方法的输出是什么;
返回通知(After-returning):在目标方法成功执行之后调用通知;
异常通知(After-throwing):在目标方法抛出异常后调用通知;
环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为。
连接点(join point):在应用执行过程中能够插入切面的一个点。这个点可以是调用方法时、抛出异常时、甚至修改一个字段时。@H_404_6@@H_404_6@织入(Weaving):织入是把切面应用到目标对象并创建新的代理对象的过程。
织入有三种方式可以实现,Spring采用的是第三种,在运行期织入的:
编译期:切面在目标类编译时被织入。这种方式需要特殊的编译器。AspectJ的织入编译器就是以这种方式织入切面的。
类加载期:切面在目标类加载到JVM时被织入。这种方式需要特殊的类加载器(ClassLoader),它可以在目标类被引入应用之前增强该目标类的字节码。AspectJ 5的加载时织入(load-time weaving,LTW)就支持以这种方式织入切面。
运行期:切面在应用运行的某个时刻被织入。一般情况下,在织入切面时,AOP容器会为目标对象动态地创建一个代理对象。代理类封装了目标类,并拦截被通知方法的调用,再把调用转发给真正的目标bean。SpringAOP就是以这种方式织入切面的。
@annotation的正确用法:在切面类上用@annotation加自定义注解就可以拦截使用这个注解的方法。比如匹配 @RequestMapPing 注解的类 @annotation(org.springframework.web.bind.annotation.RequestMapPing)@H_404_6@@target (cn.javass.spring.chapter6.Secure) 任何目标对象持有Secure注解的类方法;必须是在目标对象上声明这个注解,在接口上声明的对它不起作用。@H_404_6@二、使用注解创建切面@H_419_2@1、添加pom.xml依赖
<dependency> groupID>org.aspectj</artifactID>aspectjrtversion>1.6.11> >aspectjweaver>VIEw Code@H_419_2@2、定义切面
@Aspect //表示这是一个切面类public class AudIEnce { 使用简明的pointcut @pointcut("execution(* com.service.Performance.perform(..))") voID performance(){} 前置通知 即 @Before("execution(* com.service.Performance.perform(..))") @Before("performance()" silenceCellPhones(){ System.out.println("Silencing cell phones"); } takeSeats(){ System.out.println("Taking seats"); } 方法调用结束通知(并不是指返回值通知,即使是voID的返回值,仍然会触发通知) 即 @AfterReturning("execution(* com.service.Performance.perform(..))") @AfterReturning("performance()" applause(){ System.out.println("clap clap clap!!!"有异常抛出的时候通知,即 @AfterThrowing("execution(* com.service.Performance.perform(..))") @AfterThrowing("performance()" demandrefund(){ System.out.println("Demanding a refund"); }}VIEw Code@H_419_2@3、启用AspectJ注解的自动代理 @H_404_6@有两种方式可以启用AspectJ 注解的自动代理:@H_404_6@(1)在 Java 配置文件中显示配置
@Configuration@EnableAspectJAutoproxy 启用Aop自动代理 JavaConfig { @Bean public AudIEnce getAudIEnce(){ return new AudIEnce(); }}VIEw Code@H_404_6@(2)在XML文件中配置
<!--启用AspectJ自动代理--> aop:aspectj-autoproxy/> bean ID="audIEnce" class="com.aspect.AudIEnce"/>VIEw Code@H_404_6@不管你是使用JavaConfig还是XML,AspectJ自动代理都会为使用@Aspect注解的bean创建一个代理,这个代理会围绕着所有该切面的切点所匹配的bean。当程序执行到连接点的时候,就会由代理转到切面触发相应的通知。
@H_419_2@4、创建环绕通知
@Aspect AudIEnce3 { @pointcut("execution(* com.service.Performance.perform(..))" performance(){} @Around("performance()" watchPerformance(ProceedingJoinPoint joinPoint) { System.out.println("Silencing cell phones"); System.out.println("Taking seats"); try { joinPoint.proceed(); System.out.println("clap clap clap!!!"); } catch (Throwable throwable) { System.out.println("Demanding a refund"); throwable.printstacktrace(); } }}VIEw Code@H_404_6@ 注意 ProceedingJoinPoint 作为参数。这个对象是必须要有的,因为你要在通知中通过它来调用被通知的方法。当要将控制权交给被通知的方法时,它需要调用ProceedingJoinPoint的proceed()方法。
@H_419_2@5、切面匹配输入参数
TrackCounter { private Map<Integer,Integer> trackCounts=new HashMap<Integer,Integer>(); @pointcut("execution(* com.service.Compactdisc.playTrack(int)) && args(trackNumber)") 带入输入参数 @pointcut("target(com.service.Compactdisc) && args(trackNumber)") target 匹配目标对象(非AOP对象)为指定类型 @pointcut("within(com.service..*) && args(trackNumber)") com.service 包以及子包下的所有方法都执行 @pointcut("within(com.service..Compactdisc+) && args(trackNumber)") com.service 包的Compactdisc类型以及子类型 @pointcut("this(com.service.Compactdisc) && args(trackNumber)") 匹配当前AOP代理对象类型,必须是类型全称,不支持通配符 voID trackPlayed(int trackNumber){} @Before("trackPlayed(trackNumber)"voID countTrack( trackNumber){ int playCount = getPlayCount(trackNumber); trackCounts.put(trackNumber,playCount+1); System.out.println(trackCounts.toString()); } int getPlayCount(return trackCounts.containsKey(trackNumber) ? trackCounts.get(trackNumber) : 0; }}VIEw Code 参数的配置可以用占位符 * 和 ..
* 的意思是任意类型任意名称的一个参数
.. 的意思是任意类型,任意多个参数,并且只能放到args的后面。 @H_419_2@6、利用切面注入新功能@H_404_6@ Java并不是动态语言。一旦类编译完成了,我们就很难为该类添加新的功能了。但是,我们的切面编程却可以做到动态的添加方法...话虽如此,其实也不过是障眼法罢了。实际上,面向切面编程,不过是把方法添加到切面代理中,当要对添加的方法调用的时候,可以把被通知的 Bean 转换成相应的接口。也就是代理会把此调用委托给实现了新接口的某个其他对象。实际上,一个bean的实现被拆分到了多个类中。(说实话,想了半天,实在想不到这个功能有什么作用......)@H_404_6@@H_404_6@(1) 重新定义一个接口和实现类
interface Encoreable { performEncode();}VIEw Code
class DefaultEncoreable implements Encoreable { performEncode() { System.out.println("this is DefaultEncoreable"); }}VIEw Code@H_404_6@(2) 把接口实现类嵌入到目标类代理中
EncoreableIntroducer { @DeclareParents(value = "com.service.Compactdisc+",defaultImpl = DefaultEncoreable.class) value 表示要嵌入哪些目标类的代理 。 defaultImpl:表示要嵌入的接口的默认实现方法 static Encoreable encoreable;}VIEw Code@H_404_6@(3) JUnit 测试
@RunWith(SpringJUnit4ClassRunner.)@ContextConfiguration(locations = "classpath:applicationContext.xml") Test02 { @autowired private Compactdisc compactdisc; @Test test02(){ compactdisc.playTrack(123); Encoreable compactdisc = (Encoreable) this.compactdisc; 当要调用添加的新功能的时候,这个用法相当于由代理转换到对应类实现,不会报类型转换错误 compactdisc.performEncode(); }}VIEw Code 三、使用XML声明切面@H_404_6@@H_404_6@ @H_419_2@1、定义切面
AudIEnceXML { ); }}VIEw Code@H_419_2@2、XML配置切面
aop:configaop:aspect ref="audIEnceXML"aop:pointcut ="performance" Expression="execution(* com.service.Performance.perform(..))"/> aop:before method="silenceCellPhones" pointcut-ref="performance"="takeSeats"aop:after-returning ="applause"aop:after-throwing ="demandrefund"/> aop:aspect> >VIEw Code@H_419_2@3、创建环绕通知
AudIEnce3XML { ); throwable.printstacktrace(); } }}VIEw Code
="audIEnce3XML"="performance3"aop:around ="watchPerformance"="performance3">VIEw Code@H_419_2@4、匹配输入参数
="trackCounter"="trackPlayed"="execution(* com.service.Compactdisc.playTrack(int)) and args(trackNumber)"="countTrack"="trackPlayed">VIEw Code@H_419_2@5、注入新功能
aop:declare-parents types-matching="com.service.Compactdisc+" implement-interface="com.service.Encoreable" default-impl="com.service.impl.DefaultEncoreable" delegate-ref="encoreableDelegate"/> >VIEw Code 总结
以上是内存溢出为你收集整理的再学习之Spring(面向切面编程).全部内容,希望文章能够帮你解决再学习之Spring(面向切面编程).所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)