`

应用Spring AOP(一)

阅读更多

        AOP即Aspect-Oriented Programming,面向方面编程。AOP和OOP类似,也是一种编程模式。但是AOP并不能取代OOP,它只是对OOP的扩展和补充。Spring AOP是基于AOP编程模式的一个框架,它实现了AOP范围内的大多数功能,包括Advice、Pointcut等。

        AOP典型的应用场景:

http://pandonix.iteye.com/blog/336873 写道
1.对部分函数的调用进行日志记录,用于观察特定问题在运行过程中的函数调用情况
2.监控部分重要函数,若抛出指定的异常,需要以短信或邮件方式通知相关人员
3.金控部分重要函数的执行时间

事实上,以上需求没有AOP也能搞定,只是在实现过程中比较郁闷摆了。

1.需要打印日志的函数分散在各个包中,只能找到所有的函数体,手动添加日志。然而这些日志都是临时的,待问题解决之后应该需要清除打印日志的代码,只能再次手动清除^_^!

2.类似1的情况,需要捕获异常的地方太多,如果手动添加时想到很可能明天又要手动清除,只能再汗。OK,该需求相对比较固定,属于长期监控的范畴,并不需求临时添加后再清除。然而,客户某天要求,把其中20%的异常改为短信提醒,剩下的80%改用邮件提醒。改之,两天后,客户抱怨短信太多,全部改成邮件提醒...

3.该需求通常用于监控某些函数的执行时间,用以判断系统执行慢的瓶颈所在。瓶颈被解决之后,烦恼同情况1

       其实说白了就是给代码解耦,如果要将日志的逻辑添加到要插入的函数内,以后要更改或者删除的话,因为违背设计模式的开闭原则,改代码对程序员来说会很累很痛苦。

OOP术语:

方面(Aspect):相当于OOP中的类,就是封装用于横插入系统的功能。例如日志、事务、安全验证等。

通知(Advice):相当于OOP中的方法,是编写实际功能代码的地方。

连接点(Joinpoint):程序执行过程中插入方面的地方。Spring AOP只支持在方法调用和异常抛出中插入方面代码。

切入点(Pointcut):定义通知应该织人到哪些连接点上。通常切入点指的是类或方法名。例如某个通知要应用所有以abc开头的方法中,那么所有满足这个规则的方法都是切入点。

目标(Target):目标类或者目标接口。

代理(Proxy):AOP工作时是通过代理对象来访问目标对象。其实AOP的实现是通过动态代理,离不开代理模式,所以必须要有一个代理对象。

织入(Weaving):在目标对象中插入方面代码的过程就叫做织入。

 

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 

 

示例工程代码(用了很多《Struts 2 + Spring + Hibernate框架技术与项目实战》这本书的第21章的代码,但是也有所改动):

用MyEclipse建立的一个简单的web工程:

 前置通知:

package com.aop;

import java.lang.reflect.Method;

import org.apache.log4j.Logger;
import org.springframework.aop.MethodBeforeAdvice;

public class BeforeLogAdvice implements MethodBeforeAdvice {
	private Logger logger = Logger.getLogger(BeforeLogAdvice.class);

	public void before(Method method, Object[] args, Object target)
			throws Throwable {
		String targetClassName = target.getClass().getName();
		String targetMethodName = method.getName();

		// args[0] = "Juve";//可以改变参数

		String logInfoText = "前置通知:" + targetClassName + "类的"
				+ targetMethodName + "方法开始执行";
		logger.info(logInfoText);
	}
}

 前置通知用于将方面代码插入方法之前,也就是说,在方法执行之前,会首先执行前置通知里的代码。包含前置通知代码的类就是方面,这个类需要实现org.springframework.aop.MethodBeforeAdvice接口。该接口中的before方法如上,参数中的method表示处于切点的方法,args表示这些方法的参数值(可以在这儿改变参数值),target表示这些方法所在的类的对象实例。

 

后置通知:

package com.aop;

import java.lang.reflect.Method;

import org.apache.log4j.Logger;
import org.springframework.aop.AfterReturningAdvice;

public class AfterLogAdvice implements AfterReturningAdvice {

	private Logger logger = Logger.getLogger(AfterLogAdvice.class);

	public void afterReturning(Object returnValue, Method method,
			Object[] args, Object target) throws Throwable {
		// 获取被调用的类名
		String targetClassName = target.getClass().getName();
		// 获取被调用的方法名
		String targetMethodName = method.getName();
		
		// 日志格式字符串
		String logInfoText = "后置通知:" + targetClassName + "类的"
				+ targetMethodName + "方法已经执行";
		// 将日志信息写入配置的文件中
		logger.info(logInfoText);
	}
}

后置通知的代码在调用被拦截的方法后调用。注意afterReturning方法的第一参数returnValue表示被拦截的方法的返回值。

 

环绕通知:

package com.aop;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.log4j.Logger;

public class LogAroundAdvice implements MethodInterceptor {

	private Logger logger = Logger.getLogger(LogAroundAdvice.class);

	public Object invoke(MethodInvocation invocation) throws Throwable {
		
		// 获取被调用的方法名
		String targetMethodName = invocation.getMethod().getName();
		
		/*
		invocation.getArguments()[0] = "Alex";//也可以得到或者改变要执行的方法的参数
		 */
		
		long beginTime = System.currentTimeMillis();
		invocation.proceed(); //调用被拦截的方法
		long endTime = System.currentTimeMillis();
		
		// 日志格式字符串
		String logInfoText = "环绕通知:" + targetMethodName + "方法调用前时间" + beginTime
				+ "毫秒," + "调用后时间" + endTime + "毫秒";
		// 将日志信息写入配置的文件中
		logger.info(logInfoText);
		
		//这儿相当于强行将拦截的方法的返回值设成了null
		return null;
		//return invocation.proceed();
	}
}

 环绕通知能力最强,可以在方法调用前执行通知代码,可以决定是否还调用目标方法。也就是说它可以控制被拦截的方法的执行,还可以控制被拦截方法的返回值。

 

异常通知:

package com.aop;

import org.apache.log4j.Logger;
import java.lang.reflect.Method;
import org.springframework.aop.ThrowsAdvice;

public class ThrowsLogAdvice implements ThrowsAdvice {
	private Logger logger = Logger.getLogger(ThrowsLogAdvice.class);

	public void afterThrowing(Method method, Object[] args, Object target,
			Throwable exeptionClass) {
		// 获取被调用的类名
		String targetClassName = target.getClass().getName();
		// 获取被调用的方法名
		String targetMethodName = method.getName();
		// 日志格式字符串
		String logInfoText = "异常通知:执行" + targetClassName + "类的"
				+ targetMethodName + "方法时发生异常";
		// 将日志信息写入配置的文件中
		logger.info(logInfoText);
	}

	/*
	public void afterThrowing(IllegalArgumentException e){
		System.out.println(e.getMessage());
	}
	*/
}

如果在调用方法时发生异常,异常通知类可以提供一个机会来处理所发生的异常。org.springframework.aop.ThrowsAdvice接口中afterThrowing有两种重载形式:

public void afterThrowing(Throwable e);
public void afterThrowing(Method method, Object[] args, Object target, Throwable exeptionClass);

第一种重载形式中,e表示方法抛出的异常类型。如果参数e的类型与异常类型不匹配,afterThrowing将不会被调用。如果这两种重载形式同时存在,并且都匹配抛出的异常类型,那么第一种重载形式优先调用。

 

目标接口IUserService和类UserService:

package com.service;

public interface IUserService {
	public void addUser(String name, int age);
	public void deleteUser(String name);
}

 

package com.service;

public class UserService implements IUserService {

	public void addUser(String name, int age) {
		//省略诸如操作数据库等复杂的逻辑操作
		System.out.println("add user "+ name +" successfully");
	}

	public void deleteUser(String name) {
		//省略诸如操作数据库等复杂的逻辑操作
		System.out.println("deleted one user named " + name);
		throw new RuntimeException("这是特意抛出的异常信息!");
	}
}

这个工程中用到了log4j,log4j.properties的配置文件内容就不贴出来了。别忘了在WEB-INF里的logs目录下新建几个log文件。

 

applicationContext.xml文件内容:

<?xml version="1.0" encoding="UTF-8"?>
<beans
	xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:p="http://www.springframework.org/schema/p"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
	
	<bean id="myUserService" class="com.service.UserService"></bean>
	
	<!-- 定义前置通知 -->
    <bean id="beforeLogAdvice" class="com.aop.BeforeLogAdvice"></bean>
    <!-- 定义后置通知 -->
	<bean id="afterLogAdvice" class="com.aop.AfterLogAdvice"></bean>
	<!-- 定义异常通知 -->
	<bean id="throwsLogAdvice" class="com.aop.ThrowsLogAdvice"></bean>
    <!-- 定义环绕通知 -->
	<bean id="logAroundAdvice" class="com.aop.LogAroundAdvice"></bean>
	
    <!-- 定义代理类,名 称为myProxy,将通过myProxy访问业务类中的方法 -->
	<bean id="myProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
	  <property name="proxyInterfaces">
	    <value>com.service.IUserService</value>
	  </property>
	  <property name="interceptorNames">
	    <list>	     
	     <value>beforeLogAdvice</value>
	     <!-- 织入后置通知 -->
	     <value>afterLogAdvice</value>
	     <!-- 织入异常通知 -->
	     <value>throwsLogAdvice</value>
	     <!-- 织入环绕通知 -->
	     <value>logAroundAdvice</value>
	    </list>
	  </property>
	  <property name="target" ref="myUserService"></property>
	</bean>
</beans>

 这里目标类和几个通知类的bean定义就不说了,重点看一下id为myProxy的ProxyFactoryBean的代理类的定义,它包含三个属性:

proxyInterface:代理所实现的接口。Spring AOP无法截获未在该属性指定的接口中的方法。

interceptorNames:用于拦截方法的拦截器名,通知类是其中一种类型,也可以是Advisor。

target:目标类。要注意的是一个代理只能有一个target。

 

主测试类:

package com.test;

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

import com.service.IUserService;

public class MainTest {
	public static void main(String[] args) {
		ApplicationContext context = new ClassPathXmlApplicationContext(
				"applicationContext.xml");
		IUserService userService = (IUserService) context.getBean("myProxy");

		userService.addUser("ton", 56);
		userService.deleteUser("ton");
	}
}

 可以看到,这儿应用的bean是myProxy,而不是myUserService。运行结果如下:

[INFO ] [22:08:13] com.aop.BeforeLogAdvice - 前置通知:com.service.UserService类的addUser方法开始执行
add user ton successfully
[INFO ] [22:08:13] com.aop.LogAroundAdvice - 环绕通知:addUser方法调用前时间1382969293109毫秒,调用后时间1382969293109毫秒
[INFO ] [22:08:13] com.aop.AfterLogAdvice - 后置通知:com.service.UserService类的addUser方法已经执行
[INFO ] [22:08:13] com.aop.BeforeLogAdvice - 前置通知:com.service.UserService类的deleteUser方法开始执行
deleted one user named ton
[INFO ] [22:08:13] com.aop.ThrowsLogAdvice - 异常通知:执行com.service.UserService类的deleteUser方法时发生异常
Exception in thread "main" java.lang.RuntimeException: 这是特意抛出的异常信息!

 

 

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

注:还是这个工程,有没有办法让在取service的bean时可以不用代理呢,让AOP的通知在服务调用方毫不知情的下就进行织入呢。答案是使用BeanNameAutoProxyCreator。在applicationContext.xml声明如下bean:

<bean id="myServiceAutoProxyCreator" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
		<property name="interceptorNames">
			<list>
				<value>logAroundAdvice</value>
			</list>
		</property>
		<property name="beanNames">
			<value>*Service</value>
		</property>
	</bean>

 这个BeanNameAutoProxyCreator的bean中指明上下文中所有调用以Service结尾的服务类都会被拦截,执行logAroundAdvice的invoke方法。同时它会自动生成Service的代理,这样在使用的时候就可以直接取服务类的bean,而不用再像上面那样还用取代理类的bean。

package com.test;

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

import com.service.IUserService;

public class MainTest {
	public static void main(String[] args) {
		ApplicationContext context = new ClassPathXmlApplicationContext(
				"applicationContext.xml");
		IUserService userService = (IUserService) context.getBean("myUserService");

		userService.addUser("ton", 56);
		userService.deleteUser("ton");
	}
}

 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

 

工程代码压缩文件在附件中。。。

  • 大小: 14.9 KB
分享到:
评论

相关推荐

    spring aop jar 包

    Spring AOP(Aspect Oriented Programming,面向切面编程)是Spring框架的重要组成部分,它提供了一种在不修改源代码的情况下,对程序进行功能增强的技术。这个"spring aop jar 包"包含了实现这一功能所需的类和接口,...

    Spring AOP 16道面试题及答案.docx

    Spring AOP,全称为Aspect Oriented Programming,是面向切面编程的一种编程范式,它是对传统的面向对象编程(OOP)的一种补充。在OOP中,核心是对象,而在AOP中,核心则是切面。切面是关注点的模块化,即程序中的...

    简单spring aop 例子

    Spring AOP(面向切面编程)是Spring框架的重要组成部分,它提供了一种模块化和声明式的方式来处理系统中的交叉关注点问题,如日志、事务管理、安全性等。本示例将简要介绍如何在Spring应用中实现AOP,通过实际的...

    Spring AOP完整例子

    Spring AOP(面向切面编程)是Spring框架的核心特性之一,它允许开发者在不修改源代码的情况下,通过插入切面来增强或改变程序的行为。在本教程中,我们将深入探讨Spring AOP的不同使用方法,包括定义切点、通知类型...

    spring aop依赖jar包

    现在,我们回到主题——"springaop依赖的jar包"。在Spring 2.5.6版本中,使用Spring AOP通常需要以下核心jar包: - `spring-aop.jar`:这是Spring AOP的核心库,包含了AOP相关的类和接口。 - `spring-beans.jar`:...

    Spring AOP实现机制

    **Spring AOP 实现机制详解** Spring AOP(面向切面编程)是Spring框架的核心特性之一,它允许程序员在不修改源代码的...通过深入理解Spring AOP的实现机制,我们可以更好地利用这一强大的工具,优化我们的应用程序。

    死磕Spring之AOP篇 - Spring AOP两种代理对象的拦截处理(csdn)————程序.pdf

    Spring AOP 是一种面向切面编程的技术,它允许我们在不修改源代码的情况下,对应用程序的特定部分(如方法调用)进行增强。在 Spring 中,AOP 的实现主要依赖于代理模式,有两种代理方式:JDK 动态代理和 CGLIB 动态...

    Spring Aop四个依赖的Jar包

    Spring AOP,全称Aspect-Oriented Programming(面向切面编程),是Spring框架的一个重要模块,它通过提供声明式的方式来实现面向切面编程,从而简化了应用程序的开发和维护。在Spring AOP中,我们无需深入到每个...

    Spring AOP面向方面编程原理:AOP概念

    Spring AOP是在Spring框架的基础上实现的一种面向方面编程机制。 1. **方面(Aspect)**:这是AOP的核心概念之一,指代一个关注点的模块化,该关注点可能会横切多个对象。例如事务管理就是一个典型的横切关注点,...

    spring aop 五个依赖jar

    Spring AOP(面向切面编程)是Spring框架的重要组成部分,它提供了一种模块化和声明式的方式来处理系统中的交叉关注点,如日志、事务管理等。在Java应用中,AOP通过代理模式实现了切面编程,使得我们可以将业务逻辑...

    spring AOP依赖三个jar包

    - **事务管理**:Spring AOP的一个常见应用就是声明式事务管理,通过`@Transactional`注解可以轻松地在方法级别控制事务。 综上所述,spring AOP依赖的这三个jar包构成了Spring框架面向切面编程的基础,它们共同...

    基于java的企业级应用开发:Spring AOP简介.ppt

    Spring AOP是Spring框架的一个重要组成部分,它为Java企业级应用提供了切面编程的实现。Spring AOP允许开发者专注于核心业务逻辑,而把非业务相关的关注点,如日志、事务管理等,分离出来,从而提高开发效率和代码可...

    spring aop的demo

    在`springAop1`这个压缩包中,可能包含了一个简单的应用示例,展示了如何定义一个切面类,以及如何在该类中定义通知方法。例如,我们可能会看到一个名为`LoggingAspect`的类,其中包含了`@Before`注解的方法,用于在...

    spring-aop实例

    Spring AOP(面向切面编程)是Spring框架的重要组成部分,它提供了一种强大的方式来实现横切关注点,如日志、事务管理、安全性等,从而解耦应用程序的核心业务逻辑。在Spring AOP中,关注点被模块化为独立的“切面”...

    反射实现 AOP 动态代理模式(Spring AOP 的实现原理)

    面向切面编程(AOP)是一种编程范式,旨在将横切关注点(如日志、安全等)与业务逻辑分离,从而提高模块化。AOP通过预定义的“切面”对横切关注点进行模块化,从而可以在不修改业务逻辑代码的情况下增加新功能。动态...

    spring AOP 引入jar包,spring IOC 引入Jar包

    Spring AOP 和 Spring IOC 是 Spring 框架的两个核心组件,它们对于任何基于 Java 的企业级应用开发都至关重要。Spring AOP(面向切面编程)允许开发者在不修改源代码的情况下,通过“切面”来插入新的行为或增强已...

    Spring aop 性能监控器

    在IT行业中,Spring AOP(面向切面编程)是一种强大的工具,它允许我们在不修改代码的情况下,对应用程序的特定部分进行拦截和增强。这在性能监控、日志记录、事务管理等方面尤为有用。本篇文章将深入探讨如何使用...

    spring aop spring aop

    总的来说,Spring AOP提供了一种优雅的方式来管理横切关注点,使代码更整洁,降低了模块间的耦合。在实际开发中,它可以用于日志记录、权限控制、事务管理等多个场景,极大地提高了代码的可维护性和复用性。

    spring aop简单应用示例

    在IT行业中,Spring AOP(面向切面编程)是一种强大的工具,它允许程序员在不修改原有业务代码的情况下,实现如日志记录、性能监控、事务管理等横切关注点的功能。本示例将深入探讨Spring AOP的基础知识,以及如何在...

    spring aop用到jar包.rar

    总的来说,这个压缩文件"spring aop用到jar包.rar"包含了实现Spring AOP所需的一系列核心库,它们共同构成了Spring AOP的基础架构,使得我们可以方便地编写和应用切面,提高代码的可维护性和可扩展性。在实际项目中...

Global site tag (gtag.js) - Google Analytics