1. 程式人生 > >Spring(2)之 (2.2 使用 AspectJ實現 AOP)

Spring(2)之 (2.2 使用 AspectJ實現 AOP)

在Spring AOP程式設計中:

分離了重複程式碼:關注點
關注點程式碼:產生類即切面類
 

一、使用 AspectJ實現 AOP(註解方式):

  1. 導包
    spring-aop
    spring-core
    aspectjrt
    aspectjweaver
    aopalliance
  2. 配置檔案(開啟代理模式)
    開啟註解掃描
<context:component-scan base-package="com.asd"></context:component-scan>

開啟代理模式

<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
  1. 切面類
    @Component
    @Aspect:說明這是一個切面類
    @Before:業務程式碼之前執行
    @After:業務程式碼之後執行,無論是否出現異常都執行
    @AfterReturning:業務程式碼後執行,若出現異常不執行
    @AfterThrowing:業務程式碼後,出現異常執行(即@AfterReturning與@AfterThrowing 兩者不會同時存在)
    @Around:環繞業務程式碼,有引數joinPoint:joinPoint.proceed();方法proceed()相當於目標物件業務程式碼中的save();
  2. 切入點表示式(exection( * * . *(. .)))
    exection( * * . *(. .))

    exection( 返回型別 類的完全限定名. 方法名(引數))(返回型別前的修飾符如public可寫可不寫)

實現介面的類,Spring預設使用JDK代理;未實現介面的類,Spring預設使用Cglib代理

@Before("execution(public void com.asd.spring.UserDao.save())")
@After("execution(public void com.asd.spring.UserDao.save())")

二、使用 AspectJ實現 AOP(XML 方式):

介面實現類(UserDao.java)、未實現類(OrderDao.java)和切面類(MyAop.java)都不需要用註解;

不用註解的方式則xml中不需要開啟註解掃描和開啟代理模式,需要在 xml檔案中生成對應的 bean物件:xml 檔案中有生成UserDao、OrderDao、切面類MyAop物件;可以在< aop:before>標籤中用 pointcut屬性單獨書寫每一個切入點表示式;也可以在< aop:pointcut>用 expression屬性統一書寫切入點表示式, express屬性中可以用 “||”和“or”來寫多個表示式,再在< aop:before>標籤中用 pointcut-ref屬性引用它;

// eg:
<aop:config>
        <!-- 切面類-->
        <bean id="aop" class="com.asd.spring.MyAop"></bean>
        <!-- 切入點 -->
        <aop:pointcut id="pt" expression="execution(* *.*.*(..))"/>
  	<aop:aspect ref="aop">
  		<aop:before method="beginTrans" pointcut-ref="pt"/>
  		<aop:after method="commitTrans" pointcut-ref="pt"/>
  		<aop:after-returning method="afterReturning" pointcut-ref="pt"/>
  		<aop:after-throwing method="afterThrow" pointcut-ref="pt"/>
  		<aop:around method="around" pointcut-ref="pt"/>
  	</aop:aspect>
    </aop:config>

 

eg: 1. 使用 AspectJ實現 AOP(註解方式)
1.1 IUserDao.java

public interface IUserDao{
	public void save() throws Exception;
	
	public void update() throws Exception;
}

1.2 UserDao.java
(此目標物件實現介面

@Component("userDao")
public class UserDao implements IUserDao{
	public void save() throws Exception{	
		System.out.println("----資料已儲存----");
		// int i=1/0;
	}
	
	public void update() throws Exception{	
		System.out.println("----資料已修改----");
	}
}

1.2(1) OrderDao.java
(新增一個OrderDao,實現介面)

@Component("orderDao")
public class OrderDao{
	public void update() throws Exception{	
		System.out.println("----修改訂單----");
	}
}

1.3 MyAop.java
目標物件提取後形成的單獨類:@Aspect註解表示這是一個切面類;@Before(“execution(public void com.asd.spring.UserDao.save())”) 是切入點表示式; 若在UserDao.java中出現異常,此時@AfterReturning不執行; 為避免重複書寫切入點表示式,可以用@PointCut註解寫一個切入點方法,無需實現;後面註解中只需要引用方法即可,不需要再多次書寫切入點表示式;)

@Component
@Aspect
public class MyAop{
        //@PointCut("execution(public void com.asd.spring.UserDao.save())")
        @PointCut("execution(* *.*())")
        public void pointCut(){
        }
	//@Before("execution(public void com.asd.spring.UserDao.save())")
	@Before("pointCut()")
	public void beginTrans(){
		System.out.println("開始事務");
	}
	//@After("execution(public void com.asd.spring.UserDao.save())")
	@After("pointCut()")
	public void commitTrans(){
		System.out.println("提交事務");
	}
	//@AfterReturning("execution(public void com.asd.spring.UserDao.save())")
	@AfterReturning("pointCut()")
	public void afterReturning(){
		System.out.println("afterReturning");
	}
	//@AfterThrowing("execution(public void com.asd.spring.UserDao.save())")
	@AfterThrowing("pointCut()")
	public void afterThrow(){
		System.out.println("afterThrow");
	}
	//@Around("execution(public void com.asd.spring.UserDao.save())")
	@Around("pointCut()")
	public void around(ProceedingJoinPoint joinPoint) throws Throwable{
		System.out.println("begin");
		joinPoint.proceed();
		System.out.println("end");
	}
}

1.4 bean.xml
(xml檔案中有: xmlns:context、 xmlns:aop、xmlns:tx即context、aop、tx名稱空間, )

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
       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/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- 開啟註解掃描 -->
    <context:component-scan base-package="com.asd"></context:component-scan>
    <!-- 開啟代理模式 -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    
</beans>

Test.java

public class Test{
	public static void main(String[] args) {
		ApplicationContext applicationContext=new ApplicationContext("bean.xml");
		IUserDao userDao=(IUserDao)applicationContext.getBean("userDao");
		System.out.println(userDao.getClass());//userDao物件(即實現介面的目標物件)型別
		try{
			userDao.save();
			
			userDao.update();
		}catch(Exception e){
			e.printStackTrace();
		}	
		
		System.out.println("-------------");
		OrderDao orderDao=(OrderDao)applicationContext.getBean("orderDao");
		System.out.println(orderDao.getClass());//orderDao物件(即沒有實現介面的目標物件)型別
		try{
			orderDao.update();
		}catch(Exception e){
			e.printStackTrace();
		}
			
	}
}

① MyAop.java切面類中只有 @Before、@After 註解時,執行結果:
在這裡插入圖片描述
② MyAop.java切面類中增加 @AfterReturning 註解後且無異常,執行結果: / 出現異常執行結果:
在這裡插入圖片描述在這裡插入圖片描述
③ MyAop.java切面類中增加 @AfterThrowing 註解後且出現異常,執行結果:
在這裡插入圖片描述
④ MyAop.java切面類中增加 @Around 註解後執行結果:
在這裡插入圖片描述
⑤ 增加 OrderDao.java 類後,切面類 MyAop.java中切入點表示式方法只限定OrderDao(即未改為全部即 * )註解後執行結果:(沒有執行切入點程式碼)
在這裡插入圖片描述
⑤ 增加 OrderDao.java 類、且在UserDao.java類中新增 update() 方法,切面類 MyAop.java中切入點表示式方法為全部類和方法(即 * )後執行結果:(有執行切入點程式碼)
在這裡插入圖片描述
⑥ 輸出兩個物件userDao物件(實現介面)、orderDao物件(沒實現介面)執行結果:
userDao物件型別:class com.sun.proxy.$Proxy12
orderDao物件型別:class com.asd.spring.OrderDao $ $EnhancerBySpringCGLIB $ $ 87c00f1f
【 可看出實現介面的類Spring預設使用JDK代理,未實現介面的類Spring預設使用Cglib代理 】

 

二、使用 AspectJ實現 AOP(XML 方式):

介面實現類(UserDao.java)、未實現類(OrderDao.java)和切面類(MyAop.java)都不需要用註解; 不用註解的方式則xml中不需要開啟註解掃描和開啟代理模式,需要在 xml檔案中生成對應的 bean物件:xml 檔案中有生成UserDao、OrderDao、切面類MyAop物件;可以在< aop:before>標籤中用 pointcut屬性單獨書寫每一個切入點表示式;也可以在< aop:pointcut>用 expression屬性統一書寫切入點表示式, express屬性中可以用 “||”和“or”來寫多個表示式,再在< aop:before>標籤中用 pointcut-ref屬性引用它;)

2.1 IUserDao.java

public interface IUserDao{
	public void save() throws Exception;
	
	public void update() throws Exception;
}

2.2 UserDao.java

public class UserDao implements IUserDao{
	public void save() throws Exception{	
		System.out.println("----資料已儲存----");
		// int i=1/0;
	}
}

2.2(1) OrderDao.java

public class OrderDao{
	public void update() throws Exception{	
		System.out.println("----修改訂單----");
	}
}

2.3 MyAop.java

public class MyAop{
	public void beginTrans(){
		System.out.println("開始事務");
	}
	public void commitTrans(){
		System.out.println("提交事務");
	}
	public void afterReturning(){
		System.out.println("afterReturning");
	}
	public void afterThrow(){
		System.out.println("afterThrow");
	}
	public void around(ProceedingJoinPoint joinPoint) throws Throwable{
		System.out.println("begin");
		joinPoint.proceed();
		System.out.println("end");
	}
}

2.4 bean.xml
(xml 檔案中有生成UserDao、OrderDao、切面類MyAop物件,可以在< aop:before>標籤中用 pointcut屬性單獨書寫每一個切入點表示式;也可以在< aop:pointcut>用 expression屬性統一書寫切入點表示式, express屬性中可以用 “||”和“or”來寫多個表示式,再在< aop:before>標籤中用 pointcut-ref屬性引用它;)

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
       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/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- 容器中建立UserDao、OrderDao物件 -->
    <bean id="userDao" class="com.asd.spring.UserDao"></bean>
    <bean id="orderDao" class="com.asd.spring.OrderDao"></bean>
    <!-- 容器中建立切面類物件 -->
    <bean id="aop" class="com.asd.spring.MyAop"></bean>
    <!-- 切入點 -->
    <aop:config>
        //<aop:pointcut id="pt" expression="execution(* com.asd.UserDao.save(..))"/>//統一寫切入點表示式,再在下面pointcut-ref引用其id
        //<aop:pointcut id="pt" expression="execution(* com.asd.UserDao.save(..)) ||execution(* com.asd.UserDao.update(..))"/>//用“||”和“or”都可以
        <aop:pointcut id="pt" expression="execution(* *.*.*(..))"/>
  	<aop:aspect ref="aop">
  		//<aop:before method="beginTrans" pointcut="execution(* com.asd.UserDao.save(..))"/>//分開寫
  		<aop:before method="beginTrans" pointcut-ref="pt"/>
  		<aop:after method="commitTrans" pointcut-ref="pt"/>
  		<aop:after-returning method="afterReturning" pointcut-ref="pt"/>
  		<aop:after-throwing method="afterThrow" pointcut-ref="pt"/>
  		<aop:around method="around" pointcut-ref="pt"/>
  	</aop:aspect>
    </aop:config>
    
</beans>

Test.java

在這裡插入程式碼片