AspectJ和Spring与AOP的关系

        前面介绍过AOP面向切面编程是一种编程思想,是对OOP面向对象编程的一种补充。对于AOP这种编程思想,很多框架都进行了实现。Spring就是其中之一,可以完成面向切面编程。而AspectJ也实现了AOP的功能,且其实现方式更为简捷,使用更为方便,而且还支持注解式开发。所以,Spring又将AspectJ的对于AOP的实现也引入到了自己的框架中。后面使用AOP编程都是在Spring环境下使用AspectJ来进行的。

 

AspectJ的五种常用通知类型:

(1)前置通知

(2)后置通知

(3)环绕通知

(4)异常通知

(5)最终通知

其中最终通知是指,无论程序执行是否正常,该通知都会执行。类似于try...catch中的finally代码块。

 

AspectJ的切入点表达式

AspectJ定义了专门的表达式用于指定切入点。表达式的原型如下:

execution([modifiers-pattern]  访问权限类型

                   ret-type-pattern       返回值类型

                   [declaring-type-pattern]  全限定性类名

                   name-pattern(param-pattern)  方法名(参数名)

                   [throws-pattern]  抛出异常类型

       切入点表达式要匹配的对象就是目标方法的方法名。表达式中加[]的部分表示可省略的部分,红色部分表示不可省略的部分,各部分之间用空格分开。在其中可以使用一下符号:

举几个例子:

execution(public * *(..))           指定切入点为:任意公共方法

execution(* *.service.*.*(..))    指定只有一级包下的service子包下所有类(接口)中所有方法为切入点

execution(* *..service.*.*(..))   指定所有包下的service子包下所有类(接口)中所有方法为切入点

execution(* *.SomeService.*(..))    指定只有一级包下的SomeService类(接口)中所有方法为切入点

execution(* *..SomeService.*(..))    指定所有包下的SomeService类(接口)中所有方法为切入点

 

AspectJ的开发环境

引入AOP联盟的jar包,这是AOP的规范

引入AspectJ的jar包,这是对上面AOP规范的实现

引入整合Spring和AspectJ的jar包

引入Spring的AOP jar包,因为上面Spring和AspectJ的整合jar包用到了Spring的AOP jar包

注意:这里只列举了需要额外引入的jar包,还需要Spring的四个基础包和日志包

基于注解的实现:

前置通知:

首先定义好要增强的接口和实现类

package com.hnu.service;

public interface SomeService {
	void doFirst();
	String doSecond();
	void doThird();

}
package com.hnu.service;

public class SomeServiceImpl implements SomeService {

	@Override
	public void doFirst() {
		System.out.println("执行doFirst()方法");
	}

	@Override
	public String doSecond() {
		System.out.println("执行doSecond()方法");
		return "abcde";
	}

	@Override
	public void doThird() {
		System.out.println("执行doThird()方法");
	}

}

编写切面类(是一个POJO类)

package com.hnu.service;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect   //表示当前类是切面
public class MyAspect {
	//@Before表示这是一个后置通知   execution表示匹配的连接点
	@Before("execution(* *..SomeService.doFirst(..))")
	public void myBefore(){
		System.out.println("执行前置通知方法");
	}
	
	@Before("execution(* *..SomeService.doFirst(..))")
	public void myBefore(JoinPoint jp){              //JoinPoint表示连接点
		System.out.println("执行前置通知方法   jp = " + jp);
	}
}

配置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:aop="http://www.springframework.org/schema/aop"
        xmlns:tx="http://www.springframework.org/schema/tx" 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">
        
       <!-- 注册切面 -->
       <bean id="myAspect" class="com.hnu.service.MyAspect"/>
        
       <!--  注册目标对象 -->
       <bean id="someService" class="com.hnu.service.SomeServiceImpl"/>
       
       <!--  注册AspectJ的自动代理 ,使用到了AOP约束-->
       <aop:aspectj-autoproxy/>
       

</beans>

测试类

package com.hnu.service;

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

public class Test {

	public static void main(String[] args) {
		//创建容器对象,加载Spring配置文件
		String resource = "com/hnu/service/applicationContext.xml";
		ApplicationContext ac = new ClassPathXmlApplicationContext(resource);
		
		SomeService service = (SomeService) ac.getBean("someService");
		
		service.doFirst();
		System.out.println("--------------------------");
		service.doSecond();
		System.out.println("--------------------------");
		service.doThird();
	}

}

运行结果:

 

后置通知,环绕通知,异常通知的使用和前置通知相同,唯一需要改变的就是切面类,这里就只列出切面类的写法

package com.hnu.service;

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

@Aspect   //表示当前类是切面
public class MyAspect {
	
	@Before("execution(* *..SomeService.doFirst(..))")
	public void myBefore(){
		System.out.println("执行前置通知方法");
	}
	
	@Before("execution(* *..SomeService.doFirst(..))")
	public void myBefore(JoinPoint jp){
		System.out.println("执行前置通知方法   jp = " + jp);
	}
	
	@AfterReturning("execution(* *..SomeService.doSecond(..))")
	public void myAfterReturning(){
		System.out.println("执行后置通知方法");
	}
	
	//注意这里虽然能获取方法返回值,但是不能修改
	@AfterReturning(value="execution(* *..SomeService.doSecond(..))",returning="result")
	public void myAfterReturning(Object result){
		System.out.println("执行后置通知方法  result= " + result);
	}
	
	@Around("execution(* *..SomeService.doSecond(..))")
	public Object myAround(ProceedingJoinPoint pjp) throws Throwable{
		System.out.println("执行环绕通知方法,目标方法执行之前");
		Object result = pjp.proceed();
		System.out.println("执行环绕通知方法,目标方法执行之后");
		if(result != null){                                //可以修改目标方法的返回结果
			result = ((String)result).toUpperCase();
		}
		return result;
	}
	
	@AfterThrowing("execution(* *..SomeService.doThird(..))")
	public void myAfterThrowing(){
		System.out.println("执行异常通知方法");
	}
	
	@AfterThrowing(value="execution(* *..SomeService.doThird(..))",throwing="ex")
	public void myAfterThrowing(Exception ex){
		System.out.println("执行异常通知方法  ex = " + ex.getMessage());
	}
	
	@After("execution(* *..SomeService.doThird(..))")
	public void myAfter(){
		System.out.println("执行最终通知方法");
	}
}

 

定义切入点

通过上面的代码我们可以看到名称表达式execution()有大量冗余,可以通过定义切入点解决。

package com.hnu.service;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

@Aspect   //表示当前类是切面
public class MyAspect {
	
	@After("doThirdPointcut()")     //第二部:直接使用这个切入点
	public void myAfter(){
		System.out.println("执行最终通知方法");
	}
	
	//第一步:定义了一个切入点,叫doThirdPointcut()
	@Pointcut("execution(* *..SomeService.doThird(..))")
	public void doThirdPointcut(){}
}

 

 

基于XML的实现:

编写切面类(是一个POJO类),把上面的切面类删除所有的注解即可

package com.hnu.service;

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

public class MyAspect {
	
	public void myBefore(){
		System.out.println("执行前置通知方法");
	}
	
	public void myBefore(JoinPoint jp){
		System.out.println("执行前置通知方法   jp = " + jp);
	}
	
	public void myAfterReturning(){
		System.out.println("执行后置通知方法");
	}
	
	//注意这里虽然能获取方法返回值,但是不能修改
	public void myAfterReturning(Object result){
		System.out.println("执行后置通知方法  result= " + result);
	}
	
	public Object myAround(ProceedingJoinPoint pjp) throws Throwable{
		System.out.println("执行环绕通知方法,目标方法执行之前");
		Object result = pjp.proceed();
		System.out.println("执行环绕通知方法,目标方法执行之后");
		if(result != null){                                //可以修改目标方法的返回结果
			result = ((String)result).toUpperCase();
		}
		return result;
	}
	
	public void myAfterThrowing(){
		System.out.println("执行异常通知方法");
	}
	
	public void myAfterThrowing(Exception ex){
		System.out.println("执行异常通知方法  ex = " + ex.getMessage());
	}
	
	public void myAfter(){
		System.out.println("执行最终通知方法");
	}
	
}

下面是关键的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:aop="http://www.springframework.org/schema/aop"
        xmlns:tx="http://www.springframework.org/schema/tx" 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">
        
       <!-- 注册切面 -->
       <bean id="myAspect" class="com.hnu.service.MyAspect"/>
        
       <!--  注册目标对象 -->
       <bean id="someService" class="com.hnu.service.SomeServiceImpl"/>
       
       <!-- AOP配置 -->
       <aop:config>
       		<aop:pointcut expression="execution(* *..SomeService.doFirst())" id="doFirstPointcut"/>
       		<aop:pointcut expression="execution(* *..SomeService.doSecond())" id="doSecondPointcut"/>
       		<aop:pointcut expression="execution(* *..SomeService.doThird())" id="doThirdPointcut"/>
       		
       		<aop:aspect ref="myAspect">
       			<aop:before method="myBefore" pointcut="execution(* *..SomeService.doFirst())"/>  <!-- 不使用自定义切入点写法 -->
       			<aop:before method="myBefore(org.aspectj.lang.JoinPoint)" pointcut-ref="doFirstPointcut"/>
       			
       			<aop:after-returning method="myAfterReturning" pointcut-ref="doSecondPointcut"/>
       			<aop:after-returning method="myAfterReturning(java.lang.Object)" pointcut-ref="doSecondPointcut" returning="result"/>
       			
       			<aop:around method="myAround" pointcut-ref="doSecondPointcut"/>
       			
       			<aop:after-throwing method="myAfterThrowing" pointcut-ref="doThirdPointcut"/>
       			<aop:after-throwing method="myAfterThrowing(java.lang.Exception)" pointcut-ref="doThirdPointcut" throwing="ex"/>
       			
       			<aop:after method="myAfter" pointcut-ref="doThirdPointcut"/>
       		</aop:aspect>
       </aop:config>

</beans>

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值