【spring框架】AOP的Annotation实现(下)

了解下面的就了解了Spring的声明式异常管理的原理
1.织入点的语法:
下面给出一些通用切入点表达式的例子。

任意公共方法的执行:
execution(public * *(..))

任何一个名字以“set”开始的方法的执行:
execution(* set*(..))

AccountService接口定义的任意方法的执行:
execution(* com.xyz.service.AccountService.*(..))

在service包中定义的任意方法的执行:
execution(* com.xyz.service.*.*(..))

在service包或其子包中定义的任意方法的执行:
execution(* com.xyz.service..*.*(..))

在service包中的任意连接点(在Spring AOP中只是方法执行):
within(com.xyz.service.*)

在service包或其子包中的任意连接点(在Spring AOP中只是方法执行):
within(com.xyz.service..*)

实现了AccountService接口的代理对象的任意连接点 (在Spring AOP中只是方法执行):
this(com.xyz.service.AccountService)
'this'在绑定表单中更加常用:- 请参见后面的通知一节中了解如何使得代理对象在通知体内可用。

2.声明通知
通知是跟一个切入点表达式关联起来的,并且在切入点匹配的方法执行之前或者之后或者前后运行。 切入点表达式可能是指向已命名的切入点的简单引用或者是一个已经声明过的切入点表达式。

a)前置通知
一个切面里使用 @Before 注解声明前置通知
例子:

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class BeforeExample {

  @Before("execution(* com.xyz.myapp.dao.*.*(..))")
  public void doAccessCheck() {
    // ...
  }

}

b)后置通知(After returning advice)
返回后通知通常在一个匹配的方法返回的时候执行。使用 @AfterReturning 注解来声明:

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterReturning;

@Aspect
public class AfterReturningExample {

  @AfterReturning("com.xyz.myapp.SystemArchitecture.dataAccessOperation()")
  public void doAccessCheck() {
    // ...
  }

}

说明:你可以在相同的切面里定义多个通知,或者其他成员。 我们只是在展示如何定义一个简单的通知。这些例子主要的侧重点是正在讨论的问题。 

有时候你需要在通知体内得到返回的值。你可以使用@AfterReturning 接口的形式来绑定返回值:

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterReturning;

@Aspect
public class AfterReturningExample {

  @AfterReturning(
    pointcut="com.xyz.myapp.SystemArchitecture.dataAccessOperation()",
    returning="retVal")
  public void doAccessCheck(Object retVal) {
    // ...
  }

}

在 returning属性中使用的名字必须对应于通知方法内的一个参数名。 当一个方法执行返回后,返回值作为相应的参数值传入通知方法。 一个returning子句也限制了只能匹配到返回指定类型值的方法。 (在本例子中,返回值是Object类,也就是说返回任意类型都会匹配) 

请注意当使用后置通知时不允许返回一个完全不同的引用。

c)异常通知(After throwing advice)
抛出异常通知在一个方法抛出异常后执行。使用@AfterThrowing注解来声明:

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterThrowing;

@Aspect
public class AfterThrowingExample {

  @AfterThrowing("com.xyz.myapp.SystemArchitecture.dataAccessOperation()")
  public void doRecoveryActions() {
    // ...
  }

}

你通常会想要限制通知只在某种特殊的异常被抛出的时候匹配,你还希望可以在通知体内得到被抛出的异常。 使用throwing属性不仅可以限制匹配的异常类型(如果你不想限制,请使用 Throwable作为异常类型),还可以将抛出的异常绑定到通知的一个参数上。 

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterThrowing;

@Aspect
public class AfterThrowingExample {

  @AfterThrowing(
    pointcut="com.xyz.myapp.SystemArchitecture.dataAccessOperation()",
    throwing="ex")
  public void doRecoveryActions(DataAccessException ex) {
    // ...
  }

}

在throwing属性中使用的名字必须与通知方法内的一个参数对应。 当一个方法因抛出一个异常而中止后,这个异常将会作为那个对应的参数送至通知方法。 throwing 子句也限制了只能匹配到抛出指定异常类型的方法 (上面的示例为DataAccessException)。 

d)最终通知(After (finally) advice)
不论一个方法是如何结束的,最终通知都会运行。使用@After 注解来声明。最终通知必须准备处理正常返回和异常返回两种情况。通常用它来释放资源。

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.After;

@Aspect
public class AfterFinallyExample {

  @After("com.xyz.myapp.SystemArchitecture.dataAccessOperation()")
  public void doReleaseLock() {
    // ...
  }

}

e)环绕通知
最后一种通知是环绕通知。环绕通知在一个方法执行之前和之后执行。它使得通知有机会 在一个方法执行之前和执行之后运行。而且它可以决定这个方法在什么时候执行,如何执行,甚至是否执行。 环绕通知经常在某线程安全的环境下,你需要在一个方法执行之前和之后共享某种状态的时候使用。 请尽量使用最简单的满足你需求的通知。(比如如果简单的前置通知也可以适用的情况下不要使用环绕通知)。 

环绕通知使用@Around注解来声明。通知的第一个参数必须是 ProceedingJoinPoint类型。在通知体内,调用 ProceedingJoinPoint的proceed()方法会导致 后台的连接点方法执行。proceed 方法也可能会被调用并且传入一个 Object[]对象-该数组中的值将被作为方法执行时的参数。

当传入一个Object[]对象的时候,处理的方法与通过AspectJ编译器处理环绕通知略有不同。 对于使用传统AspectJ语言写的环绕通知来说,传入参数的数量必须和传递给环绕通知的参数数量匹配 (不是后台的连接点接受的参数数量),并且特定顺序的传入参数代替了将要绑定给连接点的原始值 (如果你看不懂不用担心)。Spring采用的方法更加简单并且能更好匹配它基于代理(proxy-based)的执行语法, 如果你使用AspectJ的编译器和编织器来编译为Spring而写的@AspectJ切面和处理参数,你只需要知道这一区别即可。
有一种方法可以让你写出100%兼容Spring AOP和AspectJ的表达式,我们将会在后续的通知参数的章节中讨论它。

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.ProceedingJoinPoint;

@Aspect
public class AroundExample {

  @Around("com.xyz.myapp.SystemArchitecture.businessService()")
  public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
    // start stopwatch
    Object retVal = pjp.proceed();//环绕完了继续环绕给其他的用
    // stop stopwatch
    return retVal;
  }

}

方法的调用者得到的返回值就是环绕通知返回的值。 例如:一个简单的缓存切面,如果缓存中有值,就返回该值,否则调用proceed()方法。 请注意proceed可能在通知体内部被调用一次,许多次,或者根本不被调用,所有这些都是合法的。 

测试@Before和@AfterReturning:

package cn.edu.hpu.aop;

import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LogInterceptor {
	//在方法执行之前先执行这个方法
	//execution是织入点语法
	@Before("execution(public * cn.edu.hpu.dao..*.*(..))")
	public void before(){
		System.out.println("method start");
	} 

	@AfterReturning("execution(public * cn.edu.hpu.dao..*.*(..))")
	public void afterReturning(){
		System.out.println("method after ruturning");
	}
}

测试:

package cn.edu.hpu.service;
import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import cn.edu.hpu.dao.UserDao;
import cn.edu.hpu.model.User;

public class UserServiceTest {

	@Test
	public void testAdd() throws Exception{
		ClassPathXmlApplicationContext ctx=new ClassPathXmlApplicationContext("beans.xml");

		UserService userService=(UserService)ctx.getBean("userService");
		//System.out.println(userService.getUserDao());
		User u=new User();
		u.setUsername("u1");
		u.setPassword("p1");
		userService.add(u);
		ctx.destroy();
	}
}

结果:
method start
add success!!
method after ruturning

在每一个方法上都要写一个织入点语法,有点麻烦,如果每个织入点语法都是一样的话,我们可以这样写:

package cn.edu.hpu.aop;

import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LogInterceptor {
	@Pointcut("execution(public * cn.edu.hpu.dao..*.*(..))")
	public void myMethod(){}

	//在方法执行之前先执行这个方法
	//execution是织入点语法
	@Before("myMethod()")
	public void before(){
		System.out.println("method start");
	} 

	@AfterReturning("myMethod()")
	public void afterReturning(){
		System.out.println("method after ruturning");
	}
}

下面测试@AfterThrowing:
现在UserDaoImpl中的save方法中添加异常

package cn.edu.hpu.dao.Impl;

import java.util.List;
import java.util.Map;
import java.util.Set;

import org.springframework.stereotype.Component;

import cn.edu.hpu.dao.UserDao;
import cn.edu.hpu.model.User;

@Component("u")
public class UserDaoImpl implements UserDao{

	public void save(User u) {
	    System.out.println("add success!!");
	    throw new RuntimeException("exception");
	}
}

package cn.edu.hpu.aop;

import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LogInterceptor {

	@AfterThrowing("execution(public * cn.edu.hpu.dao..*.*(..))")
	public void afterThrowing(){
		System.out.println("method after throwing");
	} 

}

测试:

package cn.edu.hpu.service;
import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import cn.edu.hpu.dao.UserDao;
import cn.edu.hpu.model.User;

public class UserServiceTest {

	@Test
	public void testAdd() throws Exception{
		ClassPathXmlApplicationContext ctx=new ClassPathXmlApplicationContext("beans.xml");

		UserService userService=(UserService)ctx.getBean("userService");
		User u=new User();
		u.setUsername("u1");
		u.setPassword("p1");
		userService.add(u);
		ctx.destroy();
	}
}

测试结果:
add success!!
method after throwing
编译器也报了异常

我们以后就可以实现"声明式"异常管理了,但实际上都是交给struts2来处理的。

下面测试@Around:

package cn.edu.hpu.aop;

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

@Aspect
@Component
public class LogInterceptor {
	@Pointcut("execution(public * cn.edu.hpu.dao..*.*(..))")
	public void myMethod(){}

	@Before("myMethod()")
	public void before(){
		System.out.println("method start");
	} 

	@AfterReturning("myMethod()")
	public void afterReturning(){
		System.out.println("method after ruturning");
	} 

	@Around("myMethod()")
	public void AroundMtethod(ProceedingJoinPoint pjp) throws Throwable{
		System.out.println("method around start");
		pjp.proceed();
		System.out.println("method around end");
	}
}

测试:

package cn.edu.hpu.service;
import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import cn.edu.hpu.dao.UserDao;
import cn.edu.hpu.model.User;

public class UserServiceTest {

	@Test
	public void testAdd() throws Exception{
		ClassPathXmlApplicationContext ctx=new ClassPathXmlApplicationContext("beans.xml");

		UserService userService=(UserService)ctx.getBean("userService");
		User u=new User();
		u.setUsername("u1");
		u.setPassword("p1");
		userService.add(u);
		ctx.destroy();
	}
}

结果:
method start
method around start
add success!!
method after ruturning
method around end

虽然可以看出一些顺序,但写业务逻辑的时候不要依赖于这个顺序。可以按顺序写好方法再加进去。

注意一些问题:
如果一个类没有继承接口的话,就不能使用动态代理了,此时需要加cglibrary的jar包cglib-nodep-2.1.3.jar。

原因是:
当一个类实现了接口之后,就会使用JDK自带的proxy和InvocationHandler来帮你自动产生代理,没有实现接口的话,它会用直接操作二进制码的这种类库,也就是cglibrary,来帮你产生代理的代码。

最后,我们验证一下我们使用的是代理,而不是原来的类
测试代码:

package cn.edu.hpu.service;
import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import cn.edu.hpu.dao.UserDao;
import cn.edu.hpu.model.User;

public class UserServiceTest {

	@Test
	public void testAdd() throws Exception{
		ClassPathXmlApplicationContext ctx=new ClassPathXmlApplicationContext("beans.xml");

		UserService userService=(UserService)ctx.getBean("userService");
		System.out.println(userService.getClass());
		ctx.destroy();
	}
}

结果:
class cn.edu.hpu.service.UserService$ $ EnhancerByCGLIB $ $c0951629(CSDN对“$”符号很敏感,所以加空格了)
说明使用的是代理。

总结:

整个的Annotation实现AOP编程是属于那种不太重要的东西。脑子里留印象,使用到回来查就可以。但是AOP的原理一定要懂。

转载请注明出处:http://blog.csdn.net/acmman/article/details/44344267

时间: 2024-11-03 11:55:47

【spring框架】AOP的Annotation实现(下)的相关文章

Alter dataSource in Spring By AOP And Annotation

Here is an article of how to use AOP and Annotation mechanism to alter dataSource elegantly. First, I want make sure that everyone knows how to build multiple dataSource. Please check this article Dynamic-DataSource-Routing After this, we will have a

Spring的AOP的annotation实现

记录一下使用注解实现spring AOP的小例子. 第一步,导入相关的jar包:aspectjweaver-1.6.8.jar(提供注解org.aspectj.lang.annotation.Aspect等).spring-aop-3.0.4.RELEASE.jar(提供自动代理创建器org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator).aopalliance-1.0.jar(提供拦截

【spring框架】spring的几个Annotation实现(下)

继续上一篇http://blog.csdn.net/acmman/article/details/43889483的介绍 3.注解@Required 是初始化的时候就给注入,检查这个配置是否有问题.用来容错的. 4.@Resource(重要) a)加入:j2ee/common-annotation.jar @Resource是j2ee的标准. b)默认按名称,名称找不到,按类型(比AutoWired更直观) beans.xml: <?xml version="1.0" encod

Java的Spring框架下的AOP编程模式示例_java

Spring框架的关键组件是面向方面编程(AOP)框架.面向方面的编程不仅打破程序逻辑分成不同的部分称为所谓的担忧.跨越多个点的应用程序的功能被称为横切关注点和这些横切关注点是从应用程序的业务逻辑概念上区分开来.还有像日志记录,审计,声明性事务,安全性和高速缓存等方面的各种常见的好例子 模块化的OOP中的关键单元是类,而在AOP中模块化的单元则是切面.依赖注入可以帮助你从对方解耦应用程序对象和AOP可以帮助你从他们影响的对象分离横切关注点. AOP是一样的编程语言如Perl,.NET,Java和

实例讲解Java的Spring框架中的AOP实现_java

简介面向切面编程(AOP)提供另外一种角度来思考程序结构,通过这种方式弥补了面向对象编程(OOP)的不足. 除了类(classes)以外,AOP提供了 切面.切面对关注点进行模块化,例如横切多个类型和对象的事务管理. (这些关注点术语通常称作 横切(crosscutting) 关注点.) Spring的一个关键的组件就是 AOP框架. 尽管如此,Spring IoC容器并不依赖于AOP,这意味着你可以自由选择是否使用AOP,AOP提供强大的中间件解决方案,这使得Spring IoC容器更加完善.

Java的Spring框架中AOP项目的一般配置和部署教程_java

0.关于AOP面向切面编程(也叫面向方面编程):Aspect Oriented Programming(AOP),是软件开发中的一个热点,也是Spring框架中的一个重要内容.利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率. AOP是OOP的延续. 主要的功能是:日志记录,性能统计,安全控制,事务处理,异常处理等等. 主要的意图是:将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,通过对

举例讲解Java的Spring框架中AOP程序设计方式的使用_java

1.什么是AOP AOP是Aspect Oriented Programming的缩写,意思是面向方面编程,AOP实际是GoF设计模式的延续. 2.关于Spring AOP的一些术语: A.切面(Aspect):在Spring AOP中,切面可以使用通用类或者在普通类中以@Aspect 注解(@AspectJ风格)来实现 B.连接点(Joinpoint):在Spring AOP中一个连接点代表一个方法的执行 C.通知(Advice):在切面的某个特定的连接点(Joinpoint)上执行的动作.通

使用Java的Spring框架编写第一个程序Hellow world_java

Spring框架是什么?Spring是为企业Java最流行的应用程序开发框架.数以百万计的世界各地的开发人员使用Spring框架来创建高性能,易于测试的,可重用的代码. Spring框架是一个开源的Java平台,它最初是由Rod Johnson编写并在2003年6月在Apache2.0许可下首次发布. Spring是轻量级的,当涉及到大小和透明度. spring框架的基本版本是大约2MB. Spring框架的核心功能可以在任何Java应用程序中使用,但也有扩展的Java EE平台上构建Web应用

【spring框架】AOP的Annotation实现(上)

使用Annotation来实现AOP的动态代理: @AspectJ支持 @AspectJ使用了Java 5的注解,可以将切面声明为普通的Java类.@AspectJ样式在AspectJ 5发布的AspectJ project部分中被引入.Spring 2.0使用了和AspectJ 5一样的注解,并使用AspectJ来做切入点解析和匹配.但是,AOP在运行时仍旧是纯的Spring AOP,并不依赖于AspectJ的编译器或者织入器(weaver).  使用AspectJ的编译器或者织入器的话就可以