SSH学习之——Spring面向方面编程AOP
一:概述众所周知,Spring是一个轻量级的、非侵入式的、独立于各种应用服务器的开源框架。它的两大方面被人们所熟知,也应用很广。那就是IOC(控制反转)和AOP(面向方面编程)。IOC是开发者不创建对象,但是描述创建它们的方式,对象由Spring容器根据描述来产生对象,这里特别需要指出的是Spring是依赖于接口编程的,所以描述创建对象时,改对象必须实现于对应的接口AOP允许开发者对横
一:概述
众所周知,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接口
更多推荐
所有评论(0)