spring day02——AspectJ 切面编程(xml方式)

百度百科文档:

http://baike.baidu.com/link?url=2h5jS5pGRtcNexdPQBt1CSgPXpqFyyCu0WR-dJKh9mLxjdpLloT9XsY7pQIwGdOLQB1n7Shfoqr65JXiOtmDEK

早期传统Spring1.2 AOP编程 ,  确定目标 --- 编写通知(通知需要实现一个接口)

       ---- 配置ProxyFactoryBean生成代理对象,配置非常复杂,

          spring2.0 支持AspectJ 语法 ,简化AOP开发。

开发方法还是三步:

  1. 确定目标对象(bean
  2. 编写通知,对目标对象增强(advice
  3. 配置切入点(pointcut)、切面(aspect
  1.  AspectJ 提供Advice类型

普通的pojo即可。(不需要实现接口)

 

AspectJ提供不同的通知类型:

  1. Before 前置通知,相当于BeforeAdvice
  2. AfterReturning 后置通知,相当于AfterReturningAdvice
  3. Around 环绕通知,相当于MethodInterceptor
  4. AfterThrowing抛出通知,相当于ThrowAdvice
  5. After 最终final通知,不管是否异常,该通知都会执行
  6. DeclareParents 引介通知,相当于IntroductionInterceptor (不要求掌握)

 

相比传统Spring AOP通知类型多了 After最终通知 (类似 finally )。

 

实现步骤:

第一步:确定目标对象,即确定bean对象

第二步:advice通知(编写)

第三步:配置切面(包括切入点),让切入点关联通知

 

第一步:新建web项目spring4_d02_c05

导入jar包 (11个)

         基本spring开发 6个 (4+2)(包含配置文件)

         Spring测试集成 1个

         Spring AOP编程 4 (2对)

 

  1. com.springsource.org.aopalliance-1.0.0.jar: AOP联盟规范
  2. spring-aop-4.2.4.RELEASE.jar:springAOP 整合扩展
  3. com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar:aspectJ官方包
  4. spring-aspects-4.2.4.RELEASE.jar:spring对aspectJ集成

 

AOP联盟包:

路径:spring-framework-3.0.2.RELEASE-dependencies\org.aopalliance\com.springsource.org.aopalliance\1.0.0

Aspectj包:

路径:spring-framework-3.0.2.RELEASE-dependencies\org.aspectj\com.springsource.org.aspectj.weaver\1.6.8.RELEASE

共需要导入的jar包

 

在src下,创建applicationContext.xml。

第一步: 引用aop的名称空间

查看:spring-framework-4.2.4.RELEASE\docs\spring-framework-reference\html中的xsd-config.html,搜素aop,发现:

需要我们引入aop的文件约束。如图

<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/beans

                            http://www.springframework.org/schema/beans/spring-beans.xsd

                        http://www.springframework.org/schema/context

                        http://www.springframework.org/schema/context/spring-context.xsd

                        http://www.springframework.org/schema/aop

                           http://www.springframework.org/schema/aop/spring-aop.xsd">

 

配置aop提示

 

 

第二步:编写Before 前置通知Advice增强 :

创建包:com.igeek,复制上个项目中的

CustomerServiceImpl.java,ICustomerService.java,ProductService.java到包中

配置bean

 

    <!-- 1.确定了要增强的target对象 -->

    <!-- 对于spring来说,目标对象:就是bean对象 -->

    <!-- 基于接口类 -->

    <bean id="customerService" class="com.igeek.CustomerServiceImpl"/>

    <!-- 基于一般类 -->

    <bean id="productService" class="com.igeek.ProductService"/>

 

 

创建类:MyAspect.java

编写MyAspect.java

//aspectj的advice通知增强类,无需实现任何接口

public class MyAspect {

   

    //前置通知

    //普通的方法。方法名随便,但也不能太随便,一会要配置

    public void firstbefore(){

       System.out.println("------------第一个个前置通知执行了。。。");

    }

}

将前置通知配置到spring的容器中

<!-- 2.配置advice通知增强 -->

<bean id="myAspectAdvice" class="com.igeek.MyAspect"/>

  1.  配置切入点和切面(让切入点关联通知)

第三步:配置切面(包括切入点),让切入点关联通知

核心配置文件applicationContext.xml中添加:

    <!-- 3:配置aop -->

    <aop:config>

       <!-- 切入点:拦截哪些bean的方法 -->

       <aop:pointcut expression="bean(*Service)" id="myPointcut"/>

       <!--

           切面:要对哪些方法进行怎样的增强 

           aop:aspect:aspejctj的方式!

           ref:配置通知

       -->

       <aop:aspect ref="myAspectAdvice">

          

           <!-- 第一个前置通知 :在访问目标对象方法之前,先执行通知的方法

              method:advice类中的方法名,

                pointcut-ref="myPointcut":注入切入点

                目的是让通知关联切入点

           -->

           <aop:before method="firstbefore" pointcut-ref="myPointcut"/>

       </aop:aspect>

    </aop:config>

使用SpringTest测试代码:

//springjunit集成测试

@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration(locations="classpath:applicationContext.xml")

public class SpringTest {

    //注入要测试bean

    @Autowired

    private ICustomerService customerService;

    @Autowired

    private ProductService productService;

   

    //测试

    @Test

    public void test(){

       //基于接口

       customerService.save();

       customerService.find();

       //基于类的

       productService.save();

       productService.find();

    }

 

}

 

测试结果:

和传统的aop配置相比,更灵活,advice不需要实现接口,简单的pojo就可以了;一个通知可以增强多个连接点,一个连接点可以被多次增强。

 

 

【扩展优化】:

1.将切入点放入aspectj标签里面写,同时配置多个通知方法

<!-- 2.配置advice通知增强 -->

    <bean id="myAspectAdvice" class="cn.itcast.spring.c_aspectjaop.MyAspect"/>

   

    <!-- 3:配置aop -->

    <aop:config>

       <!--

           切面:要对哪些方法进行怎样的增强 

           aop:aspect:aspejctj的方式!

           ref:配置通知

       -->

       <aop:aspect ref="myAspectAdvice">

           <aop:pointcut expression="bean(*Service)" id="myPointcut"/>

           <!-- 第一个前置通知 :在访问目标对象方法之前,先执行通知的方法

              method:advice类中的方法名,

                pointcut-ref="myPointcut":注入切入点

                                        目的是让通知关联切入点

           -->

           <aop:before method="firstbefore" pointcut-ref="myPointcut"/>

           <aop:before method="firstbefore2" pointcut-ref="myPointcut"/>

       </aop:aspect>

    </aop:config>

2.配置多个通知方法:

//aspectj的advice通知增强类,无需实现任何接口

public class MyAspect {

   

    //前置通知

    //普通的方法。方法名随便,但也不能太随便,一会要配置

    public void firstbefore(){

       System.out.println("------------第一个前置通知执行了。。。");

    }

   

    public void firstbefore2(){

       System.out.println("------------个前置通知执行了222。。。");

    }

}

 

  1. 执行结果:表示在执行目标对象方法之前执行

AspectJ切面编程,相比于传统的SpringAOP,定义的通知方法更多。

  1.  分析各种通知应用

 Before前置通知

案例应用: 实现权限控制 (即:权限不足的时候,抛出异常)、 记录方法调用信息日志

第一步:配置MyAspect类(切面),配置before方法(通知)

//aspectj的advice通知增强类,无需实现任何接口

public class MyAspect { 

    //前置通知的:方法运行之前增强

    //应用: 权限控制 (权限不足,抛出异常)、 记录方法调用信息日志

    //参数:org.aspectj.lang.JoinPoint

    //参数:连接点对象(方法的包装对象:方法,参数,目标对象)

    public void before(JoinPoint joinPoint){

       //分析:抛出异常拦截的

       //当前登录用户

       String loginName = "Rose";

       System.out.println("方法名称:"+joinPoint.getSignature().getName());

       System.out.println("目标对象:"+joinPoint.getTarget().getClass().getName());

       System.out.println("代理对象:"+joinPoint.getThis().getClass().getName());

       //判断当前用户有没有执行方法权限

       if(joinPoint.getSignature().getName().equals("find")){

           if(!loginName.equals("admin")){

              //只有超级管理员admin有权限,其他人不能执行某个方法,比如查询方法

              throw new RuntimeException("您没有权限执行方法:"+joinPoint.getSignature().getName()+",类型为:"+joinPoint.getTarget().getClass().getName());

           }

       }

      

    }

}

         通过JoinPoint 连接点对象,获取目标对象信息 !

    这里注意:引包不要引错了,使用aspectj中的连接点(org.aspectj.lang.JoinPoint):

 

第二步:Spring容器中配置,配置applicationContext.xml

    <!-- 3:配置aop -->

    <aop:config>

       <aop:aspect ref="myAspectAdvice">

           <aop:pointcut expression="bean(*Service)" id="myPointcut"/>

           <!-- 前置通知 -->

           <aop:before method="before" pointcut-ref="myPointcut" />

       </aop:aspect>

   

    </aop:config>

第三步:使用SpringTest.java进行测试:

//springjunit集成测试

@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration(locations="classpath:applicationContext.xml")

public class SpringTest {

    //注入要测试bean

    @Autowired

    private ICustomerService customerService;

    @Autowired

    private ProductService productService;

   

    //测试

    @Test

    public void test(){

       //基于接口

       customerService.save();

       customerService.find();

       //基于类的

       productService.save();

       productService.find();

    }

 

}

提示:

Aspectj的文档

JoinPoint使用参考文档:资料\jar\aspectj-1.7.3\doc\runtime-api\index.html

提示:和传统aop的对比发现,aspectj更灵活,一个类中可以写N个增强方法,但传统的只能是一个类对应一个方法。

 AfterReturing 后置通知

特点:在目标方法运行后,返回值后执行通知增强代码逻辑。

应用场景:与业务相关的,如网上营业厅查询余额后,自动下发短信功能。

 

分析: 后置通知可以获取到目标方法返回值,如果想对返回值进行操作,使用后置通知(但不能修改目标方法返回 )

第一步:配置MyAspect类(切面),配置afterReturing方法(通知)

//aspectj的advice通知增强类,无需实现任何接口

public class MyAspect { 

    //应用场景:与业务相关的,如网上营业厅查询余额后,自动下发短信。

    //后置通知:会在目标方法执行之后调用通知方法增强。

    //参数1:连接点对象(方法的包装对象:方法,参数,目标对象)

    //参数2:目标方法执行后的返回值,类型是object,“参数名”随便,但也不能太随便,一会要配置

    public void afterReturing(JoinPoint joinPoint,Object returnVal){

       //下发短信:调用运行商的接口,短信猫。。。

       System.out.println("-++++++++-后置通知-当前下发短信的方法"+"-尊敬的用户,您调用的方法返回余额为:"+returnVal);

       //同时可以可以在下发后,记录日志:

       System.out.println("----日志记录,操作的类型:"+joinPoint.getTarget().getClass().getSimpleName()

              +",操作的方法:"+joinPoint.getSignature().getName()

              );

    }

}

 

第二步:Spring容器中配置,配置applicationContext.xml

    <!-- 3:配置aop -->

    <aop:config>

       <aop:aspect ref="myAspectAdvice">

           <aop:pointcut expression="bean(*Service)" id="myPointcut"/>

           <!-- 后置通知

              returning:配置方法中的参数名字,与通知方法的第二个参数的名字,名字必须对应。

              在运行的时候,spring会自动将返回值传入该参数中。

           -->

           <aop:after-returning method="afterReturing" returning="returnVal" pointcut-ref="myPointcut"/>

       </aop:aspect>

   

    </aop:config>

第三步:使用SpringTest.java进行测试:

//springjunit集成测试

@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration(locations="classpath:applicationContext.xml")

public class SpringTest {

    //注入要测试bean

    @Autowired

    private ICustomerService customerService;

    @Autowired

    private ProductService productService;

   

    //测试

    @Test

    public void test(){

       //基于接口

       customerService.save();

       customerService.find();

       //基于类的

       productService.save();

       productService.find();

    }

 

}

 

 Around 环绕通知

特点:目标执行前后,都进行增强(控制目标方法执行)

应用场景:日志、缓存、权限、性能监控、事务管理

增强代码的方法要求:

接受的参数:ProceedingJoinPoint(可执行的连接点)

返回值:Object返回值(即目标对象方法的返回值)

抛出Throwable异常。

【示例】

第一步:配置MyAspect类(切面),配置around方法(通知)

//aspectj的advice通知增强类,无需实现任何接口

public class MyAspect { 

    //应用场景:日志、缓存、权限、性能监控、事务管理

    //环绕通知:在目标对象方法的执行前+后,可以增强

    //参数:可以执行的连接点对象ProcessdingJoinPoint(方法),特点是调用proceed()方法可以随时随地执行目标对象的方法(相当于目标对象的方法执行了)

    //必须抛出一个Throwable

    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{

       //目标:事务的控制:

       //开启事务:

       System.out.println("-----开启了事务。。。。。。。。。");

       //执行了目标对象的方法

       Object resultObject = proceedingJoinPoint.proceed();

        //结束事务

       System.out.println("-----提交了事务。。。。。。。。。");

       return resultObject;//目标对象执行的结果

    }

}

 

第二步:Spring容器中配置,配置applicationContext.xml

    <!-- 3:配置aop -->

    <aop:config>

       <aop:aspect ref="myAspectAdvice">

           <aop:pointcut expression="bean(*Service)" id="myPointcut"/>

           <!-- 环绕通知 -->

           <aop:around method="around" pointcut-ref="myPointcut"/>

       </aop:aspect>

   

    </aop:config>

第三步:使用SpringTest.java进行测试:

//springjunit集成测试

@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration(locations="classpath:applicationContext.xml")

public class SpringTest {

    //注入要测试bean

    @Autowired

    private ICustomerService customerService;

    @Autowired

    private ProductService productService;

   

    //测试

    @Test

    public void test(){

       //基于接口

       customerService.save();

       customerService.find();

       //基于类的

       productService.save();

       productService.find();

    }

 

}

 

 

 AfterThrowing 抛出通知(异常通知)

作用:目标代码出现异常,通知执行。记录异常日志、通知管理员(短信、邮件)

应用场景:处理异常(一般不可预知),记录日志

【示例】

第一步:配置MyAspect类(切面),配置aterThrowing方法(通知)

//aspectj的advice通知增强类,无需实现任何接口

public class MyAspect { 

    //作用:目标代码出现异常,通知执行。记录异常日志、通知管理员(短信、邮件)

    //只有目标对象方法抛出异常通知才会执行

    //参数1:静态连接点(方法对象)

    //参数2:目标方法抛出的异常,参数名随便,但也不能太随便

    public void afterThrowing(JoinPoint joinPoint,Throwable ex){

       //一旦发生异常,发送邮件或者短信给管理员

       System.out.println("++管理员您好,"+joinPoint.getTarget().getClass().getName()+"的方法:"

       +joinPoint.getSignature().getName()+"发生了异常,异常为:"+ex.getMessage());

    }

}

 

第二步:Spring容器中配置,配置applicationContext.xml

<!-- 3:配置aop -->

    <aop:config>

       <aop:aspect ref="myAspectAdvice">

           <aop:pointcut expression="bean(*Service)" id="myPointcut"/>

           <!-- 抛出通知

              throwing:通知中的方法的第二个参数,异常类型的参数的名字,在运行的时候,spring会自动将异常传入该参数中。-->

           <aop:after-throwing method="afterThrowing" throwing="ex" pointcut-ref="myPointcut"/>      

        </aop:aspect>

   

    </aop:config>

第三步:使用SpringTest.java进行测试:

//springjunit集成测试

@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration(locations="classpath:applicationContext.xml")

public class SpringTest {

    //注入要测试bean

    @Autowired

    private ICustomerService customerService;

    @Autowired

    private ProductService productService;

   

    //测试

    @Test

    public void test(){

       //基于接口

       customerService.save();

       customerService.find();

       //基于类的

       productService.save();

       productService.find();

    }

 

}

此时发现通知的方法并没有执行。

那我们在目标对象的方法中故意抛出异常,大家看看效果

测试:

在ProductService.java中save的方法中,制造异常:

//没有接口的类

public class ProductService {

    public void save() {

       System.out.println("商品保存了。。。。。");

       //故意制造异常

       int d = 1/0;

    }

 

    public int find() {

       System.out.println("商品查询数量了。。。。。");

       return 99;

    }

}

查看测试结果:

 After 最终通知

作用:不管目标方法是否发生异常,最终通知都会执行(类似于finally代码功能)

应用场景:释放资源 (关闭文件、 关闭数据库连接、 网络连接、 释放内存对象 )

【示例】

第一步:配置MyAspect类(切面),配置after方法(通知)

//aspectj的advice通知增强类,无需实现任何接口

public class MyAspect { 

    //应用场景:释放资源 (关闭文件、 关闭数据库连接、 网络连接、 释放内存对象 )

    //最终通知:不管是否有异常都会执行

    public void after(JoinPoint joinPoint){

       //释放数据库连接

       System.out.println("数据库的connection被释放了。。。。。,执行的方法是:"+joinPoint.getSignature().getName());

    }

}

 

第二步:Spring容器中配置,配置applicationContext-aspectJ.xml

    <!-- 3:配置aop -->

    <aop:config>

       <aop:aspect ref="myAspectAdvice">

           <aop:pointcut expression="bean(*Service)" id="myPointcut"/>

           <!-- 最终通知 -->

           <aop:after method="after" pointcut-ref="myPointcut"/>

            <!--  以上代码也可以写成:pointcut切入点表达式:只能给一个通知方法来用,相当于省略了<aop:pointcut expression="bean(*Service)" id="myPointcut"/>

           <aop:after method="after" pointcut="bean(*Service)"/>-->

       </aop:aspect>

   

    </aop:config>

第三步:使用SpringTest.java进行测试:

//springjunit集成测试

@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration(locations="classpath:applicationContext.xml")

public class SpringTest {

    //注入要测试bean

    @Autowired

    private ICustomerService customerService;

    @Autowired

    private ProductService productService;

   

    //测试

    @Test

    public void test(){

       //基于接口

       customerService.save();

       customerService.find();

       //基于类的

       productService.save();

       productService.find();

    }

 

}

查看测试结果:

 

五种通知小结:

(1)只要掌握Around(环绕通知)通知类型,就可实现其他四种通知效果。

2)因为你可以在环绕通知的方法中编写如下代码:

         try {

                   //前置通知

                   Object result = proceedingJoinPoint.proceed();

                   //后置通知

}catch(Exception){

         //抛出通知

}finally{

         //最终通知

}

 

各种Advice方法可接收的参数和返回值小结(参考)

方法格式:

public returnType method (param)

public 返回值类型 方法名 (参数类型 参数名)

返回值类型:void和Object

方法名:任意名称(但是也不能太随意)

参数类型:

  * 参数类型为JoinPoint接口类型,返回值类型为void

  * 参数类型为ProceedingJoinPoint接口类型,返回值类型为Object

具体为:

 

通知类型

输入参数(可选)

返回值类型

其他

Before前置通知

JoinPoint(静态连接点信息)

void

 

AfterReturning后置通知

JoinPoint, Object

void

 

Around环绕通知

ProceedingJoinPoint(可执行的连接点信息)

Object

throws Throwable

AfterThrowing抛出通知

JoinPoint, Throwable

void

 

After最终通知

JoinPoint

void

 

 

 

 

总结:

  1. 注解方式装配bean对象
  2. 混合配置
  3. 测试集成 (@runwith  @ContextConfiguration(核心配置))---熟悉
  4. AOP的思想(如何实现),AOP在哪些地方使用?
  5. aop的原理(动态代理:jdk,cglib)
  6. AspectJ 切面编程(xml方式)
  7. 各种通知应用
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值