Spring AOP

1.为什么要学习AOP?

案例:有一个接口Service有一个insert方法,在insert被调用时打印调用前的毫秒数与调用后的毫秒数,其实现为

java 复制代码
public class UserServiceImpl implements UserService {

    private UserDao userDao;

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    public void addUser(){
        System.out.println("方法开始时间:"+new Date());
        userDao.addUser();
        System.out.println("方法结束时间:"+new Date());
    }
}
  • 问题:输出日志的逻辑还是无法复用

2.AOP概述

AOP:全称是Aspect Oriented Programming即:面向切面编程。

简单的说它就是把我们程序重复的代码抽取出来,在需要执行的时候,使用动态代理的技术,在不修改源码的基础上,对程序进行增强:权限校验,日志记录,性能监控,事务控制.

3.AOP相关术语

  1. 连接点(joinpoint)

    被拦截到的点,因为Spring只支持方法类型的连接点,所以在Spring中连接点指的就是被拦截到的方法。

  2. 切入点(pointcut)

    切入点是指我们要对哪些连接点进行拦截的定义

  3. 通知/增强(advice)

    所谓通知指的就是指拦截到连接点之后要执行的代码,通知分为前置、后置、异常、最终、环绕通知五类

  4. 切面(aspect)

    是切入点和通知的结合

  5. 引介(introduction)

    是一种特殊的通知,在不修改代码的前提下,引介可以在运行期为类动态地添加一些方法或字段

  6. 目标对象(Target)

    要代理的目标对象(要增强的类)

  7. 织入(weave)

    将增强应用到目标的过程将advice应用到target的过程

  8. 代理(Proxy)

    一个类被AOP织入增强之后,就产生一个代理类

4.Spring的AOP配置

4.1创建工程

4.1.1pom.xml

XML 复制代码
<dependencies>
    <!-- ioc -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.1.8.RELEASE</version>
    </dependency>
    <!--        日志-->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>1.7.30</version>
    </dependency>
<!--    aop-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>5.1.8.RELEASE</version>
    </dependency>
</dependencies>

4.1.2dao

java 复制代码
package com.by.dao;

public class UserDaoImpl implements UserDao{
    @Override
    public void addUser(){
        System.out.println("insert into tb_user......");
    }
}

4.1.3service

java 复制代码
package com.by.service;

import com.by.dao.UserDao;

public class UserServiceImpl implements UserService{
    private UserDao userDao;

    public void setUserDao(UserDao userDao){
        this.userDao=userDao;
    }
    @Override
    public void addUser(){
        userDao.addUser();
//        int a=10;
//        System.out.println(a/0);
    }
}

4.1.4applicationContext.xml

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: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/aop
       			   http://www.springframework.org/schema/aop/spring-aop.xsd">
    <bean id="userDao" class="com.by.dao.UserDaoImpl"></bean>
    <bean id="userService" class="com.by.service.UserServiceImpl">
        <property name="userDao" ref="userDao"></property>
    </bean>
</beans>

4.1.5web

java 复制代码
package com.by.web;

import com.by.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Client {
    public static void main(String[] args) {
      ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService userService = ac.getBean("userService", UserService.class);
        userService.addUser();
        System.out.println(userService.getClass());

    }
}

4.2增强

1.创建增强类

java 复制代码
package com.by.advice;

import org.aspectj.lang.ProceedingJoinPoint;

public class MyAdvice {
    public void before(){
        System.out.println("前置通知....");
    }
    public void after(){
        System.out.println("最终通知....");
    }
    public void afterReturn(){
     System.out.println("后置通知.....");
    }
    public void afterThrow(){
        System.out.println("异常通知,出错了......");
    }
    public void around(ProceedingJoinPoint joinPoint){
        try {
            System.out.println("方法执行前的环绕通知.......");
            joinPoint.proceed();
            System.out.println("方法执行后的环绕通知........");
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }
}

2.配置增强类

java 复制代码
<!--    增强-->
    <bean id="myAdvice" class="com.by.advice.MyAdvice"></bean>

4.3切点

  1. 切点表达式

    表达式语法:

    execution([修饰符] 返回值类型 包名.类名.方法名(参数))

    例如:

    execution(* com.by.service.UserService.add(..))

    execution(* com.by.service.UserService.*(..))

    execution(* com.by.service.*.*(..))

  2. 配置切点

XML 复制代码
  <aop:config>
        <!--    切点-->
        <aop:pointcut id="pointcut" expression="execution(* com.by.service.*.*(..))"/>
   </aop:config>

4.4切面

  1. 增强的类型

    • aop:before:用于配置前置通知

    • aop:after-returning:用于配置后置【try】通知,它和异常通知只能有一个执行

    • aop:after-throwing:用于配置异常【catch】通知,它和后置通知只能执行一个

    • aop:after:用于配置最终【finally】通知

    • aop:around:用于配置环绕通知

  2. 配置切面

XML 复制代码
<!--    增强-->
    <bean id="myAdvice" class="com.by.advice.MyAdvice"></bean>

    <aop:config>
        <!--    切点-->
        <aop:pointcut id="pointcut" expression="execution(* com.by.service.*.*(..))"/>
        <!--    切面:将增强作用与切点上-->
        <aop:aspect ref="myAdvice">
<!--            方法执行前-->
            <aop:before method="before" pointcut-ref="pointcut"></aop:before>
<!--            最终方法-->
            <aop:after method="after" pointcut-ref="pointcut"></aop:after>
<!--            环绕通知-->
            <aop:around method="around" pointcut-ref="pointcut"></aop:around>
<!--            方法执行后-->
            <aop:after-returning method="afterReturn" pointcut-ref="pointcut"></aop:after-returning>
<!--            异常通知-->
            <aop:after-throwing method="afterThrow" pointcut-ref="pointcut"></aop:after-throwing>
        </aop:aspect>
    </aop:config>

4.5测试

5.基于注解的AOP配置

5.1创建工程

5.1.1pom.xml

XML 复制代码
   <dependencies>
        <!-- ioc -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.8.RELEASE</version>
        </dependency>
        <!--        日志-->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.30</version>
        </dependency>
        <!--    aop-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.1.8.RELEASE</version>
        </dependency>
    </dependencies>

5.1.2dao

java 复制代码
package by.dao;

import org.springframework.stereotype.Repository;

@Repository
public class UserDaoImpl implements UserDao{
    @Override
    public void addUser(){
        System.out.println("insert into tb_user......");
    }
}

5.1.3service

java 复制代码
package by.service;

import by.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService{

    @Autowired
    private UserDao userDao;
    @Override
    public void addUser(){
        userDao.addUser();
//        int a=10;
//        System.out.println(a/0);
    }
}

5.1.4.applicationContext.xml

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: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/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="by"></context:component-scan>
<!--    告诉Spring要扫描的注解-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

5.1.5 测试

java 复制代码
package by.web;

import by.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Client {
    public static void main(String[] args) {
      ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService userService = ac.getBean("userServiceImpl", UserService.class);
        userService.addUser();
        System.out.println(userService.getClass());

    }
}

5.2增强的applicationContext.xml语句

XML 复制代码
<!-- 开启spring对注解AOP的支持 -->
<aop:aspectj-autoproxy/>

5.3AOP配置

  1. 常用注解

    • @Aspect:把当前类声明为切面类

    • @Before:前置通知,可以指定切入点表达式

    • @AfterReturning:后置【try】通知,可以指定切入点表达式

    • @AfterThrowing:异常【catch】通知,可以指定切入点表达式

    • @After:最终【finally】通知,可以指定切入点表达式

    • @Around:环绕通知,可以指定切入点表达式

2.注解方式实现aop

java 复制代码
package by.advice;

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

@Component
@Aspect //标识要增强的类
public class MyAdvice {
    @Before("execution(* by.service.*.*(..))")
    public void before(){
        System.out.println("前置通知....");
    }

    @After("execution(* by.service.*.*(..))")
    public void after(){
        System.out.println("最终通知....");
    }

    @AfterReturning("execution(* by.service.*.*(..))")
    public void afterReturn(){
     System.out.println("后置通知.....");
    }

    @AfterThrowing("execution(* by.service.*.*(..))")
    public void afterThrow(){
        System.out.println("异常通知,出错了......");
    }

    @Around("execution(* by.service.*.*(..))")
    public void around(ProceedingJoinPoint joinPoint){
        try {
            System.out.println("方法执行前的环绕通知.......");
            joinPoint.proceed();
            System.out.println("方法执行后的环绕通知........");
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }
}

测试结果

6.AOP的核心概念

1.aop的核心概念

切点(pintcut):要增强的方法,eg:add()、update()

通知/增强(advice):要搞的事情,eg:日志

前置通知:aop:before

后置通知:aop:after-returning【try】

最终通知:aop:after【finally】

异常通知:aop:after-throwing【catch】

环绕通知:aop:around

try{

...

return aop:after-returning

}catch(Exception e){

...

aop:after-throwing

}finally{

...

aop:after

}

切面(aspect):把增强应用到切点上

2、切点表达式

格式:execution([修饰符] 返回值 报名.类名.方法名(参数))

eg:execution(* com.by.service.*.*(..))

3、基于xml的aop配置

1)pom.xml

spring-context、spring-aspects、slf4j-log4j12

2)advice

public class MyLogAdvice {

public void before(){

System.out.println("前置通知....");

}

}

2)aop

<!--增强(advice)-->

<bean id="myLogAdvice" class="com.by.advice.MyLogAdvice"></bean>

<aop:config>

<!--切点(pintcut)-->

<aop:pointcut id="pointcut" expression="execution(* com.by.service.*.*(..))"/>

<aop:aspect ref="myLogAdvice">

<aop:before method="before" pointcut-ref="pointcut"></aop:before>

</aop:aspect>

</aop:config>

4、spring基于注解的aop配置

1)pom.xml

spring-context、spring-aspects、slf4j-log4j12

2)advice

@Component

@Aspect

public class MyLogAdvice {

@Before("execution(* com.by.service.*.*(..)")

public void before(){

System.out.println("前置通知....");

}

}

5、开启spring对aop注解的支持

<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

相关推荐
重生之我要进大厂18 分钟前
LeetCode 876
java·开发语言·数据结构·算法·leetcode
_祝你今天愉快21 分钟前
技术成神之路:设计模式(十四)享元模式
java·设计模式
小筱在线1 小时前
SpringCloud微服务实现服务熔断的实践指南
java·spring cloud·微服务
luoluoal1 小时前
java项目之基于Spring Boot智能无人仓库管理源码(springboot+vue)
java·vue.js·spring boot
ChinaRainbowSea1 小时前
十三,Spring Boot 中注入 Servlet,Filter,Listener
java·spring boot·spring·servlet·web
小游鱼KF1 小时前
Spring学习前置知识
java·学习·spring
扎克begod1 小时前
JAVA并发编程系列(9)CyclicBarrier循环屏障原理分析
java·开发语言·python
青灯文案11 小时前
SpringBoot 项目统一 API 响应结果封装示例
java·spring boot·后端
我就是程序猿1 小时前
tomcat的配置
java·tomcat
阳光阿盖尔2 小时前
EasyExcel的基本使用——Java导入Excel数据
java·开发语言·excel