栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Spring中AOP的概述、搭建和实现

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Spring中AOP的概述、搭建和实现

文章目录
  • 如果不使用AOP我们是怎么做开发的?
  • AOP的概述
  • AOP的基本概念
  • Spring AOP的实现
    • AOP的搭建
      • 加入jar包
      • 基于AspectJ的XML配置方式实现
        • 后置通知
        • 最终通知
        • 异常通知
        • 环绕通知
  • 基于注解的方式实现AOP
    • 开启后置通知
    • 异常通知
    • 环绕通知

如果不使用AOP我们是怎么做开发的?

在没有AOP(面向切面思想)的情况下,我们是如何做开发的。
在做第一版的开发的时候,我们写好自己需要的方法,在之后的维护更新时,当我们再想做方法的实现的时候的话,就需要在已写好的代码里面添加要加的功能。
在写完第一版的情况下,再做后续的添加,就都需要改变之前写好的代码。

Spring的配置文件:



    


    
    
    


package com.demo.spring.dao;

import org.springframework.stereotype.Repository;

@Repository(value = "userDao")
public class UserDao {
//    开始时,已经写好了一个功能,在之后的维护更新中需要添加新的功能,那么我们就必须修改之前写好的代码
//    这样的话就很麻烦,扩展性也不强

//    第一版开发的代码
    public void save() {
        System.out.println("项目开发");
        doTransaction();
        doLog();
    }

//    新增的代码(加强代码)
    public void doTransaction(){
        System.out.println("事务的处理");
    }

    public void doLog(){
        System.out.println("日志的处理");
    }
}

测试代码:

package com.demo.spring.test;

import com.demo.spring.dao.UserDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test2 {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");

        UserDao userDao = applicationContext.getBean("userDao", UserDao.class);
        userDao.save();
    }
}


这种情况下,在后期添加新的功能,然后又去之前的功能里,增加代码调用这个新功能,当添加的功能多的话,就会非常非常麻烦,即使在第一次写的时候为使代码轻量化抽取一个工具类,将这些方法封装起来,但是依然还是需要显示的去调用。所以我们就出现了AOP的思想概念。

AOP的概述

AOP是为Aspect Oriented Programming的缩写,翻译过来就是“面向切面编程”,就只通过预编译的方式和运行期间==动态代理(我不做,别人给我做)==实现程序功能的统一维护的一种技术。

AOP是OOP(面向对象的编程)的延续,是软件开发中的一个热点,它也是Spring框架中的一个重要内容和核心。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各个部分之间的耦合度降低,提高了代码的可重用型,尤其大大的提高了开发的效率。

AOP、OOP两者之间的区别
AOP、OOP在字面上看似非常相近,但是它们连可以说是两个完全互不相干的的设计思想。

  1. OOP所针对的是业务处理过程的实体及其属性和行为进行抽象封装,以获得更加清晰高效的逻辑单元的体现和划分。

  2. AOP是针对于业务处理过程当中的切面提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中的各个部件之间耦合性的隔离效果。这两种设计思想在目标上存在着本质上的区别。

面向切面的优点:
减少了代码的重复,更加专注了业务的扩展。

面向切面的编程思想它只是面向对象思想的一种补充,不是什么相反的存在

核心原理: 使用动态的代理的方式在执行语句的前后或者出现异常的时候加入相关的逻辑语句。
可以在不修改之前的代码情况下,为程序增加新的功能。
抽取的代码也是有条件的:抽取的代码与我们的业务代码没有直接的关系。

使用案例:
事务处理:开启事务,关闭事务,出现异常后滚回事务
权限判断:在执行方法前,判断当前操作是否具有权限
日志:在执行前进入日志处理

图实理解:

AOP的基本概念

● 连接点(Joinpoint): 类中可以被增强的方法,这个方法就被称为连接点。
● 切入点(pointcut): 类中很多方法可以被增强,但实际中只有add和update可被增强。那么add和update的方法就可以称为切入点(实际实现的连接)。

● 通知(Advice): 通知是指一个切面在特定的连接点要做的事情(增强的功能)。通知分为前置通知(方法执行前通知),后置通知(方法执行后通知),环绕通知(方法执行前和执行后都各通知一遍)等。

== ●切面(Aspect):== 把通知添加到切入点的过程叫切面。

● 目标(Target): 代理的目标对象(要增强的类)就称为目标。

● 代理(Proxy): 向目标对象应用通知之后创建的代理对象。

Spring AOP的实现

对于AOP的这种编程思想,很多框架都进行了实现,Spring就是其中之一,是可以完成面向切面的编程。

然而在AspectJ框架中也实现了AOP的功能,AspectJ也是一个基于Java语言的AOP框架,它提供了强大的AOP的功能,并且实现方式更为简洁,使用体验也很为方便,而且还支持注解式的开发,所以Spring就将AspectJ对AOP的实现引入到自己的框架之中来了。

所以在Spring框架中使用AOP的话,一般都是使用AspectJ的实现方式。

AspectJ是什么?
AspectJ是一款非常优秀的面向切面的框架,它扩展了Java的语言,提供了非常强大的切面实现。这个框架只针对于AOP。

AspectJ中常用的通知有五种类型:
● 前置通知
● 后置通知
● 环绕通知
● 异常通知
● 最终通知

AOP的搭建 加入jar包
        
        
            org.springframework
            spring-context
            5.2.2.RELEASE
        
基于AspectJ的XML配置方式实现

在这我单独创建了一个xml的配置,然后只要在Spring的xml配置文件导入我单独创建的xml文件。

    
    

那在对应的aop.xml的配置文件中,应该这样配置文件

这里配置的是一个前置通知




    
    

    
    
        
        

        
        
            
        
    


这里加的是前置通知

切面代码:

package com.demo.spring.aop;

public class AOPDemo {

    //    通知:在连接点上要做的事情(要增强的功能)
    //    新增的代码(加强代码)

    public void doLog() {
        System.out.println("日志的处理");
    }
}

切入点所在的类

package com.demo.spring.dao;

import org.springframework.stereotype.Repository;

@Repository(value = "userDao")
public class UserDao {

//    第一版开发的代码
    public void save() {
        System.out.println("项目开发");
    }

}

运行测试的代码:

package com.demo.spring.test;

import com.demo.spring.dao.UserDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test2 {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");

        UserDao userDao = applicationContext.getBean("userDao", UserDao.class);
        userDao.save();
    }
}

前置通知的结果:

后置通知

只需要将方法执行完就执行后置通知

只修改了配置文件中的代码

    

    
    
        
        

        
        
            
        
    

最终通知

方法有返回值的话,就将返回值返回后(return执行后),再执行这条语句

    

    
    
        
        

        
        
            
        
    
异常通知

切入点所在的类

package com.demo.spring.dao;

import org.springframework.stereotype.Repository;

@Repository(value = "userDao")
public class UserDao {

//    第一版开发的代码
    public void save() {
        System.out.println("项目开发");
        int a = 10/0;//这里写入一个error
    }
}

切面代码:

package com.demo.spring.aop;

public class AOPDemo {

    //    通知:在连接点上要做的事情(要增强的功能)
    //    新增的代码(也叫加强代码)
    
     public void exceptionAdvice(Throwable e){
         System.out.println("异常通知"+e.getMessage());
     }
}



    
    

    
    
        
        

        
        
        
            
             
             
        
    


运行测试的代码:

package com.demo.spring.test;

import com.demo.spring.dao.UserDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test2 {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");

        UserDao userDao = applicationContext.getBean("userDao", UserDao.class);
        userDao.save();
    }
}

环绕通知

环绕通知是完全可以代替前置通知、后置通知、最终通知、异常通知。

环绕通知在执行的时候,会直接进去通知里面去,执行环绕通知里面的语句。然后直到执行到proceed()时,再调用到切入点的方法。

切入点所在的类

package com.demo.spring.dao;

import org.springframework.stereotype.Repository;

@Repository(value = "userDao")
public class UserDao {

//    第一版开发的代码
    public void save() {
        System.out.println("项目开发");
        int a = 10/0;//这里写入一个error
    }
}

切面代码:

package com.demo.spring.aop;

public class AOPDemo {

    //    通知:在连接点上要做的事情(要增强的功能)
    //    新增的代码(也叫加强代码)
  
    public void aroundAdvice(ProceedingJoinPoint proceedingJoinPoint){
        //用类似放射的方式将切入点的方法放到ProceedingJoinPoint里
        System.out.println("前置通知");
        try {
            proceedingJoinPoint.proceed();//在这调用切入点的方法
        } catch (Throwable throwable) {
            System.out.println("异常通知");
            throwable.printStackTrace();
        }
        System.out.println("后置通知");
    }
}

配置文件中的




    
    

    
    
        
        

        
        
        
            
            

        
    


运行测试的代码:

package com.demo.spring.test;

import com.demo.spring.dao.UserDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test2 {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");

        UserDao userDao = applicationContext.getBean("userDao", UserDao.class);
        userDao.save();
    }
}

基于注解的方式实现AOP

启动AspectJ支持:



    


    
    
    

    
    

切面所在的类(包含通知的类):
@Component//通过spring来创建对象
@Aspect//声明这个类是切面(装有通知的类)

开启的是前置通知

package com.demo.spring.aop;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Component//通过spring来创建对象
@Aspect//声明这个类是切面(装有通知的类)
public class AOPDemo {


    //前置通知,标签后的代码与xml里面的代码一模一样
    @Before("execution(* com.demo.spring.dao.UserDao.save(..))")
    public void doLog() {
        System.out.println("日志的处理");
    }
}

业务代码:

package com.demo.spring.dao;

import org.springframework.stereotype.Repository;

@Repository(value = "userDao")
public class UserDao {

    //    第一版开发的代码
    public void save() {
        System.out.println("项目开发");
//        int a = 10/0;
    }
}

启动测试执行业务代码:

package com.demo.spring.test;

import com.demo.spring.dao.UserDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test2 {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");

        UserDao userDao = applicationContext.getBean("userDao", UserDao.class);
        userDao.save();
    }
}

开启后置通知
package com.demo.spring.aop;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Component//通过spring来创建对象
@Aspect//声明这个类是切面(装有通知的类)
public class AOPDemo {


    //后置通知,标签后的代码与xml里面的代码一模一样
    @After("execution(* com.demo.spring.dao.UserDao.save(..))")
    public void doLog() {
        System.out.println("日志的处理");
    }
}

异常通知
package com.demo.spring.aop;

import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Component//通过spring来创建对象
@Aspect//声明这个类是切面(装有通知的类)
public class AOPDemo {

    
    @AfterThrowing(value = "execution(* com.demo.spring.dao.UserDao.save(..))",throwing = "e")
    public void exceptionAdvice(Throwable e){
        System.out.println("异常通知"+e.getMessage());
    }
}

业务代码写一个异常:

package com.demo.spring.dao;

import org.springframework.stereotype.Repository;

@Repository(value = "userDao")
public class UserDao {

    //    第一版开发的代码
    public void save() {
        System.out.println("项目开发");
        int a = 10/0;
    }
}


也成功的打印输出了

环绕通知
package com.demo.spring.aop;

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

@Component//通过spring来创建对象
@Aspect//声明这个类是切面(装有通知的类)
public class AOPDemo {

//环绕通知
    @Around("execution(* com.demo.spring.dao.UserDao.save(..))")
    public void aroundAdvice(ProceedingJoinPoint proceedingJoinPoint){
        //用类似放射的方式将切入点的方法放到ProceedingJoinPoint里
        System.out.println("前置通知");
        try {
            proceedingJoinPoint.proceed();//在这调用切入点的方法
        } catch (Throwable throwable) {
            System.out.println("异常通知");
            throwable.printStackTrace();
        }
        System.out.println("后置通知");
    }
}

依旧执行这个存在异常的业务代码:

package com.demo.spring.dao;

import org.springframework.stereotype.Repository;

@Repository(value = "userDao")
public class UserDao {

    //    第一版开发的代码
    public void save() {
        System.out.println("项目开发");
        int a = 10/0;
    }
}

结果:

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/600889.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号