spring02-10.16-AOP

hh兩個h發表於2020-10-16

2.AOP:面向方面程式設計
一個普通的類 -> 有特定功能的類
a.繼承類 b.實現介面 c.註解 d.配置

類 -> “通知” :實現介面

前置通知實現步驟:
a.jar
aopaliance.jar
aspectjweaver.jar

b.配置

c.編寫
aop:每當之前add()之前自動執行一個方法log();

addStudent();	業務方法(IStudentService.java中的addStudent())
before();	自動執行的通知,即aop前置通知
	<bean id="studentDao" class="org.lanqiao.dao.impl.StudentDaoImpl">	
	</bean>
	
	
	<!-- 配置前置通知 -->

	<!-- addStudent()所在方法 -->
	<bean id="studentService" class="org.lanqiao.service.impl.StudentServiceImpl">
		<property name="studentDao" ref="studentDao"></property>
	</bean>
	
	<!-- “前置通知”類 -->
	<!-- =========連線線的一方========= -->
	<bean id="logBefore" class="org.lanqiao.aop.LogBefore">
	</bean>
	
	<!--addStudent()和通知進行關聯 -->
	<aop:config>
		<!-- 配置切入點(在哪裡執行通知 ) -->
		<!-- =========連線線的另一方========= -->
		<aop:pointcut expression="execution(public void org.lanqiao.service.impl.StudentServiceImpl.deleteStudentByNo(int))  or execution(public * org.lanqiao.service.impl.StudentServiceImpl.addStudent(..))"   id="poioncut"/>
		<!-- advisor:相當於連結切入點和切面的線  -->				
		<!-- =========連線線========= -->
		<aop:advisor advice-ref="logBefore" pointcut-ref="poioncut"/>
	</aop:config>

如果出現異常:類似java.lang.NoClassDefFoundError: org/apache/commons/pool/impl/GenericObjectPool,則說明缺少jar

public static void testAop() {
		ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml") ;
		IStudentService studentService = (IStudentService)context.getBean("studentService") ;
		Student student = new Student();
		student.setStuAge(23);
		student.setStuName("zs");
		studentService.addStudent(student);
		
		
		studentService.deleteStudentByNo(1);
	}

在這裡插入圖片描述

後置通知:
a.通知類 -> 普通實現介面
b.業務類、業務方法
StudentServiceImpl中的addStudent()
c.配置:
將業務類、通知納入springIOC容器
定義切入點(一端)、定義通知類(另一端),通過pointcut-ref將兩端連線起來

public class LogAfter implements AfterReturningAdvice{

	@Override
	public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
		System.out.println("**********後置通知:目標物件:"+target+",呼叫的方法名:"+method.getName()+",方法的引數個數:"+args.length+",方法的返回值:"+returnValue);
	}

}
	<!-- 將通知納入springIOC容器 -->
	<bean id="logAfter" class="org.lanqiao.aop.LogAfter"></bean>

	<aop:config>
		<!-- 切入點(連線線的一端:業務類的具體方法) -->
		<aop:pointcut expression="execution(public * org.lanqiao.service.impl.StudentServiceImpl.addStudent(..))"   id="poioncut2"/>
		<!-- (連線線的另一端:通知類) -->
		<aop:advisor advice-ref="logAfter"  pointcut-ref="poioncut2" />
	</aop:config>

異常通知:
根據異常通知介面的定義可以發現,異常通知的實現類必須編寫以下方法:
public void afterThrowing([Method, args, target], ThrowableSubclass):

a.public void afterThrowing(Method, args, target, ThrowableSubclass)
b.public void afterThrowing( ThrowableSubclass)
	<bean id="logException" class="org.lanqiao.aop.LogException"></bean>
	<aop:config>
		<!-- 切入點(連線線的一端:業務類的具體方法) -->
		<aop:pointcut expression="execution(public * org.lanqiao.service.impl.StudentServiceImpl.addStudent(..))"   id="poioncut3"/>
		<!-- (連線線的另一端:通知 類) -->
		<aop:advisor advice-ref="logException"  pointcut-ref="poioncut3" />
	</aop:config>

環繞通知:
在目標方法的前後、異常發生時、最終等各個地方都可以進行的通知,最強大的一個通知;
可以獲取目標方法的全部控制權(目標方法是否執行、執行之前、執行之後、引數、返回值等)

在使用環繞通知時,目標方法的一切資訊都可以通過invocation引數獲取到
環繞通知底層是通過攔截器實現的。

public class LogAround  implements MethodInterceptor{

	@Override
	public Object invoke(MethodInvocation invocation) throws Throwable {
		Object result  = null ;
		//方法體1...
		try {
			//方法體2...
			System.out.println("用環繞通知實現的[前置通知]...");
			
			// invocation.proceed() 之前的程式碼:前置通知
			 result  = invocation.proceed() ;//控制著目標方法的執行,addStudent()
			//result就是目標方法addStudent()方法的返回值
			//invocation.proceed() 之後的程式碼:後置通知
			System.out.println("用環繞通知實現的[後置通知]...:");
			System.out.println("-----------------目標物件target"+invocation.getThis()+",呼叫的方法名:"+invocation.getMethod().getName()+",方法的引數個數:"+invocation.getArguments().length+",返回值:"+result);
				
		}catch(Exception e) {
			//方法體3...
			//異常通知
			System.out.println("用環繞通知實現的[異常通知]...");
		}
		
		return result;//目標方法的返回值
	}

}
	<!-- 將環繞通知加入ioc容器 -->
	<bean id="logAround" class="org.lanqiao.aop.LogAround">
	</bean>
	
	<aop:config>
		<!-- 切入點(連線線的一端:業務類的具體方法) -->
		<aop:pointcut expression="execution(public * org.lanqiao.service.impl.StudentServiceImpl.addStudent(..))" id="poioncut4"/>
		<!-- (連線線的另一端:通知 類)-->
		<aop:advisor advice-ref="logAround"  pointcut-ref="poioncut4" />
	</aop:config> 

二、實現註解實現通知
a.jar
與實現介面的方式相同
b.配置
將業務類、通知納入springIOC容器
開啟註解對AOP的支援<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
業務類addStudent - 通知

c.編寫

通知:
@Aspect //宣告該類是一個通知
public class LogBeforeAnnotation {

}

注意:通過註解形式將物件增加到ioc容器時,需要設定掃描器

<context:component-scan base-package="org.lanqiao.aop"></context:component-scan>

掃描器會將指定的包中的@Componet @Service @Respository @Controller修飾的類產生的物件增加到IOC容器中
@Aspect不需要加入掃描器,只需要開啟即可:

<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

通過註解形式實現的aop,如果想獲取目標物件的一些引數,則需要使用一個物件:JointPoint

註解形式的返回值:
a.宣告返回值的引數名:
@AfterReturning( pointcut= “execution(public * addStudent(…))” ,returning=“returningValue” )
public void myAfter(JoinPoint jp,Object returningValue) {//returningValue是返回值,但需要告訴spring
System.out.println(“返回值:”+returningValue );
註解形式實現aop時,通知的方法的引數不能多、少

實現介面形式、註解形式只捕獲宣告的特定型別的異常,而其他型別異常不捕獲。
cath()

三、通過配置將類->通知
基於Schema配置
類似與實現介面的方式

介面方式通知:public class LogAfter implements AfterReturningAdvice
Schema方式通知:
a.編寫一個普通類 public class LogAfter {}
b.將該類通過配置,轉為一個“通知”

如果要獲取目標物件資訊:
註解、schema:JoinPoint
介面:Method method, Object[] args, Object target

schema形式和註解形式相似,不同之處:註解形式使用了註冊@After,schmema形式進行了多餘的配置