AOP(Aspect Oriented Programming)面向切面编程

AOP(Aspect Oriented Programming)面向切面编程

概述:

AOP即面向切面编程,其本质是动态代理,
指在委托类前/后/前后进行处理,从而达到对目标实现类某一模块功能的扩充,
AOP的核心是切面(切点+通知)
AOP的实现方式:
1.基于Schema-based的传统方式实现(实现相应的通知接口)
2.基于XML文件AspectJ方式实现
3.基于Annotation注解AspectJ方式实现

一、AOP的实现方式–基于Schema-based的传统方式实现(实现相应的通知接口)

1. 目标实现类定义
CalculateImpl.java
(功能:用于实现简单的加减乘除方法)

package com.xxx.aspectj.xml;

public class CalculateImpl {
	
	public int add(int a, int b) {
		
		int c=a+b;
		//int d=5/0;
		return c;
	}

	
	public int subtract(int a, int b) {
		
		int c=a-b;

		return c;
	}

	
	public int multiply(int a, int b) {
		int c=a*b;
		return c;
	}

	
	public int divide(int a, int b) {
		int c=a/b;
		return c;
	}


}

2.相应的通知实现类(有5个类型的通知:前置通知(MethodBeforeAdvice)、后置通知(AfterReturningAdvice)、环绕通知(MethodInterceptor)、异常通知(ThrowsAdvice)、最终通知(AfterMethod),通过实现相应的接口以及重写接口中相应的方法来实现)

通知类型实现接口名称以及重写的方法名称
前置通知MethodBeforeAdvice 接口,重写接口中的before(Method arg0, Object[] arg1, Object arg2)方法
后置通知AfterReturningAdvice ,重写接口中的afterReturning(Object returnValue, Method method, Object[] args, Object target)方法
环绕通知MethodInterceptor 重写接口中的invoke(MethodInvocation arg0)方法
异常通知ThrowsAdvice ,重写接口中的afterThrowing (Exception ex)方法
最终通知AfterMethod

2-1 前置通知实现类

package com.xxx.schema;

import java.lang.reflect.Method;
import java.util.Arrays;

import org.springframework.aop.MethodBeforeAdvice;

public class BeforeAdvices implements MethodBeforeAdvice{
	
	@Override
	public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {
		System.out.println("切面方法对象是"+arg0);
		System.out.println("切面方法参数列表是"+Arrays.asList(arg1));
		System.out.println("切面方法应用的对象(实现类)是"+arg2);
		System.out.println("前置通知执行");
		
	}

}

2-2 后置通知实现类

package com.xxx.schema;
import java.lang.reflect.Method;
import org.springframework.aop.*;

public class AfterReturningsAdvice implements AfterReturningAdvice{
/*
 * 实现后置通知接口
 * 重写接口中的afterreturning方法
 * 该方法的第一个参数是值目标方法对象返回的结果
 */
	@Override
	public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
		System.out.println("后置通知执行");
		System.out.println("执行结果是"+returnValue);
		
		
	}


}

2-3 环绕通知实现类

package com.xxx.schema;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class Arounds implements MethodInterceptor{
/*
 * 实现环绕通知接口
 * 重写接口中的invoke方法(调用)
 * 方法第一个参数指的是目标方法对象执行环绕通知后的结果
 * 过程:前置通知
 * 环绕通知中的前置通知
 * 环绕通知中的后置通知
 * 后置通知
 * 假如在环绕通知的前置通知执行的过程中发生异常了,不会执行之后的
 * 会执行异常通知和最终通知
 */
	@Override
	public Object invoke(MethodInvocation arg0) throws Throwable {
		System.out.println("前置通知执行中");
		Object proceed = arg0.proceed();//得到一个objrct类型,指的是目标对象方法执行环绕通知后的结果
		System.out.println("环绕通知的结果"+proceed);
		System.out.println("后置通知执行中");
		return proceed;
	}

	

}

2-4 异常通知实现类

package com.xxx.schema;

import org.springframework.aop.ThrowsAdvice;

public class ThrowAdvice implements ThrowsAdvice{
	
	//注意异常类型
	public void afterThrowing(Exception ex) throws Throwable {
       System.out.println("异常信息是"+ex.getMessage());
       System.out.println("异常通知执行");
    }
	

}

2-5 最终通知实现类

package com.xxx.schema;

import org.springframework.aop.AfterAdvice;


public class Afters implements AfterAdvice{          
	
	

}

3. .XML文件的配置

<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/context http://www.springframework.org/schema/context/spring-context.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
	
	<!-- 使用Schema-based方式实现Aop -->
	<!--实现5个类对象:
	1.目标对象实现类实例化
	2.前置通知实现类
	3.后置通知实现类
	4.异常通知实现类
	5.环绕通知实现类
	  -->
	  
	<bean id="calculateImpl" class="com.xxx.schema.CalculateImpl"></bean>
	
	<!-- 实例化通知对象,把通知类注入到IOC容器中 ,在切面中引用 -->
	<bean id="before" class="com.xxx.schema.BeforeAdvices"></bean>
	<!-- 注意:最终通知实现类没有重写方法 -->
	<!-- <bean id="after" class="com.xxx.schema.Afters"></bean> -->
	<bean id="throw" class="com.xxx.schema.ThrowAdvice"></bean>
	<bean id="around" class="com.xxx.schema.Arounds"></bean>
	<bean id="afterreturn" class="com.xxx.schema.AfterReturningsAdvice"></bean>
	
	<!-- 配置AOP,把切面织入到目标对象 -->
	<!-- 注意使用到对应的命名空间 -->
	<aop:config><!--  根元素:配置一个切面(使目标实现类的功能增强):切点(何时)+通知(增强何地)-->
		
		
		<!-- 配置切点(切入点)(何时:时间),*(代表的是合格类中的所有方法)是通配符(指的是方法),方法参数(..)(不管这个方法有几个参数都可以) -->
		<!-- 使用属性expression配置切点何时的表达式
		public(公有方法)(可以使用*),返回类型是int -->
		<!-- 该切点的范围是整个切面,所有切面 -->
		<aop:pointcut expression="execution(public int com.xxx.schema.CalculateImpl.*(..))" id="point"/>
		
		
		
		<!-- 配置通知(增强)(何地:哪个地方,什么方法)
		advice-ref:引用的通知对象(bean元素的id属性)
		pointcut-ref:引用切点对象(切点<aop:pointcut/>的id属性)
		 -->
		<!-- <aop:advisor advice-ref="before" pointcut-ref="point"/> -->
		<!-- <aop:advisor advice-ref="after" pointcut-ref="point"/> -->
		<!-- <aop:advisor advice-ref="throw" pointcut-ref="point"/>
		<aop:advisor advice-ref="around" pointcut-ref="point"/> -->
		<aop:advisor advice-ref="afterreturn" pointcut-ref="point"/>
	</aop:config> 

 
</beans>

4.测试类

package com.xxx.schema;

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

public class TestSchema {

	public static void main(String[] args) {
		//加载配置文件
		//实例化IOC容器对象
		ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");
		//根据IOC容器对象来实例化对象
		CalculateImpl cal=(CalculateImpl) ac.getBean("calculateImpl");//参数是bean元素的id属性的值
		System.out.println(cal.add(2, 2));//调用其中的一个目标对象方法
		
	}

}

二、AOP的实现方式–基于XML文件AspectJ方式实现

1. 目标实现类定义
CalculateImpl.java
(功能:用于实现简单的加减乘除方法)

略(跟第一种方式类似)

2.切面类
MyAspect.java
(该类的功能用于声明五大通知的方法)

package com.xxx.aspectj.xml;

import java.util.Arrays;

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

public class MyAspect {
	//在Java文件中选择类名,然后Copy Qualified Name;
	//怎样访问当前连接点的细节
	/*
	 * 前置通知,一个参数:连接点(每个方法可以对应一个连接点)
	 */
	public void one(JoinPoint jp){
		String name = jp.getSignature().getName();//方法的名字
		Object[] args = jp.getArgs();//方法的参数列表
		System.out.println(name+"前置通知执行"+",参数列表是"+Arrays.asList(args));
	}
	/*
	 * 后置通知
	 * 有两个参数:第一个是连接点
	 * 第二个参数是返回的结果
	 */
	public void two(JoinPoint jp,Object re){
		
		String name = jp.getSignature().getName();
		System.out.println("后置通知执行的返回结果是"+re);
	}
	/*
	 * 异常通知
	 * 该方法有两个参数
	 * 第一个参数是连接点
	 * 第二个参数是异常通知执行的异常信息
	 */
	public void three(JoinPoint jp,Exception ex){
		System.out.println("异常通知执行的异常信息为"+ex);
	}
	/*
	 * 最终通知
	 */
	public void four(){
		System.out.println("最终通知执行");
	}
	/*
	 * 环绕通知(把所有的通知串起来了)
	 * 该方法的返回类型是Object(前面四个方法类型是void)
	 * 有一个参数:ProceedingJoinPoint是JoinPoint的子接口
	 * 环绕通知的结果通过proceed()方法得到
	 * 
	 */
	public Object five(ProceedingJoinPoint pjp){
		Object[] args = pjp.getArgs();//方法的参数列表
		String name = pjp.getSignature().getName();//方法的名字
		Object result=null;
		try {
			System.out.println(name+"环绕准备就绪,参数列表是:"+Arrays.asList(args));
			System.out.println("前置通知执行");
			result = pjp.proceed();//目标对方方法执行环绕通知的返回结果
			System.out.println(name+"的返回结果是:"+result);
			System.out.println("后置通知执行");
		} catch (Throwable e) {
			
			e.printStackTrace();
			System.out.println("异常通知执行");//发生异常时执行
		}finally{
			System.out.println(name+"环绕执行完毕,参数列表是:"+Arrays.asList(args));
			System.out.println("最终通知执行");
		}
		
		
		return result;
		
	}
}

3. .xml文件的配置

<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/context http://www.springframework.org/schema/context/spring-context.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
	
	<!-- AspectJ之XML文件方式实现AOP -->
 	<bean id="calculateImpl" class="com.xxx.aspectj.xml.CalculateImpl"></bean>
 	<bean id="aspects" class="com.xxx.aspectj.xml.MyAspect" />
 	<!-- 配置切面(切点(地点)+通知(时间) -->
 	<aop:config>
 		<aop:aspect id="aspect" ref="aspects">
 		
 			<!-- 配置切点表达式,语法:
 				execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)
 				问号表示当前项可以有也可以没有,其中各项的语义如下:
				modifiers-pattern:方法的可见性,如public,protected;
				ret-type-pattern:方法的返回值类型,如int,void等;
				declaring-type-pattern:方法所在类的全路径名(方法所在的完全限定名路径),如com.spring.Aspect;
				name-pattern:方法名类型,如buisinessService();
				param-pattern:方法的参数类型,如java.lang.String;
				throws-pattern:方法抛出的异常类型,如java.lang.Exception;
 			
 			
 			 -->
 			 <!-- 配置切点-->
 			 <!-- 该切点的范围是针对当前的切面 -->
 			<aop:pointcut expression="execution(* com.xxx.aspectj.xml.CalculateImpl.add(..)) " id="pointcut"/>
 			<!-- 配置不同的通知(增强)类型对应不同的子元素
 			属性method是切面类中对应方法的名称
 			属性pointcut-ref是切点的id的值
 			 -->
 			 <!--环绕通知配置  -->
 			<aop:around method="five" pointcut-ref="pointcut" />
 			<!-- 后置通知配置 --> 
 		   <!--  <aop:after-returning method="two"  pointcut-ref="pointcut"  returning="re"/> 
 			前置通知配置
 			<aop:before method="one" pointcut-ref="pointcut" />
 			 异常通知配置
 			<aop:after-throwing method="three" pointcut-ref="pointcut"  throwing="ex"/>
 			最终通知配置
 			<aop:after method="four" pointcut-ref="pointcut"  />
 			 -->
 			
 	   </aop:aspect>
 	</aop:config>
</beans>

4.测试类

略(跟第一种方式类似)

三、AOP的实现方式–基于Annotation注解AspectJ方式实现

1. 目标实现类定义
CalculateImpl.java
(功能:用于实现简单的加减乘除方法)

略(跟第一种方式类似)

2.切面类
MyAspect.java
(该类的功能用于声明五大通知的方法)

package com.xxx.aspectj.annotation;

import java.util.Arrays;

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;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

@Component//使用该注解,说明已经将该类放入了Ioc容器,已经实例化了一个类
@Aspect//使用这两个注解,说明该类是一个切面类

//通过@Order(*)标注指定切面优先级,*整数数值越小,优先级越高
public class MyAspect {
	//定义切点(地点:使用切点表达式来确定)
	/*假如每个切点表达式是一样的
	  那么,我们可以定义一个方法,当想使用的时候可以直接引用*/
	@Pointcut("execution(* com.xxx.aspectj.annotation.CalculateImpl.*(..))")
	public void pointcut(){
		
	}
	/*
	 * 当使用同一种类型通知的时候
	 * 通知的执行顺序和优先级有关(优先级越高就先执行谁)
	 * 执行顺序和方法名的ASCII码有关,值越低优先级越高
	 */
	@Before("pointcut()")//该注解是引用了方法:方法名称是ponitcut()
	public void beforeone(JoinPoint jp){
		String name = jp.getSignature().getName();//调用目标对象中方法的名称
		Object[] args = jp.getArgs();//方法的参数列表
		System.out.println(name+"前置通知1执行"+",参数列表是"+Arrays.asList(args));
	}
	/*@Before("execution(* com.xxx.aspectj.annotation.CalculateImpl.*(..))")
	public void beforefive(JoinPoint jp){
		String name = jp.getSignature().getName();
		Object[] args = jp.getArgs();
		System.out.println(name+"前置通知2执行"+",参数列表是"+Arrays.asList(args));
	}
	@After("execution(public int com.xxx.aspectj.annotation.CalculateImpl.*(..))")
	public void four(){
		System.out.println("最终通知执行");
	}*/
	@AfterReturning(pointcut="execution(publi0c int com.xxx.aspectj.annotation.CalculateImpl.*(..))",returning="re")
//	注解中第二个属性的值为方法中的第二个形参的名称
	public void two(JoinPoint jp,Object re){
		
		String name = jp.getSignature().getName();
		System.out.println(name+"后置通知执行的返回结果是"+re);
	}
	/*@AfterThrowing(pointcut="execution(public int com.xxx.aspectj.annotation.CalculateImpl.*(..))",throwing="ex")
//	注解中第二个属性的值为方法中的第二个形参的名称
	public void three(JoinPoint jp,Exception ex){
		System.out.println("异常通知执行的异常信息为"+ex);
	}*/
	
	/*@Around("execution(public int com.xxx.aspectj.annotation.CalculateImpl.*(..))")
	public Object five(ProceedingJoinPoint pjp){
		Object[] args = pjp.getArgs();
		String name = pjp.getSignature().getName();
		System.out.println(name+"环绕前置就绪,参数列表是:"+Arrays.asList(args));
		Object result=null;
		try {
			result = pjp.proceed();
			System.out.println(name+"的环绕返回结果是:"+result);
		} catch (Throwable e) {
			
			e.printStackTrace();
			System.out.println("异常通知执行");
		}finally{
			System.out.println(name+"环绕后置执行完毕,参数列表是:"+Arrays.asList(args));
		}
		
		
		return result;
		
	}*/
}

3. .xml文件配置(虽然使用了注解,但是spring不自动寻找带注解的类,需要告诉哪些包中存在带注解的类)

<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/context http://www.springframework.org/schema/context/spring-context.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">

	<!-- AspectJ之注解方式实现AOP -->
 	<!-- spring不自动寻找带注解的类,需要告诉哪些包中存在带注解的类 -->
 	<!--  注意引用相应的命名空间-->
 	<context:component-scan base-package="com.xxx.aspectj.annotation"></context:component-scan>
 	<!-- aop使用了动态代理 
 	元素aop:aspectj-autoproxy的属性
 	1.expose-proxy="false" 
 	2.proxy-target-class="false"
 	-->
 	<aop:aspectj-autoproxy ></aop:aspectj-autoproxy>
</beans>

4.实现类

略(和第一种方式类似)

注:

1.当使用同一种类型的通知时,执行顺序和优先级有关
和方法的ASCII码的值有关,值越小越优先执行

2.当通知的切点表达式是一样的时候,可以通过声明一个方法来定义切点
当使用的时候直接调用即可
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值