美文网首页
Spring学习 一 AOP

Spring学习 一 AOP

作者: Mon7ey | 来源:发表于2018-04-19 15:46 被阅读6次
什么是AOP?

面向切面编程 (AOP是Aspect Oriented Program的字母首写)。

意为:在程序运行时,动态的将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程。

一般而言,我们管切入到指定类指定方法的代码片段称为切面,而切入到哪些类、哪些方法则叫切入点。有了AOP,我们就可以把几个类共有的代码,抽取到一个切片中,等到需要时再切入对象中去,从而改变其原有的行为。

这样看来,AOP其实只是OOP的补充而已。OOP从横向上区分出一个个的类来,而AOP则从纵向上向对象中加入特定的代码。有了AOP,OOP变得立体了。如果加上时间维度,AOP使OOP由原来的二维变为三维了,由平面变成立体了。从技术上来说,AOP基本上是通过代理机制实现的。

AOP在编程历史上可以说是里程碑式的,对OOP编程是一种十分有益的补充。

在Spring中使用AOP功能

Spring中集成了AOP功能,如果我们要使用AOP功能,只需要导入相应的jar包、并做相应配置即可使用。 下面我们简单的使用一下Spring中的AOP功能。

1. 引入aop相关的jar文件
spring-aop.4.3.2.RELEASE.jar    // spring源码中可以找到
aopalliance.jar              // aopalliance 可在网上下载
aspectjweaver.jar            //  aspectj 源码
aspectjrt.jar                //  aspectj 源码
2. 命名空间

在spring的配置文件中需要引入和aop相关的命名空间

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

分别是上面三条。下面是配置文件的完整约束:

<?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:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    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.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
         http://www.springframework.org/schema/tx
         http://www.springframework.org/schema/tx/spring-tx.xsd">

</beans>     
3. 开启aop注解

在配置文件中加入下方的配置,表示开启aop功能

 <aop:aspectj-autoproxy ></aop:aspectj-autoproxy>
4. 使用aop注解

**1. 我们再IOC容器中声明需要加入容器的类。UserDao、Aop。两个类的内容如下:

UserDao :

public class UserDao implements IUserDao {

    @Override
    public void save() {
        System.out.println("------业务方法 : 数据存储完毕------");
    }
}

Aop :

@Aspect
public class Aop {
    
    @Before("execution(* com.mon7ey.proxy.dao.impl.UserDao.*(..))")
    public void before(){
        System.out.println("业务逻辑执行之前做一些羞羞的事情!");
    }
    
    @After("execution(* com.mon7ey.proxy.dao.impl.UserDao.*(..))")
    public void after(){
        System.out.println("业务逻辑执行之后抽一根完事烟。!");
    }
}

@Aspect:被这个注解修饰的类将会被Spring视为 “切面类”

@Before:这个注解用来修饰 “切面类”中的方法。这些方法我们通常叫它 “切入点”。被这个注解修饰的方法表示该方法会在核心方法执行之前执行。

@After:含义和@Before注解相同。被这个注解修饰的方法表示在核心方法执行之后执行。

"execution(* com.mon7ey.proxy.dao.impl.UserDao.*(..))":注解括号内的字符串叫做“切入点表达式”。用来指定哪些类的哪些方法需要在执行时进行扩展 一一 即为哪些类生成代理对象。表达式的具体含义我们会在后面细说,这里只是大致了解一下。

2. 切面类编写完成后,我们在IOC容器中配置这两个类:

<bean id="userDao" class="com.mon7ey.proxy.dao.impl.UserDao"></bean>
<bean id="aop" class="com.mon7ey.proxy.aop.Aop"></bean>

做完这些配置后,我们就已经完成了aop的中代码的植入了。下面写个测试方法测试一下。

@Test
public void testAop() throws Exception {
    ApplicationContext ac = new ClassPathXmlApplicationContext("com/mon7ey/proxy/bean.xml");

// IUserDao proxy = (IUserDao) ac.getBean("proxyUserDao");
IUserDao proxy = (IUserDao) ac.getBean("userDao");
proxy.save();
}

测试结果
常用的AOP注解

上面我们使用了@Before@After 注解,下面我们介绍下其他注解

@Before() 前置通知:在被拦截的方法之前执行。
@After() 后置通知:在被拦截的方法执行之后执行。(始终执行)。
@AfterReturning() 返回后通知 :在方法执行完成后执行,如果被拦截的方法抛出异常则不执行。
@AfterThrowing() 异常通知:被拦截的方法内发生异常时执行。
@Around() 环绕通知: 环绕目标方法通知,类似于前置 + 后置
@Pointcut("execution(* cn.itcast.e_aop_anno.*.*(..))") 指定切入点表达式

下面我们用代码演示下@AfterReturning、@AfterThrowing()、@Around()、@Pointcut("execution(* cn.itcast.e_aop_anno.*.*(..))")的具体使用。我们还是以上面的代码为例,使用UserDao和Aop类做演示。


@AfterReturning(): 返回后通知

在Aop类中添加方法afterReturning(),并在方法上添加@AfterReturning()注解

@AfterReturning("execution(* com.mon7ey.proxy.dao.impl.UserDao.*(..))")
public void afterReturning(){
    System.out.println("afterReturning: 返回后通知!");
}

运行测试方法查看结果:


image.png

从运行结果中我们看到,返回后通知会在业务方法执行完成后执行,类似于After注解的效果。但是我们前面说过@AfterReturning()是在被拦截方法“执行完成”后执行,如果被拦截方法中出现错误导致方法未执行完成呢?

我们再被拦截方法中加入一句代码,让它报错,然后再看下结果

public class UserDao implements IUserDao {

//  @Override
    public void save() {
        System.out.println("------业务方法 : 数据存储完毕------");
        // 出错代码
        int i = 1 / 0;
    }
}

任何数都不能除以0,所以核心代码是不会执行完成。运行测试方法查看结果:

image.png

这时我们看到,被拦截的方法没有执行完成,被@AfterReturning修饰的方法是不会运行,但是@After依然会执行,这就是他们二者的区别。


@AfterThrowing():异常通知

在Aop类中创建afterThrowing方法,并用@AfterThrowing()修饰。 依然使用上面存在错误的UserDao进行测试

@AfterThrowing("execution(* com.mon7ey.proxy.dao.impl.UserDao.*(..))")
public void afterThrowing(){
    System.out.println("@AfterThrowing: 异常通知。");
}

测试结果:

AfterThrowing

可以看到,被拦截的方法报错。被@AfterThrowing修饰的方法执行了


@Around(): 环绕通知

在Aop类中创建around方法。并用注解修饰

@Around("execution(* com.mon7ey.proxy.dao.impl.UserDao.*(..))")
public void around(ProceedingJoinPoint pjp) throws Throwable{
    System.out.println("环绕前....");
    pjp.proceed();  // 执行目标方法
    System.out.println("环绕后....");
}

运行结果:

Around

@Pointcut("execution(* cn.itcast.e_aop_anno.*.*(..))") 指定切入点表达式

如果每个切入方法要我们写一遍切入点表达式,那么在方法很多的请款,就会很痛苦,而且切入点表达式是由字符串组成的,如果不小心写错了。字符串异常排除是非常麻烦。@Pointcut()注解就是为了解决这个问题的

我们用@Before注解和@Pointcut注解配合使用。

@Pointcut("execution(* cn.itcast.e_aop_anno.*.*(..))")
public void pointCut_(){
}

// 前置通知 : 在执行目标方法之前执行
@Before("pointCut_()")
public void begin(){
    System.out.println("开始事务/异常");
}

这样写的结果。单独在@Before()中写入切入点表达式相同

被@Pointcut修饰的方法的方法名可以随意写。其他方法想要是用被@Pointcut修饰的方法上的切入点表达式时,只需在响应的注解中填入对应的方法名即可。

通过配置文件使用AOP功能

这里直接贴上配置文件,注释写的很详细:

<?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:p="http://www.springframework.org/schema/p"
    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">
    
    <!-- dao 实例 -->
    <bean id="userDao" class="cn.itcast.f_aop_xml.UserDao"></bean>
    <bean id="orderDao" class="cn.itcast.f_aop_xml.OrderDao"></bean>
    
    <!-- 切面类 -->
    <bean id="aop" class="cn.itcast.f_aop_xml.Aop"></bean>
    
    <!-- Aop配置 -->
    <aop:config>
        <!-- 定义一个切入点表达式: 拦截哪些方法 -->
        <aop:pointcut expression="execution(* cn.itcast.f_aop_xml.*.*(..))" id="pt"/>
        <!-- 切面 -->
        <aop:aspect ref="aop">
            <!-- 环绕通知 -->
            <aop:around method="around" pointcut-ref="pt"/>
            <!-- 前置通知: 在目标方法调用前执行 -->
            <aop:before method="begin" pointcut-ref="pt"/>
            <!-- 后置通知: -->
            <aop:after method="after" pointcut-ref="pt"/>
            <!-- 返回后通知 -->
            <aop:after-returning method="afterReturning" pointcut-ref="pt"/>
            <!-- 异常通知 -->
            <aop:after-throwing method="afterThrowing" pointcut-ref="pt"/>
            
        </aop:aspect>
    </aop:config>
</beans>      

相关文章

网友评论

      本文标题:Spring学习 一 AOP

      本文链接:https://www.haomeiwen.com/subject/cbtbkftx.html