- 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">-->
<!--<!– init-method="init" destroy-method="destroy"–>-->
<!-- <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 : 销毁方法。在容器关闭|销毁前执行。
-->
<!-- <!–Spring框架的核心配置文件–>-->
<!-- <!–一个bean标签就是一个domain对象,让Spring框架帮忙创建管理这个对象-->
<!-- id 唯一标识-->
<!-- class 类型的具体名称,必须写包名.类名-->
<!-- –>-->
<!--<!– <bean id="student" class="com.tianshi.domain.Student"/>–>-->
<!-- <!–配置静态工厂–>-->
<!--<!– //唯一一个静态工厂(构造注入)–>-->
<!-- <bean id="studentWithStaticFactory" class="com.tianshi.factory.StudentStaticFactory"-->
<!-- factory-method="getStudent">-->
<!-- <!–DI构造注入-->
<!-- 用来确定给哪个属性赋值(可同时使用):-->
<!-- index:参数在构造参数表中的下标,从0开始计数-->
<!-- name:构造方法参数名称-->
<!-- type:构造方法参数类型-->
<!-- 设置属性的值(只用一个):-->
<!-- value:要传入的构造方法参数值(简单类型)-->
<!-- ref:要传入的构造方法参数(引用类型)-->
<!-- 建议用index+name+value/ref完成配置-->
<!-- –>-->
<!-- <constructor-arg index="0" name="id" value="100"/>-->
<!-- <constructor-arg index="1" name="name" value="小明"/>-->
<!-- </bean>-->
<!-- <!–配置实例工厂两个–>-->
<!-- <!–C名称空间注入-->
<!-- 基于参数索引的配置:-->
<!-- c:_索引数="值" 注入简单类型数据值-->
<!-- c:_索引数-ref="bean的id" 注入Spring容器中的其他的bean对象-->
<!-- 基于参数名的配置:-->
<!-- c:参数名="值" 注入简单类型的数据值-->
<!-- c:参数名="bean的id" 注入Spring容器中其他的bean对象-->
<!--–>-->
<!--<!– //第一个实例工厂(C名称空间注入)–>-->
<!-- <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="李四"/>-->
<!--<!– //Book的实例工厂–>-->
<!-- <bean id="bookFactory" class="com.tianshi.factory.BookInstanceFactory"/>-->
<!-- <bean id="bookWithInstanceFactory" class="com.tianshi.domain.Book" p:id="1" p:name="空"/>-->
<!--<!– //第二个实例工厂(各种类型的属性注入的配置)–>-->
<!-- <bean id="studentFactory2" class="com.tianshi.factory.StudentInstanceFactory"/>-->
<!-- <!–自动装配–>-->
<!-- <!–局部的依赖注入自动装配-->
<!-- 在bean标签中,有属性 autowire,代表是否采用自动装配逻辑。-->
<!-- 可以设置的值:-->
<!-- default : 默认的,此是默认值。默认策略就是全局策略-->
<!-- no : 不使用自动装配逻辑。-->
<!-- byName : 根据名字完成自动装配。当前bean中的property的名字和当前配置文件中bean的id如果一样,则自动注入-->
<!-- 有可能,类型不匹配-->
<!-- byType : 根据类型完成自动装配。当前bean中的property的类型和当前配置文件中bean的类型如有一样,则自动注入-->
<!-- 有可能,当前配置文件中有多个bean的类型和同一个property的类型相同,抛出异常。-->
<!-- 最常用。一般情况下,一个配置文件,不会配置多个同类型bean-->
<!-- constructor : 构造器注入。扫描当前bean类型中的有参数构造方法,扫描构造方法的参数表,-->
<!-- 基于先byType,再byName的顺序逻辑,在当前配置文件中匹配bean对象,调用构造方法注入。-->
<!-- –>-->
<!-- <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="王"/>-->
<!-- <!–数组array标签,一个标签代表一个数组–>-->
<!-- <property name="hobbies">-->
<!-- <array>-->
<!-- <!–数组中的简单数据,按照属性依次从0开始赋值–>-->
<!-- <value>0下标</value>-->
<!-- <value>1下标</value>-->
<!-- <value>2下标</value>-->
<!-- <!–ref,数组中的一个引用类型数据,按照配置属性依次从0下标开始赋值–>-->
<!-- <!–<ref bean="beanId"></ref>–>-->
<!-- </array>-->
<!-- </property>-->
<!-- <!–List集合,list标签,一个标签代表一个集合对象–>-->
<!-- <property name="books">-->
<!-- <list>-->
<!-- <!–0下标位置对象–>-->
<!-- <ref bean="bookWithInstanceFactory"></ref>-->
<!-- <!–配置一个局部的bean对象,不需要命名(不写id),局部有效–>-->
<!-- <bean class="com.tianshi.domain.Book">-->
<!-- <property name="id" value="2"></property>-->
<!-- <property name="name" value="算法导论"/>-->
<!-- </bean>-->
<!-- <ref bean="bookWithInstanceFactory"></ref>-->
<!-- </list>-->
<!-- </property>-->
<!-- <!–Set集合,set标签–>-->
<!-- <property name="set">-->
<!-- <set>-->
<!-- <value>set值1</value>-->
<!-- <value>set值2</value>-->
<!-- <value>set值3</value>-->
<!-- </set>-->
<!-- </property>-->
<!-- <!–Map集合,map标签和entry子标签,一个map标签代表一个Map集合一个entry标签代表map中的一个键值对–>-->
<!-- <property name="map">-->
<!-- <map>-->
<!-- <!–entry有属性 key:简单类型键值,key-ref:引用类型键值-->
<!-- value:简单类型value值,value-ref:引用类型value值–>-->
<!-- <entry key="1" value="value1"></entry>-->
<!-- <entry key="2" value="value2"></entry>-->
<!-- <entry key="3" value="value3"></entry>-->
<!-- </map>-->
<!-- </property>-->
<!-- <!–引用类型属性,使用ref注入–>-->
<!--<!– <property name="book" ref="bookWithInstanceFactory"/>–>-->
<!-- </bean>-->
<!-- <!–配置FactoryBean工厂两个–>-->
<!-- <!–设值注入-->
<!-- 标签属性:-->
<!-- name:要注入数据的property属性名-->
<!-- value:为属性赋值(简单数据类型)-->
<!-- ref:为属性赋值(引用数据类型)-->
<!-- –>-->
<!--<!– //第一个FactoryBean工厂(设值注入)–>-->
<!-- <bean id="studentWithFactoryBean1" class="com.tianshi.factory.StudentFactoryBean">-->
<!-- <property name="id" value="102"/>-->
<!-- <property name="name" value="王五"/>-->
<!-- </bean>-->
<!-- <!–P名称空间注入-->
<!-- 仅基于参数名,无基于索引:-->
<!-- p:属性名 = "简单数据"-->
<!-- p:属性名-ref = "引用数据,其他bean标签的id"-->
<!-- –>-->
<!--<!– //第二个FactoryBean工厂(P名称空间注入)–>-->
<!-- <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">-->
<!--<!– init-method="init" destroy-method="destroy"–>-->
<!-- <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 : 销毁方法。在容器关闭|销毁前执行。
-->
<!-- <!–Spring框架的核心配置文件–>-->
<!-- <!–一个bean标签就是一个domain对象,让Spring框架帮忙创建管理这个对象-->
<!-- id 唯一标识-->
<!-- class 类型的具体名称,必须写包名.类名-->
<!-- –>-->
<!--<!– <bean id="student" class="com.tianshi.domain.Student"/>–>-->
<!-- <!–配置静态工厂–>-->
<!--<!– //唯一一个静态工厂(构造注入)–>-->
<!-- <bean id="studentWithStaticFactory" class="com.tianshi.factory.StudentStaticFactory"-->
<!-- factory-method="getStudent">-->
<!-- <!–DI构造注入-->
<!-- 用来确定给哪个属性赋值(可同时使用):-->
<!-- index:参数在构造参数表中的下标,从0开始计数-->
<!-- name:构造方法参数名称-->
<!-- type:构造方法参数类型-->
<!-- 设置属性的值(只用一个):-->
<!-- value:要传入的构造方法参数值(简单类型)-->
<!-- ref:要传入的构造方法参数(引用类型)-->
<!-- 建议用index+name+value/ref完成配置-->
<!-- –>-->
<!-- <constructor-arg index="0" name="id" value="100"/>-->
<!-- <constructor-arg index="1" name="name" value="小明"/>-->
<!-- </bean>-->
<!-- <!–配置实例工厂两个–>-->
<!-- <!–C名称空间注入-->
<!-- 基于参数索引的配置:-->
<!-- c:_索引数="值" 注入简单类型数据值-->
<!-- c:_索引数-ref="bean的id" 注入Spring容器中的其他的bean对象-->
<!-- 基于参数名的配置:-->
<!-- c:参数名="值" 注入简单类型的数据值-->
<!-- c:参数名="bean的id" 注入Spring容器中其他的bean对象-->
<!--–>-->
<!--<!– //第一个实例工厂(C名称空间注入)–>-->
<!-- <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="李四"/>-->
<!--<!– //Book的实例工厂–>-->
<!-- <bean id="bookFactory" class="com.tianshi.factory.BookInstanceFactory"/>-->
<!-- <bean id="bookWithInstanceFactory" class="com.tianshi.domain.Book" p:id="1" p:name="空"/>-->
<!--<!– //第二个实例工厂(各种类型的属性注入的配置)–>-->
<!-- <bean id="studentFactory2" class="com.tianshi.factory.StudentInstanceFactory"/>-->
<!-- <!–自动装配–>-->
<!-- <!–局部的依赖注入自动装配-->
<!-- 在bean标签中,有属性 autowire,代表是否采用自动装配逻辑。-->
<!-- 可以设置的值:-->
<!-- default : 默认的,此是默认值。默认策略就是全局策略-->
<!-- no : 不使用自动装配逻辑。-->
<!-- byName : 根据名字完成自动装配。当前bean中的property的名字和当前配置文件中bean的id如果一样,则自动注入-->
<!-- 有可能,类型不匹配-->
<!-- byType : 根据类型完成自动装配。当前bean中的property的类型和当前配置文件中bean的类型如有一样,则自动注入-->
<!-- 有可能,当前配置文件中有多个bean的类型和同一个property的类型相同,抛出异常。-->
<!-- 最常用。一般情况下,一个配置文件,不会配置多个同类型bean-->
<!-- constructor : 构造器注入。扫描当前bean类型中的有参数构造方法,扫描构造方法的参数表,-->
<!-- 基于先byType,再byName的顺序逻辑,在当前配置文件中匹配bean对象,调用构造方法注入。-->
<!-- –>-->
<!-- <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="王"/>-->
<!-- <!–数组array标签,一个标签代表一个数组–>-->
<!-- <property name="hobbies">-->
<!-- <array>-->
<!-- <!–数组中的简单数据,按照属性依次从0开始赋值–>-->
<!-- <value>0下标</value>-->
<!-- <value>1下标</value>-->
<!-- <value>2下标</value>-->
<!-- <!–ref,数组中的一个引用类型数据,按照配置属性依次从0下标开始赋值–>-->
<!-- <!–<ref bean="beanId"></ref>–>-->
<!-- </array>-->
<!-- </property>-->
<!-- <!–List集合,list标签,一个标签代表一个集合对象–>-->
<!-- <property name="books">-->
<!-- <list>-->
<!-- <!–0下标位置对象–>-->
<!-- <ref bean="bookWithInstanceFactory"></ref>-->
<!-- <!–配置一个局部的bean对象,不需要命名(不写id),局部有效–>-->
<!-- <bean class="com.tianshi.domain.Book">-->
<!-- <property name="id" value="2"></property>-->
<!-- <property name="name" value="算法导论"/>-->
<!-- </bean>-->
<!-- <ref bean="bookWithInstanceFactory"></ref>-->
<!-- </list>-->
<!-- </property>-->
<!-- <!–Set集合,set标签–>-->
<!-- <property name="set">-->
<!-- <set>-->
<!-- <value>set值1</value>-->
<!-- <value>set值2</value>-->
<!-- <value>set值3</value>-->
<!-- </set>-->
<!-- </property>-->
<!-- <!–Map集合,map标签和entry子标签,一个map标签代表一个Map集合一个entry标签代表map中的一个键值对–>-->
<!-- <property name="map">-->
<!-- <map>-->
<!-- <!–entry有属性 key:简单类型键值,key-ref:引用类型键值-->
<!-- value:简单类型value值,value-ref:引用类型value值–>-->
<!-- <entry key="1" value="value1"></entry>-->
<!-- <entry key="2" value="value2"></entry>-->
<!-- <entry key="3" value="value3"></entry>-->
<!-- </map>-->
<!-- </property>-->
<!-- <!–引用类型属性,使用ref注入–>-->
<!--<!– <property name="book" ref="bookWithInstanceFactory"/>–>-->
<!-- </bean>-->
<!-- <!–配置FactoryBean工厂两个–>-->
<!-- <!–设值注入-->
<!-- 标签属性:-->
<!-- name:要注入数据的property属性名-->
<!-- value:为属性赋值(简单数据类型)-->
<!-- ref:为属性赋值(引用数据类型)-->
<!-- –>-->
<!--<!– //第一个FactoryBean工厂(设值注入)–>-->
<!-- <bean id="studentWithFactoryBean1" class="com.tianshi.factory.StudentFactoryBean">-->
<!-- <property name="id" value="102"/>-->
<!-- <property name="name" value="王五"/>-->
<!-- </bean>-->
<!-- <!–P名称空间注入-->
<!-- 仅基于参数名,无基于索引:-->
<!-- p:属性名 = "简单数据"-->
<!-- p:属性名-ref = "引用数据,其他bean标签的id"-->
<!-- –>-->
<!--<!– //第二个FactoryBean工厂(P名称空间注入)–>-->
<!-- <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">-->
<!--<!– init-method="init" destroy-method="destroy"–>-->
<!-- <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 : 销毁方法。在容器关闭|销毁前执行。
-->
<!-- <!–Spring框架的核心配置文件–>-->
<!-- <!–一个bean标签就是一个domain对象,让Spring框架帮忙创建管理这个对象-->
<!-- id 唯一标识-->
<!-- class 类型的具体名称,必须写包名.类名-->
<!-- –>-->
<!--<!– <bean id="student" class="com.tianshi.domain.Student"/>–>-->
<!-- <!–配置静态工厂–>-->
<!--<!– //唯一一个静态工厂(构造注入)–>-->
<!-- <bean id="studentWithStaticFactory" class="com.tianshi.factory.StudentStaticFactory"-->
<!-- factory-method="getStudent">-->
<!-- <!–DI构造注入-->
<!-- 用来确定给哪个属性赋值(可同时使用):-->
<!-- index:参数在构造参数表中的下标,从0开始计数-->
<!-- name:构造方法参数名称-->
<!-- type:构造方法参数类型-->
<!-- 设置属性的值(只用一个):-->
<!-- value:要传入的构造方法参数值(简单类型)-->
<!-- ref:要传入的构造方法参数(引用类型)-->
<!-- 建议用index+name+value/ref完成配置-->
<!-- –>-->
<!-- <constructor-arg index="0" name="id" value="100"/>-->
<!-- <constructor-arg index="1" name="name" value="小明"/>-->
<!-- </bean>-->
<!-- <!–配置实例工厂两个–>-->
<!-- <!–C名称空间注入-->
<!-- 基于参数索引的配置:-->
<!-- c:_索引数="值" 注入简单类型数据值-->
<!-- c:_索引数-ref="bean的id" 注入Spring容器中的其他的bean对象-->
<!-- 基于参数名的配置:-->
<!-- c:参数名="值" 注入简单类型的数据值-->
<!-- c:参数名="bean的id" 注入Spring容器中其他的bean对象-->
<!--–>-->
<!--<!– //第一个实例工厂(C名称空间注入)–>-->
<!-- <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="李四"/>-->
<!--<!– //Book的实例工厂–>-->
<!-- <bean id="bookFactory" class="com.tianshi.factory.BookInstanceFactory"/>-->
<!-- <bean id="bookWithInstanceFactory" class="com.tianshi.domain.Book" p:id="1" p:name="空"/>-->
<!--<!– //第二个实例工厂(各种类型的属性注入的配置)–>-->
<!-- <bean id="studentFactory2" class="com.tianshi.factory.StudentInstanceFactory"/>-->
<!-- <!–自动装配–>-->
<!-- <!–局部的依赖注入自动装配-->
<!-- 在bean标签中,有属性 autowire,代表是否采用自动装配逻辑。-->
<!-- 可以设置的值:-->
<!-- default : 默认的,此是默认值。默认策略就是全局策略-->
<!-- no : 不使用自动装配逻辑。-->
<!-- byName : 根据名字完成自动装配。当前bean中的property的名字和当前配置文件中bean的id如果一样,则自动注入-->
<!-- 有可能,类型不匹配-->
<!-- byType : 根据类型完成自动装配。当前bean中的property的类型和当前配置文件中bean的类型如有一样,则自动注入-->
<!-- 有可能,当前配置文件中有多个bean的类型和同一个property的类型相同,抛出异常。-->
<!-- 最常用。一般情况下,一个配置文件,不会配置多个同类型bean-->
<!-- constructor : 构造器注入。扫描当前bean类型中的有参数构造方法,扫描构造方法的参数表,-->
<!-- 基于先byType,再byName的顺序逻辑,在当前配置文件中匹配bean对象,调用构造方法注入。-->
<!-- –>-->
<!-- <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="王"/>-->
<!-- <!–数组array标签,一个标签代表一个数组–>-->
<!-- <property name="hobbies">-->
<!-- <array>-->
<!-- <!–数组中的简单数据,按照属性依次从0开始赋值–>-->
<!-- <value>0下标</value>-->
<!-- <value>1下标</value>-->
<!-- <value>2下标</value>-->
<!-- <!–ref,数组中的一个引用类型数据,按照配置属性依次从0下标开始赋值–>-->
<!-- <!–<ref bean="beanId"></ref>–>-->
<!-- </array>-->
<!-- </property>-->
<!-- <!–List集合,list标签,一个标签代表一个集合对象–>-->
<!-- <property name="books">-->
<!-- <list>-->
<!-- <!–0下标位置对象–>-->
<!-- <ref bean="bookWithInstanceFactory"></ref>-->
<!-- <!–配置一个局部的bean对象,不需要命名(不写id),局部有效–>-->
<!-- <bean class="com.tianshi.domain.Book">-->
<!-- <property name="id" value="2"></property>-->
<!-- <property name="name" value="算法导论"/>-->
<!-- </bean>-->
<!-- <ref bean="bookWithInstanceFactory"></ref>-->
<!-- </list>-->
<!-- </property>-->
<!-- <!–Set集合,set标签–>-->
<!-- <property name="set">-->
<!-- <set>-->
<!-- <value>set值1</value>-->
<!-- <value>set值2</value>-->
<!-- <value>set值3</value>-->
<!-- </set>-->
<!-- </property>-->
<!-- <!–Map集合,map标签和entry子标签,一个map标签代表一个Map集合一个entry标签代表map中的一个键值对–>-->
<!-- <property name="map">-->
<!-- <map>-->
<!-- <!–entry有属性 key:简单类型键值,key-ref:引用类型键值-->
<!-- value:简单类型value值,value-ref:引用类型value值–>-->
<!-- <entry key="1" value="value1"></entry>-->
<!-- <entry key="2" value="value2"></entry>-->
<!-- <entry key="3" value="value3"></entry>-->
<!-- </map>-->
<!-- </property>-->
<!-- <!–引用类型属性,使用ref注入–>-->
<!--<!– <property name="book" ref="bookWithInstanceFactory"/>–>-->
<!-- </bean>-->
<!-- <!–配置FactoryBean工厂两个–>-->
<!-- <!–设值注入-->
<!-- 标签属性:-->
<!-- name:要注入数据的property属性名-->
<!-- value:为属性赋值(简单数据类型)-->
<!-- ref:为属性赋值(引用数据类型)-->
<!-- –>-->
<!--<!– //第一个FactoryBean工厂(设值注入)–>-->
<!-- <bean id="studentWithFactoryBean1" class="com.tianshi.factory.StudentFactoryBean">-->
<!-- <property name="id" value="102"/>-->
<!-- <property name="name" value="王五"/>-->
<!-- </bean>-->
<!-- <!–P名称空间注入-->
<!-- 仅基于参数名,无基于索引:-->
<!-- p:属性名 = "简单数据"-->
<!-- p:属性名-ref = "引用数据,其他bean标签的id"-->
<!-- –>-->
<!--<!– //第二个FactoryBean工厂(P名称空间注入)–>-->
<!-- <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">-->
<!--<!– init-method="init" destroy-method="destroy"–>-->
<!-- <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 : 销毁方法。在容器关闭|销毁前执行。
-->
<!-- <!–Spring框架的核心配置文件–>-->
<!-- <!–一个bean标签就是一个domain对象,让Spring框架帮忙创建管理这个对象-->
<!-- id 唯一标识-->
<!-- class 类型的具体名称,必须写包名.类名-->
<!-- –>-->
<!--<!– <bean id="student" class="com.tianshi.domain.Student"/>–>-->
<!-- <!–配置静态工厂–>-->
<!--<!– //唯一一个静态工厂(构造注入)–>-->
<!-- <bean id="studentWithStaticFactory" class="com.tianshi.factory.StudentStaticFactory"-->
<!-- factory-method="getStudent">-->
<!-- <!–DI构造注入-->
<!-- 用来确定给哪个属性赋值(可同时使用):-->
<!-- index:参数在构造参数表中的下标,从0开始计数-->
<!-- name:构造方法参数名称-->
<!-- type:构造方法参数类型-->
<!-- 设置属性的值(只用一个):-->
<!-- value:要传入的构造方法参数值(简单类型)-->
<!-- ref:要传入的构造方法参数(引用类型)-->
<!-- 建议用index+name+value/ref完成配置-->
<!-- –>-->
<!-- <constructor-arg index="0" name="id" value="100"/>-->
<!-- <constructor-arg index="1" name="name" value="小明"/>-->
<!-- </bean>-->
<!-- <!–配置实例工厂两个–>-->
<!-- <!–C名称空间注入-->
<!-- 基于参数索引的配置:-->
<!-- c:_索引数="值" 注入简单类型数据值-->
<!-- c:_索引数-ref="bean的id" 注入Spring容器中的其他的bean对象-->
<!-- 基于参数名的配置:-->
<!-- c:参数名="值" 注入简单类型的数据值-->
<!-- c:参数名="bean的id" 注入Spring容器中其他的bean对象-->
<!--–>-->
<!--<!– //第一个实例工厂(C名称空间注入)–>-->
<!-- <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="李四"/>-->
<!--<!– //Book的实例工厂–>-->
<!-- <bean id="bookFactory" class="com.tianshi.factory.BookInstanceFactory"/>-->
<!-- <bean id="bookWithInstanceFactory" class="com.tianshi.domain.Book" p:id="1" p:name="空"/>-->
<!--<!– //第二个实例工厂(各种类型的属性注入的配置)–>-->
<!-- <bean id="studentFactory2" class="com.tianshi.factory.StudentInstanceFactory"/>-->
<!-- <!–自动装配–>-->
<!-- <!–局部的依赖注入自动装配-->
<!-- 在bean标签中,有属性 autowire,代表是否采用自动装配逻辑。-->
<!-- 可以设置的值:-->
<!-- default : 默认的,此是默认值。默认策略就是全局策略-->
<!-- no : 不使用自动装配逻辑。-->
<!-- byName : 根据名字完成自动装配。当前bean中的property的名字和当前配置文件中bean的id如果一样,则自动注入-->
<!-- 有可能,类型不匹配-->
<!-- byType : 根据类型完成自动装配。当前bean中的property的类型和当前配置文件中bean的类型如有一样,则自动注入-->
<!-- 有可能,当前配置文件中有多个bean的类型和同一个property的类型相同,抛出异常。-->
<!-- 最常用。一般情况下,一个配置文件,不会配置多个同类型bean-->
<!-- constructor : 构造器注入。扫描当前bean类型中的有参数构造方法,扫描构造方法的参数表,-->
<!-- 基于先byType,再byName的顺序逻辑,在当前配置文件中匹配bean对象,调用构造方法注入。-->
<!-- –>-->
<!-- <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="王"/>-->
<!-- <!–数组array标签,一个标签代表一个数组–>-->
<!-- <property name="hobbies">-->
<!-- <array>-->
<!-- <!–数组中的简单数据,按照属性依次从0开始赋值–>-->
<!-- <value>0下标</value>-->
<!-- <value>1下标</value>-->
<!-- <value>2下标</value>-->
<!-- <!–ref,数组中的一个引用类型数据,按照配置属性依次从0下标开始赋值–>-->
<!-- <!–<ref bean="beanId"></ref>–>-->
<!-- </array>-->
<!-- </property>-->
<!-- <!–List集合,list标签,一个标签代表一个集合对象–>-->
<!-- <property name="books">-->
<!-- <list>-->
<!-- <!–0下标位置对象–>-->
<!-- <ref bean="bookWithInstanceFactory"></ref>-->
<!-- <!–配置一个局部的bean对象,不需要命名(不写id),局部有效–>-->
<!-- <bean class="com.tianshi.domain.Book">-->
<!-- <property name="id" value="2"></property>-->
<!-- <property name="name" value="算法导论"/>-->
<!-- </bean>-->
<!-- <ref bean="bookWithInstanceFactory"></ref>-->
<!-- </list>-->
<!-- </property>-->
<!-- <!–Set集合,set标签–>-->
<!-- <property name="set">-->
<!-- <set>-->
<!-- <value>set值1</value>-->
<!-- <value>set值2</value>-->
<!-- <value>set值3</value>-->
<!-- </set>-->
<!-- </property>-->
<!-- <!–Map集合,map标签和entry子标签,一个map标签代表一个Map集合一个entry标签代表map中的一个键值对–>-->
<!-- <property name="map">-->
<!-- <map>-->
<!-- <!–entry有属性 key:简单类型键值,key-ref:引用类型键值-->
<!-- value:简单类型value值,value-ref:引用类型value值–>-->
<!-- <entry key="1" value="value1"></entry>-->
<!-- <entry key="2" value="value2"></entry>-->
<!-- <entry key="3" value="value3"></entry>-->
<!-- </map>-->
<!-- </property>-->
<!-- <!–引用类型属性,使用ref注入–>-->
<!--<!– <property name="book" ref="bookWithInstanceFactory"/>–>-->
<!-- </bean>-->
<!-- <!–配置FactoryBean工厂两个–>-->
<!-- <!–设值注入-->
<!-- 标签属性:-->
<!-- name:要注入数据的property属性名-->
<!-- value:为属性赋值(简单数据类型)-->
<!-- ref:为属性赋值(引用数据类型)-->
<!-- –>-->
<!--<!– //第一个FactoryBean工厂(设值注入)–>-->
<!-- <bean id="studentWithFactoryBean1" class="com.tianshi.factory.StudentFactoryBean">-->
<!-- <property name="id" value="102"/>-->
<!-- <property name="name" value="王五"/>-->
<!-- </bean>-->
<!-- <!–P名称空间注入-->
<!-- 仅基于参数名,无基于索引:-->
<!-- p:属性名 = "简单数据"-->
<!-- p:属性名-ref = "引用数据,其他bean标签的id"-->
<!-- –>-->
<!--<!– //第二个FactoryBean工厂(P名称空间注入)–>-->
<!-- <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">-->
<!--<!– init-method="init" destroy-method="destroy"–>-->
<!-- <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 : 销毁方法。在容器关闭|销毁前执行。
-->
<!-- <!–Spring框架的核心配置文件–>-->
<!-- <!–一个bean标签就是一个domain对象,让Spring框架帮忙创建管理这个对象-->
<!-- id 唯一标识-->
<!-- class 类型的具体名称,必须写包名.类名-->
<!-- –>-->
<!--<!– <bean id="student" class="com.tianshi.domain.Student"/>–>-->
<!-- <!–配置静态工厂–>-->
<!--<!– //唯一一个静态工厂(构造注入)–>-->
<!-- <bean id="studentWithStaticFactory" class="com.tianshi.factory.StudentStaticFactory"-->
<!-- factory-method="getStudent">-->
<!-- <!–DI构造注入-->
<!-- 用来确定给哪个属性赋值(可同时使用):-->
<!-- index:参数在构造参数表中的下标,从0开始计数-->
<!-- name:构造方法参数名称-->
<!-- type:构造方法参数类型-->
<!-- 设置属性的值(只用一个):-->
<!-- value:要传入的构造方法参数值(简单类型)-->
<!-- ref:要传入的构造方法参数(引用类型)-->
<!-- 建议用index+name+value/ref完成配置-->
<!-- –>-->
<!-- <constructor-arg index="0" name="id" value="100"/>-->
<!-- <constructor-arg index="1" name="name" value="小明"/>-->
<!-- </bean>-->
<!-- <!–配置实例工厂两个–>-->
<!-- <!–C名称空间注入-->
<!-- 基于参数索引的配置:-->
<!-- c:_索引数="值" 注入简单类型数据值-->
<!-- c:_索引数-ref="bean的id" 注入Spring容器中的其他的bean对象-->
<!-- 基于参数名的配置:-->
<!-- c:参数名="值" 注入简单类型的数据值-->
<!-- c:参数名="bean的id" 注入Spring容器中其他的bean对象-->
<!--–>-->
<!--<!– //第一个实例工厂(C名称空间注入)–>-->
<!-- <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="李四"/>-->
<!--<!– //Book的实例工厂–>-->
<!-- <bean id="bookFactory" class="com.tianshi.factory.BookInstanceFactory"/>-->
<!-- <bean id="bookWithInstanceFactory" class="com.tianshi.domain.Book" p:id="1" p:name="空"/>-->
<!--<!– //第二个实例工厂(各种类型的属性注入的配置)–>-->
<!-- <bean id="studentFactory2" class="com.tianshi.factory.StudentInstanceFactory"/>-->
<!-- <!–自动装配–>-->
<!-- <!–局部的依赖注入自动装配-->
<!-- 在bean标签中,有属性 autowire,代表是否采用自动装配逻辑。-->
<!-- 可以设置的值:-->
<!-- default : 默认的,此是默认值。默认策略就是全局策略-->
<!-- no : 不使用自动装配逻辑。-->
<!-- byName : 根据名字完成自动装配。当前bean中的property的名字和当前配置文件中bean的id如果一样,则自动注入-->
<!-- 有可能,类型不匹配-->
<!-- byType : 根据类型完成自动装配。当前bean中的property的类型和当前配置文件中bean的类型如有一样,则自动注入-->
<!-- 有可能,当前配置文件中有多个bean的类型和同一个property的类型相同,抛出异常。-->
<!-- 最常用。一般情况下,一个配置文件,不会配置多个同类型bean-->
<!-- constructor : 构造器注入。扫描当前bean类型中的有参数构造方法,扫描构造方法的参数表,-->
<!-- 基于先byType,再byName的顺序逻辑,在当前配置文件中匹配bean对象,调用构造方法注入。-->
<!-- –>-->
<!-- <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="王"/>-->
<!-- <!–数组array标签,一个标签代表一个数组–>-->
<!-- <property name="hobbies">-->
<!-- <array>-->
<!-- <!–数组中的简单数据,按照属性依次从0开始赋值–>-->
<!-- <value>0下标</value>-->
<!-- <value>1下标</value>-->
<!-- <value>2下标</value>-->
<!-- <!–ref,数组中的一个引用类型数据,按照配置属性依次从0下标开始赋值–>-->
<!-- <!–<ref bean="beanId"></ref>–>-->
<!-- </array>-->
<!-- </property>-->
<!-- <!–List集合,list标签,一个标签代表一个集合对象–>-->
<!-- <property name="books">-->
<!-- <list>-->
<!-- <!–0下标位置对象–>-->
<!-- <ref bean="bookWithInstanceFactory"></ref>-->
<!-- <!–配置一个局部的bean对象,不需要命名(不写id),局部有效–>-->
<!-- <bean class="com.tianshi.domain.Book">-->
<!-- <property name="id" value="2"></property>-->
<!-- <property name="name" value="算法导论"/>-->
<!-- </bean>-->
<!-- <ref bean="bookWithInstanceFactory"></ref>-->
<!-- </list>-->
<!-- </property>-->
<!-- <!–Set集合,set标签–>-->
<!-- <property name="set">-->
<!-- <set>-->
<!-- <value>set值1</value>-->
<!-- <value>set值2</value>-->
<!-- <value>set值3</value>-->
<!-- </set>-->
<!-- </property>-->
<!-- <!–Map集合,map标签和entry子标签,一个map标签代表一个Map集合一个entry标签代表map中的一个键值对–>-->
<!-- <property name="map">-->
<!-- <map>-->
<!-- <!–entry有属性 key:简单类型键值,key-ref:引用类型键值-->
<!-- value:简单类型value值,value-ref:引用类型value值–>-->
<!-- <entry key="1" value="value1"></entry>-->
<!-- <entry key="2" value="value2"></entry>-->
<!-- <entry key="3" value="value3"></entry>-->
<!-- </map>-->
<!-- </property>-->
<!-- <!–引用类型属性,使用ref注入–>-->
<!--<!– <property name="book" ref="bookWithInstanceFactory"/>–>-->
<!-- </bean>-->
<!-- <!–配置FactoryBean工厂两个–>-->
<!-- <!–设值注入-->
<!-- 标签属性:-->
<!-- name:要注入数据的property属性名-->
<!-- value:为属性赋值(简单数据类型)-->
<!-- ref:为属性赋值(引用数据类型)-->
<!-- –>-->
<!--<!– //第一个FactoryBean工厂(设值注入)–>-->
<!-- <bean id="studentWithFactoryBean1" class="com.tianshi.factory.StudentFactoryBean">-->
<!-- <property name="id" value="102"/>-->
<!-- <property name="name" value="王五"/>-->
<!-- </bean>-->
<!-- <!–P名称空间注入-->
<!-- 仅基于参数名,无基于索引:-->
<!-- p:属性名 = "简单数据"-->
<!-- p:属性名-ref = "引用数据,其他bean标签的id"-->
<!-- –>-->
<!--<!– //第二个FactoryBean工厂(P名称空间注入)–>-->
<!-- <bean id="studentWithFactoryBean2" class="com.tianshi.factory.StudentFactoryBean" p:id="103" p:name="赵六"/>-->
</beans>
总结
- 基本内容
- 概念:AOP(Aspect Oriented Programming)面向切面编程,一种编程范式
- 作用:在不惊动原始设计的基础上为方法进行功能增强
- 核心概念
- 代理(Proxy):SpringAOP 的核心本质是采用代理模式实现的
- 连接点(JoinPoint): 在 SpringAOP 中,理解为任意方法的执行
- 切入点(Pointcut):匹配连接点的表达式,也是具有共性功能的方法描述
- 通知(Advice):若干个方法的共性功能,在切入点处执行,最终体现为一个方法
- 切面(Aspect):描述通知与切入点的对应关系
- 目标对象(Target):被代理的原始对象成为目标对象
- 表达式
- 切入点表达式标准格式:动作关键字(访问修饰符 返回值 包名.类/接口名.方法名(参 数)异常名)
- execution(* com.tianshi.service.Service.(...))
- 切入点表达式描述通配符:
- 作用:用于快速描述,范围描述
- * :匹配任意符号(常用)
- ... :匹配多个连续的任意符号(常用)
- 切入点表达式书写技巧
- 按标准规范开发
- 返回值建议使用*匹配
- 减少使用...的形式描述包
- 对接口进行描述,使用表示模块名,例如 UserService 的匹配描述为Service
- 方法名书写保留动词,例如 get,使用*表示名词,例如 getById 匹配描述为 getBy*
- 参数根据实际情况灵活调整
- 通知类型
- 前置通知
- 后置通知
- 环绕通知(重点)
- 环绕通知依赖形参 ProceedingJoinPoint 才能实现对原始方法的调用
- 环绕通知可以隔离原始方法的调用执行
- 环绕通知返回值设置为 Object 类型
- 环绕通知中可以对原始方法调用过程中出现的异常进行处理
- 最终通知
- 抛出异常后通知