Spring(六)之AOP

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

简单的说它就是把我们程序重复的代码抽取出来,在需要执行的时候,使用动态代理的技术,在不修改源码的基础上,对我们的已有方法进行增强。

如何使用Spring中的AOP来实现动态代理,简化代码

Step1:文件导入坐标

  <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.2.RELEASE</version>
  </dependency>
   <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.7</version>
        </dependency>
 

这里的aspectjweaver为后面的切入点表达式做准备

Step2:配置相关的bean.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">


<!--配置Service,Service中需要用到Dao-->
    <bean id="IUserService" class="com.imis.service.impl.UserServiceImpl">
        <property name="iUserDao" ref="IUserDao"></property>
    </bean>
<!--配置Dao,Dao中又继承BaseDao,BaseDao中需要用到QueryRunner-->
    <bean id="IUserDao" class="com.imis.dao.impl.UserDaoImpl">
        <property name="queryRunner" ref="runner"></property>
    </bean>

<!--    <context:component-scan base-package="com.imis"></context:component-scan>-->
<!--QueryRunner配置,注入数据源,由于QueryRunner中没有关于数据源的set方法,但提供了构造函数,所以用构造函数的方法来注入-->
    <bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype">
        <constructor-arg name="ds" ref="dataSource"></constructor-arg>
    </bean>

<!--  配置数据源  -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.cj.jdbc.Driver"/>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/firstmybatis?serverTimezone=Asia/Shanghai"/>
        <property name="user" value="root"/>
        <property name="password" value="zhengyunyu524"/>
    </bean>
<!--  把log放进ioc容器中  -->
    <bean id="logger" class="com.imis.log.Log">

    </bean>
<!--配置aop-->
    <aop:config>
<!--    配置切面指定类    -->
        <aop:aspect id="logAdvice" ref="logger">
<!--      配置切面指定类的方法及要切入的类的方法      -->
<!--       前置     -->
            <aop:before method="before" pointcut="execution(public * com.imis.service.impl.UserServiceImpl.queryAll())"></aop:before>
<!--        后置    -->
            <aop:after-returning method="after" pointcut="execution(public * com.imis.service.impl.UserServiceImpl.queryAll())"></aop:after-returning>
<!--         异常   -->
            <aop:after-throwing method="afterexception" pointcut="execution(public * com.imis.service.impl.UserServiceImpl.queryAll())"></aop:after-throwing>
<!--         最终   -->
            <aop:after method="end" pointcut="execution(public * com.imis.service.impl.UserServiceImpl.queryAll())"></aop:after>

        </aop:aspect>
    </aop:config>
</beans>

spring中基于XML的AOP配置步骤
1、把通知Bean也交给spring来管理
2、使用aop:config标签表明开始AOP的配置
3、使用aop:aspect标签表明配置切面
id属性:是给切面提供一个唯一标识
ref属性:是指定通知类bean的Id。
4、在aop:aspect标签的内部使用对应标签来配置通知的类型

        aop:before:表示配置前置通知
        aop:after-returning:后置通知
        aop:after-throwing:异常通知
        aop:after:最终通知
        method属性:用于指定Logger类中哪个方法是前置通知

pointcut属性:用于指定切入点表达式,该表达式的含义指的是对业务层中哪些方法增强

    切入点表达式的写法:
       关键字:execution(表达式)
       表达式:访问修饰符  返回值  包名.包名.包名...类名.方法名(参数列表)
       标准的表达式写法:
       public void com.itheima.service.impl.AccountServiceImpl.saveAccount()

示例Log代码:

public class Log {
    
    

    public void before(){
    
    
        System.out.println("执行前");
    }
    public void after(){
    
    
        System.out.println("执行后");
    }
    public void afterexception(){
    
    
        System.out.println("有异常");
    }
    public void end(){
    
    
        System.out.println("最终结束");
    }
}

此时在配置AOP基础上我们运行queryAll方法,可以得到以下结果

执行前
[User{
    
    username='zzy', userid=1}, User{
    
    username='yxx', userid=2}, User{
    
    username='ss', userid=3}]
执行后
最终结束

切入点表达式的写法:

关键字:execution(表达式)
表达式:
    访问修饰符  返回值  包名.包名.包名...类名.方法名(参数列表)
标准的表达式写法:
     public void com.itheima.service.impl.AccountServiceImpl.saveAccount()
访问修饰符可以省略
    void com.itheima.service.impl.AccountServiceImpl.saveAccount()
返回值可以使用通配符,表示任意返回值
    * com.itheima.service.impl.AccountServiceImpl.saveAccount()
包名可以使用通配符,表示任意包。但是有几级包,就需要写几个*.
    * *.*.*.*.AccountServiceImpl.saveAccount())
包名可以使用..表示当前包及其子包
    * *..AccountServiceImpl.saveAccount()
类名和方法名都可以使用*来实现通配
     * *..*.*()
参数列表:
    可以直接写数据类型:
        基本类型直接写名称           int
        引用类型写包名.类名的方式   java.lang.String
        可以使用通配符表示任意类型,但是必须有参数
        可以使用..表示有无参数均可,有参数可以是任意类型
全通配写法:
    * *..*.*(..)

实际开发中切入点表达式的通常写法:
    切到业务层实现类下的所有方法
    * com.itheima.service.impl.*.*(..)

通用切入点表达式

 <aop:pointcut id="query" expression="execution(public * com.imis.service.impl.UserServiceImpl.queryAll())"/>
<!--      配置切面指定类的方法及要切入的类的方法      -->
<!--       前置     -->
<aop:before method="before" pointcut-ref="query"></aop:before>

猜你喜欢

转载自blog.csdn.net/weixin_45925906/article/details/112846530
今日推荐