AOP的理解以及实现

AOP

一、AOP概念

​ AOP即面向切面编程。我们先来理解下为什么会需要AOP?

​ 如果在编程时多个类中有重复出现的代码,那么我们就应该考虑将这些重复代码抽取出来定义成一个抽象类,这种情况我们称为纵向抽取。但是如果这些重复的代码是分散在各个业务逻辑中的,比如事务控制中有大量的try-catch-finally代码,这些重复代码嵌套在主要的业务逻辑代码中,我们用上面的方法就抽取不了,这时我们就可以通过横向切割的方式,把这些重复代码抽取到一个独立的模块中,在以后调用对应的业务逻辑方法时,又将这些重复代码横向切入进去。从而达到让业务逻辑类保持最初的单纯。

在这里插入图片描述

​ 我们用一个例子来说明。如下图:是一个对数据库进行操作的步骤:

​ 1、连接数据库

​ 2、执行SQL语句

​ 3、是否有异常,有就回滚事务,没有就提交事务

​ 4、关闭资源

​ 我们可以看到每次对数据库进行一次操作后,都会有如下的步骤,其中的1-3-4步骤都是重复的,而作为开发人员我们每次真正需要关心的只是步骤2的SQL语句执行(因为这在每个操作中是不一样的),而对于其他的我们则可以交由AOP去实现这些步骤,我们只需通过注解或者XML的配置来告诉AOP我们需要在那些类下的哪些方法中切入这些重复的代码,这样在编写代码时我们就只需关注核心逻辑代码,而在运行时AOP会对我们需要加入重复代码的方法进行拦截,并将这些重复代码切入进去。

在这里插入图片描述

​ 值得一提的是AOP并不是Spring框架特有的,Spring只是支持AOP编程的框架之一,且SpringAOP是一种基于方法拦截的AOP,也就是说Spring只能支持方法拦截的AOP。

二、AOP术语

1、切面(Aspect):切面由切点和增加(需要切入的代码、方法也叫通知)组成,它既包括了横切逻辑的定义,也包括了连接点的定义,SpringAOP就是将切面所定义的横切逻辑织入到切面所制定的连接点中。

2、增强、通知(Advice):是切面的方法,也就是一段需要切入到目标对象的代码。根据他在代理对象真实方法调用前、后和逻辑分为以下几种:

前置通知(before):在真实对象方法调用前执行

后置通知(after):在真实对象方法调用后执行

返回通知(afterReturning):在执行业务代码后无异常执行

异常通知(afterThrowing):在执行业务代码发送异常后执行

环绕通知(around):最强的通知,可以取代原对象方法。但使用也较复杂

3、切点(Pointcut):告诉SpringAOP在什么时候启动拦截并织入对应的流程中。

4、连接点(join point):连接点对应的是一个具体的方法,比如通过切点的正则表达式去判断那些方法是连接点。

5、织入(Weaving):织入就是将增强添加到目标具体连接点上的过程。

三、基于纯注解的AOP实现:
主要步骤:

​ 1、创建业务逻辑接口,实现接口

​ 2、创建切面,切面中包含切点和需要切入的方法

​ 3、创建AOP配置类,启动Aspectj自动代理、扫描bean、生成切面类

​ 4、测试AOP

1、创建一个接口 里面的方法就是我们的连接点,也就是我们需要拦截这个方法并将通知织入

package com.test.aop;

public interface Student {
    void getName();
}

2、创建这个接口的实现

package com.test.aop;

import org.springframework.stereotype.Component;

@Component
public class StudentImpl implements Student {

    @Override
    public void getName() {
        System.out.println("我叫唐鹏,我自豪");
    }
}

3、创建切面,并定义切点和逻辑方法

package com.test.aop;

import org.aspectj.lang.annotation.*;

@Aspect
public class TestAspect {

    @Pointcut("execution(* com.test.aop.*.getName(..))")
    public void pointCut(){}

    @Before("pointCut()")
    public void before(){
        System.out.println("=====前置:小么小么小二郎哟,背着书包上学堂咯");
    }

    @After("pointCut()")
    public void after(){
        System.out.println("=====后置:终于放学了!");
    }

    @AfterReturning("pointCut()")
    public void afterReturning(){
        System.out.println("=====返回:今天真呀真高兴");
    }

    @AfterThrowing("pointCut()")
    public void afterThrowing(){
        System.out.println("=====异常:oh 我考试考了0分,可不高兴");
    }
}

4、创建AOP配置类

package com.test.aop;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

//启用Aspectj自动代理
@EnableAspectJAutoProxy
//配置扫描的包  这是扫描bean的配置
@ComponentScan("com.test")
public class AopConfig {

    //生成切面类,自定义的类里面设置了切点
    @Bean
    public TestAspect getTestAspect(){
        return new TestAspect();
    }
}

5、测试AOP

package com.test.aop;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class TestMain {
    public static void main(String[] args) {
        //因为是采用注解所以使用AnnotationConfigAopApplicationContext来实现
        ApplicationContext ac = new AnnotationConfigApplicationContext(AopConfig.class);
        Student tp = (Student) ac.getBean("studentImpl");
        tp.getName();
    }
}

6、结果打印

=====前置:小么小么小二郎哟,背着书包上学堂咯
我叫唐鹏,我自豪
=====后置:终于放学了!
=====返回:今天真呀真高兴

四、基于纯XML开发AOP的方式:

基本步骤:

​ 1、同样先创建接口和接口实现类

​ 2、创建切面类和切入方法,因为是采用XML的方式,所以这里不需要指明他是个切面类,也不需要指定切入方法对应的切入点。,只需要生成对应的方法和方法代码即可。

​ 3、创建spirng配置文件

​ 4、测试

1、接口和实现同上,这里就不在给出。(注意如果是在不同包下使用相同类名,注意包的引用)

2、编写切面类

package com.test.aopXml;

public class TestAspect {

    public void before(){
        System.out.println("=====XML前置:小么小么小二郎哟,背着书包上学堂咯");
    }

    public void after(){
        System.out.println("=====XML后置:终于放学了!");
    }

    public void afterReturning(){
        System.out.println("=====XML返回:今天真呀真高兴");
    }

    public void afterThrowing(){
        System.out.println("=====XML异常:oh 我考试考了0分,可不高兴");
    }

}

3、spring的配置文件applicationContext.xml 注意放在resources源码文件下面

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd
    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 id ="student" class="com.test.aopXml.StudentImpl"/>
    <bean id ="testAspect" class="com.test.aopXml.TestAspect"/>
    
    <!--aop配置-->
    <aop:config >
        <!--定义切面,和切点配置-->
        <aop:aspect ref="testAspect">
            <!--定义需要重复使用的切点-->
            <aop:pointcut id="pointCut" expression="execution(* com.test.aopXml.StudentImpl.*(..))"/>
            <!--定义切点,method是指切面类中对应的方法名-->
            <aop:before method="before" pointcut-ref="pointCut"/>
            <aop:after method="after" pointcut-ref="pointCut"/>
            <aop:after-returning method="afterReturning" pointcut-ref="pointCut"/>
            <aop:after-throwing method="afterThrowing" pointcut-ref="pointCut"/>
        </aop:aspect>
    </aop:config>

</beans>

4、测试

package com.test.aopXml;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestMain {
    public static void main(String[] args) {
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
        Student student = (Student) ac.getBean("student");
        student.getName();
    }
}

5、测试结果

=====XML前置:小么小么小二郎哟,背着书包上学堂咯
我叫唐鹏,我自豪
=====XML后置:终于放学了!
=====XML返回:今天真呀真高兴
五、注解和XML混合开发的方式

注解和XML混合使用:切点信息定义在切面类,xml中只需要开启对aspectj自动代理和配置扫描就好

步骤如下:

​ 1、同样给出接口和实现类

​ 2、编写切面类,切面类需要加入容器,让ioc管理

​ 3、配置applicationContext1.xml文件

​ 4、测试

1、接口和实现同上

2、编写切面类,这里切面类加上了@Component注解 而在使用纯注解时是没有加上的

package com.test.aop;

import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class TestAspect {

    @Pointcut("execution(* com.test.aop.*.getName(..))")
    public void pointCut(){}

    @Before("pointCut()")
    public void before(){
        System.out.println("=====混合前置:小么小么小二郎哟,背着书包上学堂咯");
    }

    @After("pointCut()")
    public void after(){
        System.out.println("=====混合后置:终于放学了!");
    }

    @AfterReturning("pointCut()")
    public void afterReturning(){
        System.out.println("=====混合返回:今天真呀真高兴");
    }

    @AfterThrowing("pointCut()")
    public void afterThrowing(){
        System.out.println("=====混合异常:oh 我考试考了0分,可不高兴");
    }
}

3、applicationContext1.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:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd
    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">

	<!--此配置是为了让spring知道我们配置了切面,让他扫描bean的时候注意到切面类-->
   <aop:aspectj-autoproxy/>
    <!--扫描bean-->
   <context:component-scan base-package="com.test.aop"/>

</beans>

4、测试

package com.test.aop;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestMain {
    public static void main(String[] args) {
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext1.xml");
        Student student = (Student) ac.getBean("studentImpl");
        student.getName();
    }
}

5、测试结果

=====混合前置:小么小么小二郎哟,背着书包上学堂咯
我叫唐鹏,我自豪
=====混合后置:终于放学了!
=====混合返回:今天真呀真高兴
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值