Spring應用學習——AOP

1. AOP

    1. AOP:即面向切面編程,採用橫向抽取機制,取代了傳統的繼承體系的重複代碼問題,以下圖所示,性能監控、日誌記錄等代碼圍繞業務邏輯代碼,而這部分代碼是一個高度重複的代碼,也就是在每個業務邏輯的代碼中都會有相同的代碼圍繞業務邏輯代碼,而AOP就是將這些重複代碼抽取出java

    2. AOP實現原理:就是代理模式,主要有兩種方式,分別是靜態代理和動態代理,web

  • 靜態代理:採用一些工具類對原來的類生成一個代理類,代理類以.class存在
  • 動態代理(重點,spring基於動態代理實現aop):在運行中,經過反射生成類的代理對象,在代理對象中對原來的對象進行加強。

    3. spring採用動態代理的技術實現包括:spring

  • 基於接口生成動態代理對象:使用jdk提供的反射機制實現,參考這篇博客,JDK動態代理實現詳解
  • 基於類生成動態代理對象:經過繼承實現,根據類生成一個子類(代理對象),在代理對象(子類)中對父類進行加強,參考這篇博客。實現

    cglib經過繼承的形式來實現動態功能的代理 那麼就沒法避免一些繼承的缺點,那就是沒法代理final方法和沒法代理final類,示例使用代碼以下express

import java.lang.reflect.Method;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
public class CglibProxy implements MethodInterceptor  {

	//obj生成的代理對象
	//method對目標對象方法的引用
	//args目標對象方法調用參數
    //MethodProxy代理對象中對方法的引用
	@Override
	public Object intercept(Object obj, Method method, Object[] args,
			MethodProxy methodProxy) throws Throwable {
		//在方法以前加強,開啓事務
        
		System.out.println("開啓事務...");
		//執行父類方法
		Object result = methodProxy.invokeSuper(obj, args);
		//在方法以後加強,提交事務
		System.out.println("提交事務...");
		return result;
	}
}

import org.springframework.cglib.proxy.Enhancer;
public class CglibProxyFactory {
	/**
	 * 
	 * <p>Title: createCglibProxy</p>
	 * <p>Description: </p>
	 * @param clazz 父類的class,經過class操做字節碼建立代理對象
	 * @return
	 */
	public static Object createCglibProxy(Class clazz){
		//代理對象,這裏邊有加強的代碼
		CglibProxy cglibProxy = new CglibProxy();
		//建立加強器,須要設置父類及代理對象
		Enhancer enhancer = new Enhancer();
		//在加強器設置父類
		enhancer.setSuperclass(clazz);
		//在加強器設置代理對象(包括加強代碼)
		enhancer.setCallback(cglibProxy);
		//建立一個代理對象
		return enhancer.create();
		
	}

}

 

    4. cglib與jdk實現動態代理的區別:jdk是基於接口生成代理對象,而cglib是基於類生成代理對象。spring底層使用jdk和cglib,若是原始對象實現了一個接口,spring使用jdk,不然 使用cglib生成代理。apache

    5. 相關術語:編程

  • Pointcut(切入點):肯定在哪一個方法上加強,一個類有多個切點
  • Advice(通知/加強):在切點上進行加強,包括:前置加強、後置加強、拋出異常加強
  • Target(目標對象):對目標對象進行加強,生成代理對象
  • Proxy(代理):對目標對象進行加強,生成代理對象
  • Weaving(織入):生成代理的時機(過程)
    • 動態代理織入,在運行期爲目標類生成代理對象
    • 編譯期織入
  • Aspect(切面): 包括切點和加強,面向哪一個切點進行加強(編程)。

2. SpringAOP的簡單使用

    1. 搭建環境:經過Maven加入spring-webmvc.jar和aspectjweaver.jar包便可導入全部須要依賴的包spring-mvc

    2. AspectJ相關語法:mvc

  • 配置切點的語法:切點就是方法,因此必需要確認是那一個類的哪個方法,
    • 用法:匹配任意字符
           1. com.cloud_note.entity.*:該包下全部類,僅限於此包下,不包括子包
           2. * com.cloud_note.controller.user.UserLoginController.excute(..):即com.cloud_note.controller.user.UserLoginController類中的任意返回值的excute方法
    •  .. 用法:匹配子包或方法形參,匹配子包下的類要與*結合使用
               1. com.cloud_note.entity..*:匹配該包下全部子包的全部類
               2. com.cloud_note.controller.user.UserLoginController.excute(..):即com.cloud_note.controller.user.UserLoginController類中的全部excute方法,不管該方法的參數是什麼
               3. com.cloud_note.entity.*.*(..):匹配該包下全部類的全部方法
    • 用法:按類型匹配指定類的全部類
               1. com.cloud_note.controller.user.UserLoginController+:指定匹配繼承UserLoginController類的全部子類,包括自己
    • 切點語法:表達式包括兩部分,即函數和參數,函數有execution、within、target、args,參數就是通配表達式,如
      • execution(com.entity..*)
      • within(com.entity..*)
      • target(某個類的全限定名):也就表示匹配該類中的全部方法,每一個方法都是切點,參數必須是類的全限定名
      • args(java.lang.String):表示以java.lang.String類型數據做爲參數的方法所有都是切點,參數必須是類的全限定名
      • 與或非邏輯操做:execution、within、target、args直接能夠進行與或非操做,用符號&&、||、!或者用字符and、or、not表示
  • 加強:AspectJ支持的加強類型有
    • Before :前置加強/通知,至關於BeforeAdvice
    • AfterReturning :後置加強/通知,至關於AfterReturningAdvice
    • Around :環繞加強/通知,至關於MethodInterceptor
    • AfterThrowing:拋出加強/通知,至關於ThrowAdvice
    • After:最終finally加強/通知,無論是否異常,該通知都會執行
public void save(){
	try{
	//前置加強
	調用目標對象方法執行
	//後置加強

	}catch(Exception ex){
		//拋出異常加強
	}finally{
		//最終finally加強
	}
}

    3. 在Spring中的AOP配置:ide

  • 使用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:jdbc="http://www.springframework.org/schema/jdbc"  
    	xmlns:jee="http://www.springframework.org/schema/jee" 
    	xmlns:tx="http://www.springframework.org/schema/tx"
    	xmlns:aop="http://www.springframework.org/schema/aop" 
    	xmlns:mvc="http://www.springframework.org/schema/mvc"
    	xmlns:util="http://www.springframework.org/schema/util"
    	xmlns:jpa="http://www.springframework.org/schema/data/jpa"
    	xsi:schemaLocation="
    		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
    		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
    		http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.2.xsd
    		http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd
    		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
    		http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
    		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
    		http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
    		http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.2.xsd">
    	
    	<!--配置AOP中的加強類,即將LoggerBean中的方法做用到Controller組件的全部方法上-->
    	<bean id="loggerBean" class="LoggerBean"></bean>
    
    	<!--配置AOP-->
    	<aop:config>
    	<!--配置切面:切面包括切點和加強  -->
    		<aop:aspect ref="loggerBean"><!--ref屬性就表示指定加強類-->
    		<!--配置切點  -->
    			<aop:pointcut expression="execution(* controller.user.*.*(..))" id="point"/>
    		<!--配置前置加強 ,加強就是 LoggerBean類中的logController方法-->	 
    			<aop:before method="logController" pointcut-ref="point"/>
    		<!-- 配置後置加強,加強就是 LoggerBean類中的outController方法-->
    			<aop:after-returning method="outController" pointcut-ref="point"/>
    		</aop:aspect>
    	</aop:config>
    
    	<!--開啓註解掃描,只有被掃描進Spring容器的類才能配置AOP來加強代碼-->
    	<context:component-scan base-package="controller.user"></context:component-scan>
    	
    	<!--若是要經過AOP註解來配置AOP,除了context:component-scan標籤,還有下面這個標籤開啓AOP註解標示的使用-->
    	<aop:aspectj-autoproxy/>
    </beans>
  • 經過AOP註解來配置:首先要編寫加強類,而後再添加註解配置,AOP註解有
    • @Aspect:指定切面,將組件指定爲切面組件
    • @AfterThrowing(throwing="e",pointcut="within(controller..*)"):指定切點、加強方法代碼以及加強的位置,是前置加強、後置加強、環繞加強等,好比當前就是拋出異常加強,e就是目標組件方法拋出的異常對象
    • @Before("within(controller..*)"):等價於<aop:before>定義
    • @After
    • @Around
    • @AfterReturning
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
//一個簡單的日誌記錄加強代碼
@Component//掃描添加到spring容器
@Aspect//將組件指定爲切面組件
public class ExceptionBean {
	//e就是目標組件方法拋出的異常對象
	@AfterThrowing(throwing="e",pointcut="within(* controller..*)")
	public void excute(Exception e){
		//將異常信息寫入文件中
		System.out.println("發生異常:"+e.getMessage());
		FileWriter fw;
		try {
			fw = new FileWriter("D:note_error.log",true);
			PrintWriter pw=new PrintWriter(fw);
			//利用pw對象寫信息
			Date time=new Date();
			SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			String str=sdf.format(time);
			pw.println("***************************");
			pw.println("異常類型:"+e);
			pw.println("發生時間:"+str);
			pw.println("異常詳情:");
			e.printStackTrace(pw);
			pw.close();
			fw.close();
		} catch (IOException e1) {
			// TODO Auto-generated catch block
			System.out.println("記錄異常失敗");
		}
	
	}
}

3. 配置開啓Spring聲明式事務控制

    1. 關於什麼是事務,能夠參考個人另外一篇博客JDBC,而關於Spring中的事務管理詳細參考Spring事務管理函數

  • 經過註解開啓:在想要添加事務控制的方法或類(類上標示類中全部方法都要進行事務管理)上添加@Transactional註解便可對該方法開啓Spring事務,但必須先在Spring的xml配置文件中添加一行標籤,org.springframework.jdbc.datasource.DataSourceTransactionManager該類就是JDBC提供的事務加強類,以下
    <context:property-placeholder location="classpath:conf/db.properties"/>
    	<bean id="datasource" class="org.apache.commons.dbcp.BasicDataSource">
    		<property name="driverClassName" value="${jdbc.driver}"></property>
    		<property name="url" value="${jdbc.url}"></property>
    		<property name="password" value="${jdbc.password}"></property>
    		<property name="username" value="${jdbc.name}"></property>
    	</bean>
    
    	<!--Spring事務管理-->
    	<!--1.定義事務管理Bean-->
    	<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    		<property name="dataSource" ref="ds"></property><!--注意,該行標籤指定數據源-->
    	</bean>
    	<!--2.經過配置將txManager做用到Service方法上-->
    	<!--開啓@Transactional標記,使用該標記就表示在方法上使用txManager所表明的加強類-->
    	<tx:annotation-driven transaction-manager="txManager"/>

     

  • 在Spring的xml配置文件中經過標籤配置:
    <context:property-placeholder location="classpath:conf/db.properties"/>
    	<bean id="datasource" class="org.apache.commons.dbcp.BasicDataSource">
    		<property name="driverClassName" value="${jdbc.driver}"></property>
    		<property name="url" value="${jdbc.url}"></property>
    		<property name="password" value="${jdbc.password}"></property>
    		<property name="username" value="${jdbc.name}"></property>
    	</bean>
    	<!-- 事務管理配置 -->
    	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    		<property name="dataSource" ref="datasource"></property>
    	</bean>
    	<tx:advice id="txAdvice" transaction-manager="transactionManager">
    		<tx:attributes>
    		<!-- 傳播行爲 -->
    			<tx:method name="save*" propagation="REQUIRED"/>
    			<tx:method name="insert*" propagation="REQUIRED"/>
    			<tx:method name="add*" propagation="REQUIRED"/>
    			<tx:method name="update*" propagation="REQUIRED"/>
    			<tx:method name="delete*" propagation="REQUIRED"/>
    			<tx:method name="find*" propagation="SUPPORTS" read-only="true"/>
    		</tx:attributes>
    	</tx:advice>
    	
    	<!-- AOP配置 -->
    	<aop:config>
    		<aop:advisor advice-ref="txAdvice" pointcut="execution(* service.*.*(..))"/>
    	</aop:config>
相關文章
相關標籤/搜索