前言
其实从开始使用spring开始,对spring中的aop一直就有了解,但是每次使用过后都遗忘了。导致再次使用的时候,又得重新翻找资料,重新开始写demo,今天来个总结,搞定aop的基本实例。真正从源码基本进行学习还得等一段时间。
一些概念
网上已经有很多博客针对aop的一些概念有介绍,自己也能理解一二。从实用的角度来说,概念的介绍并不是这篇博客的重点,针对这些概念的介绍可以参考以下博客,个人觉得讲解的比较通俗的是这一篇:aop概念通俗版,讲解的稍微全面一点的是这一篇:aop相关概念
简单实例
这里还是弄一个简单实例吧。针对一些细节后续会进行总结
先创建一个maven项目,pom文件如下:
<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>com.learn</groupId>
<artifactId>spring_aop</artifactId>
<version>0.0.1-SNAPSHOT</version>
<url>http://maven.apache.org</url>
<properties>
<spring.version>3.1.1.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.6.12</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.12</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2</version>
</dependency>
</dependencies>
</project>
在spring的配置文件中加入如下配置:
<aop:aspectj-autoproxy />
<context:component-scan base-package="com.learn" />
编写一个简单的业务类
package com.learn.service;
import org.springframework.stereotype.Service;
@Service
public class PersonService {
public void addPerson(String personName) {
System.out.println("add person");
}
public boolean deletePerson(String personName) {
System.out.println("delete person");
return true;
}
public void editPerson(String personName) {
System.out.println("edit person "+personName);
throw new RuntimeException("edit person infomation error!");
}
}
编写切面类
package com.learn.aspect;
import org.aspectj.lang.JoinPoint;
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.Pointcut;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class SimpleAspect {
@Pointcut("execution(* com.learn.service.*Service*.*(..))")
public void pointCut() {
}
@Pointcut("execution(* com.learn.service.*Service*.edit*(..))")
public void pointCutException() {
}
@After("pointCut()")
public void after(JoinPoint joinPoint) {
System.out.println("after aspect executed");
}
@Before("pointCut()")
public void before(JoinPoint joinPoint) {
System.out.println("before aspect executing");
}
@AfterReturning(pointcut="pointCut()",returning = "returnVal")
public void afterReturning(JoinPoint joinPoint ,Object returnVal) {
System.out.println("afterReturning executed,return result is "+returnVal);
}
@Around("pointCut()")
public void around(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("around start..");
try {
pjp.proceed();
} catch (Throwable ex) {
System.out.println("error in around");
throw ex;
}
System.out.println("around end");
}
@SuppressWarnings("rawtypes")
@AfterThrowing(pointcut="pointCutException()",throwing="error")
public void afterThrowing(JoinPoint jp,Throwable error) {
Object[] args = jp.getArgs();
System.out.println("目标函数中的参数信息:");
for(Object o:args) {
System.out.println(o.toString());
}
Object target = jp.getTarget();
System.out.println("目标函数中的target信息:"+target.toString());
Class class1 = jp.getClass();
String kind = jp.getKind();
System.out.println("切面的类型:"+kind);
System.out.println("目标函数的类信息:"+class1.getName());
System.out.println("切面捕获异常信息");
System.out.println("error:"+error);
}
}
切面类中的注解需要注意一下,@Component是将其交给spring管理,@Aspect是将其标记为切面,其实还有xml配置的方式实现,这里就不做详细探讨了,XML配置更加简单。
测试方法
package com.learn.func;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.learn.service.PersonService;
public class App {
public static void main(String[] args) {
try {
ApplicationContext appContext = new ClassPathXmlApplicationContext("classpath:appContext.xml");
PersonService personService = appContext.getBean(PersonService.class);
String personName = "liman";
personService.addPerson(personName);
personService.deletePerson(personName);
personService.editPerson(personName);
} catch (Exception e) {
System.out.println("异常信息:"+e.getMessage());
}
}
}
程序运行结果
补充
spring aop中重要的就是切入点表达式,切入点表达式是联系目标类方法和切面增强方法的桥梁。因此需要针对切入点表达式进行一些补充
execution(方法修饰符 方法返回值 方法所属类 匹配方法名 (方法的参数列表) 方法抛出的异常)
其中红色字体的不能省略,每个部分都支持通配符('*')来匹配
方法修饰符:public private protected。
参数列表中:'*'——表示一个任意类型的参数,".."——表示零个或多个任意类型的参数
(*,Integer)匹配一个接受两个参数的方法,第一个参数可以为任意类型,第二个必须为Integer类型。
execution只是切入点表达式的一种,还有within,this,target,args。
针对within,this,target和args的介绍,我建议还是参考官网文档吧,国内有些文档将这几种切入点表达式翻译的很怪。
如果出现一个增强类需要切入到多个目标类,可以进行切入点表达式的组合,||,&&等逻辑语法在切入点表达式中依旧适用
针对切面类中需要获取目标方法的相关信息,一般是通过JoinPoint这个参数来获取,同时在切面类中,为了调用目标类中的相关方法,可能需要用到反射。
最后说一句:这次的使用是基于在新网实习中,涉及到异常日志信息处理的问题来实现的,当时脑子里首先反应的就是利用AOP,经历过各种苛刻碰碰,勉强算是完成了功能,在后面迭代中进一步完善