天天看點

AOP之@AspectJ技術

1、AspectJ技術

因為在xml配置aop過程中太過繁瑣,aopalliance 中出現了一種AspectJ技術。

2、 Spring AOP支援的AspectJ切入點訓示符

切入點訓示符用來訓示切入點表達式目的,,在Spring AOP中目前隻有執行方法這一個連接配接點,Spring AOP支援的AspectJ切入點訓示符如下:

execution:用于比對方法執行的連接配接點;

within:用于比對指定類型内的方法執行;

this:用于比對目前AOP代理對象類型的執行方法;注意是AOP代理對象的類型比對,這樣就可能包括引入接口也類型比對;

target:用于比對目前目标對象類型的執行方法;注意是目标對象的類型比對,這樣就不包括引入接口也類型比對;

args:用于比對目前執行的方法傳入的參數為指定類型的執行方法;

@within:用于比對是以持有指定注解類型内的方法;

@target:用于比對目前目标對象類型的執行方法,其中目标對象持有指定的注解;

@args:用于比對目前執行的方法傳入的參數持有指定注解的執行;

@annotation:用于比對目前執行方法持有指定注解的方法;

bean:Spring AOP擴充的,AspectJ沒有對于訓示符,用于比對特定名稱的Bean對象的執行方法;

reference pointcut:表示引用其他命名切入點,隻有@ApectJ風格支援,Schema風格不支援。

3、類型比對文法

首先讓我們來了解下AspectJ類型比對的通配符:

*:比對任何數量字元;

…:比對任何數量字元的重複,如在類型模式中比對任何數量子包;而在方法參數模式中比對任何數量參數。

+:比對指定類型的子類型;僅能作為字尾放在類型模式後邊。

代碼:
java.lang.String    比對String類型;  
java.*.String       比對java包下的任何“一級子包”下的String類型;  
如比對java.lang.String,但不比對java.lang.ss.String  
java..*            比對java包及任何子包下的任何類型;  
                  如比對java.lang.String、java.lang.annotation.Annotation  
java.lang.*ing      比對任何java.lang包下的以ing結尾的類型;  
java.lang.Number+  比對java.lang包下的任何Number的自類型;  
                   如比對java.lang.Integer,也比對java.math.BigInteger 
           

4、組合切入點表達式

AspectJ使用 且(&&)、或(||)、非(!)來組合切入點表達式。

在Schema風格下,由于在XML中使用“&&”需要使用轉義字元“&&”來代替之,是以很不友善,是以Spring ASP 提供了and、or、not來代替&&、||、!。

5、切入點使用示例

execution:使用“execution(方法表達式)”比對方法執行;

模式 描述
public * *(…) 任何公共方法的執行
* cn.javass…IPointcutService.*() cn.javass包及所有子包下IPointcutService接口中的任何無參方法
* cn.javass….(…) cn.javass包及所有子包下任何類的任何方法

6、AspectJ技術執行個體

  • 通過注解配置切點(哪個方法需要被入侵)和通知(黑客)
  • 自動生産代理對象

1)建立AppConfig.java

package com.lq.aspect;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration // 标示為配置類 相當于xml檔案
//掃描指定包下包含@Component注解的類,将這個類加入spring bean工程
@ComponentScan(value = { "com.lq.aspect", "com.lq.dao" })
@EnableAspectJAutoProxy // 啟動aspectJ注解aop
public class AppConfig {

}
           
  1. 建立@Aspect 切面類
package com.lq.aspect;

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

@Component
@Aspect
public class LogAspect {

	/*
	 * 定義切點----需要增強的方法
	 * 
	 */

	@Pointcut("execution(public int com.lq.dao.UserDapImpl.add(..))")
	public void pointCut() {
	}

	// 環繞通知(循環通知/增強(黑客))

	@Around("pointCut()")
	public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
		System.out.println("1.鑒權");
		// 調用目标方法

		Object[] args = joinPoint.getArgs();
		args[0] = 22;
		args[1] = 22;
		Object result = joinPoint.proceed();

		System.out.println("2.日志留痕");
		return result;

	}
}

           

3)實體類

package com.lq.dao;

import org.springframework.stereotype.Component;

@Component
public class UserDapImpl {

	public int add(int a,int b) {
		System.out.println("調用【未實作接口的】UserDaoImpl的add方法");
		return a+b;
	}
}

           
  1. 測試
package com.lq.proxy;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.lq.aspect.AppConfig;
import com.lq.dao.UserDapImpl;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = AppConfig.class)
public class AspectJTest {

	@Autowired
	UserDapImpl userDap;

	@Test
	public void testAdd() {
		System.out.println(userDap.add(11, 22));
	}

}

           
  1. 測試結果為:
九月 29, 2020 10:40:17 下午 org.springframework.test.context.support.AbstractTestContextBootstrapper getDefaultTestExecutionListenerClassNames
資訊: Loaded default TestExecutionListener class names from location [META-INF/spring.factories]: [org.springframework.test.context.web.ServletTestExecutionListener, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener, org.springframework.test.context.support.DependencyInjectionTestExecutionListener, org.springframework.test.context.support.DirtiesContextTestExecutionListener, org.springframework.test.context.transaction.TransactionalTestExecutionListener, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener]
九月 29, 2020 10:40:17 下午 org.springframework.test.context.support.AbstractTestContextBootstrapper getTestExecutionListeners
資訊: Using TestExecutionListeners: [org.springframework.test.context.support.DirtiesContextBeforeModesTestExe[email protected], org.springframework.test.context.support.DependencyInjectionTestExecutionListener@4e04a765, org.springframework.test.context.support.DirtiesContextTestExecutionListener@783e6358]
九月 29, 2020 10:40:18 下午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
資訊: Refreshing org.springframework.context.support.GenericApplicationContext@100fc185: startup date [Tue Sep 29 22:40:18 CST 2020]; root of context hierarchy
1.鑒權
調用【未實作接口的】UserDaoImpl的add方法
2.日志留痕
33