Spring AOP

Spring的AOP

一、总括

实现AOP的技术,主要分为两大类:一是采用动态代理技术,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行;二是采用静态织入的方式,引入特定的语法创建“方面”,从而使得编译器可以在编译期间织入有关“方面”的代码。然而殊途同归,实现AOP的技术特性却是相同的,分别为:

http://wayfarer.cnblogs.com/articles/241024.html


1、join point(连接点):是程序执行中的一个精确执行点,例如类中的一个方法。它是一个抽象的概念,在实现AOP时,并不需要去定义一个join point。

2、point cut(切入点):本质上是一个捕获连接点的结构。在AOP中,可以定义一个point cut,来捕获相关方法的调用。

3、advice(通知):是point cut的执行代码,是执行“方面”的具体逻辑。

4、aspect(方面):point cut和advice结合起来就是aspect,它类似于OOP中定义的一个类,但它代表的更多是对象间横向的关系。

5、introduce(引入):为对象引入附加的方法或属性,从而达到修改对象结构的目的。有的AOP工具又将其称为mixin。

Spring AOP
       上述的技术特性组成了基本的AOP技术,大多数AOP工具均实现了这些技术。它们也可以是研究AOP技术的基本术语。

Spring AOP

所需的jar包

Spring AOP

 

二、基于注解的AOP

Spring AOP

<?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"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
	                   http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
	                    http://www.springframework.org/schema/aop
	                   http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
	                   http://www.springframework.org/schema/context
	                   http://www.springframework.org/schema/context/spring-context-2.5.xsd
	                  ">

<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<bean id="myInterceptor" class="com.persia.service.MyInterceptor"></bean>
<bean id="personServiceImpl" class="com.persia.service.impl.PersonServiceImpl"></bean>
</beans>
一个切面的定义如下:
import org.aspectj.lang.ProceedingJoinPoint;
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.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.After;


@Aspect //声明一个切面
public class MyInterceptor {
	 //用来定义要拦截的方法
   @Pointcut("execution (* com.persia.service.impl.PersonServiceImpl.*(..))")
   private void anyMethod(){}//声明一个切入点
   
   @Before("anyMethod() &&args(userName)")//切入点的名称,附加条件:要求有String类型的参数,即这里只拦截save(String name)方法
   public void doAccessCheck(String userName){
	   System.out.println("前置通知"+userName);
   }
   
   @AfterReturning(pointcut="anyMethod()",returning="result")
   public void doAfterReturning(String result){
	   System.out.println("后置通知"+result);
   }
   
   @After("anyMethod()")
   public void doAfter(){
	   System.out.println("最终通知");  
   }
   
   @AfterThrowing(pointcut="anyMethod()",throwing="e")
   public void doThrowing(Exception e){
	   System.out.println("例外通知"+e);
   }
   
   @Around("anyMethod()")
   public Object arround(ProceedingJoinPoint pjp) throws Throwable{
	   System.out.println("进入方法");
	   Object result=pjp.proceed();//确保切面能够进行,比较适合在这里做权限判断,没有权限则不执行proceed
	   System.out.println("退出进入方法");
	   return result;
   }
}
Junit测试如下:
package junit.test;


import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.persia.service.IPersonService;

public class SpringAopTest {

	@BeforeClass
	public static void setUpBeforeClass() throws Exception {
	}
     
	@Test public void interceptorTest(){
		ApplicationContext cxt=new ClassPathXmlApplicationContext("applicationContext.xml");
	    IPersonService personService=(IPersonService) cxt.getBean("personServiceImpl");
	    personService.save("hello");
	   // personService.getPersonName(1);
	
	}
}

服务类:

public class PersonServiceImpl implements IPersonService {

	public String getPersonName(Integer id) {
		// TODO Auto-generated method stub
		System.out.println("我是getPersonName方法");
		return "spring aop";
	}

	public void save(String name) {
		// TODO Auto-generated method stub
		//throw new RuntimeException("测试例外异常");
        System.out.println("我是save方法");
	}

	public void update(String name, Integer id) {
		// TODO Auto-generated method stub
		System.out.println("我是update方法");
	}

}

结果:

前置通知hello
进入方法
我是save方法
后置通知null
最终通知
退出进入方法

三、基于XML的AOP

Spring AOP

 

 

 

 

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"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
	                   http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
	                    http://www.springframework.org/schema/aop
	                   http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
	                   http://www.springframework.org/schema/context
	                   http://www.springframework.org/schema/context/spring-context-2.5.xsd
	                  ">

<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

<bean id="personService" class="com.persia.service.impl.PersonServiceImpl"></bean>
<bean id="aspectBean" class="com.persia.service.MyInterceptor"></bean>

<aop:config>
 <aop:aspect id="myaop" ref="aspectBean">
    <aop:pointcut id="mycut" expression="execution(* com.persia.service.impl.PersonServiceImpl.*(..))"/>
    <aop:before pointcut-ref="mycut" method="doAccessCheck"/>
    <aop:after-returning pointcut-ref="mycut" method="doAfterReturning"/>
    <aop:after-throwing pointcut-ref="mycut" method="doThrowing"/>
    <aop:after pointcut-ref="mycut" method="doAfter"/>
    <aop:around pointcut-ref="mycut" method="arround"/>
 </aop:aspect>
  
</aop:config>

</beans>

切面的定义无需注解:

import org.aspectj.lang.ProceedingJoinPoint;

public class MyInterceptor {

     
   public void doAccessCheck(){
	   System.out.println("前置通知");
   }
   
  
   public void doAfterReturning(){
	   System.out.println("后置通知");
   }
   
 
   public void doAfter(){
	   System.out.println("最终通知");  
   }
   
 
   public void doThrowing(){
	   System.out.println("例外通知");
   }
   
  
   public Object arround(ProceedingJoinPoint pjp) throws Throwable{
	   System.out.println("进入方法");
	   Object result=pjp.proceed();//确保切面能够进行,比较适合在这里做权限判断,没有权限则不执行proceed
	   System.out.println("退出进入方法");
	   return result;
   }
}

注意,上面的XML配置不提供参数的支持,若切面里的方法有参数,会报错:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'personServiceImpl' defined in class path resource [applicationContext.xml]: BeanPostProcessor before instantiation of bean failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.aop.aspectj.AspectJPointcutAdvisor#0': Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [org.springframework.aop.aspectj.AspectJPointcutAdvisor]: Constructor threw exception; nested exception is java.lang.IllegalArgumentException: error at ::0 formal unbound in pointcut 
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory$1.run(AbstractAutowireCapableBeanFactory.java:405)
	at java.security.AccessController.doPrivileged(Native Method)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:380)
	at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:264)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:220)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:261)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:185)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:164)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:429)
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:729)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:381)
	at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:139)
	at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:83)
	at junit.test.SpringAopTest.interceptorTest(SpringAopTest.java:18)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
	at java.lang.reflect.Method.invoke(Unknown Source)
	at org.junit.internal.runners.TestMethodRunner.executeMethodBody(TestMethodRunner.java:99)
	at org.junit.internal.runners.TestMethodRunner.runUnprotected(TestMethodRunner.java:81)
	at org.junit.internal.runners.BeforeAndAfterRunner.runProtected(BeforeAndAfterRunner.java:34)
	at org.junit.internal.runners.TestMethodRunner.runMethod(TestMethodRunner.java:75)
	at org.junit.internal.runners.TestMethodRunner.run(TestMethodRunner.java:45)
	at org.junit.internal.runners.TestClassMethodsRunner.invokeTestMethod(TestClassMethodsRunner.java:66)
	at org.junit.internal.runners.TestClassMethodsRunner.run(TestClassMethodsRunner.java:35)
	at org.junit.internal.runners.TestClassRunner$1.runUnprotected(TestClassRunner.java:42)
	at org.junit.internal.runners.BeforeAndAfterRunner.runProtected(BeforeAndAfterRunner.java:34)
	at org.junit.internal.runners.TestClassRunner.run(TestClassRunner.java:52)
	at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:45)
	at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:460)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:673)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:386)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.aop.aspectj.AspectJPointcutAdvisor#0': Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [org.springframework.aop.aspectj.AspectJPointcutAdvisor]: Constructor threw exception; nested exception is java.lang.IllegalArgumentException: error at ::0 formal unbound in pointcut 
	at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:243)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:923)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:833)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:440)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory$1.run(AbstractAutowireCapableBeanFactory.java:409)
	at java.security.AccessController.doPrivileged(Native Method)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:380)
	at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:264)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:220)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:261)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:185)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:164)
	at org.springframework.aop.framework.autoproxy.BeanFactoryAdvisorRetrievalHelper.findAdvisorBeans(BeanFactoryAdvisorRetrievalHelper.java:87)
	at org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator.findCandidateAdvisors(AbstractAdvisorAutoProxyCreator.java:98)
	at org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator.findCandidateAdvisors(AnnotationAwareAspectJAutoProxyCreator.java:83)
	at org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator.shouldSkip(AspectJAwareAdvisorAutoProxyCreator.java:105)
	at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.postProcessBeforeInstantiation(AbstractAutoProxyCreator.java:266)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInstantiation(AbstractAutowireCapableBeanFactory.java:789)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.resolveBeforeInstantiation(AbstractAutowireCapableBeanFactory.java:760)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory$1.run(AbstractAutowireCapableBeanFactory.java:399)
	... 33 more
Caused by: org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [org.springframework.aop.aspectj.AspectJPointcutAdvisor]: Constructor threw exception; nested exception is java.lang.IllegalArgumentException: error at ::0 formal unbound in pointcut 
	at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:111)
	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:87)
	at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:237)
	... 52 more
Caused by: java.lang.IllegalArgumentException: error at ::0 formal unbound in pointcut 
	at org.aspectj.weaver.tools.PointcutParser.parsePointcutExpression(PointcutParser.java:316)
	at org.springframework.aop.aspectj.AspectJExpressionPointcut.buildPointcutExpression(AspectJExpressionPointcut.java:205)
	at org.springframework.aop.aspectj.AspectJExpressionPointcut.checkReadyToMatch(AspectJExpressionPointcut.java:192)
	at org.springframework.aop.aspectj.AspectJExpressionPointcut.getMethodMatcher(AspectJExpressionPointcut.java:178)
	at org.springframework.aop.aspectj.AbstractAspectJAdvice.buildSafePointcut(AbstractAspectJAdvice.java:189)
	at org.springframework.aop.aspectj.AspectJPointcutAdvisor.<init>(AspectJPointcutAdvisor.java:51)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
	at java.lang.reflect.Constructor.newInstance(Unknown Source)
	at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:96)
	... 54 more

去掉所有方法的参数即可,或者对XML的配置如下:

"execution(java.lang.String com.persia.service.impl.PersonServiceImpl.*(..))"

代表要拦截返回值为String类型的方法。

"execution(* com.persia.service.impl.PersonServiceImpl.*(java.lang.String,..))"

代表要拦截第一个参数为String类型的方法,其他参数不限制。

"execution(!void com.persia.service.impl.PersonServiceImpl.*(..))"

代表要拦截返回值不是void类型的方法。

"execution(!void com.persia.service..*.*(..))"

代表要拦截com.persia.servie这个包的所有子包.*下的类的所有方法.*

给Advice传递参数如下:

<aop:config>
 <aop:aspect id="myaop" ref="aspectBean">
 <aop:pointcut id="mycut" expression="execution(* com.persia.service.impl.PersonServiceImpl.*(..))"/>
 <aop:pointcut id="argcut" expression="execution(* com.persia.service.impl.PersonServiceImpl.*(..)) and args(name)"/>  
 <aop:before pointcut-ref="mycut" method="doAccessCheck"  />
 <aop:after-returning pointcut-ref="mycut" method="doAfterReturning"/>
   <aop:after-throwing pointcut-ref="mycut" method="doThrowing"/>
   <aop:after pointcut-ref="argcut" method="doAfter" arg-names="name"/>
 <aop:around pointcut-ref="mycut" method="arround"/>
 </aop:aspect>
  
</aop:config>

切面的方法定义如下:

 public void doAfter(String name){
	   System.out.println("最终通知" +name);  
   }