AOP编程,面向切面编程实例

Spring两大底层机制,一个是IOC,控制反转,用于减低计算机代码之间的耦合度。最常见的方式是依赖注入(Dependency Injection,简称DI)。另一个则是AOP,面向切面编程。

什么是面向切面编程AOP?

在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

作用:

对程序进行增强:不修改源码的情况下,AOP可以进行权限的校验,日志记录,性能监控,事务控制

Spring底层的AOP原理:

动态代理

JDK动态代理: 面向接口的,只能对实现了接口的类产生代理
Cglib动态代理(类似于JavaSsit第三方代理技术):对没有实现接口的类产生代理对象(生成子类对象)

类实现了接口,Spring就用JDK动态代理,没有实现接口的,用Cglib动态代理,Spring底层可以自动切换

Spring的AOP开发的相关术语

1.通知(Advice)
  就是你想要的功能,也就是上面说的 安全,事物,日志等。你给先定义好把,然后在想用的地方用一下。
2.连接点(JoinPoint)
   spring允许你使用通知的地方,基本每个方法的前,后(两者都有也行),或抛出异常时都可以是连接点,spring只支持方法连接点.和方法有关的前后(抛出异常),都是连接点。
3.切入点(Pointcut)
  上面说的连接点的基础上,来定义切入点,你的一个类里,有5个方法,那就有5个连接点了,但是你并不想在所有方法附近都使用通知(使用叫织入),你只想让其中的几个,在调用这几个方法之前,之后或者抛出异常时干点什么,那么就用切点来定义这几个方法,让切点来筛选连接点,选中那几个你想要的方法。
4.切面(Aspect)
  切面是通知和切入点的结合。通知说明了干什么和什么时候干(什么时候通过方法名中的before,after,around等就能知道),而切入点说明了在哪干(指定到底是哪个方法),这就是一个完整的切面定义。
5.引入(introduction)
  允许我们向现有的类添加新方法属性。就是把切面(也就是新方法属性:通知定义的)用到目标类中
6.目标(target)
  引入中所提到的目标类,也就是要被通知的对象,也就是真正的业务逻辑,他可以在毫不知情的情况下,被咱们织入切面。而自己专注于业务本身的逻辑。
7.代理(proxy)
  怎么实现整套aop机制的. 就是通过代理.织入增强功能后产生的代理对象
8.织入(weaving)
把切面应用到目标对象来创建新的代理对象的过程。有3种方式,spring采用的是运行时。
图解:
在这里插入图片描述

编写我的第一个切面类

Spring的AOP开发入门(基于XML开发)

▪ 引入基本开发包
▪ 引入AOP开发的相关包

第一个aop联盟
第二个aspectj的依赖
第三个AOP核心包
第四个Spring与aspects整合的包

引入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.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> 
</beans>

配置web项目

编写一个切面类

配置切面类

<!-- 配置aop切面 -->
		<aop:config>  
		<!-- 切入点:   指定要被增强哪个类里面哪个方法;*:表示任何的修饰符或返回值类型 ; ..表示匹配save()方法中里面可以有任意参数 -->
			<aop:pointcut expression="execution(* com.spring.service.UserServiceImpl.save(..))" id="p1"/> 
			<!-- 配置切面 -->
			<aop:aspect ref="myAspectj">  
			 <!--   把check方法的权限校验功能织入切面(目标类的方法之前) -->
				<aop:before method="check" pointcut-ref="p1"/> 
			</aop:aspect>
		</aop:config>
<?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.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- bean definitions here -->
   <!-- 把要增强的目标交给Spring管理-->
    <bean id="userServiceImpl" class="com.spring.service.impl.UserServiceImpl"></bean>
    <!--把通知所在的类也交给Spring管理-->
    <!-- 配置aop切面 -->
    <bean id="myAspectj" class="com.spring.aspectj.MyAspectj"></bean>
    <aop:config>
        <!-- 切入点:   指定要被增强哪个类里面哪个方法;*:表示任何的修饰符或返回值类型 ; ..表示匹配add()方法中里面可以有任意参数 -->
        <aop:pointcut expression="execution(* com.spring.service.UserService.add(..))" id="p1"/>
        <!-- 配置切面 -->
        <aop:aspect ref="myAspectj">
            <!--   把check通知的织入切入点p1(目标类的方法之前) -->
            <aop:before method="check" pointcut-ref="p1"/>
        </aop:aspect>
    </aop:config>
</beans>

通知的类型
▪ 前置通知before:在目标方法执行之前的操作
可以获取切入点的信息
▪ 后置通知after-returning:在目标方法执行之后的操作
获得目标方法的返回值
▪ 环绕通知around:在目标方法执行之前和之后进行操作
▪ 异常抛出通知after-throwing:在目标方法抛出异常时的操作(比如出现异常后进行回滚)。
▪ 最终通知after:无论是否有异常都会执行的,相当于try-catch-finally中的finally块

<?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.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- bean definitions here -->
   <!-- 把要增强的目标交给Spring管理-->
    <bean id="userServiceImpl" class="com.spring.service.impl.UserServiceImpl"></bean>
    <!--把通知所在的类也交给Spring管理-->
    <!-- 配置aop切面 -->
    <bean id="myAspectj" class="com.spring.aspectj.MyAspectj"></bean>
    <aop:config>
        <!-- 切入点:   指定要被增强哪个类里面哪个方法;*:表示任何的修饰符或返回值类型 ; ..表示匹配add()方法中里面可以有任意参数 -->
        <aop:pointcut expression="execution(* com.spring.service.UserService.add(..))" id="p1"/>
        <aop:pointcut expression="execution(* com.spring.service.UserService.select(..))" id="p2"/>
        <aop:pointcut expression="execution(* com.spring.service.UserService.selectReturn(..))" id="p3"/>
        <aop:pointcut expression="execution(* com.spring.service.UserService.update(..))" id="p4"/>
        <aop:pointcut expression="execution(* com.spring.service.UserService.delete(..))" id="p5"/>
        <aop:pointcut expression="execution(* com.spring.service.UserService.deleteAfter(..))" id="p6"/>
        <!-- 配置切面 -->
        <aop:aspect ref="myAspectj">
            <!--   把check通知的织入切入点p1(目标类的方法之前) -->
            <aop:before method="check" pointcut-ref="p1"/>
            <aop:after-returning method="back" pointcut-ref="p2"/>
            <aop:after-returning method="backReturn" pointcut-ref="p3" returning="obj"/>  <!--obj名字取MyAspectj类中的backReturn(Object obj)-->
            <aop:around method="around" pointcut-ref="p4"/>

            <aop:after-throwing method="doException" pointcut-ref="p5"/>
            <aop:after method="doAfter" pointcut-ref="p6"/>
        </aop:aspect>
    </aop:config>
</beans>
package com.spring.aspectj;

import org.aspectj.lang.ProceedingJoinPoint;

/**
 * 这是做增强功能的类
 */
public class MyAspectj {
    public void check(){   //通知
        System.out.println("--校验身份--");
    }

    public void back(){
        System.out.println("--之后执行--");
    }
    public void backReturn(Object obj){
        System.out.println("--之后执行,返回的数据:"+(String)obj);
    }

    public void around(ProceedingJoinPoint point) throws Throwable {  //point用来接收切入点update()
        System.out.println("--之前操作--");
        point.proceed();  //执行跌入点
        System.out.println("--之后操作--");
    }

    public void doException(){  //被增强的切入点报异常之后会执行,不报异常不会执行
        System.out.println("--异常抛出了--");
    }
    public void doAfter(){
        System.out.println("--不管有没有异常始终都会执行--");
    }
}

使用场景:
前置通知:转账之前校验用户名和密码及余额

切入点表达式语法
基于execution的函数完成的
语法
execution([访问修饰符] 方法返回值 包名.类名.方法名(参数))
com.spring.service.UserServiceImpl.add(…)
com.spring.service.UserServiceImpl.(…) 开发中用的最多的是这种,对当前类下所有的方法做增强处理(场景:事务处理)
com.spring.service.impl.save(…)
com.spring.service.impl.
(…) 表示: com.spring.service.impl类下所有方法被增强

  • ..service.impl.add(…)没有包可以用*代替
  • com.spring..(…)表示com.spring包下所有的类,所有方法都被增强
Spring的AOP基于注解的开发

Spring注解的通知类型
开启注解的aop开发
aop:aspectj-autoproxy</aop:aspectj-autoproxy>

前置通知:@Before
后置通知:@AfterReturning(value=“execution()”,returning=“result”)

环绕通知:@Around
异常抛出通知:@AfterThrowing(value=“execution()”,throwing=“e”)
最终通知:@After

package com.spring.aspectj;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.beans.factory.annotation.Value;

/**
 * 这是做增强功能的类
 */
@Aspect  //注解通知类,告知Spring这是通知所在的类
public class MyAspectj {
    @Before(value = "execution(* com.spring.service.UserService.add(..))")
    public void check(){   //通知
        System.out.println("--校验身份--");
    }

    @AfterReturning(value = "execution(* com.spring.service.UserService.select(..))")
    public void back(){
        System.out.println("--之后执行--");
    }

    @AfterReturning(value = "execution(* com.spring.service.UserService.selectReturn(..))",returning = "obj")
    public void backReturn(Object obj){
        System.out.println("--之后执行,返回的数据:"+(String)obj);
    }

    @Around(value = "execution(* com.spring.service.UserService.update(..))")
    public void around(ProceedingJoinPoint point) throws Throwable {  //point用来接收切入点update()
        System.out.println("--之前操作--");
        point.proceed();  //执行跌入点
        System.out.println("--之后操作--");
    }

    @AfterThrowing(value = "execution(* com.spring.service.UserService.delete(..))",throwing = "e")  //e可以接收切入点抛出的异常
    public void doException(Exception e){  //被增强的切入点报异常之后会执行,不报异常不会执行
        System.out.println("--异常抛出了--");
        System.out.println(e.toString());
    }

    @After(value = "execution(* com.spring.service.UserService.deleteAfter(..))")
    public void doAfter(){
        System.out.println("--不管有没有异常始终都会执行--");
    }
}

配置文件:

<?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.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- bean definitions here -->
    <!--开启AOP注解开发-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

    <!-- 把要增强的目标交给Spring管理-->
    <bean id="userServiceImpl" class="com.spring.service.impl.UserServiceImpl"></bean>
    <!--把通知所在的类也交给Spring管理-->
    <!-- 配置aop切面 -->
    <bean id="myAspectj" class="com.spring.aspectj.MyAspectj"></bean>

</beans>

目标类没有实现接口,Spring底层会自动使用Cglib的代理
目标类写了接口,Sping底层会使用jdk的动态代理

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值