开涛spring3(6.2) - AOP 之 6.2 AOP的HelloWorld

6.2.1  准备环境

首先准备开发需要的jar包

 

org.springframework.aop-3.0.5.RELEASE.jar

com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar

com.springsource.org.aopalliance-1.0.0.jar

com.springsource.net.sf.cglib-2.2.0.jar

目前,项目里包是

6.2.2  定义目标类

1)定义目标接口:

package lqy.springh6_2;

public interface IHelloWorldService {
    public void sayHello();
}  

2)定义目标接口实现:

package lqy.springh6_2;

public class HelloWorldService implements IHelloWorldService {  

    public void sayHello() {
        System.out.println("============Hello World!");
    }
}

注: 在日常开发中最后将业务逻辑定义在一个专门的service包下,而实现定义在service包下的impl包中,服务接口以IXXXService形 式,而服务实现就是XXXService,这就是规约设计,见名知义。当然可以使用公司内部更好的形式,只要大家都好理解就可以了。

6.2.2  定义切面支持类

有了目标类,该定义切面了,切面就是通知和切入点的组合,而切面是通过配置方式定义的,因此这定义切面前,我们需要定义切面支持类,切面支持类提供了通知实现:

package lqy.springh6_2;

public class HelloWorldAspect {
    //前置通知
 public void beforeAdvice() {
     System.out.println("===========before advice");
}
//后置最终通知
 public void afterFinallyAdvice() {
     System.out.println("===========after finally advice");
 }
}

此处HelloWorldAspect类不是真正的切面实现,只是定义了通知实现的类,在此我们可以把它看作就是缺少了切入点的切面。

注:对于AOP相关类最后专门放到一个包下,如“aop”包,因为AOP是动态织入的,所以如果某个目标类被AOP拦截了并应用了通知,可能很难发现这个通知实现在哪个包里,因此推荐使用规约命名,方便以后维护人员查找相应的AOP实现。

6.2.3  在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: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">  

           <bean id="helloWorldService"  class="lqy.springh6_2.HelloWorldService"/>  

<bean id="aspect" class="lqy.springh6_2.HelloWorldAspect"/>
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* lqy.springh6_2..*.*(..))"/>
    <aop:aspect ref="aspect">
        <aop:before pointcut-ref="pointcut" method="beforeAdvice"/>
        <aop:after pointcut="execution(* lqy.springh6_2..*.*(..))" method="afterFinallyAdvice"/>
    </aop:aspect>
</aop:config> 

</beans>  

切入点使用<aop:config>标签下的<aop:pointcut>配置,expression属性用于定义切入点模式, 默认是AspectJ语法,“execution(* lqy.springh6_2..*.*(..))”表示匹配 lqy.springh6_2包及子包下的任何方法执行。

切面使用<aop:config>标签下的<aop:aspect>标签配置,其中“ref”用来引用切面支持类的方法。

前置通知使用<aop:aspect>标签下的<aop:before>标签来定义,pointcut-ref属性用于引用切入点Bean,而method用来引用切面通知实现类中的方法,该方法就是通知实现,即在目标类方法执行之前调用的方法。

最 终通知使用<aop:aspect>标签下的<aop:after >标签来定义,切入点除了使用pointcut-ref属性来引用已经存在的切入点,也可以使用pointcut属性来定义,如 pointcut="execution(* lqy.springh6_2..*.*(..))",method属性同样是指定通知实现,即在目标类方法执行之后调用的方法。

6.2.4    运行测试

测试类非常简单,调用被代理Bean跟调用普通Bean完全一样,Spring AOP将为目标对象创建AOP代理,具体测试代码如下:

package lqy.springh6_2;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class test1 {

    /**
     * @param args
     */
    public static void main(String[] args) {
        ApplicationContext ctx =  new ClassPathXmlApplicationContext("lqy/springh6_2/springh6_2.xml");
        IHelloWorldService helloworldService =  ctx.getBean("helloWorldService", IHelloWorldService.class);
        helloworldService.sayHello();  

    }

}

该测试将输出如下如下内容:



番外1:入出现错误

原来:

execution(*lqy.springh6_2..*.*(..))

execution(* lqy.springh6_2..*.*(..))

仔细看

* lqy之间的空格不能少。



从输出我们可以看出:前置通知在切入点选择的连接点(方法)之前允许,而后置通知将在连接点(方法)之后执行,具体生成AOP代理及执行过程如图6-4所示。

时间: 2024-10-17 06:42:01

开涛spring3(6.2) - AOP 之 6.2 AOP的HelloWorld的相关文章

开涛spring3(6.1) - AOP 之 6.1 AOP基础

6.1.1  AOP是什么 考虑这样一个问题:需要对系统中的某些业务做日志记录,比如支付系统中的支付业务需要记录支付相关日志,对于支付系统可能相当复杂,比如可能有自己的支付系统,也可能引入第三方支付平台,面对这样的支付系统该如何解决呢? 传统解决方: 1)日志部分提前公共类LogUtils,定义“longPayBegin”方法用于记录支付开始日志,“logPayEnd”用于记录支付结果: 2)支付部分,定义IPayService接口并定义支付方法“pay”,并定义了两个实现:“PointPayS

开涛spring3(6.4) - AOP 之 6.4 基于@AspectJ的AOP

Spring除了支持Schema方式配置AOP,还支持注解方式:使用@AspectJ风格的切面声明. 6.4.1  启用对@AspectJ的支持 Spring默认不支持@AspectJ风格的切面声明,为了支持需要使用如下配置: <aop:aspectj-autoproxy/> 这样Spring就能发现@AspectJ风格的切面并且将切面应用到目标对象. 6.4.2  声明切面 @AspectJ风格的声明切面非常简单,使用@Aspect注解进行声明: @Aspect() Public class

开涛spring3(6.9) - AOP 之 6.9 代理机制

Spring AOP通过代理模式实现,目前支持两种代理:JDK动态代理.CGLIB代理来创建AOP代理,Spring建议优先使用JDK动态代理. JDK动态代理:使用java.lang.reflect.Proxy动态代理实现,即提取目标对象的接口,然后对接口创建AOP代理. CGLIB代理:CGLIB代理不仅能进行接口代理,也能进行类代理,CGLIB代理需要注意以下问题: 不能通知final方法,因为final方法不能被覆盖(CGLIB通过生成子类来创建代理). 会产生两次构造器调用,第一次是目

开涛spring3(6.3) - AOP 之 6.3 基于Schema的AOP

6.3  基于Schema的AOP 基于Schema的AOP从Spring2.0之后通过“aop”命名空间来定义切面.切入点及声明通知. 在Spring配置文件中,所以AOP相关定义必须放在<aop:config>标签下,该标签下可以 有<aop:pointcut>.<aop:advisor>.<aop:aspect>标签,配置顺序不可变. <aop:pointcut>:用来定义切入点,该切入点可以重用: <aop:advisor>:

开涛spring3(1) - Spring概述

1.1.1  Spring是什么 Spring是一个开源的轻量级Java SE(Java 标准版本)/Java EE(Java 企业版本)开发应用框架,其目的是用于简化企业级应用程序开发.应用程序是由一组相互协作的对象组成.而在传统应用程序开发中,一个完整的应用是由一组相 互协作的对象组成.所以开发一个应用除了要开发业务逻辑之外,最多的是关注如何使这些对象协作来完成所需功能,而且要低耦合.高内聚.业务逻辑开发是不可 避免的,那如果有个框架出来帮我们来创建对象及管理这些对象之间的依赖关系.可能有人

开涛spring3(6.6) - AOP 之 6.6 通知参数

前边章节已经介绍了声明通知,但如果想获取被被通知方法参数并传递给通知方法,该如何实现呢?接下来我们将介绍两种获取通知参数的方式. 使用JoinPoint获取:Spring AOP提供使用org.aspectj.lang.JoinPoint类型获取连接点数据,任何通知方法的第一个参数都可以是JoinPoint(环绕通 知是ProceedingJoinPoint,JoinPoint子类),当然第一个参数位置也可以是JoinPoint.StaticPart类型,这 个只返回连接点的静态部分. 1) J

开涛spring3(6.8) - AOP 之 6.8 切面实例化模型

所谓切面实例化模型指何时实例化切面. Spring AOP支持AspectJ的singleton.perthis.pertarget实例化模型(目前不支持percflow.percflowbelow 和pertypewithin). singleton:即切面只会有一个实例: perthis:每个切入点表达式匹配的连接点对应的AOP对象都会创建一个新切面实例: pertarget:每个切入点表达式匹配的连接点对应的目标对象都会创建一个新的切面实例: 默认是singleton实例化模型,Schem

开涛spring3(6.7) - AOP 之 6.7 通知顺序

如果我们有多个通知想要在同一连接点执行,那执行顺序如何确定呢?Spring AOP使用AspectJ的优先级规则来确定通知执行顺序.总共有两种情况:同一切面中通知执行顺序.不同切面中的通知执行顺序. 首先让我们看下 1) 同一切面中通知执行顺序:如图6-6所示. 图6-6 同一切面中的通知执行顺序 而如果在同一切面中定义两个相同类型通知(如同是前置通知或环绕通知(proceed之前))并在同一连接点执行时,其执行顺序是未知的,如果确实需要指定执行顺序需要将通知重构到两个切面,然后定义切面的执行顺

开涛spring3(12.3) - 零配置 之 12.3 注解实现Bean定义

12.3  注解实现Bean定义 12.3.1  概述 前边介绍的Bean定义全是基于XML方式定义配置元数据,且在[12.2注解实现Bean依赖注入]一节中介绍了通过注解来减少配置数量,但并没有完全消除在XML配置文件中的Bean定义,因此有没有方式完全消除XML配置Bean定义呢? Spring提供通过扫描类路径中的特殊注解类来自动注册 Bean定义.同注解驱动事务一样需要开启自动扫描并注册Bean定义支持,使用方式如下(resources/chapter12/ componentDefin