SSH学习之——Spring面向方面编程AOP

原创 2012年09月13日 10:50:49

一:概述

众所周知,Spring是一个轻量级的、非侵入式的、独立于各种应用服务器的开源框架。它的两大方面被人们所熟知,也应用很广。那就是IOC(控制反转)和AOP(面向方面编程)。

IOC是开发者不创建对象,但是描述创建它们的方式,对象由Spring容器根据描述来产生对象,这里特别需要指出的是Spring是依赖于接口编程的,所以描述创建对象时,改对象必须实现于对应的接口

AOP允许开发者对横切关注点或横切典型的职责分界线的行为(例如日志和事务管理)进行模块化。AOP 的核心构造是方面,它将那些影响多个类的行为封装到可重用的模块中。

二:实现

下面针对AOP来进行调试研究,Spring实现AOP有注解和非注解两种方式,这里我们采用注册时记录日志为案例来分别实现这两种方式。


首先我们创建业务,RegistService接口和RegistServiceImpl实现类

package org.cyxl.spring.aop;

public interface RegistService {
	void add(String name);
}

package org.cyxl.spring.aop;

public class RegistServiceImpl implements RegistService {

	public void add(String name) {
		System.out.println(name+" add...");
		throw new RuntimeException("runtime exception happened...");
	}

}

接下来实现非注解方式

第一步:定义日志切面类LogAspect

package org.cyxl.spring.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;

/**
 * 日志切面类
 * 
 */
public class LogAspect {
	// 任何通知方法都可以将第一个参数定义为 org.aspectj.lang.JoinPoint类型
	public void before(JoinPoint call) {
		String clazz = call.getTarget().getClass().getName();
		// 获取目标对象上正在执行的方法名
		String methodName = call.getSignature().getName();
		System.out.println("前置通知:" + clazz + "类的" + methodName + "方法开始了...");
	}

	public void afterReturn() {
		System.out.println("后置通知:方法正常结束...");
	}

	public void after() {
		System.out.println("最终通知:不管方法有没有正常执行完成,一定会返回的...");
	}

	public void afterThrowing() {
		System.out.println("异常抛出后通知:方法执行时出现异常...");
	}

	// 用来做环绕通知的方法可以第一个参数定义为org.aspectj.lang.ProceedingJoinPoint类型
	public Object doAround(ProceedingJoinPoint call) throws Throwable {
		Object result = null;
		this.before(call);// 相当于前置通知
		try {
			result = call.proceed();
			this.afterReturn(); // 相当于后置通知
		} catch (Throwable e) {
			this.afterThrowing(); // 相当于异常抛出后通知
			throw e;
		} finally {
			this.after(); // 相当于最终通知
		}
		return result;
	}
}
第二步:添加Spring的支持,这里可以采用工具(如myeclipse)或者手动两种方式添加,我这里采用myeclipse添加Spring的支持,勾选Spring的core和aop两方面的jar库文件,添加的同时创建applicationContext.xml

第三步:添加spring配置文件applicationContext.xml对aop的支持。自动创建的配置文件没有包含aop的支持,所以我们在beans标签的属性中添加spring对aop的支持,如下

<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"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
	http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
	http://www.springframework.org/schema/aop 
	http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
看看里面有aop字眼的配置就应该明白了

第四步:配置applicationContext.xml文件,首先创建业务方法对象

<bean id="registService" class="org.cyxl.spring.aop.RegistServiceImpl">
	</bean>
然后创建日志切面类对象

 <bean id="logAspectBean" class="org.cyxl.spring.aop.LogAspect"/>
最后配置aop

<aop:config>  
        <!-- 第2步:配置一个切面 -->  
        <aop:aspect id="logAspect" ref="logAspectBean">  
            <!-- 第3步:定义切入点,指定切入点表达式 -->  
            <aop:pointcut id="allMethod"   
                expression="execution(* org.cyxl.spring.aop.*.*(..))"/>   
            <!-- 第4步:应用前置通知 -->  
            <aop:before method="before" pointcut-ref="allMethod" />  
            <!-- 第4步:应用后置通知 -->  
            <aop:after-returning method="afterReturn" pointcut-ref="allMethod"/>  
            <!-- 第4步:应用最终通知 -->  
            <aop:after method="after" pointcut-ref="allMethod"/>  
            <!-- 第4步:应用抛出异常后通知 -->  
            <aop:after-throwing method="afterThrowing" pointcut-ref="allMethod"/>  
        </aop:aspect>  
    </aop:config> 

上面采用对各种通知分别配置的方式,我们也可以采用环绕通知的方式一次性配置所有的通知、

<aop:config>  
        <!-- 第2步:配置一个切面 -->  
        <aop:aspect id="logAspect" ref="logAspectBean">  
            <!-- 第3步:定义切入点,指定切入点表达式 -->  
            <aop:pointcut id="allMethod"   
                expression="execution(* org.cyxl.spring.aop.*.*(..))"/>     
            <!-- 第4步:应用环绕通知 -->  
            <aop:around method="doAround" pointcut-ref="allMethod" /> 
        </aop:aspect>  
    </aop:config> 

这里<aop:pointcut>中的expression属性起到了关键性的作用,它定义了哪些对象的哪些方法执行时会去执行日志记录

第五步:测试结果,创建测试类

package org.cyxl.spring.aop;

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

public class Test {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
		RegistService service=(RegistService)context.getBean("registService");
		try
		{
			service.add("cyxl");
		}
		catch(Exception e){
			
		}
		
	}

}

输出结果

前置通知:org.cyxl.spring.aop.RegistServiceImpl类的add方法开始了...
cyxl add...
最终通知:不管方法有没有正常执行完成,一定会返回的...
异常抛出后通知:方法执行时出现异常...

当然你也可以试试将故意抛出的异常去掉,这样就会得到结果如下

前置通知:org.cyxl.spring.aop.RegistServiceImpl类的add方法开始了...
cyxl add...
后置通知:方法正常结束...
最终通知:不管方法有没有正常执行完成,一定会返回的...

非注解的方式大致如此,下面采用注解的方式实现一遍

第一步:定义采用注解方式的日志切面类

package org.cyxl.spring.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Pointcut;

/**
 * 日志切面类
 */
@Aspect
// 定义切面类
public class LogAnnotationAspect {
	@SuppressWarnings("unused")
	// 定义切入点,提供一个方法,这个方法的名字就是改切入点的id
	@Pointcut("execution(* org.cyxl.spring.aop.*.*(..))")
	private void allMethod() {
	}

	// 针对指定的切入点表达式选择的切入点应用前置通知
	@Before("execution(* org.cyxl.spring.aop.*.*(..))")
	public void before(JoinPoint call) {
		String className = call.getTarget().getClass().getName();
		String methodName = call.getSignature().getName();
		System.out.println("【前置通知(Annotation)】:" + className + "类的" + methodName
				+ "方法开始了...");
	}

	// 访问命名切入点来应用后置通知
	@AfterReturning("allMethod()")
	public void afterReturn() {
		System.out.println("【后置通知(Annotation)】:方法正常结束...");
	}

	// 应用最终通知
	@After("allMethod()")
	public void after() {
		System.out.println("【最终通知(Annotation)】:不管方法有没有正常执行完成,一定会返回的...");
	}

	// 应用异常抛出后通知
	@AfterThrowing("allMethod()")
	public void afterThrowing() {
		System.out.println("【异常抛出后通知(Annotation)】:方法执行时出现异常...");
	}

	// 应用周围通知
	// @Around("allMethod()")
	public Object doAround(ProceedingJoinPoint call) throws Throwable {
		Object result = null;
		this.before(call);// 相当于前置通知
		try {
			result = call.proceed();
			this.afterReturn(); // 相当于后置通知
		} catch (Throwable e) {
			this.afterThrowing(); // 相当于异常抛出后通知
			throw e;
		} finally {
			this.after(); // 相当于最终通知
		}
		return result;
	}
}

这里和非注解方式的差不多,只是用一些annotation来赋予了每个方法的职能

第二步:配置applicationContext.xml,定义注解方式的切面类的对象

<bean id="logAspectAnnotationBean" class="org.cyxl.spring.aop.LogAnnotationAspect"/> 
第三步:配置applicationContext.xml来添加采用注解方式的aop实现

<aop:aspectj-autoproxy/>

第四步:测试结果。测试类不变,结果如下

【前置通知(Annotation)】:org.cyxl.spring.aop.RegistServiceImpl类的add方法开始了...
cyxl add...
【异常抛出后通知(Annotation)】:方法执行时出现异常...
【最终通知(Annotation)】:不管方法有没有正常执行完成,一定会返回的...

三:总结

1、采用注解的方式在配置方面简化了,但在理解方面就显得相对复杂些,必须结合具体的实现类加以理解

2、这两种方式可以同时使用,结果也是叠加的,如整体配置文件如下

<?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"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
	http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
	http://www.springframework.org/schema/aop 
	http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">

	<bean id="registService" class="org.cyxl.spring.aop.RegistServiceImpl">
	</bean>  
    <!-- 日志切面类 -->  
    <bean id="logAspectBean" class="org.cyxl.spring.aop.LogAspect"/>  
    <!-- 日志切面类Annotation实现 -->  
    <bean id="logAspectAnnotationBean" class="org.cyxl.spring.aop.LogAnnotationAspect"/> 
    <!-- 第1步: AOP的配置 -->  
    <aop:config>  
        <!-- 第2步:配置一个切面 -->  
        <aop:aspect id="logAspect" ref="logAspectBean">  
            <!-- 第3步:定义切入点,指定切入点表达式 -->  
            <aop:pointcut id="allMethod"   
                expression="execution(* org.cyxl.spring.aop.*.*(..))"/>   
            <!-- 第4步:应用前置通知 -->  
            <aop:before method="before" pointcut-ref="allMethod" />  
            <!-- 第4步:应用后置通知 -->  
            <aop:after-returning method="afterReturn" pointcut-ref="allMethod"/>  
            <!-- 第4步:应用最终通知 -->  
            <aop:after method="after" pointcut-ref="allMethod"/>  
            <!-- 第4步:应用抛出异常后通知 -->  
            <aop:after-throwing method="afterThrowing" pointcut-ref="allMethod"/>  
            <!-- 第4步:应用环绕通知 -->  
            <!--  
            <aop:around method="doAround" pointcut-ref="allMethod" /> 
             -->  
        </aop:aspect>  
    </aop:config> 
    
    <!-- 启用spring对AspectJ注解的支持 -->  
    <aop:aspectj-autoproxy/>
</beans>

运行测试,结果如下

前置通知:org.cyxl.spring.aop.RegistServiceImpl类的add方法开始了...
【前置通知(Annotation)】:org.cyxl.spring.aop.RegistServiceImpl类的add方法开始了...
cyxl add...
【异常抛出后通知(Annotation)】:方法执行时出现异常...
【最终通知(Annotation)】:不管方法有没有正常执行完成,一定会返回的...
最终通知:不管方法有没有正常执行完成,一定会返回的...
异常抛出后通知:方法执行时出现异常...

3、理解Spring中的AOP代理可以从理解JDK的动态代理开始,也就是某个类去实现java.lang.reflect.InvocationHandler接口


版权声明:本文为博主原创文章,未经博主允许不得转载。

AOP面向方面编程

1.引言         软件开发的目标是要对世界的部分元素或者信息流建立模型,实现软件系统的工程需要将系统分解成可以创建和管理的模块。于是出现了以系统模块化特性的面向对象程序设计技术。模块...
  • hguisu
  • hguisu
  • 2012年05月21日 11:37
  • 20539

ssh添加aop配置

  • 2013年09月25日 11:24
  • 16.33MB
  • 下载

【SSH进阶之路】Spring的AOP逐层深入——AOP的基本原理(六)

AOP(Aspect Oriented Programming),意思是面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。...
  • jiuqiyuliang
  • jiuqiyuliang
  • 2015年02月27日 11:40
  • 12774

菜鸟学SSH(十四)——Spring容器AOP的实现原理——动态代理

之前写了一篇关于IOC的博客——《Spring容器IOC解析及简单实现》,今天再来聊聊AOP。大家都知道Spring的两大特性是IOC和AOP,换句话说,容器的两大特性就是IOC和AOP。IOC负责将...
  • liushuijinger
  • liushuijinger
  • 2014年07月16日 16:16
  • 8046

【SSH进阶之路】Spring的AOP逐层深入——采用注解完成AOP(七)

采用注解方式,实现AOP,解析五类Advice的执行顺序。
  • jiuqiyuliang
  • jiuqiyuliang
  • 2015年02月28日 09:58
  • 6865

ssh+aop+log4j+日志拦截器+注解

  • 2015年11月27日 17:58
  • 20.22MB
  • 下载

SSH 基本配置--OA系统的经典配置

一、structs配置文件
  • lovoo
  • lovoo
  • 2016年05月09日 23:05
  • 3275

SSH中各个框架的作用以及Spring AOP,IOC,DI详解

在SSH框假中spring充当了管理容器的角色。我们都知道Hibernate用来做持久层,因为它将JDBC做了一个良好的封装,程序员在与数据库进行交互时可以不用书写大量的SQL语句。Struts是用来...
  • tantexian
  • tantexian
  • 2015年06月24日 15:44
  • 1194

AOP

1. AOP是什么? AOP是功能代码在整个程序中位置的形象描述。AOP(Aspect Oriented Programming,缩写)——面向切面编程。因为功能代码常常处在数据库的操...
  • wangqingbo0829
  • wangqingbo0829
  • 2015年07月31日 23:52
  • 877

ssh框架demo

  • 2017年11月07日 11:37
  • 27.21MB
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:SSH学习之——Spring面向方面编程AOP
举报原因:
原因补充:

(最多只允许输入30个字)