spring学习笔记 -- day08 基于XML的Spring中的AOP

一、搭建AOP环境

1、创建项目,导入jar包


2、导入spring核心配置文件约束

<?xml version="1.0" encoding="UTF-8"?>
<!-- 导入约束:
	导入beans和aop两个约束
-->
<beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:aop="http://www.springframework.org/schema/aop"
        xsi:schemaLocation="http://www.springframework.org/schema/beans 
        					http://www.springframework.org/schema/beans/spring-beans.xsd
        					http://www.springframework.org/schema/aop 
        					http://www.springframework.org/schema/aop/spring-aop.xsd"> 
        					
</beans>

3、创建业务层接口、实现类

package cn.itcast.service;

/**
 * 客户的业务层接口
 * @author wingzhe
 *
 */
public interface ICustomerService {
	
	/**
	 * 保存客户
	 */
	void saveCustomer();
	
	/**
	 * 更新客户
	 * @param i
	 */
	void updateCustomer(int i);
	
	/**
	 * 删除客户
	 * @return
	 */
	int deleteCustomer();
}

package cn.itcast.service.impl;

import cn.itcast.service.ICustomerService;
import cn.itcast.utils.Logger;
/**
 * 客户的业务层实现类
 * @author wingzhe
 *
 */
public class CustomerServiceImpl implements ICustomerService {

	@Override
	public void saveCustomer() {
		System.out.println("执行了保存客户");
	}

	@Override
	public void updateCustomer(int i) {
		System.out.println("执行了更新客户");
	}

	@Override
	public int deleteCustomer() {
		System.out.println("执行了删除客户");
		return 0;
	}

}

4、编写要使用aop切入的动作(此处以记录日志类为例)

package cn.itcast.utils;

/**
 * 记录日志的工具类
 * 	提供一个记录日志的方法
 * @author winghze
 *
 */
public class Logger {
	
	/**
	 * 计划让此方法在我们的业务方法执行之前执行
	 * 此方法是前置通知
	 */
	public void printLog(){
		System.out.println("Logger类中的printLog方法开始记录日志了");
	}
}

二、使用XML方式进行配置

1、配置spring主配置文件

<?xml version="1.0" encoding="UTF-8"?>
<!-- 导入约束:
	导入beans和aop两个约束
-->
<beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:aop="http://www.springframework.org/schema/aop"
        xsi:schemaLocation="http://www.springframework.org/schema/beans 
        					http://www.springframework.org/schema/beans/spring-beans.xsd
        					http://www.springframework.org/schema/aop 
        					http://www.springframework.org/schema/aop/spring-aop.xsd"> 
	<!-- 把资源交给spring来管理 -->
	<bean id="customerService" class="cn.itcast.service.impl.CustomerServiceImpl"></bean>
	
	<!-- 配置spring基于xml的aop:
			第一步:把通知类也交给spring来管理
	 -->
	<bean id="logger" class="cn.itcast.utils.Logger"></bean>
	
	<!-- 第二步:使用aop:config表示开始配置aop -->
	<aop:config>
		<!--第三步:使用aop:aspect开始配置切面
				id属性:给切面提供一个唯一标识
				ref属性:引用通知类bean的id
		 -->
		<aop:aspect id="logAdvice" ref="logger">
			<!-- 第四步:配置通知的类型 
				使用aop:before来配置前置通知
				method:指定前置通知的方法名称
				pointcut:指定切入点表达式。
					关键字:execution(表达式)
					表达式写法:
						访问修饰符 返回值 包名....类名.方法名(参数列表)
					全匹配方式:
						public void cn.itcast.service.impl.CustomerServiceImpl.saveCustomer()
					访问修饰符可以省略
						void cn.itcast.service.impl.CustomerServiceImpl.saveCustomer()
					返回值可以使用*,表示任意返回值
						* cn.itcast.service.impl.CustomerServiceImpl.saveCustomer()
					包名可以使用*号,表示任意包。但是有几个包,就得写几个*号
						* *.*.*.*.CustomerServiceImpl.saveCustomer()
					包名可以使用..表达式当前包极其子包 
						* cn..CustomerServiceImpl.saveCustomer()
					类名可以使用*,表示任意类
						* cn..*.saveCustomer()
					方法名可以使用*,表示任意方法
						* cn..*.*()
					参数列表可以使用*号,表示任意数据类型,但是有几个*就表示有几个参数
						* cn..*.*(*)
					参数列表可以使用..,表示有无参数均可
						* cn..*.*(..)
					全通配方式:
						* *..*.*(..)
			-->
			<aop:before method="printLog" pointcut="execution(* cn..*.*(..))"/>
		</aop:aspect>
	</aop:config>
</beans>

2、编写测试类

package cn.itcast.ui;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import cn.itcast.service.ICustomerService;

public class Client {

	public static void main(String[] args) {
		ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
		ICustomerService customerService = (ICustomerService)ac.getBean("customerService");
		customerService.saveCustomer();
		customerService.updateCustomer(1);
		customerService.deleteCustomer();
	}

}

三、切入点表达式

1、切入点表达式详解

关键字:execution(表达式)

表达式写法:

        访问修饰符 返回值 包名....类名.方法名(参数列表)

全匹配方式:

        public void cn.itcast.service.impl.CustomerServiceImpl.saveCustomer()

访问修饰符可以省略

        void cn.itcast.service.impl.CustomerServiceImpl.saveCustomer()

返回值可以使用*,表示任意返回值

        * cn.itcast.service.impl.CustomerServiceImpl.saveCustomer()

包名可以使用*号,表示任意包。但是有几个包,就得写几个*号

        * *.*.*.*.CustomerServiceImpl.saveCustomer()

包名可以使用..表达式当前包极其子包 

        * cn..CustomerServiceImpl.saveCustomer()

类名可以使用*,表示任意类

        * cn..*.saveCustomer()

方法名可以使用*,表示任意方法

        * cn..*.*()

参数列表可以使用*号,表示任意数据类型,但是有几个*就表示有几个参数

        * cn..*.*(*)

参数列表可以使用..,表示有无参数均可

        * cn..*.*(..)

全通配方式:

        * *..*.*(..)

四、通知类型详解

1、通知类型配置方法

aop:before 用于配置前置通知。永远在切入点方法执行之前执行

aop:after-returning 用于配置后置通知。在切入点方法正常执行之后执行。

aop:after-throwing 用于配置异常通知。在切入点方法执行产生异常后执行。

aop:after 用于配置最终通知。无论切入点方法是否正常执行,它都会在其后面执行。

2、修改记录日记的方法

package cn.itcast.utils;

import org.aspectj.lang.ProceedingJoinPoint;

/**
 * 记录日志的工具类
 * @author zhy
 *
 */
public class Logger {
	
	/**
	 * 前置通知
	 */
	public void beforePrintLog(){
		System.out.println("Logger类中的beforePrintLog方法开始记录日志了");
	}
	
	/**
	 * 后置通知
	 */
	public void afterReturningPrintLog(){
		System.out.println("Logger类中的afterReturningPrintLog方法开始记录日志了");
	}
	
	
	/**
	 * 异常通知
	 */
	public void afterThrowingPrintLog(){
		System.out.println("Logger类中的afterThrowingPrintLog方法开始记录日志了");
	}
	
	
	/**
	 * 最终通知
	 */
	public void afterPrintLog(){
		System.out.println("Logger类中的afterPrintLog方法开始记录日志了");
	}
	
	
	/**
	 * 环绕通知
	 * 问题:
	 * 	当我们配置了环绕通知,发现运行时,通知里面的代码执行了。而业务核心方法的代码没有执行。
	 * 分析:
	 * 	通过动态代理,我们得知环绕通知是指的整个invoke方法,里面明确的调用业务核心方法。
	 *  在我们配置的环绕通知中,没有明确的调用业务核心方法。
	 *  所以,才会只有环绕通知的代码执行,而service中的方法没有执行
	 * 解决:
	 * 	spring框架给我们提供了一个接口,ProceedingJoinPoint,该接口可以作为环绕通知方法的参数。
	 * 	在程序运行期间,spring框架会为我们提供该接口的实现类。
	 * 	该接口中有一个方法:proceed();此方法就相当于明确的调用了业务核心方法
	 * 
	 * 环绕通知:
	 * 	它不是用于指定增强代码何时执行的,而是spring框架为我们提供的一种在代码中手动控制增强方法何时执行的方式。
	 */
	public void aroundPringLog(ProceedingJoinPoint pjp){
		try {
			System.out.println("前:Logger类中的aroundPringLog方法开始记录日志了");
			pjp.proceed();//相当于调用service的方法(执行了业务层的核心方法)
			System.out.println("后:Logger类中的aroundPringLog方法开始记录日志了");
		} catch (Throwable e) {
			System.out.println("异:Logger类中的aroundPringLog方法开始记录日志了");
			e.printStackTrace();
		}finally{
			System.out.println("终:Logger类中的aroundPringLog方法开始记录日志了");
		}
	}
}

2、修改配置文件

注意:以下配置文件中不包含环绕通知,环绕通知需要另行配置

<?xml version="1.0" encoding="UTF-8"?>
<!-- 导入约束:
	导入beans和aop两个约束
-->
<beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:aop="http://www.springframework.org/schema/aop"
        xsi:schemaLocation="http://www.springframework.org/schema/beans 
        					http://www.springframework.org/schema/beans/spring-beans.xsd
        					http://www.springframework.org/schema/aop 
        					http://www.springframework.org/schema/aop/spring-aop.xsd"> 
	<!-- 把资源交给spring来管理 -->
	<bean id="customerService" class="cn.itcast.service.impl.CustomerServiceImpl"></bean>
	

	<bean id="logger" class="cn.itcast.utils.Logger"></bean>
	

	<aop:config>
		<!--所有的aop:aspect都可以使用  -->
		<aop:pointcut expression="execution(void cn.itcast.service.impl.CustomerServiceImpl.saveCustomer())" id="pt1"/>
		<aop:aspect id="logAdvice" ref="logger">
			<!--配置通知的类型 
				aop:before 用于配置前置通知。永远在切入点方法执行之前执行
				aop:after-returning 用于配置后置通知。在切入点方法正常执行之后执行。
				aop:after-throwing 用于配置异常通知。在切入点方法执行产生异常后执行。
				aop:after 用于配置最终通知。无论切入点方法是否正常执行,它都会在其后面执行。
			-->
			<aop:before method="beforePrintLog" pointcut-ref="pt1"/>
			<aop:after-returning method="afterReturningPrintLog" pointcut-ref="pt1"/>
			<aop:after-throwing method="afterThrowingPrintLog" pointcut-ref="pt1"/>
			<aop:after method="afterPrintLog" pointcut-ref="pt1"/>
			
			<!-- 配置切入点表达式 :只能在当前的aop:apsect中使用,如果是另外的一个aop:aspect不能用
			<aop:pointcut expression="execution(void cn.itcast.service.impl.CustomerServiceImpl.saveCustomer())" id="pt1"/>
			-->
			
		</aop:aspect>
	</aop:config>
</beans>

3、配置环绕通知

	/**
	 * 环绕通知
	 * 问题:
	 * 	当我们配置了环绕通知,发现运行时,通知里面的代码执行了。而业务核心方法的代码没有执行。
	 * 分析:
	 * 	通过动态代理,我们得知环绕通知是指的整个invoke方法,里面明确的调用业务核心方法。
	 *  在我们配置的环绕通知中,没有明确的调用业务核心方法。
	 *  所以,才会只有环绕通知的代码执行,而service中的方法没有执行
	 * 解决:
	 * 	spring框架给我们提供了一个接口,ProceedingJoinPoint,该接口可以作为环绕通知方法的参数。
	 * 	在程序运行期间,spring框架会为我们提供该接口的实现类。
	 * 	该接口中有一个方法:proceed();此方法就相当于明确的调用了业务核心方法
	 * 
	 * 环绕通知:
	 * 	它不是用于指定增强代码何时执行的,而是spring框架为我们提供的一种在代码中手动控制增强方法何时执行的方式。
	 */
	public void aroundPringLog(ProceedingJoinPoint pjp){
		try {
			System.out.println("前:Logger类中的aroundPringLog方法开始记录日志了");
			pjp.proceed();//相当于调用service的方法(执行了业务层的核心方法)
			System.out.println("后:Logger类中的aroundPringLog方法开始记录日志了");
		} catch (Throwable e) {
			System.out.println("异:Logger类中的aroundPringLog方法开始记录日志了");
			e.printStackTrace();
		}finally{
			System.out.println("终:Logger类中的aroundPringLog方法开始记录日志了");
		}
	}

	<aop:config>
		<!--所有的aop:aspect都可以使用  -->
		<aop:pointcut expression="execution(void cn.itcast.service.impl.CustomerServiceImpl.saveCustomer())" id="pt1"/>
		<aop:aspect id="logAdvice" ref="logger">
			
			<!-- 配置环绕通知:详细注释请看Logger类 -->
			<aop:around method="aroundPringLog" pointcut-ref="pt1"/>
		</aop:aspect>
	</aop:config>


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值