Spring AOP 详解

痞子三分冷 提交于 2020-02-26 05:57:42

AOP:

AOP(Aspect Oriented Programming),即面向切面编程,可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善。OOP引入封装、继承、多态等概念来建立一种对象层次结构,用于模拟公共行为的一个集合。不过 OOP允许开发者定义纵向的关系,但并不适合定义横向的关系,例如日志功能。日志代码往往横向地散布在所有对象层次中,而与它对应的对象的核心功能毫无关 系对于其他类型的代码,如安全性、异常处理和透明的持续性也都是如此,这种散布在各处的无关的代码被称为横切(cross cutting),在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。

AOP核心概念

1、横切关注点

对哪些方法进行拦截,拦截后怎么处理,这些关注点称之为横切关注点

2、切面(aspect)

类是对物体特征的抽象,切面就是对横切关注点的抽象

3、连接点(joinpoint)

被拦截到的点,因为Spring只支持方法类型的连接点,所以在Spring中连接点指的就是被拦截到的方法,实际上连接点还可以是字段或者构造器

4、切入点(pointcut)

对连接点进行拦截的定义

5、通知(advice)

所谓通知指的就是指拦截到连接点之后要执行的代码,通知分为前置、后置、异常、最终、环绕通知五类

6、目标对象

代理的目标对象

7、织入(weave)

将切面应用到目标对象并导致代理对象创建的过程

8、引入(introduction)

在不修改代码的前提下,引入可以在运行期为类动态地添加一些方法或字段

 

Spring对AOP的支持

 

Spring中AOP代理由Spring的IOC容器负责生成、管理,其依赖关系也由IOC容器负责管理。因此,AOP代理可以直接使用容器中的其它bean实例作为目标,这种关系可由IOC容器的依赖注入提供。Spring创建代理的规则为:

 

1、默认使用Java动态代理来创建AOP代理,这样就可以为任何接口实例创建代理了

 

2、当需要代理的类不是代理接口的时候,Spring会切换为使用CGLIB代理,也可强制使用CGLIB

 

AOP编程其实是很简单的事情,纵观AOP编程,程序员只需要参与三个部分:

 

1、定义普通业务组件

 

2、定义切入点,一个切入点可能横切多个业务组件

 

3、定义增强处理,增强处理就是在AOP框架为普通业务组件织入的处理动作

 

所以进行AOP编程的关键就是定义切入点和定义增强处理,一旦定义了合适的切入点和增强处理,AOP框架将自动生成AOP代理,即:代理对象的方法=增强处理+被代理对象的方法。

 

下面给出一个Spring AOP的.xml文件模板,名字叫做aop.xml,之后的内容都在aop.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"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-4.2.xsd">
            
</beans>

基于Spring的AOP简单实现:

说明一点:使用Spring AOP,要成功运行起代码,只用Spring提供给开发者的jar包是不够的,还需要额外两个jar包:

1、aopalliance.jar

2、aspectjweaver.jar

以下先是用Spring AOP的XML实现方式,

①:先定义一个接口:

package com.gxxy.spring_06aop.xml;
public interface IStudentDAO {
    void save(Student stu);
}

②:定义一个实现类:

 

package com.gxxy.spring_06aop.xml;

public class StudentImp implements IStudentDAO {
    @Override
    public void save(Student stu) {
        System.out.println("正在保存学生" + stu);
        int x= 10/0;
        System.out.println(x);
    }
}

 

③:定义一个事务处理的类(下面的类为伪类,没有完成具体实现);

package com.gxxy.spring_06aop.xml;
public class StudentManager {
    public void begin() {
        System.out.println("StudentManager.begin()");
    }
    public void commit() {
        System.out.println("StudentManager.commit()");
    }
    public void rollback(Throwable e) {
        System.out.println("StudentManager.rollback()");
        System.out.println(e);
    }
    public void finished() {
        System.out.println("StudentManager.finisher()");
    }     
}

④:有这三个类就可以实现一个简单的Spring AOP了,看一下aop.xml的配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop" 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
        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">
<bean id="manager" class="com.gxxy.spring_06aop.xml.StudentManager">
</bean>
<bean id="service" class="com.gxxy.spring_06aop.xml.StudentImp">
</bean>
<!--AOP配置 -->
    <aop:config>
        <!--  aop:pointcut AOP切入点-->
        <aop:pointcut expression="execution(* com.gxxy.spring_06aop.xml.IStudentDAO.*(..))" id="stuService" />
        <!--  aop:aspect   AOP切面配置 -->
        <aop:aspect ref="manager">
            <aop:before method="begin" pointcut-ref="stuService" />
            <aop:after-returning method="commit" pointcut-ref="stuService" />
            <aop:after-throwing method="rollback" pointcut-ref="stuService"  throwing="e"/>
            <!-- <aop:around method="" pointcut-ref="stuService" /> -->
            <aop:after method="finished" pointcut-ref="stuService" />
        </aop:aspect>
    </aop:config>
</beans>

⑤:测试类:

package com.gxxy.spring_06aop.xml;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class AopTest {

    @Autowired
    IStudentDAO service;

    @Test
    public void testAop() {
        service.save(new Student("Jack",18));
    }
}

注意:

Pointcut is not well-formed: expecting 'name pattern' at character position
配置aop报错:原因是配置切点表达式的时候报错了,
星号后面没有加空格:
<aop:config>
  <aop:pointcut id="transactionPointcut" expression="execution(* project.mybatis.service.*.*(..))" />
  <aop:advisor pointcut-ref="transactionPointcut" advice-ref="omsMTransactionAdvice" />
</aop:config>
其中,切入点表达式的使用规则:
execution(* com.gxxy.spring_06aop.xml.IStudentDAO.*(..))
 1、execution(): 表达式主体。
 2、第一个*号:表示返回类型,*号表示所有的类型。
 3、包名:表示需要拦截的包名,后面的两个句点表示当前包和当前包的所有子包,com.sample.service.impl包、子孙包下所有类的方法。
 4、第二个*号:表示类名,*号表示所有的类。
 5、*(..):最后这个星号表示方法名,*号表示所有的方法,后面括弧里面表示方法的参数,两个句点表示任何参数。

 

以下是用Spring AOP的Annotation实现方式

①:定义一个接口,接口和XML方式一样,不变

②:实现类:在类上加注解@service

package com.gxxy.spring_06aop.annotation;
import org.springframework.stereotype.Service;
@Service
public class StudentImp implements IStudentDAO {
    @Override
    public void save(Student stu) {
        System.out.println("正在保存学生" + stu);
        int x=10/0;
        System.out.println(x);
    }
}

③:主要的实现事务类,也就是实现AOP的类;

package com.gxxy.spring_06aop.annotation;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

//AOP: Aspect Oriented Programming
//aspectj: Aspect Java
//Execution表达式: 过滤method
@Component
@Aspect // <aop:aspect ref="manager">
public class StudentManager {
    // <aop:pointcut expression="execution(*
    // com.gxxy.spring_06aop.annotation.IStudentDAO.*(..))" id="service"
    @Pointcut("execution(* com.gxxy.spring_06aop.annotation.IStudentDAO.*(..))")
    public void service() {
    }// 方法的名称就是上面execution表达式的别名

    @Before("service()") // <aop:before method="begin" pointcut-ref="service" />
    // @Before("execution(*
    // com.gxxy.spring_06aop.annotation.IStudentDAO.*(..))")
    public void begin() {
        System.out.println("StudentManager.begin()");
    }

    @AfterReturning("service()") // <aop:after-returning method="commit"
                                    // pointcut-ref="service" />
    public void commit() {
        System.out.println("StudentManager.commit()");
    }

    @AfterThrowing(value = "service()", throwing = "e")
    // <aop:after-throwing method="rollback" pointcut-ref="service"
    // throwing="e"/>
    public void rollback(Throwable e) {
        System.out.println("StudentManager.rollback()");
        System.out.println(e);
    }

    // aop:after method="finished" pointcut-ref="service" />
    @After("service()")
    public void finished() {
        System.out.println("StudentManager.finisher()");
    }

    /**
     * 全部, 这个方法中必须要有一个参数,这个参数能够让整个执行过程连贯起来 ProceedingJoinPoint point
     * 
     * @param point
     */
    // @Around("service()")
    public void all(ProceedingJoinPoint point) {
        try {
            begin();
            point.proceed();// 继续往下执行
            commit();
        } catch (Throwable e) {
            rollback(e);
        } finally {
            finished();
        }
    }
}

④:XML配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop" 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
        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:aspectj-autoproxy/>
    <!-- 用来指定Ioc扫描的包 -->
    <context:component-scan base-package="com.gxxy.spring_06aop.annotation"></context:component-scan>
</beans>
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!