不明觉厉的spring(3)---Aop

aop的定义

将程序中的交叉业务逻辑提取出来,称之为切面。将这些切面动态织入到目标对象,然后生成一个代理对象的过程

aop的核心概念

Aspect(切面):指横切性关注点的抽象即为切面,它与类相似,只是两者的关注点不一样,类是对物体特征的抽象,而切面横切性关注点的抽象.
joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点,实际上joinpoint还可以是field或类构造器)
Pointcut(切入点):所谓切入点是指我们要对那些joinpoint进行拦截的定义.
Advice(通知):所谓通知是指拦截到joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知
Target(目标对象):代理的目标对象
Weave(织入):指将aspects应用到target对象并导致proxy对象创建的过程称为织入.
Introduction(引入):在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field.
Proxy(代理对象)代理对象,指切面织入目标对象之后形成的对象

aop的实现原理

可以通过操作字节码,比如asm,Javassist,SERP等
也可使用用动态代理模式。
  Spring AOP采用动态代理的过程:
(1) 将切面使用动态代理的方式动态织入到目标对象(被代理类),形成一个代理对象;
(2) 目标对象如果没有实现代理接口,那么Spring会采用CGLib来生成代理对象,该代理对象是目标对象的子类;
(3) 目标对象如果是final类,并且也没实现代理接口,就不能运用AOP。

可以参考我的另一篇博文:http://blog.csdn.net/senssic/article/details/10564311

Spring的通知

Spring的通知类型

(1) MethodBeforeAdvice

  全名:前置通知

  在方法调用之前,做处理。

  不能够改变返回值

  不能够改变目标方法的流程,也不能中断流程的处理过程(除非抛出异常)

(2) AfterReturningAdvice

  全名:后置通知

在方法调用之后,做处理。

  不能够改变返回值

  不能够改变目标方法的流程,也不能中断流程的处理过程(除非抛出异常)

(3) MethodInterceptor

  全名:环绕通知

在方法调用之前以及之后,做处理。

  可以改变返回值,也可以改变流程。

(4) ThrowsAdvice

  全名:异常通知

在方法抛出异常后,做处理。

  当该通知处理完异常后,会简单地将异常再次抛出给目标调用方法。

(5)After
全名:最终通知
     无论一个方法是如何结束的,最终通知都会运行。

使用spring的aop编程

1.使用注解的方式

首先启动对注解的支持,配置文件如下
<?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:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-3.1.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd">
<aop:aspectj-autoproxy/><!-- 启动对aop注解的支持 -->  </beans>
注意: spring3.1不再把那些依赖的第三方jar包打包进来所以我们在引入org.springframework.aop-3.1.1.RELEASE.jar之外,还需要自己下载第三方依赖包:
spectjrt.jar,aspectjweaver.jar, 以及aopalliance.jar,如果有需要可以联系我
使用到的jar包,jdk1.6

切面编程代码:
package spring.senssic.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class MyAop {
	// 切入点,其中的为匹配为匹配所有函数
	@Pointcut("execution(* *(..))")
	private void anyMethod() {
	}// 声明一个切入点,不会使用到,只是一个标注点,返回空,参数空
	@Before(value = "anyMethod() && args(userName)")
	// 可以获取到参数为userName并使用
	// 定义前置通知
	public void doAccessCheck(String userName) {
		System.out.println("我是方法执行前调用的前置通知。\n参数名字:" + userName);
	}
	@AfterReturning(pointcut = "anyMethod()", returning = "revalue")
	// 可以获取到返回值为revalue并使用
	// 定义后置通知
	public void doReturnCheck(String revalue) {
		System.out.println("我是方法执行后调用的后置通知。\n方法返回值:" + revalue);
	}
	@AfterThrowing(pointcut = "anyMethod()", throwing = "ex")
	// 定义例外通知
	public void doExceptionAction(Exception ex) {
		System.out.println("我是方法抛出例外时调用的例外通知。\n抛出的例外:");
		ex.printStackTrace();
	}
	@After(value = "anyMethod()")
	// 定义最终通知
	public void doReleaseAction() {
		System.out.println("无论方法怎样,我都会被执行,方法参数为:");
	}
	@Around("anyMethod()")
	// 环绕通知
	public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
		System.out
				.println("我是环绕通知,可以再方法执行前和执行后运行,也可以确定此方法什么时候执行,如何执行,是否执行,我的功能最强大");
		// 通知的第一个参数必须是ProceedingJoinPoint类型,当调用proceed()方法会导致后面的连接的的方法的执行
		// startwacth
		System.out.println("我第一个执行");
		Object retval = pjp.proceed();
		System.out.println("我最后执行");
		// stopwacth
		return retval;
	}
}
要织入的类:
package spring.senssic.temp;
public class AopTo {
	public String doString(String name) {
	return "我是一个方法,我的参数为:" + name;
	}
}
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:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-3.1.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd">
<aop:aspectj-autoproxy/><!-- 启动对aop注解的支持 -->              
<context:annotation-config></context:annotation-config>
<bean id="myaop" class="spring.senssic.aop.MyAop"></bean>
<bean id="aopto" class="spring.senssic.temp.AopTo"></bean>
</beans>
测试类:
package spring.senssic.test;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import spring.senssic.temp.AopTo;
import spring.senssic.temp.B;
import spring.senssic.temp.C;
import spring.senssic.temp.D;
public class STest {
	private static B b;
	private static C c;
	private static D d;
	private static AopTo aTo;
	@BeforeClass
	public static void TWork() {
		ApplicationContext act = new ClassPathXmlApplicationContext("beans.xml");
		/*
		 * b = (B) act.getBean("classb"); c = (C) act.getBean("c"); C c2 = (C)
		 * act.getBean("c"); d = (D) act.getBean("d"); System.out.println(c ==
		 * c2);
		 */
		aTo = (AopTo) act.getBean("aopto");
	}
	@Test
	public void test() {
		System.out.println(aTo.doString("senssic"));
		// System.out.println(c.toString());
	}
}

测试结果:
2013-10-14 20:41:30 org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@471e30: startup date [Mon Oct 14 20:41:30 CST 2013]; root of context hierarchy
2013-10-14 20:41:31 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [beans.xml]
2013-10-14 20:41:33 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
信息: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1f66cff: defining beans [org.springframework.aop.config.internalAutoProxyCreator,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,myaop,aopto,org.springframework.context.annotation.ConfigurationClassPostProcessor$ImportAwareBeanPostProcessor#0]; root of factory hierarchy
我是方法执行前调用的前置通知。
参数名字:senssic
我是环绕通知,可以再方法执行前和执行后运行,也可以确定此方法什么时候执行,如何执行,是否执行,我的功能最强大
我第一个执行
我是方法执行后调用的后置通知。
方法返回值:我是一个方法,我的参数为:senssic
无论方法怎样,我都会被执行,方法参数为:
我最后执行
我是一个方法,我的参数为:senssic

其中关于execution的匹配问题

Spring AOP 用户可能会经常使用 execution切入点指示符。执行表达式的格式如下:
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern)
          throws-pattern?)
  除了返回类型模式(上面代码片断中的ret-type-pattern),
名字模式和参数模式以外, 所有的部分都是可选的。返回类型模式决定了方法的返回类型必须依次匹配一个连接点。
 *--->它代表了匹配任意的返回类型。 一个全限定的类型名将只会匹配返回给定类型的方法。
名字模式匹配的是方法名。 你可以使用*通配符作为所有或者部分命名模式。
 参数模式稍微有点复杂:()匹配了一个不接受任何参数的方法, 而(..)匹配了一个接受任意数量参数的方法
(零或者更多)。 模式(*)匹配了一个接受一个任何类型的参数的方法。 模式(*,String)匹配了一个接受两个参数的方法
第一个可以是任意类型, 第二个则必须是String类型。
除了返回类型模式(上面代码片断中的ret-type-pattern),
名字模式和参数模式以外, 所有的部分都是可选的。返回类型模式决定了方法的返回类型必须依次匹配一个连接点。
 你会使用的最频繁的返回类型模式是*,它代表了匹配任意的返回类型。 
一个全限定的类型名将只会匹配返回给定类型的方法。名字模式匹配的是方法名。
 你可以使用*通配符作为所有或者部分命名模式。 参数模式稍微有点复杂:()匹配了一个不接受任何参数的方法
, 而(..)匹配了一个接受任意数量参数的方法(零或者更多)。 模式(*)匹配了一个接受一个任何类型的参数的方法。
 模式(*,String)匹配了一个接受两个参数的方法,第一个可以是任意类型, 第二个则必须是String类型。
下面给出一些通用切入点表达式的例子。
任意公共方法的执行:
execution(public * *(..))
任何一个名字以“set”开始的方法的执行:
execution(* set*(..))
AccountService接口定义的任意方法的执行:
execution(* com.xyz.service.AccountService.*(..))
在service包中定义的任意方法的执行:
execution(* com.xyz.service.*.*(..))
在service包或其子包中定义的任意方法的执行:
execution(* com.xyz.service..*.*(..))
在service包中的任意连接点(在Spring AOP中只是方法执行):

2.使用xml的方式配置

切面编程代码:
package spring.senssic.aop;
import org.aspectj.lang.ProceedingJoinPoint;
public class Myaop2 {

	// 定义前置通知
	public void doAccessCheck() {
		System.out.println("我是方法执行前调用的前置通知。");
	}
	// 定义后置通知
	public void doReturnCheck(String revalue) {
		System.out.println("我是方法执行后调用的后置通知。\n方法返回值:" + revalue);
	}
	// 定义例外通知
	public void doExceptionAction(Exception ex) {
		System.out.println("我是方法抛出例外时调用的例外通知。\n抛出的例外:");
		ex.printStackTrace();
	}

	// 定义最终通知
	public void doReleaseAction() {
		System.out.println("无论方法怎样,我都会被执行,方法参数为:");
	}
	// 环绕通知
	public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
		System.out
				.println("我是环绕通知,可以再方法执行前和执行后运行,也可以确定此方法什么时候执行,如何执行,是否执行,我的功能最强大");
		// 通知的第一个参数必须是ProceedingJoinPoint类型,当调用proceed()方法会导致后面的连接的的方法的执行
		// startwacth
		System.out.println("我第一个执行");
		Object retval = pjp.proceed();
		System.out.println("我最后执行");
		// stopwacth
		return retval;
	}
}
要织入的java
package spring.senssic.temp;

public class AopTo {
	public String doString(String name) {
		return "我是一个方法,我的参数为:" + name;
	}
}

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:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-3.1.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd">
<aop:aspectj-autoproxy/><!-- 启动对aop注解的支持 -->              
<context:annotation-config></context:annotation-config>
<bean id="myaop" class="spring.senssic.aop.Myaop2"></bean>
<bean id="aopto" class="spring.senssic.temp.AopTo"></bean>
<aop:config>
  <aop:aspect id="my" ref="myaop">
     <aop:pointcut expression="execution(* *(..))" id="mycut"/>
     <aop:before pointcut-ref="mycut" method="doAccessCheck"/>
     <aop:after-returning pointcut-ref="mycut" method="doReturnCheck" returning="revalue"/>
     <aop:after-throwing pointcut-ref="mycut" method="doExceptionAction" throwing="ex"/>
     <aop:after pointcut-ref="mycut" method="doReleaseAction"/>
     <aop:around pointcut-ref="mycut" method="doBasicProfiling"/>
  </aop:aspect>
</aop:config>
</beans>

引入(Introduction)

引入(在AspectJ中被称为inter-type声明)使得一个切面可以定义被通知对象实现给定的接口, 并且可以为那些对象提供具体的实现。

使用@DeclareParents注解来定义引入。这个注解用来定义匹配的类型 拥有一个新的父类(所以有了这个名字)。比如,给定一个接口UsageTracked, 和接口的具体实现DefaultUsageTracked类, 接下来的切面声明了所有的service接口的实现都实现了UsageTracked接口。 (比如为了通过JMX输出统计信息)。 


@Aspect
public class myIntroduction{

  @DeclareParents(value="srping.senssic.service.*+",//所有的service下的类或接口都将实现UsageTracked接口
                  defaultImpl=spring.senssic.service.impl.DefaultUsageTracked.class)//UsageTracked接口实现类

  public static UsageTracked mixin;//UsageTracked接口

	@Before(value = "execution(* *(..)) && args(userName)")//匹配切入点
	public void doAccessCheck(UsageTracked  userName) {
		System.out.println("我是方法执行前调用的前置通知。\n参数名字:" + userName.toString());
	}

  
}


实现的接口通过被注解的字段类型来决定。@DeclareParents注解的 value属性是一个AspectJ的类型模式:- 任何匹配类型的bean都会实现 UsageTracked接口。请注意,在上面的前置通知的例子中,service beans 可以直接用作UsageTracked接口的实现。如果需要编程式的来访问一个bean, 你可以这样写:


UsageTracked usageTracked = (UsageTracked) context.getBean("myService");




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值