Spring3.0读书笔记----(七)基于@AspectJ和Schema的AOP

 一、了解注解

 JDK5.0注解可以看成是Javadoc标签和Xdoclet标签的延伸和发展。在JDK5.0中,我们可以自定义这些标签,并通过Java语言反射机制获取类中标注的注解,完成特定的功能。

 注解是代码的附属信息,它遵循一个基本的原值:注解不能直接干扰程序代码的运行,无论增加或删除注解,代码都能够正常运行。Java语言注解器会忽略这些注解,而由第三方工具负责对注解进行处理。第三方工具可以利用代码中的注解间接控制程序代码的运行,它们通过Java反射机制读取注解的信息,并根据这些信息更改目标程序的逻辑,而这正是Spring AOP对@AspectJ提供支持所采取的方法。

 (一)、一个简单的注解类

 (1)、注解类的编写:

 
package com.baobao.springtest7.anno;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)//声明注解的保留期限
@Target(ElementType.METHOD)//声明可以使用该注解的目标类型
public @interface NeedTest {//定义注解
	boolean value() default true;//声明注解成员
}

 @Retention(RetentionPolicy.RUNTIME)表示NeedTest这个注解可以在运行期被JVM读取,注解的保留期限类型在java.lang.annotation.Retention类中定义。

 @Target(ElementType.METHOD)表示NeedTest这个注解只能应用到目标类的方法上,注解的应用目标在java.lang.annotation.ElementType类中定义。

 (2)、使用注解
 
public class ForumService {
	@NeedTest(value=true)//标注注解
	public void deleteForum(int forumId){
		System.out.println("删除模块:"+forumId);
	}
	@NeedTest(value=false)//标注注解
	public void deleteTopic(int postId){
		System.out.println("删除主题:"+postId);
	}
}
 (3)、访问注解
 
@Test
	public void test() {
		//得到ForumService的class对象
		Class clazz = ForumService.class;
		//得到ForumService对应的Method数组,Method是类或接口中的方法的信息
		Method[] methods = clazz.getDeclaredMethods();
		System.out.println("一共有方法:"+methods.length);
		for(Method method:methods){
			//获取方法上所标注的注解对象
			NeedTest nt = method.getAnnotation(NeedTest.class);
			if(nt!=null){
				if(nt.value()){
					System.out.println(method.getName()+"()需要测试");
				}else{
					System.out.println(method.getName()+"()不需要测试");
				}
			}
		}
 通过方法的反射对象获取了方法上标注的NeedTest注解对象,接着就可以访问注解对象的成员,从而得到ForumService类方法的测试需求。

 二、使用@AspectJ

 @AspectJ是采用注解来描述切点、增强。

 (一)、一个简单的例子

 目标类

 

public class NaiveWaiter implements Waiter {
	public void greetTo(String clientName) {
		System.out.println("NaiveWaiter:greetTo"+clientName);
	}
	public void serveTo(String clientName) {
		System.out.println("NaiveWaiter:serveTo"+clientName);
	}
}
 切面类
 
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
//通过该注解将PreGreetingAspect标识为一个切面
@Aspect
public class PreGreetingAspect {
	//定义切点和增强类型
	@Before("execution(* greetTo(..))")
	public void beforeGreeting(){//增强的横切逻辑
		System.out.println("How are you");
	}
}
 测试

import static org.junit.Assert.*;
import org.junit.Test;
import org.springframework.aop.aspectj.annotation.AspectJProxyFactory;
import com.baobao.springtest7.aspectj.PreGreetingAspect;
import com.baobao.springtest7.service.Waiter;
import com.baobao.springtest7.service.impl.NaiveWaiter;
public class AspectJProxyTest {
	@Test
	public void test() {
		Waiter target = new NaiveWaiter();
		AspectJProxyFactory factory = new AspectJProxyFactory();
		//设置目标对象
		factory.setTarget(target);
		//添加切面类
		factory.addAspect(PreGreetingAspect.class);
		//生成织入切面的代理对象
		Waiter proxy = factory.getProxy();
		proxy.greetTo("John");
		proxy.serveTo("John");
	}
}
 (二)、 通过配置使用@AspectJ切面

 配置信息:


	
	
	
	
 测试代码:

import static org.junit.Assert.*;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.baobao.springtest7.service.Waiter;
public class AspectTest {
	@Test
	public void test() {
		ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext1.xml");
		Waiter waiter = (Waiter) ctx.getBean("waiter");
		waiter.greetTo("John");
		waiter.serveTo("Tom");
	}
}

 在配置文件中引入aop命名空间,通过aop命名空间的自动为Spring容器中那些匹配@AspectJ切面的Bean创建代理,完成切面织入。Spring在内部依旧采用AnnotationAwareAspectJAutoProxyCreator进行自动代理的创建,但具体实现细节被隐藏起来了。

 三、@AspectJ语法基础

 (一)、切点表达式函数

 Spring支持9个@AspectJ切点表达式函数,它们用不同的方式描述目标类的连接点,根据描述对象的不同,可以大致分为4中类型:

 (1)、方法切点函数:通过描述目标类方法信息定义连接点;

 (2)、方法入参切点函数:通过描述目标类方法入参的信息定义连接点;

 (3)、目标类切点函数:通过描述目标类类型信息定义连接点;

 (4)、代理类切点函数:通过描述目标类的代理类的信息定义连接点。

 这4中类型的切点函数,如表说明:

  Spring3.0读书笔记----(七)基于@AspectJ和Schema的AOP_第1张图片

 (四)、引介增强的用法

  Spring3.0读书笔记----(七)基于@AspectJ和Schema的AOP_第2张图片

 假设NaiveWaiter能够同时充当售货员的角色,即通过切面技术为NaiveWaiter增强Seller接口的实现。

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.DeclareParents;
import com.baobao.springtest7.service.Seller;
import com.baobao.springtest7.service.impl.SmartSeller;
@Aspect
public class EnableSellerAspect {
	//引介切面
	@DeclareParents(value="com.baobao.springtest7.service.impl.NaiveWaiter",
			defaultImpl=SmartSeller.class)
	public Seller seller;
}

 在EnableSellerAspect切面中,我们通过@DeclareParents为NaiveWaiter添加了一个需要实现的Seller接口,并制定其默认实现类为SmartSeller,然后通过切面节数将SmartSeller融合到NaiveWaiter中,这样NaiveWaiter就实现Seller接口了。

 在Spring配置文件中配置好切面和NaiveWaiter的Bean


	
	

 测试代码

 

ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext1.xml");
		Waiter waiter = (Waiter) ctx.getBean("waiter");
		waiter.greetTo("John");
		Seller seller = (Seller) waiter;
		seller.sell("beer");

 代码成功执行,可见NaiveWaiter已经成功地新增了Seller接口的实现。

 四、切点函数详解

 (一)、@annotation()

 @annotation表示标注了某个注解的所有方法。我们通过一个实例说明@annotation()的用法,TestAspect定义了一个后置增强切面,该增强将应有到标注有NeedTest的目标方法中:

@Aspect
public class TestAspect {
	//后置增强切面@annotation()括号中为标签类的类名,所有使用了该标签的类都会被织入该增强逻辑
	@AfterReturning("@annotation(com.baobao.springtest7.anno.NeedTest)")
	public void needTestFun(){
		System.out.println("needTestFun()executed!");
	}
}

 (二)、execution()

 execution(<修饰符模式>?<返回类型模式><方法名模式>(<参数模式>)<异常模式>?)

 (1)、通过方法签名定义切点

 1)、execution(public**(..)):匹配所有目标类Public方法。第一个*代表返回类型;第二个*代表方法名;而..代表任意入参的方法;

 2)、execution(**To(..)):匹配目标类所有以To为后缀的方法。第一个*代表返回类型;而*To代表任意以To为后缀的方法。

 (2)、通过类定义切点

 1)、execution(*com.baobao.Waiter.*(..)):匹配Waiter接口的所有方法,它匹配NaireWaiter和NaughtyWaiter类的greetTo()和serveTo()方法。第一个*代表返回任意类型;com.baobao.Waiter.*代表Waiter接口中的所有方法;

 2)、execution(*com.baobao.Waiter+.*(..)):匹配Waiter接口及其所有实现类的方法,它不但匹配NaiveWaiter和NaughtyWaiter类的greetTo()和serveTo()这两个Waiter接口定义的方法,同时还匹配NaiveWaiter#smile()和NaugtyWaiter#joke()这两个接口中定义的方法。

 (3)、通过类包定义切点

 在类名模式串中,“.*”表示包下的所有类,而“..*”表示包、子孙包下的所有类。

 1)、execution(*com.baobao.*(..)):匹配com.baobao包下所有类的所有方法;

 2)execution(*com.baobao..*(..)):匹配com.baobao包、子孙包下所有类的所有方法

 3)execution(*com..*.*Dao.find*(..)):匹配包名前缀为com的任何包下类名后缀到Dao的方法,方法名必须以find为前缀。

 (4)、通过方法入参定义切点

 切点表达式中方法入参部分比较复杂,可以使用“*”和“..”通配符,其中“*”表示任意类型的参数;而“..”表示任意类型参数且参数个数不限。

 1)、execution(*joke(String,in)):匹配joke(String,int)方法,且第一个入参是String,第二个入参是int。







你可能感兴趣的:(spring框架)