Spring-Aop编程(三)-AspectJ

AspectJ

1. 介绍

AspectJ是一个基于Java语言的AOP框架,Spring2.0以后新增了对AspectJ切点表达式支持,@AspectJ 是AspectJ1.5新增功能,通过JDK5注解技术,允许直接在Bean类中定义切面,它是一种新版本Spring框架,建议使用AspectJ方式来开发AOP。主要用途:自定义开发

2.切入点表达式

  为了能够灵活定义切入点位置,Spring AOP提供了多种切入点指示符。

execution———用来匹配执行方法的连接点

  语法结构:   execution(   方法修饰符  方法返回值  方法所属类 匹配方法名 (  方法中的形参表 )  方法申明抛出的异常  )

  其中红色字体的部分时不能省略的,各部分都支持通配符 “*” 来匹配全部

  比较特殊的为形参表部分,其支持两种通配符

  •   "*":代表一个任意类型的参数;
  •   “..”:代表零个或多个任意类型的参数。

  例如:

    ()匹配一个无参方法

    (..)匹配一个可接受任意数量参数和类型的方法

    (*)匹配一个接受一个任意类型参数的方法

    (*,Integer)匹配一个接受两个参数的方法,第一个可以为任意类型,第二个必须为Integer。

  下面举一些execution的使用实例:

分类 示例 描述
通过方法签名定义切入点 execution(public * * (..)) 匹配所有目标类的public方法,第一个*为返回类型,第二个*为方法名
execution(* save* (..)) 匹配所有目标类以save开头的方法,第一个*代表返回类型
execution(**product(*,String)) 匹配目标类所有以product结尾的方法,并且其方法的参数表第一个参数可为任意类型,第二个参数必须为String
通过类定义切入点 execution(* aop_part.Demo1.service.*(..)) 匹配service接口及其实现子类中的所有方法
通过包定义切入点 execution(* aop_part.*(..)) 匹配aop_part包下的所有类的所有方法,但不包括子包
execution(* aop_part..*(..)) 匹配aop_part包下的所有类的所有方法,包括子包。(当".."出现再类名中时,后面必须跟“*”,表示包、子孙包下的所有类)
execution(* aop_part..*.*service.find*(..)) 匹配aop_part包及其子包下的所有后缀名为service的类中,所有方法名必须以find为前缀的方法
通过方法形参定义切入点 execution(*foo(String,int)) 匹配所有方法名为foo,且有两个参数,其中,第一个的类型为String,第二个的类型为int
execution(* foo(String,..)) 匹配所有方法名为foo,且至少含有一个参数,并且第一个参数为String的方法(后面可以有任意个类型不限的形参)

within————通过类匹配模式申明切入点(只能通过类型匹配连接点)

    例如:within(aop_part..*)             表示匹配包aop_part以及子包的所有方法

    由于execution可以匹配包、类、方法,而within只能匹配包、类,因此execution完全可以代替within的功能。


this————限定AOP代理必须时指定类型的实例,用于匹配该对象的所有连接点

    例如:this(aop_part.service.GodService)        表示匹配了GodService接口的代理对象的所有连接点


target————通过判断目标类的类型确定判断的是否匹配

    this通过判断代理类的类型来决定是否和切入点匹配,两者限定的对象都是指定类型的实例。

    例如: target(aop_part.service.GodService)      表示匹配实现了GodService接口的目标对象的所有连接点


args————用于对连接点的参数类型进行限制,要求参数类型时指定类型的实例

    例如:args(aop_part.service)                     表示匹配时,出入的参数类型时service的方法

    其与execution(**(aop_part.service))的区别为,execution针对的时方法签名,而args针对的是运行时的实际参数类型。

    args既匹配buyGoods(service newService),也匹配buyGoods(Buyservice newService)   <Buyservice为service的子类>

    execution只匹配buyGoods(service newService)


组合切入点

    支持 &&、 || 、!

    与其他语言所代表的意思相同

    例:args(aop_part.service)  &&execution(**(aop_part.service))

3.AspectJ 通知类型

aop联盟定义通知类型,具有特性接口,必须实现,从而确定方法名称。

aspectj 通知类型,只定义类型名称。已经方法格式。

个数:6种,知道5种,掌握1中。

before:前置通知(应用:各种校验)在方法执行前执行,如果通知抛出异常,阻止方法运行

afterReturning:后置通知(应用:常规数据处理)方法正常返回后执行,如果方法中抛出异常,通知无法执行必须在方法执行后才执行,所以可以获得方法的返回值。

around:环绕通知(应用:十分强大,可以做任何事情)方法执行前后分别执行,可以阻止方法的执行必须手动执行目标方法

afterThrowing:抛出异常通知(应用:包装异常信息)方法抛出异常后执行,如果方法没有抛出异常,无法执行

after:最终通知(应用:清理现场) 方法执行完毕后执行,无论方法中是否出现异常

Spring Aop实例:

方式一 :注解配置 

UserService.java

package com.zk.b_annotation;

public interface UserService {
	
	public void addUser();
	public String updateUser();
	public void deleteUser();

}

UserServiceImpl.java

package com.zk.b_annotation;

import org.springframework.stereotype.Service;

@Service("userServiceId")
public class UserServiceImpl implements UserService {

	@Override
	public void addUser() {
		System.out.println("d_aspect.b_anno addUser");
	}

	@Override
	public String updateUser() {
		System.out.println("d_aspect.b_anno updateUser");
		int i = 1/ 0;
		return "阳志就是";
	}

	@Override
	public void deleteUser() {
		
		System.out.println("d_aspect.b_anno deleteUser");
	}
}

  

切面

MyAspect.java

package com.zk.b_annotation;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

/**
 * 切面类,含有多个通知
 */
@Component
@Aspect
public class MyAspect {
	
	//前置通知
//	@Before("execution(* com.itheima.d_aspect.b_anno.UserServiceImpl.*(..))")
@Before("myPointCut()") public void myBefore(JoinPoint joinPoint){ System.out.println("前置通知" + joinPoint.getSignature().getName()); } //声明公共切入点 @Pointcut("execution(* com.zk.b_annotation.UserServiceImpl.*(..))") private void myPointCut(){ } // @AfterReturning(value="myPointCut()" ,returning="ret")
@AfterReturning("myPointCut()") public void myAfterReturning(JoinPoint joinPoint,Object ret){ System.out.println("后置通知" + joinPoint.getSignature().getName() + " , -->" + ret); } // @Around(value = "myPointCut()")
@Around("myPointCut") public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable{ System.out.println("前置通知"); //手动执行目标方法 Object obj = joinPoint.proceed(); System.out.println("后置通知"); return obj; } // @AfterThrowing(value="execution(* com.itheima.d_aspect.b_anno.UserServiceImpl.*(..))" ,throwing="e")
@AfterThrowing("myPointCut()") public void myAfterThrowing(JoinPoint joinPoint,Throwable e){ System.out.println("抛出异常通知" + e.getMessage()); } @After("myPointCut()") public void myAfter(JoinPoint joinPoint){ System.out.println("后置通知"); } }

  

beans.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:context="http://www.springframework.org/schema/context"
       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
       					   http://www.springframework.org/schema/context 
       					   http://www.springframework.org/schema/context/spring-context.xsd">
	
	<!-- 1.扫描 注解类 -->
	<context:component-scan base-package="com.zk.b_annotation"></context:component-scan>
	
	<!-- 2.确定 aop注解生效 -->
	<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

</beans>

  

TestAspectAnno.java

package com.zk.b_annotation;

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

public class TestAspectAnno {
	
	@Test
	public void demo01(){
		String xmlPath = "com/zk/b_annotation/beans.xml";
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
		
		//获得目标bean
		UserService userService = (UserService) applicationContext.getBean("userServiceId");
		userService.addUser();
		userService.updateUser();
		userService.deleteUser();
	}

}

  

运行效果图:

方式二:xml配置 

UserService.java

package com.itheima.d_aspect.a_xml;

public interface UserService {
	
	public void addUser();
	public String updateUser();
	public void deleteUser();

}

 

UserServiceImpl.java

package com.itheima.d_aspect.a_xml;

public class UserServiceImpl implements UserService {

	@Override
	public void addUser() {
		System.out.println("d_aspect.a_xml addUser");
	}

	@Override
	public String updateUser() {
		System.out.println("d_aspect.a_xml updateUser");
		int i = 1/ 0;
		return "阳志就是屌";
	}

	@Override
	public void deleteUser() {
		
		System.out.println("d_aspect.a_xml deleteUser");
	}

}

  

MyAspect.java

package com.itheima.d_aspect.a_xml;

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

/**
 * 切面类,含有多个通知
 */
public class MyAspect {
	
	public void myBefore(JoinPoint joinPoint){
		System.out.println("前置通知 : " + joinPoint.getSignature().getName());
	}
	
	public void myAfterReturning(JoinPoint joinPoint,Object ret){
		System.out.println("后置通知 : " + joinPoint.getSignature().getName() + " , -->" + ret);
	}
	
	public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable{
		System.out.println("前");
		//手动执行目标方法
		Object obj = joinPoint.proceed();
		
		System.out.println("后");
		return obj;
	}
	
	public void myAfterThrowing(JoinPoint joinPoint,Throwable e){
		System.out.println("抛出异常通知 : " + e.getMessage());
	}
	
	public void myAfter(JoinPoint joinPoint){
		System.out.println("最终通知");
	}

}

  

TestAspectXml.java

package com.itheima.d_aspect.a_xml;

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

public class TestAspectXml {
	
	@Test
	public void demo01(){
		String xmlPath = "com/itheima/d_aspect/a_xml/beans.xml";
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
		
		//获得目标类
		UserService userService = (UserService) applicationContext.getBean("userServiceId");
		userService.addUser();
		userService.updateUser();
		userService.deleteUser();
	}

}

  

beans.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"
       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">
	<!-- 1 创建目标类 -->
	<bean id="userServiceId" class="com.itheima.d_aspect.a_xml.UserServiceImpl"></bean>
	<!-- 2 创建切面类(通知) -->
	<bean id="myAspectId" class="com.itheima.d_aspect.a_xml.MyAspect"></bean>
	<!-- 3 aop编程 
		<aop:aspect> 将切面类 声明“切面”,从而获得通知(方法)
			ref 切面类引用
		<aop:pointcut> 声明一个切入点,所有的通知都可以使用。
			expression 切入点表达式
			id 名称,用于其它通知引用
	-->
	<aop:config>
		<aop:aspect ref="myAspectId">
			<aop:pointcut expression="execution(* com.itheima.d_aspect.a_xml.UserServiceImpl.*(..))" id="myPointCut"/>
			
			<!-- 3.1 前置通知 
				<aop:before method="" pointcut="" pointcut-ref=""/>
					method : 通知,及方法名
					pointcut :切入点表达式,此表达式只能当前通知使用。
					pointcut-ref : 切入点引用,可以与其他通知共享切入点。
				通知方法格式:public void myBefore(JoinPoint joinPoint){
					参数1:org.aspectj.lang.JoinPoint  用于描述连接点(目标方法),获得目标方法名等
				例如:
			
			-->
			<aop:before method="myBefore" pointcut-ref="myPointCut"/>
			<!-- 3.2后置通知  ,目标方法后执行,获得返回值
				<aop:after-returning method="" pointcut-ref="" returning=""/>
					returning 通知方法第二个参数的名称
				通知方法格式:public void myAfterReturning(JoinPoint joinPoint,Object ret){
					参数1:连接点描述
					参数2:类型Object,参数名 returning="ret" 配置的
				例如:
			
			-->
			<aop:after-returning method="myAfterReturning" pointcut-ref="myPointCut" returning="ret" />
			<!-- 3.3 环绕通知 
				<aop:around method="" pointcut-ref=""/>
				通知方法格式:public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable{
					返回值类型:Object
					方法名:任意
					参数:org.aspectj.lang.ProceedingJoinPoint
					抛出异常
				执行目标方法:Object obj = joinPoint.proceed();
				例如:
			
			-->
			<aop:around method="myAround" pointcut-ref="myPointCut"/>
			<!-- 3.4 抛出异常
				<aop:after-throwing method="" pointcut-ref="" throwing=""/>
					throwing :通知方法的第二个参数名称
				通知方法格式:public void myAfterThrowing(JoinPoint joinPoint,Throwable e){
					参数1:连接点描述对象
					参数2:获得异常信息,类型Throwable ,参数名由throwing="e" 配置
				例如:
			
			-->
			<aop:after-throwing method="myAfterThrowing" pointcut-ref="myPointCut" throwing="e"/>
			<!-- 3.5 最终通知 -->			
			<aop:after method="myAfter" pointcut-ref="myPointCut"/>
			
			
			
		</aop:aspect>
	</aop:config>
</beans>

  

运行效果:

 

 

 

posted @ 2018-11-26 22:15  leagueandlegends  阅读(206)  评论(0编辑  收藏  举报