Java EE3-我独自整合(第七章:Spring AOP 通知类型)

  • AOP 通知描述了抽取的共性功能,根据共性功能抽取的位置不同,最终运行代码时要将 其加入到合理的位置
  • AOP 通知共分为 5 种类型
    • 前置通知
    • 后置通知
    • 环绕通知(重点)
    • 最终通知(了解)
    • 抛出异常后通知(了解)

前置通知

  • 名称:前置通知
  • 标签:aop:before
  • 作用:设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方 法前运行

代码

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

/**
 * 通知示例。
 */
public class MyAdvice {
    /**
     * 前置通知
     * 在切入点表达式代表的连接点代码运行之前执行的逻辑。
     * 这种通知类型中的所有方法的命名没有任何要求,推荐见名知意。
     * 方法不要有返回值。不推荐写参数表。
     */
    public void before(){
        System.out.println("前置通知方法运行。");
    }
}

Spring配置

java 复制代码
<?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:c="http://www.springframework.org/schema/c"
       xmlns:p="http://www.springframework.org/schema/p"
       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"
       default-autowire="default">

    <!--配置StudentDao和StudentService,通过设置注入实现依赖注入。-->
    <bean id="studentDao" class="com.tianshi.dao.impl.StudentDaoImpl"/>

    <bean id="studentService" class="com.tianshi.service.impl.StudentServiceImpl">
        <property name="studentDao" ref="studentDao"/>
    </bean>

    <!--配置通知对象-->
    <bean id="loggerAdvice" class="com.tianshi.advice.LoggerAdvice"></bean>
    <!--通知对象-->
    <bean id="myAdvice" class="com.tianshi.advice.MyAdvice"></bean>
    <!--定义AOP配置信息-->
    <aop:config>
        <!--定义切入点,也就是连接点表达式
            id 唯一名字
            expression 表达式字符串
        -->
        <aop:pointcut id="loggerPC"
                      expression="execution( * com.tianshi..service.*Service.*(..) )"/>
        <!--<aop:pointcut id="loggerPC2"
                      expression="execution( * com.tianshi.service.*Service.*() )"/>-->
        <!--定义切面 id唯一名字,可以省略
            ref 属性代表,标签aop:aspect 切面内定义的所有内容,使用的通知类型对象是什么。
        -->
        <aop:aspect ref="loggerAdvice">
            <!--定义具体的切入位置。
               把通知类型对象LoggerAdvice中的方法log4Timestamp,在StudentService.addStudent方法前运行。
               标签 aop:before 代表的就是 "之前"。 method属性代表通知类型中的方法名.
               pointcut-ref属性代表执行通知逻辑的具体位置,使用表达式描述的位置。
            -->
            <aop:before method="log4Timestamp" pointcut-ref="loggerPC"/>
            <!--<aop:before method="log4Timestamp" pointcut-ref="loggerPC2"/>-->
        </aop:aspect>

        <!--配置切面,学习不同的通知类型。 ref属性:使用的通知对象
            多个advice通知,运行顺序:
              前置:先配置的先运行。后配置的后运行。按照配置文件顺序依次执行。
        -->
        <aop:aspect ref="myAdvice">
            <!--前置通知
                aop:before - 前置通知标签
                method - 使用的通知对象中的方法命名。
                pointcut-ref - 使用的切入点表达式
            -->
            <aop:before method="before" pointcut-ref="loggerPC"/>
        </aop:aspect>
    </aop:config>

<!--    <bean id="book" class="com.tianshi.domain.Book">-->
<!--&lt;!&ndash;        init-method="init" destroy-method="destroy"&ndash;&gt;-->
<!--        <property name="id" value="100"/>-->
<!--        <property name="name" value="spring in action"/>-->
<!--    </bean>-->

<!--        <bean id="student1" name="stu1 stu2" class="com.tianshi.domain.Student" >-->
<!--                <property name="id" value="1"/>-->
<!--                <property name="name" value="单例学生"/>-->
<!--        </bean>-->

<!--        <bean id="student" name="student_" class="com.tianshi.domain.Student" scope="prototype">-->
<!--                <property name="id" value="1"/>-->
<!--                <property name="name" value="多例学生"/>-->
<!--        </bean>-->

        <!--
    标签常用属性:
      name: 别名,给bean增加别名。功能和id相同。一个标签id唯一。name可以定义若干个。
             别名的值要求全局唯一。定义多个别名,可以使用符合分割。常用 ',' ' '。
             现在使用的不多。曾经struts框架流行的时候常用。
      scope : 作用域。可以赋予的值只有 singleton 和 prototype
             singleton, 单例的。Spring IoC容器启动的时候自动创建bean对象,且仅创建唯一一次。默认
             prototype, 多例的。IoC容器启动时不创建对象,每次调用geBean方法时,创建一个对象。
      init-method : 初始化方法。在对象创建完成,设置注入完成后,getBean获取对象前,运行。
             初始化逻辑,会在容器创建完成时,就已完成。
      destroy-method : 销毁方法。在容器关闭|销毁前执行。
        -->



        <!--        &lt;!&ndash;Spring框架的核心配置文件&ndash;&gt;-->
<!--        &lt;!&ndash;一个bean标签就是一个domain对象,让Spring框架帮忙创建管理这个对象-->
<!--            id      唯一标识-->
<!--            class   类型的具体名称,必须写包名.类名-->
<!--        &ndash;&gt;-->
<!--&lt;!&ndash;        <bean id="student" class="com.tianshi.domain.Student"/>&ndash;&gt;-->

<!--        &lt;!&ndash;配置静态工厂&ndash;&gt;-->
<!--&lt;!&ndash;        //唯一一个静态工厂(构造注入)&ndash;&gt;-->
<!--        <bean id="studentWithStaticFactory" class="com.tianshi.factory.StudentStaticFactory"-->
<!--              factory-method="getStudent">-->
<!--                &lt;!&ndash;DI构造注入-->
<!--                    用来确定给哪个属性赋值(可同时使用):-->
<!--                        index:参数在构造参数表中的下标,从0开始计数-->
<!--                        name:构造方法参数名称-->
<!--                        type:构造方法参数类型-->
<!--                    设置属性的值(只用一个):-->
<!--                        value:要传入的构造方法参数值(简单类型)-->
<!--                        ref:要传入的构造方法参数(引用类型)-->
<!--                    建议用index+name+value/ref完成配置-->
<!--                &ndash;&gt;-->
<!--                <constructor-arg index="0" name="id" value="100"/>-->
<!--                <constructor-arg index="1" name="name" value="小明"/>-->
<!--        </bean>-->

<!--        &lt;!&ndash;配置实例工厂两个&ndash;&gt;-->
<!--        &lt;!&ndash;C名称空间注入-->
<!--            基于参数索引的配置:-->
<!--                c:_索引数="值" 注入简单类型数据值-->
<!--                c:_索引数-ref="bean的id" 注入Spring容器中的其他的bean对象-->
<!--            基于参数名的配置:-->
<!--                c:参数名="值" 注入简单类型的数据值-->
<!--                c:参数名="bean的id" 注入Spring容器中其他的bean对象-->
<!--&ndash;&gt;-->
<!--&lt;!&ndash;        //第一个实例工厂(C名称空间注入)&ndash;&gt;-->
<!--        <bean id="studentFactory1" class="com.tianshi.factory.StudentInstanceFactory"/>-->
<!--        <bean id="studentWithInstanceFactory1" class="com.tianshi.domain.Student"-->
<!--        factory-bean="studentFactory1" factory-method="getStudent" c:_0="101" c:name="李四"/>-->
<!--&lt;!&ndash;        //Book的实例工厂&ndash;&gt;-->
<!--        <bean id="bookFactory" class="com.tianshi.factory.BookInstanceFactory"/>-->
<!--        <bean id="bookWithInstanceFactory" class="com.tianshi.domain.Book" p:id="1" p:name="空"/>-->
<!--&lt;!&ndash;        //第二个实例工厂(各种类型的属性注入的配置)&ndash;&gt;-->
<!--        <bean id="studentFactory2" class="com.tianshi.factory.StudentInstanceFactory"/>-->
<!--        &lt;!&ndash;自动装配&ndash;&gt;-->
<!--        &lt;!&ndash;局部的依赖注入自动装配-->
<!--            在bean标签中,有属性 autowire,代表是否采用自动装配逻辑。-->
<!--            可以设置的值:-->
<!--              default : 默认的,此是默认值。默认策略就是全局策略-->
<!--              no : 不使用自动装配逻辑。-->
<!--              byName : 根据名字完成自动装配。当前bean中的property的名字和当前配置文件中bean的id如果一样,则自动注入-->
<!--                有可能,类型不匹配-->
<!--              byType : 根据类型完成自动装配。当前bean中的property的类型和当前配置文件中bean的类型如有一样,则自动注入-->
<!--                有可能,当前配置文件中有多个bean的类型和同一个property的类型相同,抛出异常。-->
<!--                最常用。一般情况下,一个配置文件,不会配置多个同类型bean-->
<!--              constructor : 构造器注入。扫描当前bean类型中的有参数构造方法,扫描构造方法的参数表,-->
<!--                基于先byType,再byName的顺序逻辑,在当前配置文件中匹配bean对象,调用构造方法注入。-->
<!--        &ndash;&gt;-->
<!--        <bean id="studentWithInstanceFactory2" class="com.tianshi.domain.Student"-->
<!--              factory-bean="studentFactory2" factory-method="getStudent" autowire="byType">-->
<!--                <property name="id" value="105"/>-->
<!--                <property name="name" value="王"/>-->
<!--                &lt;!&ndash;数组array标签,一个标签代表一个数组&ndash;&gt;-->
<!--                <property name="hobbies">-->
<!--                        <array>-->
<!--                                &lt;!&ndash;数组中的简单数据,按照属性依次从0开始赋值&ndash;&gt;-->
<!--                                <value>0下标</value>-->
<!--                                <value>1下标</value>-->
<!--                                <value>2下标</value>-->
<!--                                &lt;!&ndash;ref,数组中的一个引用类型数据,按照配置属性依次从0下标开始赋值&ndash;&gt;-->
<!--                                &lt;!&ndash;<ref bean="beanId"></ref>&ndash;&gt;-->
<!--                        </array>-->
<!--                </property>-->
<!--                &lt;!&ndash;List集合,list标签,一个标签代表一个集合对象&ndash;&gt;-->
<!--                <property name="books">-->
<!--                        <list>-->
<!--                                &lt;!&ndash;0下标位置对象&ndash;&gt;-->
<!--                                <ref bean="bookWithInstanceFactory"></ref>-->
<!--                                &lt;!&ndash;配置一个局部的bean对象,不需要命名(不写id),局部有效&ndash;&gt;-->
<!--                                <bean class="com.tianshi.domain.Book">-->
<!--                                        <property name="id" value="2"></property>-->
<!--                                        <property name="name" value="算法导论"/>-->
<!--                                </bean>-->
<!--                                <ref bean="bookWithInstanceFactory"></ref>-->
<!--                        </list>-->
<!--                </property>-->
<!--                &lt;!&ndash;Set集合,set标签&ndash;&gt;-->
<!--                <property name="set">-->
<!--                        <set>-->
<!--                                <value>set值1</value>-->
<!--                                <value>set值2</value>-->
<!--                                <value>set值3</value>-->
<!--                        </set>-->
<!--                </property>-->
<!--                &lt;!&ndash;Map集合,map标签和entry子标签,一个map标签代表一个Map集合一个entry标签代表map中的一个键值对&ndash;&gt;-->
<!--                <property name="map">-->
<!--                        <map>-->
<!--                                &lt;!&ndash;entry有属性 key:简单类型键值,key-ref:引用类型键值-->
<!--                    value:简单类型value值,value-ref:引用类型value值&ndash;&gt;-->
<!--                                <entry key="1" value="value1"></entry>-->
<!--                                <entry key="2" value="value2"></entry>-->
<!--                                <entry key="3" value="value3"></entry>-->
<!--                        </map>-->
<!--                </property>-->
<!--                &lt;!&ndash;引用类型属性,使用ref注入&ndash;&gt;-->
<!--&lt;!&ndash;                <property name="book" ref="bookWithInstanceFactory"/>&ndash;&gt;-->
<!--        </bean>-->
<!--        &lt;!&ndash;配置FactoryBean工厂两个&ndash;&gt;-->
<!--        &lt;!&ndash;设值注入-->
<!--            标签属性:-->
<!--                name:要注入数据的property属性名-->
<!--                value:为属性赋值(简单数据类型)-->
<!--                ref:为属性赋值(引用数据类型)-->
<!--        &ndash;&gt;-->
<!--&lt;!&ndash;        //第一个FactoryBean工厂(设值注入)&ndash;&gt;-->
<!--        <bean id="studentWithFactoryBean1" class="com.tianshi.factory.StudentFactoryBean">-->
<!--                <property name="id" value="102"/>-->
<!--                <property name="name" value="王五"/>-->
<!--        </bean>-->

<!--        &lt;!&ndash;P名称空间注入-->
<!--            仅基于参数名,无基于索引:-->
<!--                p:属性名 = "简单数据"-->
<!--                p:属性名-ref = "引用数据,其他bean标签的id"-->
<!--        &ndash;&gt;-->
<!--&lt;!&ndash;        //第二个FactoryBean工厂(P名称空间注入)&ndash;&gt;-->
<!--        <bean id="studentWithFactoryBean2" class="com.tianshi.factory.StudentFactoryBean" p:id="103" p:name="赵六"/>-->
</beans>

后置通知

  • 名称:后置通知
  • 标签:aop:after
  • 作用:设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法后运行

代码

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

/**
 * 通知示例。
 */
public class MyAdvice {
    /**
     * 前置通知
     * 在切入点表达式代表的连接点代码运行之前执行的逻辑。
     * 这种通知类型中的所有方法的命名没有任何要求,推荐见名知意。
     * 方法不要有返回值。不推荐写参数表。
     */
    public void before(){
        System.out.println("前置通知方法运行。");
    }
    /**
     * 后置通知
     * 方法不推荐写返回值和参数表。
     */
    public void after(){
        System.out.println("后置通知方法运行");
    }
}

Spring配置

java 复制代码
<?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:c="http://www.springframework.org/schema/c"
       xmlns:p="http://www.springframework.org/schema/p"
       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"
       default-autowire="default">

    <!--配置StudentDao和StudentService,通过设置注入实现依赖注入。-->
    <bean id="studentDao" class="com.tianshi.dao.impl.StudentDaoImpl"/>

    <bean id="studentService" class="com.tianshi.service.impl.StudentServiceImpl">
        <property name="studentDao" ref="studentDao"/>
    </bean>

    <!--配置通知对象-->
    <bean id="loggerAdvice" class="com.tianshi.advice.LoggerAdvice"></bean>
    <!--通知对象-->
    <bean id="myAdvice" class="com.tianshi.advice.MyAdvice"></bean>
    <!--定义AOP配置信息-->
    <aop:config>
        <!--定义切入点,也就是连接点表达式
            id 唯一名字
            expression 表达式字符串
        -->
        <aop:pointcut id="loggerPC"
                      expression="execution( * com.tianshi..service.*Service.*(..) )"/>
        <!--<aop:pointcut id="loggerPC2"
                      expression="execution( * com.tianshi.service.*Service.*() )"/>-->
        <!--定义切面 id唯一名字,可以省略
            ref 属性代表,标签aop:aspect 切面内定义的所有内容,使用的通知类型对象是什么。
        -->
        <aop:aspect ref="loggerAdvice">
            <!--定义具体的切入位置。
               把通知类型对象LoggerAdvice中的方法log4Timestamp,在StudentService.addStudent方法前运行。
               标签 aop:before 代表的就是 "之前"。 method属性代表通知类型中的方法名.
               pointcut-ref属性代表执行通知逻辑的具体位置,使用表达式描述的位置。
            -->
            <aop:before method="log4Timestamp" pointcut-ref="loggerPC"/>
            <!--<aop:before method="log4Timestamp" pointcut-ref="loggerPC2"/>-->
        </aop:aspect>

        <!--配置切面,学习不同的通知类型。 ref属性:使用的通知对象
            多个advice通知,运行顺序:
              前置:先配置的先运行。后配置的后运行。按照配置文件顺序依次执行。
        -->
        <aop:aspect ref="myAdvice">
            <!--前置通知
                aop:before - 前置通知标签
                method - 使用的通知对象中的方法命名。
                pointcut-ref - 使用的切入点表达式
            -->
            <aop:before method="before" pointcut-ref="loggerPC"/>
            <!--后置通知。切入点表达式对应的连接点方法运行完毕后运行的通知。连接点方法运行时是否发生异常,
                  后置通知都会正常执行。
                  如果同一个切入点配置了多个后置通知和最终通知。执行的顺序是配置顺序的逆序。
                aop:after - 后置通知标签
                method - 通知方法名称
            -->
            <aop:after method="after" pointcut-ref="loggerPC"/>
        </aop:aspect>
    </aop:config>

<!--    <bean id="book" class="com.tianshi.domain.Book">-->
<!--&lt;!&ndash;        init-method="init" destroy-method="destroy"&ndash;&gt;-->
<!--        <property name="id" value="100"/>-->
<!--        <property name="name" value="spring in action"/>-->
<!--    </bean>-->

<!--        <bean id="student1" name="stu1 stu2" class="com.tianshi.domain.Student" >-->
<!--                <property name="id" value="1"/>-->
<!--                <property name="name" value="单例学生"/>-->
<!--        </bean>-->

<!--        <bean id="student" name="student_" class="com.tianshi.domain.Student" scope="prototype">-->
<!--                <property name="id" value="1"/>-->
<!--                <property name="name" value="多例学生"/>-->
<!--        </bean>-->

        <!--
    标签常用属性:
      name: 别名,给bean增加别名。功能和id相同。一个标签id唯一。name可以定义若干个。
             别名的值要求全局唯一。定义多个别名,可以使用符合分割。常用 ',' ' '。
             现在使用的不多。曾经struts框架流行的时候常用。
      scope : 作用域。可以赋予的值只有 singleton 和 prototype
             singleton, 单例的。Spring IoC容器启动的时候自动创建bean对象,且仅创建唯一一次。默认
             prototype, 多例的。IoC容器启动时不创建对象,每次调用geBean方法时,创建一个对象。
      init-method : 初始化方法。在对象创建完成,设置注入完成后,getBean获取对象前,运行。
             初始化逻辑,会在容器创建完成时,就已完成。
      destroy-method : 销毁方法。在容器关闭|销毁前执行。
        -->



        <!--        &lt;!&ndash;Spring框架的核心配置文件&ndash;&gt;-->
<!--        &lt;!&ndash;一个bean标签就是一个domain对象,让Spring框架帮忙创建管理这个对象-->
<!--            id      唯一标识-->
<!--            class   类型的具体名称,必须写包名.类名-->
<!--        &ndash;&gt;-->
<!--&lt;!&ndash;        <bean id="student" class="com.tianshi.domain.Student"/>&ndash;&gt;-->

<!--        &lt;!&ndash;配置静态工厂&ndash;&gt;-->
<!--&lt;!&ndash;        //唯一一个静态工厂(构造注入)&ndash;&gt;-->
<!--        <bean id="studentWithStaticFactory" class="com.tianshi.factory.StudentStaticFactory"-->
<!--              factory-method="getStudent">-->
<!--                &lt;!&ndash;DI构造注入-->
<!--                    用来确定给哪个属性赋值(可同时使用):-->
<!--                        index:参数在构造参数表中的下标,从0开始计数-->
<!--                        name:构造方法参数名称-->
<!--                        type:构造方法参数类型-->
<!--                    设置属性的值(只用一个):-->
<!--                        value:要传入的构造方法参数值(简单类型)-->
<!--                        ref:要传入的构造方法参数(引用类型)-->
<!--                    建议用index+name+value/ref完成配置-->
<!--                &ndash;&gt;-->
<!--                <constructor-arg index="0" name="id" value="100"/>-->
<!--                <constructor-arg index="1" name="name" value="小明"/>-->
<!--        </bean>-->

<!--        &lt;!&ndash;配置实例工厂两个&ndash;&gt;-->
<!--        &lt;!&ndash;C名称空间注入-->
<!--            基于参数索引的配置:-->
<!--                c:_索引数="值" 注入简单类型数据值-->
<!--                c:_索引数-ref="bean的id" 注入Spring容器中的其他的bean对象-->
<!--            基于参数名的配置:-->
<!--                c:参数名="值" 注入简单类型的数据值-->
<!--                c:参数名="bean的id" 注入Spring容器中其他的bean对象-->
<!--&ndash;&gt;-->
<!--&lt;!&ndash;        //第一个实例工厂(C名称空间注入)&ndash;&gt;-->
<!--        <bean id="studentFactory1" class="com.tianshi.factory.StudentInstanceFactory"/>-->
<!--        <bean id="studentWithInstanceFactory1" class="com.tianshi.domain.Student"-->
<!--        factory-bean="studentFactory1" factory-method="getStudent" c:_0="101" c:name="李四"/>-->
<!--&lt;!&ndash;        //Book的实例工厂&ndash;&gt;-->
<!--        <bean id="bookFactory" class="com.tianshi.factory.BookInstanceFactory"/>-->
<!--        <bean id="bookWithInstanceFactory" class="com.tianshi.domain.Book" p:id="1" p:name="空"/>-->
<!--&lt;!&ndash;        //第二个实例工厂(各种类型的属性注入的配置)&ndash;&gt;-->
<!--        <bean id="studentFactory2" class="com.tianshi.factory.StudentInstanceFactory"/>-->
<!--        &lt;!&ndash;自动装配&ndash;&gt;-->
<!--        &lt;!&ndash;局部的依赖注入自动装配-->
<!--            在bean标签中,有属性 autowire,代表是否采用自动装配逻辑。-->
<!--            可以设置的值:-->
<!--              default : 默认的,此是默认值。默认策略就是全局策略-->
<!--              no : 不使用自动装配逻辑。-->
<!--              byName : 根据名字完成自动装配。当前bean中的property的名字和当前配置文件中bean的id如果一样,则自动注入-->
<!--                有可能,类型不匹配-->
<!--              byType : 根据类型完成自动装配。当前bean中的property的类型和当前配置文件中bean的类型如有一样,则自动注入-->
<!--                有可能,当前配置文件中有多个bean的类型和同一个property的类型相同,抛出异常。-->
<!--                最常用。一般情况下,一个配置文件,不会配置多个同类型bean-->
<!--              constructor : 构造器注入。扫描当前bean类型中的有参数构造方法,扫描构造方法的参数表,-->
<!--                基于先byType,再byName的顺序逻辑,在当前配置文件中匹配bean对象,调用构造方法注入。-->
<!--        &ndash;&gt;-->
<!--        <bean id="studentWithInstanceFactory2" class="com.tianshi.domain.Student"-->
<!--              factory-bean="studentFactory2" factory-method="getStudent" autowire="byType">-->
<!--                <property name="id" value="105"/>-->
<!--                <property name="name" value="王"/>-->
<!--                &lt;!&ndash;数组array标签,一个标签代表一个数组&ndash;&gt;-->
<!--                <property name="hobbies">-->
<!--                        <array>-->
<!--                                &lt;!&ndash;数组中的简单数据,按照属性依次从0开始赋值&ndash;&gt;-->
<!--                                <value>0下标</value>-->
<!--                                <value>1下标</value>-->
<!--                                <value>2下标</value>-->
<!--                                &lt;!&ndash;ref,数组中的一个引用类型数据,按照配置属性依次从0下标开始赋值&ndash;&gt;-->
<!--                                &lt;!&ndash;<ref bean="beanId"></ref>&ndash;&gt;-->
<!--                        </array>-->
<!--                </property>-->
<!--                &lt;!&ndash;List集合,list标签,一个标签代表一个集合对象&ndash;&gt;-->
<!--                <property name="books">-->
<!--                        <list>-->
<!--                                &lt;!&ndash;0下标位置对象&ndash;&gt;-->
<!--                                <ref bean="bookWithInstanceFactory"></ref>-->
<!--                                &lt;!&ndash;配置一个局部的bean对象,不需要命名(不写id),局部有效&ndash;&gt;-->
<!--                                <bean class="com.tianshi.domain.Book">-->
<!--                                        <property name="id" value="2"></property>-->
<!--                                        <property name="name" value="算法导论"/>-->
<!--                                </bean>-->
<!--                                <ref bean="bookWithInstanceFactory"></ref>-->
<!--                        </list>-->
<!--                </property>-->
<!--                &lt;!&ndash;Set集合,set标签&ndash;&gt;-->
<!--                <property name="set">-->
<!--                        <set>-->
<!--                                <value>set值1</value>-->
<!--                                <value>set值2</value>-->
<!--                                <value>set值3</value>-->
<!--                        </set>-->
<!--                </property>-->
<!--                &lt;!&ndash;Map集合,map标签和entry子标签,一个map标签代表一个Map集合一个entry标签代表map中的一个键值对&ndash;&gt;-->
<!--                <property name="map">-->
<!--                        <map>-->
<!--                                &lt;!&ndash;entry有属性 key:简单类型键值,key-ref:引用类型键值-->
<!--                    value:简单类型value值,value-ref:引用类型value值&ndash;&gt;-->
<!--                                <entry key="1" value="value1"></entry>-->
<!--                                <entry key="2" value="value2"></entry>-->
<!--                                <entry key="3" value="value3"></entry>-->
<!--                        </map>-->
<!--                </property>-->
<!--                &lt;!&ndash;引用类型属性,使用ref注入&ndash;&gt;-->
<!--&lt;!&ndash;                <property name="book" ref="bookWithInstanceFactory"/>&ndash;&gt;-->
<!--        </bean>-->
<!--        &lt;!&ndash;配置FactoryBean工厂两个&ndash;&gt;-->
<!--        &lt;!&ndash;设值注入-->
<!--            标签属性:-->
<!--                name:要注入数据的property属性名-->
<!--                value:为属性赋值(简单数据类型)-->
<!--                ref:为属性赋值(引用数据类型)-->
<!--        &ndash;&gt;-->
<!--&lt;!&ndash;        //第一个FactoryBean工厂(设值注入)&ndash;&gt;-->
<!--        <bean id="studentWithFactoryBean1" class="com.tianshi.factory.StudentFactoryBean">-->
<!--                <property name="id" value="102"/>-->
<!--                <property name="name" value="王五"/>-->
<!--        </bean>-->

<!--        &lt;!&ndash;P名称空间注入-->
<!--            仅基于参数名,无基于索引:-->
<!--                p:属性名 = "简单数据"-->
<!--                p:属性名-ref = "引用数据,其他bean标签的id"-->
<!--        &ndash;&gt;-->
<!--&lt;!&ndash;        //第二个FactoryBean工厂(P名称空间注入)&ndash;&gt;-->
<!--        <bean id="studentWithFactoryBean2" class="com.tianshi.factory.StudentFactoryBean" p:id="103" p:name="赵六"/>-->
</beans>

环绕通知(重点)

  • 名称:环绕通知
  • 标签:aop:around
  • 作用:设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方 法前后运行

代码

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

import org.aspectj.lang.ProceedingJoinPoint;

/**
 * 通知示例。
 */
public class MyAdvice {
    /**
     * 前置通知
     * 在切入点表达式代表的连接点代码运行之前执行的逻辑。
     * 这种通知类型中的所有方法的命名没有任何要求,推荐见名知意。
     * 方法不要有返回值。不推荐写参数表。
     */
    public void before(){
        System.out.println("前置通知方法运行。");
    }
    /**
     * 后置通知
     * 方法不推荐写返回值和参数表。
     */
    public void after(){
        System.out.println("后置通知方法运行");
    }
    /**
     * 环绕通知
     * 通知方法包围目标方法(切入点表达式代表的方法),形成一个外层的环绕。
     * 环绕通知方法,要求返回值最好是Object类型。可以兼容目标方法的任意类型返回值。
     * 方法必须有一个参数, 类型是 ProceedingJoinPoint 。用于完成目标方法的调用。
     * 建议通知方法抛出Throwable类型的异常。用于兼容目标方法可能抛出的异常。
     */
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable{
        // 常见的实现方式如下:
        try{
            // 前置通知逻辑
            System.out.println("环绕通知 -- 前置逻辑");
            // 调用目标方法
            Object rtnValue = joinPoint.proceed();
            // 最终逻辑
            System.out.println("环绕通知 -- 最终逻辑");
            // 把目标方法运行结果返回。
            return rtnValue;
        }catch (Throwable e){ // 处理异常,是为了提供弥补逻辑。
            // 异常逻辑
            System.out.println("环绕通知 -- 异常逻辑");
            throw e; // 抛出异常,是为了传递异常
        }finally {
            // 后置逻辑
            System.out.println("环绕通知 -- 后置逻辑");
        }
    }
}

Spring配置

java 复制代码
<?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:c="http://www.springframework.org/schema/c"
       xmlns:p="http://www.springframework.org/schema/p"
       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"
       default-autowire="default">

    <!--配置StudentDao和StudentService,通过设置注入实现依赖注入。-->
    <bean id="studentDao" class="com.tianshi.dao.impl.StudentDaoImpl"/>

    <bean id="studentService" class="com.tianshi.service.impl.StudentServiceImpl">
        <property name="studentDao" ref="studentDao"/>
    </bean>

    <!--配置通知对象-->
    <bean id="loggerAdvice" class="com.tianshi.advice.LoggerAdvice"></bean>
    <!--通知对象-->
    <bean id="myAdvice" class="com.tianshi.advice.MyAdvice"></bean>
    <!--定义AOP配置信息-->
    <aop:config>
        <!--定义切入点,也就是连接点表达式
            id 唯一名字
            expression 表达式字符串
        -->
        <aop:pointcut id="loggerPC"
                      expression="execution( * com.tianshi..service.*Service.*(..) )"/>
        <!--<aop:pointcut id="loggerPC2"
                      expression="execution( * com.tianshi.service.*Service.*() )"/>-->
        <!--定义切面 id唯一名字,可以省略
            ref 属性代表,标签aop:aspect 切面内定义的所有内容,使用的通知类型对象是什么。
        -->
        <aop:aspect ref="loggerAdvice">
            <!--定义具体的切入位置。
               把通知类型对象LoggerAdvice中的方法log4Timestamp,在StudentService.addStudent方法前运行。
               标签 aop:before 代表的就是 "之前"。 method属性代表通知类型中的方法名.
               pointcut-ref属性代表执行通知逻辑的具体位置,使用表达式描述的位置。
            -->
            <aop:before method="log4Timestamp" pointcut-ref="loggerPC"/>
            <!--<aop:before method="log4Timestamp" pointcut-ref="loggerPC2"/>-->
        </aop:aspect>

        <!--配置切面,学习不同的通知类型。 ref属性:使用的通知对象
            多个advice通知,运行顺序:
              前置:先配置的先运行。后配置的后运行。按照配置文件顺序依次执行。
        -->
        <aop:aspect ref="myAdvice">
            <!--前置通知
                aop:before - 前置通知标签
                method - 使用的通知对象中的方法命名。
                pointcut-ref - 使用的切入点表达式
            -->
            <aop:before method="before" pointcut-ref="loggerPC"/>
            <!--后置通知。切入点表达式对应的连接点方法运行完毕后运行的通知。连接点方法运行时是否发生异常,
                  后置通知都会正常执行。
                  如果同一个切入点配置了多个后置通知和最终通知。执行的顺序是配置顺序的逆序。
                aop:after - 后置通知标签
                method - 通知方法名称
            -->
            <aop:after method="after" pointcut-ref="loggerPC"/>
            <!--环绕通知。围绕切入点表达式对应方法,通过代码逻辑完成通知切入。可实现前置+后置+最终+异常通知
                aop:around : 环绕通知标签
            -->
            <aop:around method="around" pointcut-ref="loggerPC"/>
        </aop:aspect>
    </aop:config>

<!--    <bean id="book" class="com.tianshi.domain.Book">-->
<!--&lt;!&ndash;        init-method="init" destroy-method="destroy"&ndash;&gt;-->
<!--        <property name="id" value="100"/>-->
<!--        <property name="name" value="spring in action"/>-->
<!--    </bean>-->

<!--        <bean id="student1" name="stu1 stu2" class="com.tianshi.domain.Student" >-->
<!--                <property name="id" value="1"/>-->
<!--                <property name="name" value="单例学生"/>-->
<!--        </bean>-->

<!--        <bean id="student" name="student_" class="com.tianshi.domain.Student" scope="prototype">-->
<!--                <property name="id" value="1"/>-->
<!--                <property name="name" value="多例学生"/>-->
<!--        </bean>-->

        <!--
    标签常用属性:
      name: 别名,给bean增加别名。功能和id相同。一个标签id唯一。name可以定义若干个。
             别名的值要求全局唯一。定义多个别名,可以使用符合分割。常用 ',' ' '。
             现在使用的不多。曾经struts框架流行的时候常用。
      scope : 作用域。可以赋予的值只有 singleton 和 prototype
             singleton, 单例的。Spring IoC容器启动的时候自动创建bean对象,且仅创建唯一一次。默认
             prototype, 多例的。IoC容器启动时不创建对象,每次调用geBean方法时,创建一个对象。
      init-method : 初始化方法。在对象创建完成,设置注入完成后,getBean获取对象前,运行。
             初始化逻辑,会在容器创建完成时,就已完成。
      destroy-method : 销毁方法。在容器关闭|销毁前执行。
        -->



        <!--        &lt;!&ndash;Spring框架的核心配置文件&ndash;&gt;-->
<!--        &lt;!&ndash;一个bean标签就是一个domain对象,让Spring框架帮忙创建管理这个对象-->
<!--            id      唯一标识-->
<!--            class   类型的具体名称,必须写包名.类名-->
<!--        &ndash;&gt;-->
<!--&lt;!&ndash;        <bean id="student" class="com.tianshi.domain.Student"/>&ndash;&gt;-->

<!--        &lt;!&ndash;配置静态工厂&ndash;&gt;-->
<!--&lt;!&ndash;        //唯一一个静态工厂(构造注入)&ndash;&gt;-->
<!--        <bean id="studentWithStaticFactory" class="com.tianshi.factory.StudentStaticFactory"-->
<!--              factory-method="getStudent">-->
<!--                &lt;!&ndash;DI构造注入-->
<!--                    用来确定给哪个属性赋值(可同时使用):-->
<!--                        index:参数在构造参数表中的下标,从0开始计数-->
<!--                        name:构造方法参数名称-->
<!--                        type:构造方法参数类型-->
<!--                    设置属性的值(只用一个):-->
<!--                        value:要传入的构造方法参数值(简单类型)-->
<!--                        ref:要传入的构造方法参数(引用类型)-->
<!--                    建议用index+name+value/ref完成配置-->
<!--                &ndash;&gt;-->
<!--                <constructor-arg index="0" name="id" value="100"/>-->
<!--                <constructor-arg index="1" name="name" value="小明"/>-->
<!--        </bean>-->

<!--        &lt;!&ndash;配置实例工厂两个&ndash;&gt;-->
<!--        &lt;!&ndash;C名称空间注入-->
<!--            基于参数索引的配置:-->
<!--                c:_索引数="值" 注入简单类型数据值-->
<!--                c:_索引数-ref="bean的id" 注入Spring容器中的其他的bean对象-->
<!--            基于参数名的配置:-->
<!--                c:参数名="值" 注入简单类型的数据值-->
<!--                c:参数名="bean的id" 注入Spring容器中其他的bean对象-->
<!--&ndash;&gt;-->
<!--&lt;!&ndash;        //第一个实例工厂(C名称空间注入)&ndash;&gt;-->
<!--        <bean id="studentFactory1" class="com.tianshi.factory.StudentInstanceFactory"/>-->
<!--        <bean id="studentWithInstanceFactory1" class="com.tianshi.domain.Student"-->
<!--        factory-bean="studentFactory1" factory-method="getStudent" c:_0="101" c:name="李四"/>-->
<!--&lt;!&ndash;        //Book的实例工厂&ndash;&gt;-->
<!--        <bean id="bookFactory" class="com.tianshi.factory.BookInstanceFactory"/>-->
<!--        <bean id="bookWithInstanceFactory" class="com.tianshi.domain.Book" p:id="1" p:name="空"/>-->
<!--&lt;!&ndash;        //第二个实例工厂(各种类型的属性注入的配置)&ndash;&gt;-->
<!--        <bean id="studentFactory2" class="com.tianshi.factory.StudentInstanceFactory"/>-->
<!--        &lt;!&ndash;自动装配&ndash;&gt;-->
<!--        &lt;!&ndash;局部的依赖注入自动装配-->
<!--            在bean标签中,有属性 autowire,代表是否采用自动装配逻辑。-->
<!--            可以设置的值:-->
<!--              default : 默认的,此是默认值。默认策略就是全局策略-->
<!--              no : 不使用自动装配逻辑。-->
<!--              byName : 根据名字完成自动装配。当前bean中的property的名字和当前配置文件中bean的id如果一样,则自动注入-->
<!--                有可能,类型不匹配-->
<!--              byType : 根据类型完成自动装配。当前bean中的property的类型和当前配置文件中bean的类型如有一样,则自动注入-->
<!--                有可能,当前配置文件中有多个bean的类型和同一个property的类型相同,抛出异常。-->
<!--                最常用。一般情况下,一个配置文件,不会配置多个同类型bean-->
<!--              constructor : 构造器注入。扫描当前bean类型中的有参数构造方法,扫描构造方法的参数表,-->
<!--                基于先byType,再byName的顺序逻辑,在当前配置文件中匹配bean对象,调用构造方法注入。-->
<!--        &ndash;&gt;-->
<!--        <bean id="studentWithInstanceFactory2" class="com.tianshi.domain.Student"-->
<!--              factory-bean="studentFactory2" factory-method="getStudent" autowire="byType">-->
<!--                <property name="id" value="105"/>-->
<!--                <property name="name" value="王"/>-->
<!--                &lt;!&ndash;数组array标签,一个标签代表一个数组&ndash;&gt;-->
<!--                <property name="hobbies">-->
<!--                        <array>-->
<!--                                &lt;!&ndash;数组中的简单数据,按照属性依次从0开始赋值&ndash;&gt;-->
<!--                                <value>0下标</value>-->
<!--                                <value>1下标</value>-->
<!--                                <value>2下标</value>-->
<!--                                &lt;!&ndash;ref,数组中的一个引用类型数据,按照配置属性依次从0下标开始赋值&ndash;&gt;-->
<!--                                &lt;!&ndash;<ref bean="beanId"></ref>&ndash;&gt;-->
<!--                        </array>-->
<!--                </property>-->
<!--                &lt;!&ndash;List集合,list标签,一个标签代表一个集合对象&ndash;&gt;-->
<!--                <property name="books">-->
<!--                        <list>-->
<!--                                &lt;!&ndash;0下标位置对象&ndash;&gt;-->
<!--                                <ref bean="bookWithInstanceFactory"></ref>-->
<!--                                &lt;!&ndash;配置一个局部的bean对象,不需要命名(不写id),局部有效&ndash;&gt;-->
<!--                                <bean class="com.tianshi.domain.Book">-->
<!--                                        <property name="id" value="2"></property>-->
<!--                                        <property name="name" value="算法导论"/>-->
<!--                                </bean>-->
<!--                                <ref bean="bookWithInstanceFactory"></ref>-->
<!--                        </list>-->
<!--                </property>-->
<!--                &lt;!&ndash;Set集合,set标签&ndash;&gt;-->
<!--                <property name="set">-->
<!--                        <set>-->
<!--                                <value>set值1</value>-->
<!--                                <value>set值2</value>-->
<!--                                <value>set值3</value>-->
<!--                        </set>-->
<!--                </property>-->
<!--                &lt;!&ndash;Map集合,map标签和entry子标签,一个map标签代表一个Map集合一个entry标签代表map中的一个键值对&ndash;&gt;-->
<!--                <property name="map">-->
<!--                        <map>-->
<!--                                &lt;!&ndash;entry有属性 key:简单类型键值,key-ref:引用类型键值-->
<!--                    value:简单类型value值,value-ref:引用类型value值&ndash;&gt;-->
<!--                                <entry key="1" value="value1"></entry>-->
<!--                                <entry key="2" value="value2"></entry>-->
<!--                                <entry key="3" value="value3"></entry>-->
<!--                        </map>-->
<!--                </property>-->
<!--                &lt;!&ndash;引用类型属性,使用ref注入&ndash;&gt;-->
<!--&lt;!&ndash;                <property name="book" ref="bookWithInstanceFactory"/>&ndash;&gt;-->
<!--        </bean>-->
<!--        &lt;!&ndash;配置FactoryBean工厂两个&ndash;&gt;-->
<!--        &lt;!&ndash;设值注入-->
<!--            标签属性:-->
<!--                name:要注入数据的property属性名-->
<!--                value:为属性赋值(简单数据类型)-->
<!--                ref:为属性赋值(引用数据类型)-->
<!--        &ndash;&gt;-->
<!--&lt;!&ndash;        //第一个FactoryBean工厂(设值注入)&ndash;&gt;-->
<!--        <bean id="studentWithFactoryBean1" class="com.tianshi.factory.StudentFactoryBean">-->
<!--                <property name="id" value="102"/>-->
<!--                <property name="name" value="王五"/>-->
<!--        </bean>-->

<!--        &lt;!&ndash;P名称空间注入-->
<!--            仅基于参数名,无基于索引:-->
<!--                p:属性名 = "简单数据"-->
<!--                p:属性名-ref = "引用数据,其他bean标签的id"-->
<!--        &ndash;&gt;-->
<!--&lt;!&ndash;        //第二个FactoryBean工厂(P名称空间注入)&ndash;&gt;-->
<!--        <bean id="studentWithFactoryBean2" class="com.tianshi.factory.StudentFactoryBean" p:id="103" p:name="赵六"/>-->
</beans>
  • 注意事项
    • 环绕通知必须依赖形参 ProceedingJoinPoint 才能实现对原始方法的调用,进而实现原始 方法调用前后同时添加通知
    • 通知中如果未使用 ProceedingJoinPoint 对原始方法进行调用将跳过原始方法的执行
    • 对原始方法的调用可以不接收返回值,通知方法设置成 void 即可,如果接收返回值, 必须设定为 Object 类型
    • 原始方法的返回值如果是 void 类型,通知方法的返回值类型可以设置成 void,也可以 设置成 Object
    • 由于无法预知原始方法运行后是否会抛出异常,因此环绕通知方法必须抛出 Throwable 对象

最终通知(了解)

  • 名称:最终通知(了解)
  • 标签:aop:after-returning
  • 作用:设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方 法正常执行完毕后运行

代码

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

import org.aspectj.lang.ProceedingJoinPoint;

/**
 * 通知示例。
 */
public class MyAdvice {
    /**
     * 前置通知
     * 在切入点表达式代表的连接点代码运行之前执行的逻辑。
     * 这种通知类型中的所有方法的命名没有任何要求,推荐见名知意。
     * 方法不要有返回值。不推荐写参数表。
     */
    public void before(){
        System.out.println("前置通知方法运行。");
    }
    /**
     * 后置通知
     * 方法不推荐写返回值和参数表。
     */
    public void after(){
        System.out.println("后置通知方法运行");
    }
    /**
     * 环绕通知
     * 通知方法包围目标方法(切入点表达式代表的方法),形成一个外层的环绕。
     * 环绕通知方法,要求返回值最好是Object类型。可以兼容目标方法的任意类型返回值。
     * 方法必须有一个参数, 类型是 ProceedingJoinPoint 。用于完成目标方法的调用。
     * 建议通知方法抛出Throwable类型的异常。用于兼容目标方法可能抛出的异常。
     */
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable{
        // 常见的实现方式如下:
        try{
            // 前置通知逻辑
            System.out.println("环绕通知 -- 前置逻辑");
            // 调用目标方法
            Object rtnValue = joinPoint.proceed();
            // 最终逻辑
            System.out.println("环绕通知 -- 最终逻辑");
            // 把目标方法运行结果返回。
            return rtnValue;
        }catch (Throwable e){ // 处理异常,是为了提供弥补逻辑。
            // 异常逻辑
            System.out.println("环绕通知 -- 异常逻辑");
            throw e; // 抛出异常,是为了传递异常
        }finally {
            // 后置逻辑
            System.out.println("环绕通知 -- 后置逻辑");
        }
    }
    /**
     * 最终通知
     */
    public void afterReturning(){
        System.out.println("最终通知方法运行");
    }
}

Spring配置

java 复制代码
<?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:c="http://www.springframework.org/schema/c"
       xmlns:p="http://www.springframework.org/schema/p"
       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"
       default-autowire="default">

    <!--配置StudentDao和StudentService,通过设置注入实现依赖注入。-->
    <bean id="studentDao" class="com.tianshi.dao.impl.StudentDaoImpl"/>

    <bean id="studentService" class="com.tianshi.service.impl.StudentServiceImpl">
        <property name="studentDao" ref="studentDao"/>
    </bean>

    <!--配置通知对象-->
    <bean id="loggerAdvice" class="com.tianshi.advice.LoggerAdvice"></bean>
    <!--通知对象-->
    <bean id="myAdvice" class="com.tianshi.advice.MyAdvice"></bean>
    <!--定义AOP配置信息-->
    <aop:config>
        <!--定义切入点,也就是连接点表达式
            id 唯一名字
            expression 表达式字符串
        -->
        <aop:pointcut id="loggerPC"
                      expression="execution( * com.tianshi..service.*Service.*(..) )"/>
        <!--<aop:pointcut id="loggerPC2"
                      expression="execution( * com.tianshi.service.*Service.*() )"/>-->
        <!--定义切面 id唯一名字,可以省略
            ref 属性代表,标签aop:aspect 切面内定义的所有内容,使用的通知类型对象是什么。
        -->
        <aop:aspect ref="loggerAdvice">
            <!--定义具体的切入位置。
               把通知类型对象LoggerAdvice中的方法log4Timestamp,在StudentService.addStudent方法前运行。
               标签 aop:before 代表的就是 "之前"。 method属性代表通知类型中的方法名.
               pointcut-ref属性代表执行通知逻辑的具体位置,使用表达式描述的位置。
            -->
            <aop:before method="log4Timestamp" pointcut-ref="loggerPC"/>
            <!--<aop:before method="log4Timestamp" pointcut-ref="loggerPC2"/>-->
        </aop:aspect>

        <!--配置切面,学习不同的通知类型。 ref属性:使用的通知对象
            多个advice通知,运行顺序:
              前置:先配置的先运行。后配置的后运行。按照配置文件顺序依次执行。
        -->
        <aop:aspect ref="myAdvice">
            <!--前置通知
                aop:before - 前置通知标签
                method - 使用的通知对象中的方法命名。
                pointcut-ref - 使用的切入点表达式
            -->
            <aop:before method="before" pointcut-ref="loggerPC"/>
            <!--后置通知。切入点表达式对应的连接点方法运行完毕后运行的通知。连接点方法运行时是否发生异常,
                  后置通知都会正常执行。
                  如果同一个切入点配置了多个后置通知和最终通知。执行的顺序是配置顺序的逆序。
                aop:after - 后置通知标签
                method - 通知方法名称
            -->
            <aop:after method="after" pointcut-ref="loggerPC"/>
            <!--环绕通知。围绕切入点表达式对应方法,通过代码逻辑完成通知切入。可实现前置+后置+最终+异常通知
                aop:around : 环绕通知标签
            -->
            <aop:around method="around" pointcut-ref="loggerPC"/>
            <!--最终通知。切入点表达式对应的连接点方法正常执行结束并返回后的通知
                  如果连接点方法发生异常,则最终通知不运行。
                  切入点位置如果存在后置通知和最终通知,执行顺序和配置的前后顺序一直。
                aop:after-returning : 最终通知标签
            -->
            <aop:after-returning method="afterReturning" pointcut-ref="loggerPC"/>
        </aop:aspect>
    </aop:config>

<!--    <bean id="book" class="com.tianshi.domain.Book">-->
<!--&lt;!&ndash;        init-method="init" destroy-method="destroy"&ndash;&gt;-->
<!--        <property name="id" value="100"/>-->
<!--        <property name="name" value="spring in action"/>-->
<!--    </bean>-->

<!--        <bean id="student1" name="stu1 stu2" class="com.tianshi.domain.Student" >-->
<!--                <property name="id" value="1"/>-->
<!--                <property name="name" value="单例学生"/>-->
<!--        </bean>-->

<!--        <bean id="student" name="student_" class="com.tianshi.domain.Student" scope="prototype">-->
<!--                <property name="id" value="1"/>-->
<!--                <property name="name" value="多例学生"/>-->
<!--        </bean>-->

        <!--
    标签常用属性:
      name: 别名,给bean增加别名。功能和id相同。一个标签id唯一。name可以定义若干个。
             别名的值要求全局唯一。定义多个别名,可以使用符合分割。常用 ',' ' '。
             现在使用的不多。曾经struts框架流行的时候常用。
      scope : 作用域。可以赋予的值只有 singleton 和 prototype
             singleton, 单例的。Spring IoC容器启动的时候自动创建bean对象,且仅创建唯一一次。默认
             prototype, 多例的。IoC容器启动时不创建对象,每次调用geBean方法时,创建一个对象。
      init-method : 初始化方法。在对象创建完成,设置注入完成后,getBean获取对象前,运行。
             初始化逻辑,会在容器创建完成时,就已完成。
      destroy-method : 销毁方法。在容器关闭|销毁前执行。
        -->



        <!--        &lt;!&ndash;Spring框架的核心配置文件&ndash;&gt;-->
<!--        &lt;!&ndash;一个bean标签就是一个domain对象,让Spring框架帮忙创建管理这个对象-->
<!--            id      唯一标识-->
<!--            class   类型的具体名称,必须写包名.类名-->
<!--        &ndash;&gt;-->
<!--&lt;!&ndash;        <bean id="student" class="com.tianshi.domain.Student"/>&ndash;&gt;-->

<!--        &lt;!&ndash;配置静态工厂&ndash;&gt;-->
<!--&lt;!&ndash;        //唯一一个静态工厂(构造注入)&ndash;&gt;-->
<!--        <bean id="studentWithStaticFactory" class="com.tianshi.factory.StudentStaticFactory"-->
<!--              factory-method="getStudent">-->
<!--                &lt;!&ndash;DI构造注入-->
<!--                    用来确定给哪个属性赋值(可同时使用):-->
<!--                        index:参数在构造参数表中的下标,从0开始计数-->
<!--                        name:构造方法参数名称-->
<!--                        type:构造方法参数类型-->
<!--                    设置属性的值(只用一个):-->
<!--                        value:要传入的构造方法参数值(简单类型)-->
<!--                        ref:要传入的构造方法参数(引用类型)-->
<!--                    建议用index+name+value/ref完成配置-->
<!--                &ndash;&gt;-->
<!--                <constructor-arg index="0" name="id" value="100"/>-->
<!--                <constructor-arg index="1" name="name" value="小明"/>-->
<!--        </bean>-->

<!--        &lt;!&ndash;配置实例工厂两个&ndash;&gt;-->
<!--        &lt;!&ndash;C名称空间注入-->
<!--            基于参数索引的配置:-->
<!--                c:_索引数="值" 注入简单类型数据值-->
<!--                c:_索引数-ref="bean的id" 注入Spring容器中的其他的bean对象-->
<!--            基于参数名的配置:-->
<!--                c:参数名="值" 注入简单类型的数据值-->
<!--                c:参数名="bean的id" 注入Spring容器中其他的bean对象-->
<!--&ndash;&gt;-->
<!--&lt;!&ndash;        //第一个实例工厂(C名称空间注入)&ndash;&gt;-->
<!--        <bean id="studentFactory1" class="com.tianshi.factory.StudentInstanceFactory"/>-->
<!--        <bean id="studentWithInstanceFactory1" class="com.tianshi.domain.Student"-->
<!--        factory-bean="studentFactory1" factory-method="getStudent" c:_0="101" c:name="李四"/>-->
<!--&lt;!&ndash;        //Book的实例工厂&ndash;&gt;-->
<!--        <bean id="bookFactory" class="com.tianshi.factory.BookInstanceFactory"/>-->
<!--        <bean id="bookWithInstanceFactory" class="com.tianshi.domain.Book" p:id="1" p:name="空"/>-->
<!--&lt;!&ndash;        //第二个实例工厂(各种类型的属性注入的配置)&ndash;&gt;-->
<!--        <bean id="studentFactory2" class="com.tianshi.factory.StudentInstanceFactory"/>-->
<!--        &lt;!&ndash;自动装配&ndash;&gt;-->
<!--        &lt;!&ndash;局部的依赖注入自动装配-->
<!--            在bean标签中,有属性 autowire,代表是否采用自动装配逻辑。-->
<!--            可以设置的值:-->
<!--              default : 默认的,此是默认值。默认策略就是全局策略-->
<!--              no : 不使用自动装配逻辑。-->
<!--              byName : 根据名字完成自动装配。当前bean中的property的名字和当前配置文件中bean的id如果一样,则自动注入-->
<!--                有可能,类型不匹配-->
<!--              byType : 根据类型完成自动装配。当前bean中的property的类型和当前配置文件中bean的类型如有一样,则自动注入-->
<!--                有可能,当前配置文件中有多个bean的类型和同一个property的类型相同,抛出异常。-->
<!--                最常用。一般情况下,一个配置文件,不会配置多个同类型bean-->
<!--              constructor : 构造器注入。扫描当前bean类型中的有参数构造方法,扫描构造方法的参数表,-->
<!--                基于先byType,再byName的顺序逻辑,在当前配置文件中匹配bean对象,调用构造方法注入。-->
<!--        &ndash;&gt;-->
<!--        <bean id="studentWithInstanceFactory2" class="com.tianshi.domain.Student"-->
<!--              factory-bean="studentFactory2" factory-method="getStudent" autowire="byType">-->
<!--                <property name="id" value="105"/>-->
<!--                <property name="name" value="王"/>-->
<!--                &lt;!&ndash;数组array标签,一个标签代表一个数组&ndash;&gt;-->
<!--                <property name="hobbies">-->
<!--                        <array>-->
<!--                                &lt;!&ndash;数组中的简单数据,按照属性依次从0开始赋值&ndash;&gt;-->
<!--                                <value>0下标</value>-->
<!--                                <value>1下标</value>-->
<!--                                <value>2下标</value>-->
<!--                                &lt;!&ndash;ref,数组中的一个引用类型数据,按照配置属性依次从0下标开始赋值&ndash;&gt;-->
<!--                                &lt;!&ndash;<ref bean="beanId"></ref>&ndash;&gt;-->
<!--                        </array>-->
<!--                </property>-->
<!--                &lt;!&ndash;List集合,list标签,一个标签代表一个集合对象&ndash;&gt;-->
<!--                <property name="books">-->
<!--                        <list>-->
<!--                                &lt;!&ndash;0下标位置对象&ndash;&gt;-->
<!--                                <ref bean="bookWithInstanceFactory"></ref>-->
<!--                                &lt;!&ndash;配置一个局部的bean对象,不需要命名(不写id),局部有效&ndash;&gt;-->
<!--                                <bean class="com.tianshi.domain.Book">-->
<!--                                        <property name="id" value="2"></property>-->
<!--                                        <property name="name" value="算法导论"/>-->
<!--                                </bean>-->
<!--                                <ref bean="bookWithInstanceFactory"></ref>-->
<!--                        </list>-->
<!--                </property>-->
<!--                &lt;!&ndash;Set集合,set标签&ndash;&gt;-->
<!--                <property name="set">-->
<!--                        <set>-->
<!--                                <value>set值1</value>-->
<!--                                <value>set值2</value>-->
<!--                                <value>set值3</value>-->
<!--                        </set>-->
<!--                </property>-->
<!--                &lt;!&ndash;Map集合,map标签和entry子标签,一个map标签代表一个Map集合一个entry标签代表map中的一个键值对&ndash;&gt;-->
<!--                <property name="map">-->
<!--                        <map>-->
<!--                                &lt;!&ndash;entry有属性 key:简单类型键值,key-ref:引用类型键值-->
<!--                    value:简单类型value值,value-ref:引用类型value值&ndash;&gt;-->
<!--                                <entry key="1" value="value1"></entry>-->
<!--                                <entry key="2" value="value2"></entry>-->
<!--                                <entry key="3" value="value3"></entry>-->
<!--                        </map>-->
<!--                </property>-->
<!--                &lt;!&ndash;引用类型属性,使用ref注入&ndash;&gt;-->
<!--&lt;!&ndash;                <property name="book" ref="bookWithInstanceFactory"/>&ndash;&gt;-->
<!--        </bean>-->
<!--        &lt;!&ndash;配置FactoryBean工厂两个&ndash;&gt;-->
<!--        &lt;!&ndash;设值注入-->
<!--            标签属性:-->
<!--                name:要注入数据的property属性名-->
<!--                value:为属性赋值(简单数据类型)-->
<!--                ref:为属性赋值(引用数据类型)-->
<!--        &ndash;&gt;-->
<!--&lt;!&ndash;        //第一个FactoryBean工厂(设值注入)&ndash;&gt;-->
<!--        <bean id="studentWithFactoryBean1" class="com.tianshi.factory.StudentFactoryBean">-->
<!--                <property name="id" value="102"/>-->
<!--                <property name="name" value="王五"/>-->
<!--        </bean>-->

<!--        &lt;!&ndash;P名称空间注入-->
<!--            仅基于参数名,无基于索引:-->
<!--                p:属性名 = "简单数据"-->
<!--                p:属性名-ref = "引用数据,其他bean标签的id"-->
<!--        &ndash;&gt;-->
<!--&lt;!&ndash;        //第二个FactoryBean工厂(P名称空间注入)&ndash;&gt;-->
<!--        <bean id="studentWithFactoryBean2" class="com.tianshi.factory.StudentFactoryBean" p:id="103" p:name="赵六"/>-->
</beans>

抛出异常后通知(了解)

  • 名称:异常通知(了解)
  • 标签:aop:after-throwing
  • 作用:设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方 法运行抛出异常后执行

代码

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

import org.aspectj.lang.ProceedingJoinPoint;

/**
 * 通知示例。
 */
public class MyAdvice {
    /**
     * 前置通知
     * 在切入点表达式代表的连接点代码运行之前执行的逻辑。
     * 这种通知类型中的所有方法的命名没有任何要求,推荐见名知意。
     * 方法不要有返回值。不推荐写参数表。
     */
    public void before(){
        System.out.println("前置通知方法运行。");
    }
    /**
     * 后置通知
     * 方法不推荐写返回值和参数表。
     */
    public void after(){
        System.out.println("后置通知方法运行");
    }
    /**
     * 环绕通知
     * 通知方法包围目标方法(切入点表达式代表的方法),形成一个外层的环绕。
     * 环绕通知方法,要求返回值最好是Object类型。可以兼容目标方法的任意类型返回值。
     * 方法必须有一个参数, 类型是 ProceedingJoinPoint 。用于完成目标方法的调用。
     * 建议通知方法抛出Throwable类型的异常。用于兼容目标方法可能抛出的异常。
     */
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable{
        // 常见的实现方式如下:
        try{
            // 前置通知逻辑
            System.out.println("环绕通知 -- 前置逻辑");
            // 调用目标方法
            Object rtnValue = joinPoint.proceed();
            // 最终逻辑
            System.out.println("环绕通知 -- 最终逻辑");
            // 把目标方法运行结果返回。
            return rtnValue;
        }catch (Throwable e){ // 处理异常,是为了提供弥补逻辑。
            // 异常逻辑
            System.out.println("环绕通知 -- 异常逻辑");
            throw e; // 抛出异常,是为了传递异常
        }finally {
            // 后置逻辑
            System.out.println("环绕通知 -- 后置逻辑");
        }
    }
    /**
     * 最终通知
     */
    public void afterReturning(){
        System.out.println("最终通知方法运行");
    }
    /**
     * 异常通知
     * 必须至少有一个参数,参数的类型是 Throwable 及其子孙类型。
     */
    public void afterThrowing(Exception ex){
        System.out.println("异常通知运行,异常消息是:" + ex.getMessage());
    }
}

Spring配置

java 复制代码
<?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:c="http://www.springframework.org/schema/c"
       xmlns:p="http://www.springframework.org/schema/p"
       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"
       default-autowire="default">

    <!--配置StudentDao和StudentService,通过设置注入实现依赖注入。-->
    <bean id="studentDao" class="com.tianshi.dao.impl.StudentDaoImpl"/>

    <bean id="studentService" class="com.tianshi.service.impl.StudentServiceImpl">
        <property name="studentDao" ref="studentDao"/>
    </bean>

    <!--配置通知对象-->
    <bean id="loggerAdvice" class="com.tianshi.advice.LoggerAdvice"></bean>
    <!--通知对象-->
    <bean id="myAdvice" class="com.tianshi.advice.MyAdvice"></bean>
    <!--定义AOP配置信息-->
    <aop:config>
        <!--定义切入点,也就是连接点表达式
            id 唯一名字
            expression 表达式字符串
        -->
        <aop:pointcut id="loggerPC"
                      expression="execution( * com.tianshi..service.*Service.*(..) )"/>
        <!--<aop:pointcut id="loggerPC2"
                      expression="execution( * com.tianshi.service.*Service.*() )"/>-->
        <!--定义切面 id唯一名字,可以省略
            ref 属性代表,标签aop:aspect 切面内定义的所有内容,使用的通知类型对象是什么。
        -->
        <aop:aspect ref="loggerAdvice">
            <!--定义具体的切入位置。
               把通知类型对象LoggerAdvice中的方法log4Timestamp,在StudentService.addStudent方法前运行。
               标签 aop:before 代表的就是 "之前"。 method属性代表通知类型中的方法名.
               pointcut-ref属性代表执行通知逻辑的具体位置,使用表达式描述的位置。
            -->
            <aop:before method="log4Timestamp" pointcut-ref="loggerPC"/>
            <!--<aop:before method="log4Timestamp" pointcut-ref="loggerPC2"/>-->
        </aop:aspect>

        <!--配置切面,学习不同的通知类型。 ref属性:使用的通知对象
            多个advice通知,运行顺序:
              前置:先配置的先运行。后配置的后运行。按照配置文件顺序依次执行。
        -->
        <aop:aspect ref="myAdvice">
            <!--前置通知
                aop:before - 前置通知标签
                method - 使用的通知对象中的方法命名。
                pointcut-ref - 使用的切入点表达式
            -->
            <aop:before method="before" pointcut-ref="loggerPC"/>
            <!--后置通知。切入点表达式对应的连接点方法运行完毕后运行的通知。连接点方法运行时是否发生异常,
                  后置通知都会正常执行。
                  如果同一个切入点配置了多个后置通知和最终通知。执行的顺序是配置顺序的逆序。
                aop:after - 后置通知标签
                method - 通知方法名称
            -->
            <aop:after method="after" pointcut-ref="loggerPC"/>
            <!--环绕通知。围绕切入点表达式对应方法,通过代码逻辑完成通知切入。可实现前置+后置+最终+异常通知
                aop:around : 环绕通知标签
            -->
            <aop:around method="around" pointcut-ref="loggerPC"/>
            <!--最终通知。切入点表达式对应的连接点方法正常执行结束并返回后的通知
                  如果连接点方法发生异常,则最终通知不运行。
                  切入点位置如果存在后置通知和最终通知,执行顺序和配置的前后顺序一直。
                aop:after-returning : 最终通知标签
            -->
            <aop:after-returning method="afterReturning" pointcut-ref="loggerPC"/>
            <!--异常通知。切入点表达式对应方法发生异常的时候,且异常类型和通知方法参数异常类型匹配的时候运行。
                  这里的匹配值,通知方法参数类型的异常类型,和切入点表达式方法配出的异常类型一直,或是其父(祖先)类型。
                aop:after-throwing : 异常通知标签
                throwing属性,方法method的异常类型参数名。
            -->
            <aop:after-throwing method="afterThrowing" pointcut-ref="loggerPC" throwing="ex"/>
        </aop:aspect>
    </aop:config>

<!--    <bean id="book" class="com.tianshi.domain.Book">-->
<!--&lt;!&ndash;        init-method="init" destroy-method="destroy"&ndash;&gt;-->
<!--        <property name="id" value="100"/>-->
<!--        <property name="name" value="spring in action"/>-->
<!--    </bean>-->

<!--        <bean id="student1" name="stu1 stu2" class="com.tianshi.domain.Student" >-->
<!--                <property name="id" value="1"/>-->
<!--                <property name="name" value="单例学生"/>-->
<!--        </bean>-->

<!--        <bean id="student" name="student_" class="com.tianshi.domain.Student" scope="prototype">-->
<!--                <property name="id" value="1"/>-->
<!--                <property name="name" value="多例学生"/>-->
<!--        </bean>-->

        <!--
    标签常用属性:
      name: 别名,给bean增加别名。功能和id相同。一个标签id唯一。name可以定义若干个。
             别名的值要求全局唯一。定义多个别名,可以使用符合分割。常用 ',' ' '。
             现在使用的不多。曾经struts框架流行的时候常用。
      scope : 作用域。可以赋予的值只有 singleton 和 prototype
             singleton, 单例的。Spring IoC容器启动的时候自动创建bean对象,且仅创建唯一一次。默认
             prototype, 多例的。IoC容器启动时不创建对象,每次调用geBean方法时,创建一个对象。
      init-method : 初始化方法。在对象创建完成,设置注入完成后,getBean获取对象前,运行。
             初始化逻辑,会在容器创建完成时,就已完成。
      destroy-method : 销毁方法。在容器关闭|销毁前执行。
        -->



        <!--        &lt;!&ndash;Spring框架的核心配置文件&ndash;&gt;-->
<!--        &lt;!&ndash;一个bean标签就是一个domain对象,让Spring框架帮忙创建管理这个对象-->
<!--            id      唯一标识-->
<!--            class   类型的具体名称,必须写包名.类名-->
<!--        &ndash;&gt;-->
<!--&lt;!&ndash;        <bean id="student" class="com.tianshi.domain.Student"/>&ndash;&gt;-->

<!--        &lt;!&ndash;配置静态工厂&ndash;&gt;-->
<!--&lt;!&ndash;        //唯一一个静态工厂(构造注入)&ndash;&gt;-->
<!--        <bean id="studentWithStaticFactory" class="com.tianshi.factory.StudentStaticFactory"-->
<!--              factory-method="getStudent">-->
<!--                &lt;!&ndash;DI构造注入-->
<!--                    用来确定给哪个属性赋值(可同时使用):-->
<!--                        index:参数在构造参数表中的下标,从0开始计数-->
<!--                        name:构造方法参数名称-->
<!--                        type:构造方法参数类型-->
<!--                    设置属性的值(只用一个):-->
<!--                        value:要传入的构造方法参数值(简单类型)-->
<!--                        ref:要传入的构造方法参数(引用类型)-->
<!--                    建议用index+name+value/ref完成配置-->
<!--                &ndash;&gt;-->
<!--                <constructor-arg index="0" name="id" value="100"/>-->
<!--                <constructor-arg index="1" name="name" value="小明"/>-->
<!--        </bean>-->

<!--        &lt;!&ndash;配置实例工厂两个&ndash;&gt;-->
<!--        &lt;!&ndash;C名称空间注入-->
<!--            基于参数索引的配置:-->
<!--                c:_索引数="值" 注入简单类型数据值-->
<!--                c:_索引数-ref="bean的id" 注入Spring容器中的其他的bean对象-->
<!--            基于参数名的配置:-->
<!--                c:参数名="值" 注入简单类型的数据值-->
<!--                c:参数名="bean的id" 注入Spring容器中其他的bean对象-->
<!--&ndash;&gt;-->
<!--&lt;!&ndash;        //第一个实例工厂(C名称空间注入)&ndash;&gt;-->
<!--        <bean id="studentFactory1" class="com.tianshi.factory.StudentInstanceFactory"/>-->
<!--        <bean id="studentWithInstanceFactory1" class="com.tianshi.domain.Student"-->
<!--        factory-bean="studentFactory1" factory-method="getStudent" c:_0="101" c:name="李四"/>-->
<!--&lt;!&ndash;        //Book的实例工厂&ndash;&gt;-->
<!--        <bean id="bookFactory" class="com.tianshi.factory.BookInstanceFactory"/>-->
<!--        <bean id="bookWithInstanceFactory" class="com.tianshi.domain.Book" p:id="1" p:name="空"/>-->
<!--&lt;!&ndash;        //第二个实例工厂(各种类型的属性注入的配置)&ndash;&gt;-->
<!--        <bean id="studentFactory2" class="com.tianshi.factory.StudentInstanceFactory"/>-->
<!--        &lt;!&ndash;自动装配&ndash;&gt;-->
<!--        &lt;!&ndash;局部的依赖注入自动装配-->
<!--            在bean标签中,有属性 autowire,代表是否采用自动装配逻辑。-->
<!--            可以设置的值:-->
<!--              default : 默认的,此是默认值。默认策略就是全局策略-->
<!--              no : 不使用自动装配逻辑。-->
<!--              byName : 根据名字完成自动装配。当前bean中的property的名字和当前配置文件中bean的id如果一样,则自动注入-->
<!--                有可能,类型不匹配-->
<!--              byType : 根据类型完成自动装配。当前bean中的property的类型和当前配置文件中bean的类型如有一样,则自动注入-->
<!--                有可能,当前配置文件中有多个bean的类型和同一个property的类型相同,抛出异常。-->
<!--                最常用。一般情况下,一个配置文件,不会配置多个同类型bean-->
<!--              constructor : 构造器注入。扫描当前bean类型中的有参数构造方法,扫描构造方法的参数表,-->
<!--                基于先byType,再byName的顺序逻辑,在当前配置文件中匹配bean对象,调用构造方法注入。-->
<!--        &ndash;&gt;-->
<!--        <bean id="studentWithInstanceFactory2" class="com.tianshi.domain.Student"-->
<!--              factory-bean="studentFactory2" factory-method="getStudent" autowire="byType">-->
<!--                <property name="id" value="105"/>-->
<!--                <property name="name" value="王"/>-->
<!--                &lt;!&ndash;数组array标签,一个标签代表一个数组&ndash;&gt;-->
<!--                <property name="hobbies">-->
<!--                        <array>-->
<!--                                &lt;!&ndash;数组中的简单数据,按照属性依次从0开始赋值&ndash;&gt;-->
<!--                                <value>0下标</value>-->
<!--                                <value>1下标</value>-->
<!--                                <value>2下标</value>-->
<!--                                &lt;!&ndash;ref,数组中的一个引用类型数据,按照配置属性依次从0下标开始赋值&ndash;&gt;-->
<!--                                &lt;!&ndash;<ref bean="beanId"></ref>&ndash;&gt;-->
<!--                        </array>-->
<!--                </property>-->
<!--                &lt;!&ndash;List集合,list标签,一个标签代表一个集合对象&ndash;&gt;-->
<!--                <property name="books">-->
<!--                        <list>-->
<!--                                &lt;!&ndash;0下标位置对象&ndash;&gt;-->
<!--                                <ref bean="bookWithInstanceFactory"></ref>-->
<!--                                &lt;!&ndash;配置一个局部的bean对象,不需要命名(不写id),局部有效&ndash;&gt;-->
<!--                                <bean class="com.tianshi.domain.Book">-->
<!--                                        <property name="id" value="2"></property>-->
<!--                                        <property name="name" value="算法导论"/>-->
<!--                                </bean>-->
<!--                                <ref bean="bookWithInstanceFactory"></ref>-->
<!--                        </list>-->
<!--                </property>-->
<!--                &lt;!&ndash;Set集合,set标签&ndash;&gt;-->
<!--                <property name="set">-->
<!--                        <set>-->
<!--                                <value>set值1</value>-->
<!--                                <value>set值2</value>-->
<!--                                <value>set值3</value>-->
<!--                        </set>-->
<!--                </property>-->
<!--                &lt;!&ndash;Map集合,map标签和entry子标签,一个map标签代表一个Map集合一个entry标签代表map中的一个键值对&ndash;&gt;-->
<!--                <property name="map">-->
<!--                        <map>-->
<!--                                &lt;!&ndash;entry有属性 key:简单类型键值,key-ref:引用类型键值-->
<!--                    value:简单类型value值,value-ref:引用类型value值&ndash;&gt;-->
<!--                                <entry key="1" value="value1"></entry>-->
<!--                                <entry key="2" value="value2"></entry>-->
<!--                                <entry key="3" value="value3"></entry>-->
<!--                        </map>-->
<!--                </property>-->
<!--                &lt;!&ndash;引用类型属性,使用ref注入&ndash;&gt;-->
<!--&lt;!&ndash;                <property name="book" ref="bookWithInstanceFactory"/>&ndash;&gt;-->
<!--        </bean>-->
<!--        &lt;!&ndash;配置FactoryBean工厂两个&ndash;&gt;-->
<!--        &lt;!&ndash;设值注入-->
<!--            标签属性:-->
<!--                name:要注入数据的property属性名-->
<!--                value:为属性赋值(简单数据类型)-->
<!--                ref:为属性赋值(引用数据类型)-->
<!--        &ndash;&gt;-->
<!--&lt;!&ndash;        //第一个FactoryBean工厂(设值注入)&ndash;&gt;-->
<!--        <bean id="studentWithFactoryBean1" class="com.tianshi.factory.StudentFactoryBean">-->
<!--                <property name="id" value="102"/>-->
<!--                <property name="name" value="王五"/>-->
<!--        </bean>-->

<!--        &lt;!&ndash;P名称空间注入-->
<!--            仅基于参数名,无基于索引:-->
<!--                p:属性名 = "简单数据"-->
<!--                p:属性名-ref = "引用数据,其他bean标签的id"-->
<!--        &ndash;&gt;-->
<!--&lt;!&ndash;        //第二个FactoryBean工厂(P名称空间注入)&ndash;&gt;-->
<!--        <bean id="studentWithFactoryBean2" class="com.tianshi.factory.StudentFactoryBean" p:id="103" p:name="赵六"/>-->
</beans>

总结

  1. 基本内容
    • 概念:AOP(Aspect Oriented Programming)面向切面编程,一种编程范式
    • 作用:在不惊动原始设计的基础上为方法进行功能增强
    • 核心概念
      • 代理(Proxy):SpringAOP 的核心本质是采用代理模式实现的
      • 连接点(JoinPoint): 在 SpringAOP 中,理解为任意方法的执行
      • 切入点(Pointcut):匹配连接点的表达式,也是具有共性功能的方法描述
      • 通知(Advice):若干个方法的共性功能,在切入点处执行,最终体现为一个方法
      • 切面(Aspect):描述通知与切入点的对应关系
      • 目标对象(Target):被代理的原始对象成为目标对象
  2. 表达式
    • 切入点表达式标准格式:动作关键字(访问修饰符 返回值 包名.类/接口名.方法名(参 数)异常名)
      • execution(* com.tianshi.service.Service.(...))
    • 切入点表达式描述通配符:
      • 作用:用于快速描述,范围描述
      • * :匹配任意符号(常用)
      • ... :匹配多个连续的任意符号(常用)
    • 切入点表达式书写技巧
      • 按标准规范开发
      • 返回值建议使用*匹配
      • 减少使用...的形式描述包
      • 对接口进行描述,使用表示模块名,例如 UserService 的匹配描述为Service
      • 方法名书写保留动词,例如 get,使用*表示名词,例如 getById 匹配描述为 getBy*
      • 参数根据实际情况灵活调整
  3. 通知类型
  • 前置通知
  • 后置通知
  • 环绕通知(重点)
    • 环绕通知依赖形参 ProceedingJoinPoint 才能实现对原始方法的调用
    • 环绕通知可以隔离原始方法的调用执行
    • 环绕通知返回值设置为 Object 类型
    • 环绕通知中可以对原始方法调用过程中出现的异常进行处理
  • 最终通知
  • 抛出异常后通知
相关推荐
曹牧2 小时前
Spring :component-scan
java·后端·spring
weixin_458580122 小时前
CSS如何让flex布局支持老版本浏览器_添加-webkit-前缀与兼容性写法
jvm·数据库·python
Shorasul2 小时前
CSS viewport单位在旧移动端支持不佳_利用固定像素值与rem配合
jvm·数据库·python
z4424753262 小时前
CSS如何实现响应式布局_使用Flexbox与Grid提升适配效率
jvm·数据库·python
醇氧2 小时前
Hermes Agent 学习(安装部署详细教程)
人工智能·python·学习·阿里云·ai·云计算
Absurd5872 小时前
优化文本分类中堆叠模型的网格搜索性能:避免训练卡顿的实用指南
jvm·数据库·python
2301_815279522 小时前
怎样通过Navicat高效导出ER模型为PDF文档_大幅提升绘制效率
jvm·数据库·python
2401_871696522 小时前
CSS如何让带Flex属性的元素自身不脱离文本流控制
jvm·数据库·python
2301_813599552 小时前
SQL如何提取两个表的交集_INTERSECT与INNER JOIN结合
jvm·数据库·python