9. Spring之AOP概述

Spring之AOP概述

  1. AOP(Aspect-Oriented Programming,面向切面编程):是一种新的方法论,是对传统 OOP(Object-Oriented Programming,面向对象编程)的补充。
  2. AOP编程操作的主要对象是切面(aspect),而切面模块化横切关注点
  3. 在应用AOP编程时,仍然需要定义公共功能,但可以明确的定义这个功能应用在哪里,以什么方式应用,并且不必修改受影响的类。这样一来横切关注点就被模块化到特殊的类里——这样的类我们通常称之为“切面”。
  4. AOP的好处:
    1. 每个事物逻辑位于一个位置,代码不分散,便于维护和升级
    2. 业务模块更简洁,只包含核心业务代码
1. AOP专业术语

在这里插入图片描述

1.1 横切关注点

从每个方法中抽取出来的同一类非核心业务。

1.2 切面(Aspect)

封装横切关注点信息的类,每个关注点体现为一个通知方法。

1.3 通知(Advice)

切面必须要完成的各个具体工作

1.4 目标(Target)

被通知的对象

1.5 代理(Proxy)

向目标对象应用通知之后创建的代理对象

1.6 连接点(Joinpoint)

横切关注点在程序代码中的具体体现,对应程序执行的某个特定位置。例如:类某个方法调用前、调用后、方法捕获到异常后等。

1.7 切入点(pointcut)

定位连接点的方式。每个类的方法中都包含多个连接点,所以连接点是类中客观存在的事物。如果把连接点看作数据库中的记录,那么切入点就是查询条件——AOP可以通过切入点定位到特定的连接点。切点通过org.springframework.aop.Pointcut 接口进行描述,它使用类和方法作为连接点的查询条件。

2. AspectJ
2.1 AspectJ简介
  1. AspectJ是Java社区里最完整最流行的AOP框架。
  2. 在Spring2.0以上版本中,可以使用基于AspectJ注解或基于XML配置的AOP
  3. Spring AOP旨在通过Spring IoC提供一个简单的AOP实现,以解决编码人员面临的最常出现的问题。这并不是完整的AOP解决方案,它只能用于Spring容器管理的beans。
  4. AspectJ是最原始的AOP实现技术,提供了完整的AOP解决方案。AspectJ更为健壮,相对于Spring AOP也显得更为复杂。值得注意的是,AspectJ能够被应用于所有的领域对象。
2.2 Spring中启用AspectJ注解支持

导入JAR包

  • 加强包
  • com.springsource.net.sf.cglib-2.2.0.jar
  • com.springsource.org.aopalliance-1.0.0.jar
  • com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
  • 基础包
  • spring-aop-4.0.0.RELEASE.jar
  • spring-aspects-4.0.0.RELEASE.jar

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

	<context:component-scan base-package="cn.justweb"></context:component-scan>

	<!--  开启基于注解的AOP功能;aop名称空间-->
	<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

​ 当Spring IOC容器侦测到bean配置文件中的<aop:aspectj-autoproxy>元素时,会自动为与AspectJ切面匹配的bean创建代理。

2.3 用AspectJ注解声明切面
  1. 要在Spring中声明AspectJ切面,只需要在IOC容器中将切面声明为bean实例。
  2. 当在Spring IOC容器中初始化AspectJ切面之后,Spring IOC容器就会为那些与 AspectJ切面相匹配的bean创建代理。
  3. 在AspectJ注解中,切面只是一个带有@Aspect注解的Java类,它往往要包含很多通知。
  4. 通知是标注有某种注解的简单的Java方法。
  5. AspectJ支持5种类型的通知注解:
    1. @Before:前置通知,在方法执行之前执行
    2. @After:后置通知,在方法执行之后执行
    3. @AfterRunning:返回通知,在方法返回结果之后执行
    4. @AfterThrowing:异常通知,在方法抛出异常之后执行
    5. @Around:环绕通知,围绕着方法执行
3. AOP测试
3.1 接口部分

Calculator

/**
 * 虽然不通过@Component添加到ioc容器中,但是我们仍然使用接口来获取
 * 接口不加载容器中:实际上可以加,因为加入了也不创建对象,只要这个组件是一个接口
 * 相当于告诉Spring,ioc容器中可能有这种类型的组件
 * @Date 2020/5/23 23:23
 * @Version 10.21
 * @Author DuanChaojie
 */
public interface Calculator {
    int add(int m,int n);

    int sub(int m,int n);
}
3.2 实现类

CalculatorImpl

/**
 * @Date 2020/5/23 23:24
 * @Version 10.21
 * @Author DuanChaojie
 */
@Service
public class CalculatorImpl implements Calculator {

    public int add(int m, int n) {
        int result = m + n;
        return result;

    }

    public int sub(int m, int n) {
        int result = m - n;
        return result;
    }
}
3.3 配置文件

applicationContext-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: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
">

    <context:component-scan base-package="cn.justweb.calculator"/>
    <aop:aspectj-autoproxy />


</beans>
3.4 切面类

LogUtils

/**
 * 如何将这个切面类中的这些方法(通知方法)动态地在目标方法运行的各个位置切入
 * @Date 2020/5/24 10:51
 * @Version 10.21
 * @Author DuanChaojie
 */
@Aspect
@Component
public class LogUtils {

    /**
     * 告诉Spring每个方法都什么时候运行
     *         try {
     *              @Before
     *              method.invoke(obj,args)
     *              @After
     *         } catch (Exception e) {
     *              @AfterThrowing
     *              e.printStackTrace();
     *         } finally {
     *             @AfterReturning
     *         }
     * 五个通知方法:
     * @Before 在目标方法之前运行  前置通知
     * @After 在目标方法结束之后运行  后置通知
     * @AfterReturning 在目标方法正常返回之后  返回通知
     * @AfterThrowing 在目标方法抛出异常之后执行 异常通知
     * @Around 环绕通知
     */


    // 想在执行目标方法之前运行,写切入点表达式
    // execution(访问权限符 返回值类型 方法签名)
    @Before("execution(public int cn.justweb.calculator.impl.CalculatorImpl.*(int,int))")
    public static void logStart(){
        System.out.println("[xxx]:方法执行了,其参数列表为xxx");
    }

    @After("execution(public int cn.justweb.calculator.impl.CalculatorImpl.*(int,int))")
    public static void logEnd(){
        System.out.println("[xxx]:方法正常执行完成了,其结果为xxx");
    }
    @AfterThrowing("execution(public int cn.justweb.calculator.impl.CalculatorImpl.*(int,int))")
    public static void logError(){
        System.out.println("[xxx]:方法执行出现了异常,原因为:xxx");
    }

    @AfterReturning("execution(public int cn.justweb.calculator.impl.CalculatorImpl.*(int,int))")
    public static void logFinally(){
        System.out.println("[xxx]:方法最终执行完成了,其结果为xxx");
    }
}
3.5 最终测试
/**
 * @Date 2020/5/24 10:39
 * @Version 10.21
 * @Author DuanChaojie
 */
public class TestCalculator {
    /**
     * 动态代理存在的问题:
     *  1. 写起来很复杂,很难实现
     *  2. jdk默认的动态代理,如果目标对象没有实现任何接口,是无法为他创建代理对象的
     *  所以Spring实现了AOP功能,底层就是动态代理
     */
    @Test
    public void testCalculator() {

        // 测试aop
        ClassPathXmlApplicationContext ioc = new 
            ClassPathXmlApplicationContext("applicationContext.xml");
        
        // 1. 从IOC容器中拿到目标对象,注意:如果想用类型,一定用他的接口类型,不用它本类
        // 细节一:
        //bean = cn.justweb.calculator.impl.CalculatorImpl@1aafa419
        //bean.getClass() = class com.sun.proxy.$Proxy13
        // AOP的底层就是动态代理,容器中保存的组件是他的代理对象:$Proxy13,当然不是本类的类型
        // 通过接口拿目标对象,是jdk帮我们创建代理对象的
        //Calculator bean = ioc.getBean(Calculator.class);
        // 为什么接口没有添加到ioc容器中也能使用?详情见接口部分注解
        //System.out.println("bean = " + bean);
        //System.out.println("bean.getClass() = " + bean.getClass());
        //bean.add(2,1);

        // 去掉接口测试,也能通过实现类拿到
        //CalculatorImpl bean = ioc.getBean(CalculatorImpl.class);
        // 通过id拿到对象
        CalculatorImpl bean =(CalculatorImpl) ioc.getBean("calculatorImpl");
        bean.add(2,2);

        // bean.getClass() = class cn.justweb.calculator.impl.CalculatorImpl$$EnhancerByCGLIB$$7305b6d6
        // cglib帮我们创建好的代理对象
        System.out.println("bean.getClass() = " + bean.getClass());
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值