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>
版权声明:本文为博主原创文章,未经博主允许不得转载。