AOP(Aspect Oriented Programming),是面向切面编程的技术。AOP基于IoC基础,是对OOP的有益补充。spring中AOP的配置方式有2种方式:xml配置和AspectJ注解方式。

一、xml配置的方式:

1、service接口和服务类:


package cn.edu.nuc.SpringTest.service;




public interface DemoService {


public String sayHello(String name) ;


}






package cn.edu.nuc.SpringTest.service.impl;




import org.springframework.stereotype.Service;


import cn.edu.nuc.SpringTest.common.anno.Permission;


import cn.edu.nuc.SpringTest.service.DemoService;




@Service  


public class DemoServiceImpl implements DemoService{



@Permission(value="no")


public String sayHello(String name) {


System.out.println("hello word.........."+name);


return "返回值";


}


}




2、切面类开发:


package cn.edu.nuc.SpringTest.interceptor;




import java.lang.reflect.Method;


import org.aspectj.lang.JoinPoint;


import org.aspectj.lang.ProceedingJoinPoint;


import org.aspectj.lang.Signature;


import org.aspectj.lang.reflect.MethodSignature;


import cn.edu.nuc.SpringTest.common.anno.Permission;




public class MyInterceptor1 {


public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable{


System.out.println("环绕通知进入方法----");


Object object=pjp.proceed();


System.out.println("环绕通知退出方法------");


return object;


}



//后置通知(不需要获取返回值)


public void doAfterReturning(JoinPoint jp,String name,Object rvt){


System.out.println("后置通知,参数:"+name);


System.out.println("后置返回:"+rvt);


}



public void doAfterThrowing(JoinPoint jp,Throwable ex){


System.out.println("例外通知~~~~~~~~~~~~~~~~~~~");


System.out.println("method " + jp.getTarget().getClass().getName()  


                + "." + jp.getSignature().getName() + " throw exception");  


        System.out.println(ex.getMessage()+ex.toString());  


}



//最终通知


public void doAfter(JoinPoint jp,String name){


        System.out.println("最终通知--------");


}



//前置通知(不需要获取输入参数)


public void doAccessCheck(JoinPoint jp){


Object[] args = jp.getArgs();


Signature signature = jp.getSignature();


MethodSignature methodSignature = (MethodSignature)signature;    


   Method targetMethod = methodSignature.getMethod();  


   if (targetMethod.isAnnotationPresent(Permission.class)) {


   

Permission annotation = targetMethod.getAnnotation(Permission.class);


   

System.out.println("我是anno:"+annotation.value());


   }


System.out.println("前置通知--------"+",参数:"+args[0]);


}


}




3、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:aop="http://www.springframework.org/schema/aop"


    xmlns:context="http://www.springframework.org/schema/context"


    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.0.xsd ​​      http://www.springframework.org/schema/aop


http://www.springframework.org/schema/aop/spring-aop-3.0.xsd


     http://www.springframework.org/schema/context


    http://www.springframework.org/schema/context/spring-context-3.0.xsd


     http://www.springframework.org/schema/tx


http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">


   


     <context:component-scan base-package="cn.edu.nuc.SpringTest"/>


<bean id="myInterceptor11" class="cn.edu.nuc.SpringTest.interceptor.MyInterceptor1"></bean> 



<aop:config proxy-target-class="true">


   <aop:aspect id="asp1" ref="myInterceptor11">


     <aop:pointcut expression="execution (* cn.edu.nuc.SpringTest.service..*.*(..))"  id="mycut"/>  


     <aop:before method="doAccessCheck" pointcut="execution (* cn.edu.nuc.SpringTest.service..*.*(..))" />


     <aop:after method="doAfter" pointcut="execution (* cn.edu.nuc.SpringTest.service..*.*(..)) and args(name)" />


     <aop:after-returning method="doAfterReturning" pointcut="execution (* cn.edu.nuc.SpringTest.service..*.*(..)) and args(name)" returning="rvt" />


     <aop:around method="doBasicProfiling" pointcut="execution (* cn.edu.nuc.SpringTest.service..*.*(..))" />


     <aop:after-throwing method="doAfterThrowing" pointcut-ref="mycut" throwing="ex"/>


   </aop:aspect>


</aop:config>


</beans> 




4、测试:


public class App  {


    public static void main( String[] args ) {


      String[] fileUrl = new String[]{"classpath*:applicationContext.xml"};   


         ApplicationContext appContext = new ClassPathXmlApplicationContext(fileUrl);


         


          DemoServiceImpl ser = appContext.getBean(DemoServiceImpl.class);


      ser.sayHello("刘晓111");


    }


}


注意点:


1)后置返回通知方法中,如果要获得切入点方法返回值,需要配置returning,且需要保持与后置返回通知方法的参数名一致;


2)后置异常通知方法中,如果要获得切入点方法异常,需要配置throwing,且保持与方法中异常的参数名一致;


3)在xml中,可以通过<aop:pointcut… 配置多个切入点,然后在切面类中的前置、后置、环绕、例外通知中使用不同的切入点;


4)在前置、后置、例外通知的方法中,JoinPoint要 作为第一个参数,(环绕通知使用的是ProceedingJoinPoint),通过JoinPoint 配合反射可以获得切入点的类、方法、注解等信息;(这里注意,JoinPoint 一定要放在第一个参数的位置,当JoinPoint 和切入点方法参数、切入点方法返回值、例外对象放在一起时,如果JoinPoint 不在第一个参数位置,那么spring会报一个无法绑定切入点的错误error at ::0 formal unbound in pointcut。例如:public void doAfterReturning(JoinPoint jp,String name,Object rvt)中,既有JoinPoint 也有切入点方法参数、还有切入点方法返回,JoinPoint 一定放在第一位)


5)切面类的通知方法中获取切入点方法的参数:


 1.在通知方法中,可以通过JoinPoint 获取切入点方法的所有参数;


 2.可以在xml中配置and args(name) ,name要和切入点方法参数名、通知方法的参数名一致,这样就可以在通知方法中的参数获取切入点方法的参数了;




补充:切面类是指附加功能的类,例如 MyInterceptor1 ,通知方法是切面类中,aop规定好的前置、后置...标记的方法;


切入点类,是要被切入的业务类,如DemoServiceImpl ,切入点方法是切入点类中,被切入的方法。


二、注解方式:

1、service接口和服务类:(同上)

 

2、切面类开发:

packagecn.edu.nuc.SpringTest.interceptor;

 

importjava.lang.reflect.Method;

importorg.aspectj.lang.JoinPoint;

importorg.aspectj.lang.ProceedingJoinPoint;

importorg.aspectj.lang.Signature;

importorg.aspectj.lang.annotation.After;

importorg.aspectj.lang.annotation.AfterReturning;

importorg.aspectj.lang.annotation.AfterThrowing;

importorg.aspectj.lang.annotation.Around;

importorg.aspectj.lang.annotation.Aspect;

importorg.aspectj.lang.annotation.Before;

importorg.aspectj.lang.annotation.Pointcut;

importorg.aspectj.lang.reflect.MethodSignature;

importorg.springframework.stereotype.Component;

importcn.edu.nuc.SpringTest.common.anno.Permission;

 

@Component

@Aspect

public classMyInterceptor {

 //切入点要拦截的类,声明一个切入点,切入点的名称其实是一个方法

@Pointcut("execution(* cn.edu.nuc.SpringTest.service..*.*(..))")

privatevoid anyMethod(){}

 

//声明一个切入点,切入点的名称其实是一个方法

@Pointcut(value="execution(* cn.edu.nuc.SpringTest.service..*.*(java.lang.String)) && args(name)")

privatevoid nameParameMethod(String name){}

 

@Around("anyMethod()")

publicObject doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable{

System.out.println("环绕通知进入方法");

Objectobject=pjp.proceed();

System.out.println("环绕通知退出方法");

returnobject;

}

 

//后置通知(不需要获取返回值)

//        @AfterReturning("anyMethod()")

@AfterReturning (pointcut="anyMethod()",returning="result")

publicvoid doAfterReturning(JoinPoint jp,String result){

Object[]args = jp.getArgs();

 

Objecttarget = jp.getTarget();

Signaturesignature = jp.getSignature();

MethodSignaturemethodSignature = (MethodSignature)signature;   

    Method targetMethod =methodSignature.getMethod(); 

   

    boolean b =targetMethod.isAnnotationPresent(Permission.class);

    if (b) {

           Permission perm =targetMethod.getAnnotation(Permission.class);

           String value =perm.value();

           System.out.println("@@@@@@@@@@@@@@@@@"+value);

    }

   

   

System.out.println("======="+signature.getName()+";"+jp.getThis().toString());

System.out.println("后置通知:"+args[0]+target.toString()+";返回值:"+result);

}

 

//@AfterThrowing("anyMethod()")

@AfterThrowing(pointcut="anyMethod()" ,throwing="e")

publicvoid doAfterThrowing(Exception e){

System.out.println("例外通知:");

}

 

//最终通知

@After(value="anyMethod()")

publicvoid doAfter(JoinPoint jp){

Object[]args = jp.getArgs();

   System.out.println("最终通知:================="+args[0]);

}

 

//前置通知(不需要获取输入参数)

@Before("nameParameMethod(name)")//第一个参数为切入点的名称,第二个是测试获取输入参数,此处为string类型的,参数名称与方法中的名称相同,如果不获取输入参数,可以不要

//@Before("anyMethod()")//第一个参数为切入点的名称

public void doAccessCheck(JoinPoint jp,String name){

 

System.out.println("前置通知:"+name);

}

}

 

3、配置文件:

<?xmlversion="1.0" encoding="UTF-8"?>

<beansxmlns="http://www.springframework.org/schema/beans"

   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

   xmlns:aop="http://www.springframework.org/schema/aop"

   xmlns:context="http://www.springframework.org/schema/context"

   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.0.xsd​

     ​​http://www.springframework.org/schema/aop​

 ​​http://www.springframework.org/schema/aop/spring-aop-3.0.xsd​

     ​​http://www.springframework.org/schema/context​

           ​​ http://www.springframework.org/schema/context/spring-context-3.0.xsd​

     ​​http://www.springframework.org/schema/tx​

 ​​http://www.springframework.org/schema/tx/spring-tx-3.0.xsd​​">

  

        <context:component-scanbase-package="cn.edu.nuc.SpringTest"/>

  <!-- 注解方式的aop -->

 <aop:aspectj-autoproxy  proxy-target-class="true"/>

</beans>

注:需要使用<aop:aspectj-autoproxy  proxy-target-class="true"/>  开启注解aop功能,如果使用了ioc,那么一定要加上proxy-target-class="true"

 

4、测试类:(同上)

 

注意点:

1)后置返回通知方法中,如果要获得切入点方法返回值,需要在AfterReturning 注解中加上returning,且需要保持与后置返回通知方法的参数名一致;

2)后置异常通知方法中,如果要获得切入点方法异常,需要在AfterThrowing注解中加上throwing,且保持与方法中异常的参数名一致;

3)同样,在通知方法中,JoinPoint要 作为第一个参数,例如:public void doAccessCheck(JoinPoint jp,String name)中;

4)切面类的通知方法中获取切入点方法的参数:

 1.在通知方法中,可以通过JoinPoint 获取切入点方法的所有参数;

 2.在切面类的注解上配置andargs(name),name要和切入点方法参数名、通知方法的参数名一致,这样就可以在通知方法中的参数获取切入点方法的参数了;



pom.xml

​<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>cn.edu.nuc</groupId>
  <artifactId>SpringTest</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>SpringTest</name>
  <url>http://maven.apache.org</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <org.springframework.version>3.1.1.RELEASE</org.springframework.version>
  </properties>
  
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.8.1</version>
      <scope>test</scope>
    </dependency>
    
    <dependency>
	    <groupId>org.springframework</groupId>
	    <artifactId>spring-webmvc</artifactId>
	    <version>${org.springframework.version}</version>
	</dependency>
	<dependency>
	    <groupId>org.springframework</groupId>
	    <artifactId>spring-orm</artifactId>
	    <version>${org.springframework.version}</version>
	</dependency>
	<dependency>
	    <groupId>org.springframework</groupId>
	    <artifactId>spring-test</artifactId>
	    <version>${org.springframework.version}</version>
	    <type>jar</type>
	    <scope>test</scope>
	</dependency>
	<dependency>
	    <groupId>org.aspectj</groupId>
	    <artifactId>aspectjweaver</artifactId>
	    <version>1.8.2</version>
	</dependency>
	<dependency>
		<groupId>com.fasterxml.jackson.core</groupId>
		<artifactId>jackson-core</artifactId>
		<version>2.2.3</version>
	</dependency>
	<dependency>
		<groupId>com.fasterxml.jackson.core</groupId>
		<artifactId>jackson-databind</artifactId>
		<version>2.2.3</version>
	</dependency>
	<dependency>
		<groupId>com.fasterxml.jackson.core</groupId>
		<artifactId>jackson-annotations</artifactId>
		<version>2.2.3</version>
	</dependency>
	
	<dependency>
		<groupId>cglib</groupId>
		<artifactId>cglib</artifactId>
		<version>2.2</version>
	</dependency>
	
  </dependencies>

</project>​