`
jinnianshilongnian
  • 浏览: 21431578 次
  • 性别: Icon_minigender_1
博客专栏
5c8dac6a-21dc-3466-8abb-057664ab39c7
跟我学spring3
浏览量:2404524
D659df3e-4ad7-3b12-8b9a-1e94abd75ac3
Spring杂谈
浏览量:2997232
43989fe4-8b6b-3109-aaec-379d27dd4090
跟开涛学SpringMVC...
浏览量:5631111
1df97887-a9e1-3328-b6da-091f51f886a1
Servlet3.1规范翻...
浏览量:257451
4f347843-a078-36c1-977f-797c7fc123fc
springmvc杂谈
浏览量:1593013
22722232-95c1-34f2-b8e1-d059493d3d98
hibernate杂谈
浏览量:248914
45b32b6f-7468-3077-be40-00a5853c9a48
跟我学Shiro
浏览量:5847055
Group-logo
跟我学Nginx+Lua开...
浏览量:697960
5041f67a-12b2-30ba-814d-b55f466529d5
亿级流量网站架构核心技术
浏览量:780294
社区版块
存档分类
最新评论

【第六章】 AOP 之 6.4 基于@AspectJ的AOP ——跟我学spring3

阅读更多

       Spring除了支持Schema方式配置AOP,还支持注解方式:使用@AspectJ风格的切面声明。

6.4.1  启用对@AspectJ的支持

       Spring默认不支持@AspectJ风格的切面声明,为了支持需要使用如下配置:

 

java代码:
<aop:aspectj-autoproxy/>

 

这样Spring就能发现@AspectJ风格的切面并且将切面应用到目标对象。

6.4.2  声明切面

       @AspectJ风格的声明切面非常简单,使用@Aspect注解进行声明:

 

java代码:
@Aspect()
Public class Aspect{
……
}

 

       然后将该切面在配置文件中声明为Bean后,Spring就能自动识别并进行AOP方面的配置:

 

java代码:
<bean id="aspect" class="……Aspect"/>

 

       该切面就是一个POJO,可以在该切面中进行切入点及通知定义,接着往下看吧。

 

6.4.3  声明切入点

       @AspectJ风格的命名切入点使用org.aspectj.lang.annotation包下的@Pointcut+方法(方法必须是返回void类型)实现。

 

java代码:
@Pointcut(value="切入点表达式", argNames = "参数名列表")
public void pointcutName(……) {}
 

       value指定切入点表达式;

       argNames指定命名切入点方法参数列表参数名字,可以有多个用“,”分隔,这些参数将传递给通知方法同名的参数,同时比如切入点表达式“args(param)”将匹配参数类型为命名切入点方法同名参数指定的参数类型。

       pointcutName切入点名字,可以使用该名字进行引用该切入点表达式。

 

java代码:
@Pointcut(value="execution(* cn.javass..*.sayAdvisorBefore(..)) && args(param)", argNames = "param")
public void beforePointcut(String param) {}

 

定义了一个切入点,名字为“beforePointcut”,该切入点将匹配目标方法的第一个参数类型为通知方法实现中参数名为“param”的参数类型。

 

6.4.4  声明通知

       @AspectJ风格的声明通知也支持5种通知类型:

 

一、前置通知:使用org.aspectj.lang.annotation 包下的@Before注解声明;

 

java代码:
@Before(value = "切入点表达式或命名切入点", argNames = "参数列表参数名")

 

       value指定切入点表达式或命名切入点;

       argNames与Schema方式配置中的同义。

 

接下来示例一下吧:

1、定义接口和实现,在此我们就使用Schema风格时的定义;

2、定义切面:

 

java代码:
package cn.javass.spring.chapter6.aop;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class HelloWorldAspect2 {
 
}

 

3、定义切入点:

 

java代码:
@Pointcut(value="execution(* cn.javass..*.sayAdvisorBefore(..)) && args(param)", argNames = "param")
public void beforePointcut(String param) {}

 

4、定义通知:

 

java代码:
@Before(value = "beforePointcut(param)", argNames = "param")
public void beforeAdvice(String param) {
    System.out.println("===========before advice param:" + param);
}

 

5、在chapter6/advice2.xml配置文件中进行如下配置:

 

java代码:
<?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:aop="http://www.springframework.org/schema/aop"
        xsi:schemaLocation="
           http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
           http://www.springframework.org/schema/aop
           http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
          
  <aop:aspectj-autoproxy/>
  <bean id="helloWorldService"
            class="cn.javass.spring.chapter6.service.impl.HelloWorldService"/>
 
  <bean id="aspect"
             class="cn.javass.spring.chapter6.aop.HelloWorldAspect2"/>
 
</beans>
 

 

6、测试代码cn.javass.spring.chapter6.AopTest:

 

java代码:
@Test
public void testAnnotationBeforeAdvice() {
    System.out.println("======================================");
    ApplicationContext ctx = new ClassPathXmlApplicationContext("chapter6/advice2.xml");
    IHelloWorldService helloworldService = ctx.getBean("helloWorldService", IHelloWorldService.class);
    helloworldService.sayBefore("before");
    System.out.println("======================================");
}

 

 

将输出:

 

 
 

==========================================

===========before advice param:before

============say before

==========================================

 

 

 

 

 

 

 

 

 

 

切面、切入点、通知全部使用注解完成:

       1)使用@Aspect将POJO声明为切面;

       2)使用@Pointcut进行命名切入点声明,同时指定目标方法第一个参数类型必须是java.lang.String,对于其他匹配的方法但参数类型不一致的将也是不匹配的,通过argNames = "param"指定了将把该匹配的目标方法参数传递给通知同名的参数上;

       3)使用@Before进行前置通知声明,其中value用于定义切入点表达式或引用命名切入点;

       4)配置文件需要使用<aop:aspectj-autoproxy/>来开启注解风格的@AspectJ支持;

       5)需要将切面注册为Bean,如“aspect”Bean;

       6)测试代码完全一样。

 

 

二、后置返回通知:使用org.aspectj.lang.annotation 包下的@AfterReturning注解声明;

 

java代码:
@AfterReturning(
value="切入点表达式或命名切入点",
pointcut="切入点表达式或命名切入点",
argNames="参数列表参数名",
returning="返回值对应参数名")

       value指定切入点表达式或命名切入点;

       pointcut同样是指定切入点表达式或命名切入点,如果指定了将覆盖value属性指定的,pointcut具有高优先级;

       argNames与Schema方式配置中的同义;

       returning与Schema方式配置中的同义。

 

 

java代码:
@AfterReturning(
    value="execution(* cn.javass..*.sayBefore(..))",
    pointcut="execution(* cn.javass..*.sayAfterReturning(..))",
    argNames="retVal", returning="retVal")
public void afterReturningAdvice(Object retVal) {
    System.out.println("===========after returning advice retVal:" + retVal);
}

 

其中测试代码与Schema方式几乎一样,在此就不演示了,如果需要请参考AopTest.java中的testAnnotationAfterReturningAdvice测试方法。

 

三、后置异常通知:使用org.aspectj.lang.annotation 包下的@AfterThrowing注解声明;

 

java代码:
@AfterThrowing (
value="切入点表达式或命名切入点",
pointcut="切入点表达式或命名切入点",
argNames="参数列表参数名",
throwing="异常对应参数名")
 

 

       value指定切入点表达式或命名切入点;

       pointcut同样是指定切入点表达式或命名切入点,如果指定了将覆盖value属性指定的,pointcut具有高优先级;

       argNames与Schema方式配置中的同义;

       throwing与Schema方式配置中的同义。

 

 

java代码:
@AfterThrowing(
    value="execution(* cn.javass..*.sayAfterThrowing(..))",
    argNames="exception", throwing="exception")
public void afterThrowingAdvice(Exception exception) {
    System.out.println("===========after throwing advice exception:" + exception);
}

 

其中测试代码与Schema方式几乎一样,在此就不演示了,如果需要请参考AopTest.java中的testAnnotationAfterThrowingAdvice测试方法。

 

四、后置最终通知:使用org.aspectj.lang.annotation 包下的@After注解声明;

 

java代码:
@After (
value="切入点表达式或命名切入点",
argNames="参数列表参数名")

       value指定切入点表达式或命名切入点;

       argNames与Schema方式配置中的同义;

 

 

java代码:
@After(value="execution(* cn.javass..*.sayAfterFinally(..))")
public void afterFinallyAdvice() {
    System.out.println("===========after finally advice");
}

 

其中测试代码与Schema方式几乎一样,在此就不演示了,如果需要请参考AopTest.java中的testAnnotationAfterFinallyAdvice测试方法。

 

 

五、环绕通知:使用org.aspectj.lang.annotation 包下的@Around注解声明;

 

java代码:
@Around (
value="切入点表达式或命名切入点",
argNames="参数列表参数名")

 

       value指定切入点表达式或命名切入点;

       argNames与Schema方式配置中的同义;

 

 

java代码:
@Around(value="execution(* cn.javass..*.sayAround(..))")
public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable {
    System.out.println("===========around before advice");
    Object retVal = pjp.proceed(new Object[] {"replace"});
    System.out.println("===========around after advice");
    return retVal;
}

 

 

其中测试代码与Schema方式几乎一样,在此就不演示了,如果需要请参考AopTest.java中的annotationAroundAdviceTest测试方法。

6.4.5  引入

       @AspectJ风格的引入声明在切面中使用org.aspectj.lang.annotation包下的@DeclareParents声明:

 

java代码:
@DeclareParents(
value=" AspectJ语法类型表达式",
defaultImpl=引入接口的默认实现类)
private Interface interface;

 

       value匹配需要引入接口的目标对象的AspectJ语法类型表达式;与Schema方式中的types-matching属性同义;

       private Interface interface指定需要引入的接口;

       defaultImpl指定引入接口的默认实现类,没有与Schema方式中的delegate-ref属性同义的定义方式;

 

java代码:
@DeclareParents(
    value="cn.javass..*.IHelloWorldService+", defaultImpl=cn.javass.spring.chapter6.service.impl.IntroductiondService.class)
private IIntroductionService introductionService;
 

 

       其中测试代码与Schema方式几乎一样,在此就不演示了,如果需要请参考AopTest.java中的testAnnotationIntroduction测试方法。

 

原创内容,转载请注明出处【http://sishuok.com/forum/blogPost/list/0/2471.html

18
1
分享到:
评论
20 楼 hui1170907501 2016-11-25  
我用 AspectJ注解的方式好像没生效。按照步骤一步一步来的,具体如下:

    @Before(value="beforePointcut(param)",argNames="param")
    public void sayAdvisorBefore(String param){
        System.out.println("=================before advice param:"+param);
    }

@Aspect
public class HelloWorldAspect2 {
    @Pointcut(value="execution(* cn.javass..*.sayAdvisorBefore(..)) && args(param)",argNames="param")
    public void beforePointcut(String param){
        System.out.println("==============before pointcut execute:"+param);
    }
}
//测试类
    @Test
    public void testHelloWorld(){
        ApplicationContext ctx = new FileSystemXmlApplicationContext("src/spring-config.xml");
        IHelloWorldService helloWorldService = ctx.getBean("helloWorldService",IHelloWorldService.class);
        helloWorldService.sayAdvisorBefore("oldzhang");
    }
//配置
       <!-- 启用AOP注解 -->
       <aop:aspectj-autoproxy/> 
        
       <bean id="helloWorldService" class="cn.javass.spring.chapter6.service.impl.HelloWorldService"></bean>
       <bean id="aspect2" class="cn.javass.spring.chapter6.aop.HelloWorldAspect2"/>
     
执行结果:
=================before advice param:oldzhang
没有执行参数替代,并且没有输出前置通知,不知道哪里的配置有问题?
19 楼 jinyingone 2016-09-06  
测试之后,发现会性能衰减
一万次循环:直接执行 需要100毫秒左右
aop:400毫秒左右

会这样吗
18 楼 hx252502115 2016-08-10  
jia_rx 写道
麻烦问下为spring bean中添加新方法中使用的aop:declare-parents里面的delegate-ref和default-impl有啥区别 我怎么看书说的是一样的效果 但是我测试 如果使用default-impl的时候里面的属性不能获取到 但是delegate-ref可以正常的获取到属性呢?
代码在:http://www.cnblogs.com/a757956132/p/5036672.html  里面有块红字显示
谢谢



虽然时间过了很久了,但是我还是回答下:
1、delegat-ref和default-impl的区别:
    1)、delegat-ref用来引用spring的bean的定义,而default-impl是用来指定完整的包路径,这是他们语法上的区别;
    2)、由于两种定义方式不同,也造成接口的默认实现类的初始化方式不同,delegat-ref是引用bean的,由bean指定的默认实现类是由spring在加载bean时初始化的,初始化时仁兄定义的两个属性也被注入,而且这个默认实现类是单例的。而由defaul-impl指定的完整的包路径后,在执行目标方法时,由springAop实例化的,调用的是默认的无参构造方法(springAOP采用的是懒加载的方式,第一次执行目标方法时才初始化,初始化后会将初始化的实例存入cache,后面再调用目标方法就采用缓存里的实例,和bean的单例实现一样都是用注册表单例的形式,只是springAOP是用代理的方法名的hash值为key)
    
     所以仁兄上面的问题就很明了了,当您用default-impl定义时,spring帮你初始化了一个全新的对象,这个对象和您在bean中定义对象是不一样的。

     
17 楼 hx252502115 2016-08-10  
王千博1990 写道
走了很多弯路,终于发现pointcut中args用于筛选切入点,而arg-names是advice方法的参数,但是arg-names必须包含args里的参数设置,否则就会报错。
比如after-returning中由于要配置返回参数,因此arg-names就会比args多一个返回值参数。
配置例子
<aop:config>
        <aop:pointcut id="pointcut2" expression="execution(* com.springapp.impl.bean.*.sayHello(..)) and args(para,a)"/>
        <aop:aspect ref="aop2">
            <!--<aop:before pointcut-ref="pointcut1" method="beforeAdvice(java.lang.String)" arg-names="para"/>-->
            <aop:after-returning pointcut-ref="pointcut2" method="afterAdvice" argi-names="ob,para,a"  returning="ob" />
        </aop:aspect>
    </aop:config>
</beans>
相应的afterAdvice
public void afterAdvice(Object ob,String para,int a) {
        System.out.println("=============after finally advice " + ob);
    }



argNames参数其实是advic的参数列表,argNames必须包含全部的args里的参数,否则会报错提示错误的参数传入。args其实表示的也是advice里的参数,根据参数名和对应名称的advice的参数类型去匹配目标方法
16 楼 Meros_Z 2016-03-30  
在这里给各位学习的童鞋们指正一些问题:
一,在前置通知的方法中的确如13楼所述,切入点的方法不一致,歪打正着~~
二,如果使用高版本的jdk如果引入的AspectJ版本较低的话会报错,亲测
error at ::0 can't find referenced pointcut
解决办法:maven中引入最新版本的AspectJ,如果没用maven的导入最新的jar包就行
yannikcao 写道
--添加前置通知这里:   
@Test 
    public void testAnnotationBeforeAdvice() { 
        System.out.println("======================================"); 
        ApplicationContext ctx = new ClassPathXmlApplicationContext("chapter6/advice2.xml"); 
        IHelloWorldService helloworldService = ctx.getBean("helloWorldService", IHelloWorldService.class); 
        helloworldService.sayBefore("before"); 
        System.out.println("======================================"); 
    } 

--里面的
     helloworldService.sayBefore("before");  这句应该是helloworldService.sayAdvisorBefore("before");吧?


--因为切入点配置的不是sayAdvisorBefore吗?
    @Pointcut(value="execution(* cn.javass..*.sayAdvisorBefore(..)) && args(param)", argNames = "param") 
    public void beforePointcut(String param) {} 

--求解

15 楼 jia_rx 2015-12-11  
麻烦问下为spring bean中添加新方法中使用的aop:declare-parents里面的delegate-ref和default-impl有啥区别 我怎么看书说的是一样的效果 但是我测试 如果使用default-impl的时候里面的属性不能获取到 但是delegate-ref可以正常的获取到属性呢?
代码在:http://www.cnblogs.com/a757956132/p/5036672.html  里面有块红字显示
谢谢
14 楼 王千博1990 2015-11-05  
走了很多弯路,终于发现pointcut中args用于筛选切入点,而arg-names是advice方法的参数,但是arg-names必须包含args里的参数设置,否则就会报错。
比如after-returning中由于要配置返回参数,因此arg-names就会比args多一个返回值参数。
配置例子
<aop:config>
        <aop:pointcut id="pointcut2" expression="execution(* com.springapp.impl.bean.*.sayHello(..)) and args(para,a)"/>
        <aop:aspect ref="aop2">
            <!--<aop:before pointcut-ref="pointcut1" method="beforeAdvice(java.lang.String)" arg-names="para"/>-->
            <aop:after-returning pointcut-ref="pointcut2" method="afterAdvice" argi-names="ob,para,a"  returning="ob" />
        </aop:aspect>
    </aop:config>
</beans>
相应的afterAdvice
public void afterAdvice(Object ob,String para,int a) {
        System.out.println("=============after finally advice " + ob);
    }
13 楼 yannikcao 2015-09-01  
--添加前置通知这里:   
@Test 
    public void testAnnotationBeforeAdvice() { 
        System.out.println("======================================"); 
        ApplicationContext ctx = new ClassPathXmlApplicationContext("chapter6/advice2.xml"); 
        IHelloWorldService helloworldService = ctx.getBean("helloWorldService", IHelloWorldService.class); 
        helloworldService.sayBefore("before"); 
        System.out.println("======================================"); 
    } 

--里面的
     helloworldService.sayBefore("before");  这句应该是helloworldService.sayAdvisorBefore("before");吧?


--因为切入点配置的不是sayAdvisorBefore吗?
    @Pointcut(value="execution(* cn.javass..*.sayAdvisorBefore(..)) && args(param)", argNames = "param") 
    public void beforePointcut(String param) {} 

--求解
12 楼 陈英杰_-_ 2015-08-04  
  为何好多跟着配置不能解析 照样通过了。。。
比如:  <aop:before pointcut="execution(* cn.javass..*.sayBefore(..)) and args(param)"
                                 method="beforeAdvice(java.lang.String)"

中 method 方法报错 can not resolve
11 楼 90opui 2015-07-08  
            
10 楼 hxlzpnyist 2013-10-12  
jinnianshilongnian 写道
hxlzpnyist 写道
开涛兄 有个问题
spring 是如何通过配置<aop:aspectj-autoproxy/> 去自动扫描添加了注解@Aspectj的类?

spring自动扫描bean组件的时候是设置了扫描包路径,<aop:aspectj-autoproxy/> 这个是怎么实现的呢 


它不会扫描, 它只是找@Aspect注解的bean;  扫描是context:component-scan实现的



额 搞明白了 <aop:aspectj-autoproxy/>  这个只是说 开启spring对@Aspectj的支持,并不是自动装载切面类
9 楼 jinnianshilongnian 2013-10-12  
hxlzpnyist 写道
开涛兄 有个问题
spring 是如何通过配置<aop:aspectj-autoproxy/> 去自动扫描添加了注解@Aspectj的类?

spring自动扫描bean组件的时候是设置了扫描包路径,<aop:aspectj-autoproxy/> 这个是怎么实现的呢 


它不会扫描, 它只是找@Aspect注解的bean;  扫描是context:component-scan实现的
8 楼 hxlzpnyist 2013-10-12  
开涛兄 有个问题
spring 是如何通过配置<aop:aspectj-autoproxy/> 去自动扫描添加了注解@Aspectj的类?

spring自动扫描bean组件的时候是设置了扫描包路径,<aop:aspectj-autoproxy/> 这个是怎么实现的呢 
7 楼 jinnianshilongnian 2013-09-03  
czj4451 写道
AopTest在哪里有下哦?

http://jinnianshilongnian.iteye.com/blog/1508589
6 楼 czj4451 2013-09-02  
AopTest在哪里有下哦?
5 楼 jinnianshilongnian 2012-04-09  
kimifdw 写道
很不错,支持一下

谢谢
4 楼 kimifdw 2012-04-09  
很不错,支持一下
3 楼 whatfangfang 2012-03-29  
给你发信息了啊,你确认下,谢谢!
2 楼 jinnianshilongnian 2012-03-29  
whatfangfang 写道
哥们问个问题:Caused by: java.lang.IllegalArgumentException: error at ::0 can't find referenced pointcut allMethods
我查了资料,有的说是aspectj.jar 的版本低,后来我把aspectj升级到aspectj-1.6.12.jar
后我的问题依然存在
我用的是spring3+aspectj-1.6.12.jar 请问怎样解决这个问题?



can't find referenced pointcut allMethods

找不到 切入点 allMethods 能否代码发一下
1 楼 whatfangfang 2012-03-29  
哥们问个问题:Caused by: java.lang.IllegalArgumentException: error at ::0 can't find referenced pointcut allMethods
我查了资料,有的说是aspectj.jar 的版本低,后来我把aspectj升级到aspectj-1.6.12.jar
后我的问题依然存在
我用的是spring3+aspectj-1.6.12.jar 请问怎样解决这个问题?

相关推荐

Global site tag (gtag.js) - Google Analytics