SSMSpring6(十.面向切面编程AOP)

Posted Beyong2019

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SSMSpring6(十.面向切面编程AOP)相关的知识,希望对你有一定的参考价值。

文章目录

1.AOP

将与核心业务无关的代码独立的抽取出来,形成一个独立的组件,然后以横向交叉的方式应用到业务流程当中的过程被称为AOP。

优点:
● 第一:代码复用性增强。
● 第二:代码易维护。
● 第三:使开发者更关注业务逻辑。

2. AOP的七大术语

public class UserService
    public void do1()
        System.out.println("do 1");
    
    public void do2()
        System.out.println("do 2");
    
    public void do3()
        System.out.println("do 3");
    
    public void do4()
        System.out.println("do 4");
    
    public void do5()
        System.out.println("do 5");
    
    // 核心业务方法
    public void service()
    try
    	//Joinpoint连接点
        do1();  //Pointcut切点
        //Joinpoint连接点
        do2();   //Pointcut切点
        //Joinpoint连接点
        do3();   //Pointcut切点
        //Joinpoint连接点
        do5();   //Pointcut切点
          //Joinpoint连接点
     catch (Exception e)
    	  //Joinpoint连接点
      finallly
      	  //Joinpoint连接点
      
    

  • 连接点 Joinpoint
    描述的是位置,程序执行过程中,可以织入切面的位置。方法的执行前后,异常抛出之后等位置
  • 切点 Pointcut
    真正织入切面的方法。
  • 通知 Advice
    又叫增强,就是要增强的那段具体的代码。
    包括
    ■ 前置通知
    ■ 后置通知
    ■ 环绕通知
    ■ 异常通知
    ■ 最终通知
  • 切面 Aspect
    切面 = 切点+通知
  • 织入 Weaving
    把通知应用到目标对象上的过程。
  • 代理对象Proxy
    一个目标对象被织入通知后产生的新对象。
  • 目标对象Target
    被织入通知的对象。

(注:连接点和切入点的区别)

举个例子,开车到高速路口有很多的出口(连接点),但是我们实际上只从一个出口出去(切入点)

3. 切点表达式

切点表达式用来定义通知(Advice)往哪些方法上切入。
语法:

execution([访问控制权限修饰符] 返回值类型 [全限定类名]方法名(形式参数列表) [异常])

访问控制权限修饰符:
● 可选项。
● 没写,就是4个权限都包括。
● 写public就表示只包括公开的方法。
返回值类型:
● 必填项。
* 表示返回值类型任意。
全限定类名:
● 可选项。
● 两个点“…”代表当前包以及子包下的所有类。
● 省略时表示所有的类。
方法名:
● 必填项。
*表示所有方法。
● set*表示所有的set方法。
形式参数列表:
● 必填项
● () 表示没有参数的方法
● (…) 参数类型和个数随意的方法
● (*) 只有一个参数的方法
● (*, String) 第一个参数类型随意,第二个参数是String的。
异常:
● 可选项。
● 省略时表示任意异常类型。

4.使用Spring的AOP

Spring对于AOP的实现主要有三种方式:
● 第一种方式:Spring框架结合AspectJ框架实现的AOP,基于注解方式。
● 第二种方式:Spring框架结合AspectJ框架实现的AOP,基于XML方式。
● 第三种方式:Spring框架自己实现的AOP,基于XML配置方式。

4.1 环境准备

依赖

<!--仓库-->
    <repositories>
        <repository>
            <id>repository.spring.milestone</id>
            <name>Spring Milestone Repository</name>
            <url>https://repo.spring.io/milestone</url>
        </repository>
    </repositories>
    <!--依赖-->
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>6.0.0-M2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>6.0.0-M2</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

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

</beans>

4.2 基于AspectJ的AOP注解式开发步骤

目标类

package com.sdnu.spring6.service;

import org.springframework.stereotype.Service;

@Service("userService")
public class UserService   //目标类
    public void login()  //目标方法
        System.out.println("系统正在进行身份验证");
    

切面

package com.sdnu.spring6.service;

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

@Component("logAspect")
@Aspect //切面类需要注解标注
public class LogAspect   //切面 = 通知 + 切点
    @Before("execution(* com.sdnu.spring6.service.UserService.*(..))")
    public void myMethod()
        System.out.println("一个通知--》增强代码");
    

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: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="com.sdnu.spring6.service"/>
    <!--开启aspectj的动态代理-->
    <!--
    proxy-target-class="true"表示强制使用CGLIB动态代理
    proxy-target-class="false"默认的,表示接口使用JDK动态代理,反之使用CGLIB动态代理
    -->
    <aop:aspectj-autoproxy proxy-target-class="true"/>
</beans>

测试

package com.sdnu.spring6.Test;

import com.sdnu.spring6.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringAopTest 
    @Test
    public void testBefore()
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        UserService userService = applicationContext.getBean("userService", UserService.class);
        userService.login();
    

4.3 所有通知类型

● 前置通知:@Before 目标方法执行之前的通知
● 后置通知:@AfterReturning 目标方法执行之后的通知
● 环绕通知:@Around 目标方法之前添加通知,同时目标方法执行之后添加通知。
● 异常通知:@AfterThrowing 发生异常之后执行的通知
● 最终通知:@After 放在finally语句块中的通知

4.2的例子是前置通知,后置通知也类似。

环绕通知:

    //环绕在前置通知之前,后置通知之后
    @Around("execution(* com.sdnu.spring6.service..*(..))")
    public void aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable 
        System.out.println("前环绕");
        //执行目标代码
        joinPoint.proceed();
        System.out.println("后环绕");
    

异常通知:

    @AfterThrowing("execution(* com.sdnu.spring6.service..*(..))")
    public void afterThrowingAdvice()
        System.out.println("异常通知");
    

最终通知:

 @After("execution(* com.sdnu.spring6.service..*(..))")
    public void afterAdvice()
        System.out.println("最终通知");
    

4.4 切面顺序



@Order(num) ,num越小,则优先级越高。

4.5 通用切点

    @Pointcut("execution(* com.sdnu.spring6.service..*(..))")
    public void universalPointcut()

    

4.6 获取目标方法的方法签名

    @Before("universalPointcut()")
    public void myMethod(JoinPoint joinPoint)
        System.out.println("一个通知--》增强代码");
        Signature signature = joinPoint.getSignature();//获取目标方法的方法签名
        System.out.println(signature.getName());
    

4.7 全注解式开发

Spring6Config

package com.sdnu.spring6.service;

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

@Configuration //代替spring配置文件
@ComponentScan("com.sdnu.spring6.service") //组件扫描
@EnableAspectJAutoProxy(proxyTargetClass = true) //启用aspect的自动代理机制
public class Spring6Config 

测试

    @Test
    public void testNoXml()
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(Spring6Config.class);
        UserService userService = applicationContext.getBean("userService", UserService.class);
        userService.logout();
    

4.8 基于XML配置的AOP

 <!--纳入spring bean管理-->
    <bean id="vipService" class="com.sdnu.spring6.service.VipService"/>
    <bean id="timerAspect" class="com.sdnu.spring6.service.TimerAspect"/>

    <!--aop配置-->
    <aop:config>
        <!--切点表达式-->
        <aop:pointcut id="p" expression="execution(* com.sdnu.spring6.service.VipService.*(..))"/>
        <!--切面-->
        <aop:aspect ref="timerAspect">
            <!--切面=通知 + 切点-->
            <aop:around method="time" pointcut-ref="p"/>
        </aop:aspect>
    </aop:config>

5. 案例:事务处理

账户类

package com.sdnu.spring6.service;

import org.springframework.stereotype.Service;

@Service
public class AccountService 
    public void transfer()
        System.out.println("正在转账");
    
    public void withdraw()
        System.out.println("正在取钱");
    

订单类

package com.sdnu.spring6.service;

import org.springframework.stereotype.Service;

@Service
public class OrderService 
    public void generate()
        System.out.println("正在生成订单");
    
    public void cancel()
        System.out.println("订单已经取消");
    

切面

package com.sdnu.spring6.service;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class TransactionAspect 
    @Around("execution(* com.sdnu.spring6.service..*(..))")
    public void aroundAdvice(ProceedingJoinPoint proceedingJoinPoint)
        try 
            //前环绕
            System.out.println("开启事务");
            //执行目标
            proceedingJoinPoint.proceed();
            //后环绕
            System.out.println("关闭事务");
         catch (Throwable e) 
            System.out.println("回滚事务");
            e.printStackTrace();
        
    

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: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="com.sdnu.spring6.service"/>
    <!--启动自动代理-->
    <aop:aspectj-autoproxy/>
</beans>

测试

package com.sdnu.spring.spring6.test;

import com.sdnu.spring6.service.AccountService;
import com.sdnu.spring6.service.OrderService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AopRealAppTest 
    @Test
    public void testTransaction()
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        AccountService accountService = applicationContext.getBean("accountService", AccountService.class);
        OrderService orderService = applicationContext.getBean("orderService", OrderService.class);
        accountService.transfer();
        accountService.withdraw();
        orderService.generate();
        orderService.cancel();
    

三面向切面编程

第八讲  面向切面编程--aop

1、aop:aspect oriented programming 面向切面编程

2、aop在spring中作用

  提供声明式事务服务(声明式事务)

  允许用户实现自定义切面

3、aop:在不改变原有代码的情况下增加新的功能。

  传统的编程模式:

技术图片

Aop的编程模式:横向的编程

技术图片

aop的好处:(代理的好处就是aop的好处)

  a)使得真实角色处理的业务更加纯粹,不再去关注一些公共的事情。

  b)公共的业务由代理来完成---实现业务的分工

  c)公共业务发生扩展时变得更加集中和方便

4、名词解释

      关注点:增加的某个业务。如日志,安全,缓存,事务,异常处理等。(例:在前面代码的代理类的方法中增加了log();方法,那么这个方法就是一个关注点,对于这个方法我们有更好的处理,下面~~)

   切面(Aspect):一个关注点的模块化。(上面log()算是一个日志关注点,但是这个关注点,我们可以将这个方法封装到一个类里面,形成切面,因为可能在方法前面和方法后面都要使用这个方法)

   连接点:连接点表示一个方法的执行,切面在连接点上切入(如:add(); delete(); update(); select()方法是连接点)

   通知:在切面的某个特定的连接点上执行的动作是通知(其实这都是有迹可循的,例如log();是在目标方法执行前执行把它称为关注点,spring把它整理出来了前置通知,后置通知,异常通知,环绕通知,可以认为通知是一个特定的连接点)(也是一个过程,一个动作)

  目标对象:被代理的对象就叫目标对象(如前面写的推广到一般情况下的Object target)

  织入:把切面连接到其它应用程序类型或者对象上,并创建一个被通知的对象(有几种类型,上面).(其实是一个过程)

  综上,只有三个概念  1.关注点------模块化--->2.切面     3.连接点

  再综上,只有切面,连接点两个概念。

 

5、使用spring实现aop

   第一种实现方式--通过springAPI来实现

 

以上是关于SSMSpring6(十.面向切面编程AOP)的主要内容,如果未能解决你的问题,请参考以下文章

面向切面编程(AOP)

什么是面向切面编程

三面向切面编程

AOP面向切面编程(个人笔记1.1)

Spring的AOP面向切面编程

AOP 面向切面编程