切面編程-自定義註解方式進行切點攔截

切面編程(AOP)是基於動態代理的一種編程,它是把不同對象的共同功能摘出來,通過切點觸發的形式進行調用,切點可以是對象,可以是註解等。通過切面編程,可以降低代碼的耦合性。

例如,日誌的打印,銀行系統中,所有進行了存取款的操作,需要進行日誌的打印。

寫一個例子

在pom.xml中添加依賴

<dependency>
   	<groupId>org.aspectj</groupId>
  	<artifactId>aspectjweaver</artifactId>
    	<version>1.9.2</version>
</dependency>
<dependency>
    	<groupId>aspectj</groupId>
    	<artifactId>aspectj-tools</artifactId>
    	<version>1.0.6</version>
</dependency>

創建一個接口

package com.test.sprintboot;

public interface Flyable {
	void fly();
}

寫一個自定義註解,切點通過註解的方式進行攔截

package com.test.sprintboot.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(value = {ElementType.FIELD,ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Value {
	String value();
}

創建一個接口的實現類

package com.test.sprintboot.services;

import org.springframework.stereotype.Component;

import com.test.sprintboot.Flyable;
import com.test.sprintboot.annotation.Value;

@Component

public class Parrot implements Flyable {

	@Override
	@Value(value="18")
	public void fly() {
		System.out.println("鸚鵡在飛翔...");
        
	} 

}

切面

package com.test.sprintboot;


import java.lang.reflect.Method;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
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.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import com.test.sprintboot.annotation.Value;

@Aspect
@Component
public class AspectDemo {
	
	public AspectDemo(){
		
	}	

	@Pointcut("@annotation(com.test.sprintboot.annotation.Value)")//匹配方法上有對應註釋
	
	public void targetValue(){
		
	}
	
	@Around("targetValue()")
	public void aroundTarget(ProceedingJoinPoint joinPoint) throws Throwable{
		Class<?> aclass = joinPoint.getTarget().getClass();
		String name = joinPoint.getSignature().getName();
		MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();	
		Class[] parameterType = methodSignature.getParameterTypes();
		Method method = aclass.getMethod(name, parameterType);		
		joinPoint.proceed();

	}
}

調用方法

package com.test.sprintboot;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.test.sprintboot.services.Eagle;
import com.test.sprintboot.services.Parrot;
import com.test.sprintboot.services.Prox;



/**
 * Hello world!
 *
 */
@RestController
public class App 
{
	
	@RequestMapping("/parrot")
	public void parrot(){
		ApplicationContext contxt = new ClassPathXmlApplicationContext("applicationContext.xml");
		Parrot parrot = contxt.getBean(Parrot.class);
		parrot.fly();
	}
}

自此,一個簡單的通過註解進行攔截的切面的代碼就寫好了,通過控制檯調用localhost:8080/parrot即可進行調用(端口根據自己springboot的端口進行修改)

 

 

1、“within(com.elim.spring.aop.service.UserServiceImpl)”匹配UserServiceImpl類對應對象的所有方法外部調用,而且這個對象只能是UserServiceImpl類型,不能是其子類型。

2、“within(com.elim..*)”匹配com.elim包及其子包下面所有的類的所有方法的外部調用。

 

1、“@within(com.elim.spring.support.MyAnnotation)”匹配被調用的方法聲明的類上擁有MyAnnotation註解的情況。比如有一個ClassA上使用了註解MyAnnotation標註,並且定義了一個方法a(),那麼在調用ClassA.a()方法時將匹配該Pointcut;如果有一個ClassB上沒有MyAnnotation註解,但是它繼承自ClassA,同時它上面定義了一個方法b(),那麼在調用ClassB().b()方法時不會匹配該Pointcut,但是在調用ClassB().a()時將匹配該方法調用,因爲a()是定義在父類型ClassA上的,且ClassA上使用了MyAnnotation註解。但是如果子類ClassB覆寫了父類ClassA的a()方法,則調用ClassB.a()方法時也不匹配該Pointcut。

 

1、“execution(* add())”匹配所有的不帶參數的add()方法。

2、“execution(public * com.elim..*.add*(..))”匹配所有com.elim包及其子包下所有類的以add開頭的所有public方法。

3、“execution(* *(..) throws Exception)”匹配所有拋出Exception的方法。

 

1、“target(com.elim.spring.aop.service.IUserService)”則匹配所有被代理的目標對象能夠轉換爲IUserService類型的所有方法的外部調用。

 

1、“@target(com.elim.spring.support.MyAnnotation)”匹配被代理的目標對象對應的類型上擁有MyAnnotation註解時。

args用來匹配方法參數的。

1、“args()”匹配任何不帶參數的方法。

2、“args(java.lang.String)”匹配任何只帶一個參數,而且這個參數的類型是String的方法。

3、“args(..)”帶任意參數的方法。

4、“args(java.lang.String,..)”匹配帶任意個參數,但是第一個參數的類型是String的方法。

5、“args(..,java.lang.String)”匹配帶任意個參數,但是最後一個參數的類型是String的方法。

 

1、“@args(com.elim.spring.support.MyAnnotation)”匹配方法參數類型上擁有MyAnnotation註解的方法調用。如我們有一個方法add(MyParam param)接收一個MyParam類型的參數,而MyParam這個類是擁有註解MyAnnotation的,則它可以被Pointcut表達式“@args(com.elim.spring.support.MyAnnotation)”匹配上。

 

 

1、“@annotation(com.elim.spring.support.MyAnnotation)”匹配所有的方法上擁有MyAnnotation註解的方法外部調用。

 

 

bean用於匹配當調用的是指定的Spring的某個bean的方法時。

1、“bean(abc)”匹配Spring Bean容器中id或name爲abc的bean的方法調用。

2、“bean(user*)”匹配所有id或name爲以user開頭的bean的方法調用。

 

表達式組合使用

1、“bean(userService) && args()”匹配id或name爲userService的bean的所有無參方法。

2、“bean(userService) || @annotation(MyAnnotation)”匹配id或name爲userService的bean的方法調用,或者是方法上使用了MyAnnotation註解的方法調用。

3、“bean(userService) && !args()”匹配id或name爲userService的bean的所有有參方法調用。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章