SpringAOP了解&练习—(6)

SpringAOP学习笔记

SpringAOP实现底层就是对动态代理的代码进行了封装,封装后我们只需要对需要关注的部分进行代码编写,并通过配置的方式完成指定目标的方法增强即可。

1、Spring AOP相关概念

小编在这里主要涉及通过注解的方式实现AOP。所以接下来我们现需要了解AOP相关术语:

  • Target(目标对象)

这里是要被增强的对象,一般而言就是对业务逻辑类

  • Proxy(代理)

一个类被AOP织入增强后就会产生一个结果的代理。

  • Aspect(切面)

表示增强的功能,就是一些代码完成的某个功能,非业务功能。

  • Joinpoint(连接点)

连接点就是被拦截到的点。在spring中这些点就是指的是方法(即一般类中的业务方法),因为spring只支持方法类型的连接点。

  • Pointcut(切入点)

切入点指声明的一个或多个连接点的集合。通过切入点指定一组方法。

被标记为 final 的方法是不能作为连接点与切入点的。因为最终的是不能被修改的,不能被增强的。

  • Advice(通知/增强)

通知是指拦截到 Joinpoint 之后所要做的事情就是通知。通知定义了增强代码切入到目标代码的时间点,是目标方法执行之前执行,还是之后执行等。通知类型不同,切入时间不同。

通知的类型:前置通知,后置通知,异常通知,最终通知,环绕通知。

  • Weaving(织入)

指把增强应用到目标对象来创建新的代理对象的过程。 spring 采用动态代理织入,而 AspectJ 采用编译期织入和类装载期织入。

2、AspectJ对AOP的实现

对于AOP的编程思想,很多框架实现了其内容。当然spring就是其中之一可以完成面向切面编程。

AspectJ也实现了AOP的功能,且实现方式更为简捷而且还支持注解式开发。所以SpringAspectJ对于AOP实现也引入到自己的框架中。

在 Spring 中使用 AOP 开发时,一般使用 AspectJ 的实现方式

2.1 AspectJ通知类型

AspectJ 中常用的通知有5种类型:

  • 前置通知
  • 后置通知
  • 环绕通知
  • 异常通知
  • 最终通知

2.2 AspectJ的切入点表达式

AspectJ 定义了专门的表达式用于指定切入点。
表达式的原型如下:
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern)
 throws-pattern?)
 说明:
	modifiers-pattern] 访问权限类型
	ret-type-pattern 返回值类型
	declaring-type-pattern 包名类名
	name-pattern(param-pattern) 方法名(参数类型和参数个数)
	throws-pattern 抛出异常类型
	?表示可选的部分

以上表达式主要分为四个部分。

execution(访问权限 方法返回值 方法声明(参数) 异常类型)

切入点表达式要匹配的对象**就是目标方法的方法名。**所以,execution表达式就是方法的签名。

在这里插入图片描述

示例:
execution(* com.xaf.service.*.*(..))
指定切入点为:定义在 service 包里的任意类的任意方法。

execution(* com.xaf.service..*.*(..))
指定切入点为:定义在 service 包或者子包里的任意类的任意方法。“..”出现在类名中时,后面必须跟“*”,表示包、子包下的所有类。

execution(* com.xaf.service.IUserService+.*(..))
指定切入点为:IUserService 若为接口,则为接口中的任意方法及其所有实现类中的任意方法;若为类,则为该类及其子类中的任意方法。

3、通过注解方式实现AOP

开发阶段:关注核心业务和AOP代码。

运行阶段:spring框架会在运行的时候将核心业务和AOP代码通过动态代理的方式编织在一起。

1、创建项目引入依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>springDemo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.13.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.10</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.2.13.RELEASE</version>
        </dependency>

    </dependencies>
    <build>
        <plugins>
            <!--编译插件-->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.0</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

2、创建spring配置文件引入约束

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

    <!--
    <bean id="teamService" class="com.xaf.service.TeamService"></bean>
    -->
    <!--包扫描-->
    <context:component-scan base-package="com.xaf.service,com.xaf.aop"/>

    <!--开始注解aop使用-->
    <aop:aspectj-autoproxy proxy-target-class="true"/>
</beans>

3.创建核心业务

package com.xaf.service;

public interface IService {
    void add(int id,String name);
    boolean update(int num);
}
package com.xaf.service;

import org.springframework.stereotype.Service;

@Service("nbaService")
public class NBAService implements IService{

    @Override
    public void add(int id, String name) {
        System.out.println("NBAService---add---");
    }

    @Override
    public boolean update(int num) {
        System.out.println("NBAService---update---");
        if(num>666){
            return true;
        }
        return false;
    }
}
package com.xaf.service;

import org.springframework.stereotype.Service;

@Service
public class TeamService implements IService{
    @Override
    public void add(int id, String name) {
        System.out.println("TeamService---add---");
    }

    @Override
    public boolean update(int num) {
        System.out.println("TeamService----update----");
        if(num>666){
            return true;
        }
        return false;
    }
}

在包中创建核心业务。

4.定义切口类

package com.xaf.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

/*
* 切面类
* */
@Component  //切面对象创建权限依旧给spring容器
@Aspect  //框架注解,当前是一个切面
public class MyAspect {

    @Pointcut("execution(* com.xaf.service..*.*(..))")
    private void pointCut(){

    }

    @Pointcut("execution(* com.xaf.service..*.add*(..))")
    private void pointCut2(){

    }

    //前置的切面通知
    @Before("pointCut()")
    public void before(JoinPoint jp){
        System.out.println("前置通知:在目标方法执行前被调用的通知");
        String name = jp.getSignature().getName();
        System.out.println("拦截的方法:"+name);
        Object[] args = jp.getArgs();
        System.out.println("方法参数格式"+args.length);
        System.out.println("方法的参数列表");
        for (Object arg:args){
            System.out.println("\t"+arg);
        }
    }

    @AfterReturning(value = "pointCut2()",returning = "result")
    public Object afterReturn(Object result){
        if(result!=null){
            boolean res=(boolean) result;
            if(res){
                result=false;
            }
        }
        System.out.println("后置通知:在目标方法执行之后被调用的通知,result="+result);
        return result;
    }


    /**
    * 注解声明环绕注解
    * */
    @Around(value = "pointCut()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("环绕方法---目标方法的执行之前");
        Object proceed = pjp.proceed();
        System.out.println("环绕方法---目标方法的执行之后");
        return proceed;
    }

    /**
     * AfterThrowing 注解声明异常通知方法
     * value: 表示切入点表达式
     * returning 属性表示 返回的结果,如果需要的话可以在后置通知的方法中修改结果
     */
    @AfterThrowing(value = "pointCut()",throwing = "ex")
    public void exception(JoinPoint jp,Throwable ex){
    //一般会把异常发生的时间、位置、原有都记录下来
        System.out.println("异常通知:在目标方法执行出现异常的时候才会别调用的通知,否则不 执行");
        System.out.println(jp.getSignature()+"方法出现异常,异常信息 是:"+ex.getMessage());
    }

    /**
     * After 注解声明为最终通知
     */
    @After( "pointCut()")
    public void myFinally(){
        System.out.println("最终通知:无论是否出现异常都是最后被调用的通知");
    }

}

详解可以关注注释信息。

5.application.xml配置文件中开启包扫描和 注册aspectj的自动代理

如上第2个步骤即可

<!--包扫描-->
    <context:component-scan base-package="com.xaf.service,com.xaf.aop"/>
<!--开始注解aop使用-->
    <aop:aspectj-autoproxy proxy-target-class="true"/>
<!--aop:aspectj-autoproxy的底层是由 AnnotationAwareAspectJAutoProxyCreator 实现的,是基于 AspectJ 的注解适配自动代理生成器。
    其工作原理是,aop:aspectj-autoproxy通过扫描找到@Aspect 定义的切面类,再由切面类根据切入点找到目标类的目标方法,再由通知类型找到切入的时间点。-->

比如我们在第4个步骤已经扫描到类上面的@AspectJ,然后由切面类根据切入点找到目标方法。

6、测试类

public class Test01 {
    @Test
    public void test01(){

        //1.加载springxml文件
        ApplicationContext ac =new ClassPathXmlApplicationContext("application.xml");
        TeamService teamService = (TeamService) ac.getBean("teamService");
        teamService.add(1001,"湖人队");
        System.out.println("---------------");
        boolean update = teamService.update(888);
        System.out.println("update 结果为:"+update);
        System.out.println("================");
        NBAService nbaService = (NBAService) ac.getBean("nbaService");
        nbaService.add(1002,"热火");
        System.out.println("~~~~~~~`~~~`~");
        boolean update2 = teamService.update(888);
        System.out.println("update结果为:"+update2);

    }
}

最后的编译结果如下图:

在这里插入图片描述

以上就是AOP的练习和分享,感谢大家的阅读。本节基本的spring讲解差不多,后续若有补充会继续进行探讨。谢谢大家。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Xiao艾扶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值