Spring——AOP(1)之Spring1中的配置

标签: Java Spring Spring AOP
35人阅读 评论(0) 收藏 举报
分类:

AOP介绍

AOP(Aspect-Oriented Programming),即面向切面编程,作为面向对象(OOP,Object-Oriented Programming)的一种补充,广泛应用于处理一些具有横切性质的系统级服务,比如安全性检查、日志记录、事务管理等。

在OOP中,我们以类(class)作为基本单元,而AOP中的基本单元是Aspect(切面)

定义AOP术语

  • 1.切面(Aspect)

要实现的交叉功能,是系统模块化的一个切面或领域。如日志记录。

Aspectpointcutadvice组成,它既包含了横切逻辑的定义,也包括了连接点的定义。Spring AOP就是负责实施切面的框架,它将切面所定义的横切逻辑织入到切面所指定的连接点中。

  • 2.连接点(Join Point)

应用程序执行过程中插入切面的地点,可以是方法调用,异常抛出,或者要修改的字段。

在Spring AOP中,Join Point总是方法的执行点,即只有方法连接点。

  • 3.通知(Advice)

切面的实际实现,他通知系统新的行为。如在日志通知包含了实现日志功能的代码,如向日志文件写日志。通知在连接点插入到应用系统中。

许多AOP框架中,包括Spring AOP,会将Advice模拟为一个拦截器(Interceptor),并且在Join Point上维护多个Advice,进行层层拦截。

  • 4.切入点(Point Cut)

定义了通知应该应用在哪些连接点,通知可以应用到AOP框架支持的任何连接点。Advice是和特定的Point Cut关联的,并且在Point Cut相匹配的Join Point中执行。

Spring中,所有的方法都可以认为是Join Point,但是我们并不希望在所有的方法上都添加Advice,而Point Cut的作用就是提供一组规则(可以使用Aspect pointcut expression language来描述)来匹配Join Point,给满足规则的Join Point添加Advice

  • 5.引入(Introduction)

为类添加新方法和属性。Spring AOP 允许我们为目标对象Adviced Object引入新的接口(和对应的实现)。

  • 6.目标对象(Adviced Object)

被通知的对象。既可以是你编写的类,也可以是第三方的类。

  • 7.代理对象(AOP Proxy)

将通知(Advice)应用到目标对象后创建的对象,应用系统的其他部分不用为了支持代理对象而改变。

Spring AOP使用运行时代理的方式来实现aspect。在Spring AOP中,一个AOP代理是一个JDK动态代理对象或者CGLIB代理对象。

  • 8.织入(Weaving)

将切面(Aspect)应用到目标对象从而创建一个新代理对象的过程。织入发生在目标对象生命周期的多个点上:

  • 编译期:切面在目标对象编译时织入,这需要一个特殊的编译器。

  • 类装载期:切面在目标对象被载入JVM时织入,这需要一个特殊的类载入器。

  • 运行期:切面在应用系统运行时织入。

Spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入。

创建通知

本文将通过Spring1的配置方式来讲解相关例子,使用ProxyFactoryBean来创建AOP代理,并且使用Advisor指定需要增加的方法。

主要有四大通知类型,如下表:

通知类型 接口 描述
Around org.aopalliance.intercept.MethodInterceptor 拦截对目标方法调用
Before org.springframework.aop.MethodBeforeAdvice 在目标方法调用前调用
After org.springframework.aop.AfterReturningAdvice 在目标方法调用后调用
Throws org.springframework.aop.ThrowsAdvice 当目标方法抛出异常时调用

注意:异常通知ThrowsAdvice是标识性接口,没有任何方法,但实现该接口的类必须要有如下形式的方法:

void afterThrowing(Throwable throwable);
void afterThrowing(Method m, Object[] objects, Object target, Exception throwable);

第一个方法只接受一个参数:需要抛出的异常。

第二个方法接受异常、被调用的方法、参数以及目标对象。

引入通知

引入通知可以自定义切入点。

自定义切入点可以通过正则表达式匹配。

案例

1.定义接口

两个接口ITestServiceITestService2

public interface ITestService {
    void sayHello();
}

public interface ITestService2 {
    void sayBye();
}

2.编写对象(被代理的对象,也即目标对象)

目标对象具有name属性,并且有sayHello()方法和sayBye()方法。

public class TestService1 implements ITestService, ITestService2 {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public void sayHello() {
        System.out.println("Hello, " + name);
    }

    @Override
    public void sayBye() {
        System.out.println("Bye, " + name);
        try {
            int i = 1 / 0;
        } catch (Exception e) {
        }
    }
}

3.编写通知

这里我们编写上面介绍的4个通知。

public class MyMethodBeforeAdvice implements MethodBeforeAdvice {
    /**
     *
     * @param method 被调用的方法
     * @param objects 给方法传递的参数
     * @param o 被代理的对象,目标对象
     * @throws Throwable
     */
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println("=============");
        System.out.println("记录日志:" + method.getName());
    }
}

public class MyAfterReturningAdvice implements AfterReturningAdvice {

    @Override
    public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
        System.out.println("关闭资源....");
    }
}

public class myMethodInterceptor implements MethodInterceptor {

    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        System.out.println("调用方法前执行!");
        // proceed()真正执行方法,object为返回的对象
        Object object = methodInvocation.proceed();
        System.out.println("方法调用后!");
        return object;
    }
}

public class MyThrowsAdvice implements ThrowsAdvice {

    public void afterThrowing(Method m, Object[] objects, Object target, Exception throwable){
        System.out.println("出大事了!" + throwable.getMessage());
    }
}

4.在beans.xml文件中配置

4.1 先配置被代理对象

4.2 配置通知

4.3 配置代理对象,是ProxyFactoryBean的实例

4.3.1 配置代理接口集

4.3.2 把通知织入到代理对象

4.3.3 指定被代理对象

  • 配置文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--配置被代理的对象-->
    <bean id="testService1" class="com.gavin.aop.TestService1">
        <property name="name" value="Gavin"/>
    </bean>

    <!--配置前置通知-->
    <bean id="myMethodBeforeAdvice" class="com.gavin.aop.MyMethodBeforeAdvice"/>
    <!--配置后置通知-->
    <bean id="myAfterReturningAdvice" class="com.gavin.aop.MyAfterReturningAdvice"/>
    <!--配置环绕通知-->
    <bean id="myMethodInterceptor" class="com.gavin.aop.myMethodInterceptor"/>
    <!--配置异常通知-->
    <bean id="myThrowsAdvice" class="com.gavin.aop.MyThrowsAdvice"/>

    <!--使用Advisor来定义前置通知的切入点,该定义只允许sayHello使用前置通知,sayBye不使用前置通知-->
    <bean id="myMethodBeforeAdviceFilter" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
        <property name="advice" ref="myMethodBeforeAdvice"/>
        <property name="mappedNames">
            <list>
                <value>sayHello</value>
            </list>
        </property>
    </bean>

    <!--配置代理对象-->
    <bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!--代理接口集-->
        <property name="proxyInterfaces">
            <list>
                <value>com.gavin.aop.ITestService</value>
                <value>com.gavin.aop.ITestService2</value>
            </list>
        </property>
        <!--把通知织入到代理对象-->
        <property name="interceptorNames">
            <!--相当于把前置通知和代理对象关联起来-->
            <!--我们也可以把通知看成拦截器-->
            <list>
                <!--使用自定义切入点,来控制前置通知-->
                <value>myMethodBeforeAdviceFilter</value>
                <value>myAfterReturningAdvice</value>
                <value>myMethodInterceptor</value>
                <value>myThrowsAdvice</value>
            </list>
        </property>
        <!--指定被代理对象-->
        <property name="target">
            <ref bean="testService1"/>
        </property>
    </bean>
</beans>

在这里,如果只使用ProxyFactoryBean来配置代理对象的话,会把代理接口中的所有方法都代理了,也就是每个方法都被增强了,如果不想被增强,我们可以通过配置Advisor来指定切入点。这里我们使用了NameMatchMethodPointcutAdvisor,通过指定具体方法名称来指定需要被增强的方法。另外如果方法很多,也可以通过RegexpMethodPointcutAdvisor,该Advisor可以通过正则表达式来匹配要增强的方法。

5.测试类:

public class App1 {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("com/gavin/aop/beans.xml");
        ITestService iTestService = (ITestService) applicationContext.getBean("proxyFactoryBean");
        iTestService.sayHello();
        System.out.println();
        ((ITestService2)iTestService).sayBye();
    }
}

6.输出

这里写图片描述

可以看到,TestService中的sayHello()sayBye()方法都被增强了。其中由于我们对前置通知配置了Advisor指定了只有sayHello()方法织入了前置通知,所以sayHello()方法记录了日志,而sayBye()方法没有。

sayBye()方法中,由于我们捕获并处理了异常,所以程序的执行过程中并没有遇到异常报错,异常通知也就没有被调用。如果在sayBye()方法中,我们对int i = 1 / 0;这句话不处理异常,异常在执行的时候就会被抛出,异常通知就会被调用。


本文使用了Spring1中配置AOP的方式进行配置,但是这里有一个问题,当我们的业务增多了之后,ProxyFactoryBean的配置也会增多,导致xml迅速变得很臃肿。

后面会介绍AspectJ框架,以及Spring2以后的版本中加入的使用@AspectJ的方式进行配置的方式。要知道的是Spring只是引入了@AspectJ方式配置,但其底层实现AOP的方式仍是通过代理的方式,不同于AspectJ框架。

查看评论

Python 数据挖掘与机器学习进阶实训-1

-
  • 1970年01月01日 08:00

spring aop 1

前两天,在给新入职的同事做技术介绍时,讲到spring的AOP。使我又一次认识到,对于AOP,特别是spring AOP的理解,虽然大家都能说上来几句,但是许多人认识并不太全面,甚至可以说是一知半解-...
  • henryzhang2009
  • henryzhang2009
  • 2016-01-14 14:28:46
  • 286

spring+hessian

Spring 集成hessian hessian作为一个基于rpc的接口服务,使用起来特别方便。在之前都是使用webservice或者http+json进行数据传输。但是webservice传输过程都...
  • zhenshibendan
  • zhenshibendan
  • 2015-12-29 15:04:44
  • 526

Spring的AOP基于xml常用的几种配置

一般spring的AOP实现方式有二中 1、spring-aop自己实现与IOC结合使用,也是我们最常用的方式 2、使用AspectJ,即注解的方式 个人觉得,spring中使用配置文件还是比较...
  • u011955252
  • u011955252
  • 2016-10-27 14:10:05
  • 1444

Spring -- 基于XML的AOP通知配置

上一篇博客,我们学习了基于注解配置AOP。下面我们基于XML来配置AOP。看代码public interface Calculation { public int add(int x, int ...
  • u010926176
  • u010926176
  • 2015-07-30 16:06:18
  • 1342

Spring AOP配置选项

Spring实现动态代理配置是有两种配置文件:1、   xml文件方式;2、   annotation方式(使用AspectJ类库实现的。)一、       AOP配置annotation方式(一) ...
  • voyage_mh1987
  • voyage_mh1987
  • 2010-08-18 15:39:00
  • 9153

Spring Aop实例之xml配置

上篇博客《3幅图让你了解Spring AOP》中介绍了aop通知类型,AOP的配置方式有2种方式:xml配置和AspectJ注解方式。今天我们就来实践一下xml配置方式。...
  • xiaoxian8023
  • xiaoxian8023
  • 2013-12-19 08:49:40
  • 37607

Spring AOP面向切面编程详解(基于XML方式 注解方式 注入Aspectj方式)

AOP:即面向切面编程,是一种编程思想,OOP的延续。在程序开发中主要用来解决一些系统层面上的问题,比如日志,事务,权限等等。耐心看完本篇文章,会对理解AOP有一定的帮助。1.了解AOP相关术语(如有...
  • RobertoHuang
  • RobertoHuang
  • 2017-04-12 22:38:40
  • 9872

利用spring整合jersey和Protobuf,搭建REST web服务

  • 2012年05月24日 17:24
  • 15.62MB
  • 下载

Spring MVC和Spring配置AOP

1.首先Spring MVC和Spring是两个东西,配置文件是分开的,配置文件的加载也是分开的。所以要想给Spring MVC的Controller添加AOP的话,得在Spring MVC的配置文件...
  • chen517611641
  • chen517611641
  • 2016-01-09 12:48:56
  • 3412
    个人资料
    专栏达人 持之以恒
    等级:
    访问量: 48万+
    积分: 6840
    排名: 4288
    GitHub
    博客专栏
    最新评论