关于SpringAop学习的一点记录
AOP思想是一种面向切面编程思想,是将已经封装好的对象“切开”,将内部与业务无关的却又被多个业务模块所共同调用的部分进行抽取、封装。提高代码的重用性,降低各模块之间的耦合度(解耦),提高系统的可维护性。
举个栗子:
在系统管理过程中,经常会需要打印日志来对用户操作进行留痕处理,这个时候就可以将业务逻辑处理中的日志打印代码进行提取封装,以达到简化日志相关代码,降低各个模块之间耦合度的目的。下面用实际代码做简要说明。
Eclipse创建一个Maven项目:
pom.xml 配置如下:
org.springframework
spring-aop
4.1.0.RELEASE
org.springframework
spring-aspects
4.1.0.RELEASE
org.springframework
spring-context
4.1.4.RELEASE
org.aspectj
aspectjweaver
1.6.8
junit
junit
3.8.1
test
方案一:
XML配置方式实现AOP
新建Student类如下:
package com.spring.aop.annotation;
public class Student {
private Integer age;
private String name;
public void setAge(Integer age) {
this.age = age;
}
public Integer getAge() {
System.out.println("Age : " + age );
return age;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
System.out.println("Name : " + name );
return name;
}
public void printThrowException(){
System.out.println("Exception raised");
throw new IllegalArgumentException();
}
}
新建切面类LoggingAnnotation如下:
package com.spring.aop.xml;
import org.springframework.core.annotation.Order;
public class LoggingAnnotation {
/**
* This is the method which I would like to execute
* before a selected method execution.
*/
@Order(1)
public void beforeAdvice(){
System.out.println("Going to setup student profile.");
}
/**
* This is the method which I would like to execute
* after a selected method execution.
*/
public void afterAdvice(){
System.out.println("Student profile has been setup.");
}
/**
* This is the method which I would like to execute
* when any method returns.
*/
public void afterReturningAdvice(Object obj){
obj = (Object)true;
int error = 1/0;
System.out.println("Returning:" + obj.toString() );
}
/**
* This is the method which I would like to execute
* if there is an exception raised.
* @throws Exception
*/
@Order(2)
public void AfterThrowingAdvice(IllegalArgumentException ex){
System.out.println(ex.toString());
throw ex;
}
}
注1:以上方法包含了多个切点(Pointcut,可逐一添加测试方便理解);
注2:@Order注解是设置方法执行优先级。
注3:afterReturningAdvice中以0做分母是为了触发AfterThrowingAdvice方法,以打印出相应得到异常信息。
新建MainApp类如下:
package com.spring.aop.xml;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Hello world!
*
*/
public class MainApp {
public static void main(String[] args) {
//TODO:XML实现Spring AOP
@SuppressWarnings("resource")
ApplicationContext context = new ClassPathXmlApplicationContext("xml-spring-aop.xml");
Student student = (Student) context.getBean("student");
student.getName();
student.getAge();
}
}
新建resource文件夹下,新建xml-spring-aop.xml文件如下:
注:配置文件名称与MainApp中的加载资源文件名一致;
Student类路径与创建的Student类路径一致;
执行MainApp中的main方法打印日志如下:
方案二:
注解方式实现AOP
新建resource文件夹下,新建annotation-spring-aop.xml文件,配置如下:
新建Student类:
package com.spring.aop.annotation;
public class Student {
private Integer age;
private String name;
public void setAge(Integer age) {
this.age = age;
}
public Integer getAge() {
System.out.println("Age : " + age );
return age;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
System.out.println("Name : " + name );
return name;
}
public void printThrowException(){
System.out.println("Exception raised");
throw new IllegalArgumentException();
}
}
新建LoggingAnnotation类:
package com.spring.aop.annotation;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class LoggingAnnotation {
/** Following is the definition for a pointcut to select
* all the methods available. So advice will be called
* for all the methods.
*/
@Pointcut("execution(* com.spring.aop.annotation.*.*(..))")
private void selectAll(){}
/**
* This is the method which I would like to execute
* before a selected method execution.
*/
@Before("selectAll()")
public void beforeAdvice(){
System.out.println("[beforeAdvice] Going to setup student profile.");
}
@Pointcut("execution(* com.spring.aop.annotation.Student.getAge(..))")
private void selectAge(){};
@After("selectAge()")
public void afterAdvice(){
System.out.println("[afterAdvice] Going to setup student profile.");
}
@Pointcut("execution(* com.spring.aop.annotation.Student.getAge(..))")
private void selectGetName(){}
@Around("selectGetName()")
public void aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
System.out.println("[aroundAdvice] Around advice");
Object[] args = proceedingJoinPoint.getArgs();
if(args.length>0){
System.out.print("[aroundAdvice] Arguments passed: " );
for (int i = 0; i < args.length; i++) {
System.out.print("[aroundAdvice] arg "+(i+1)+": "+args[i]);
}
}
Object result = proceedingJoinPoint.proceed(args);
System.out.println("[aroundAdvice] Returning " + result);
}
}
新建MainApp类:
package com.spring.aop.annotation;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Hello world!
*
*/
public class MainApp {
public static void main(String[] args) {
//TODO:注解方式实现Spring AOP
@SuppressWarnings("resource")
ApplicationContext context = new ClassPathXmlApplicationContext("annotation-spring-aop.xml");
Student student = (Student) context.getBean("student");
student.getName();
student.getAge();
student.printThrowException();
}
}
执行MainApp中的main方法,打印结果如下:
方案三:
代理方式实现AOP
新建resource文件下新建proxy-spring-aop.xml文件,配置如下:
新建Student类:
package com.spring.aop.proxy;
public class Student {
private Integer age;
private String name;
public void setAge(Integer age) {
this.age = age;
}
public Integer getAge() {
System.out.println("Age : " + age );
return age;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
System.out.println("Name : " + name );
return name;
}
public void printThrowException(){
System.out.println("Exception raised");
throw new IllegalArgumentException();
}
}
新建LoggingAnnotation类:
package com.spring.aop.proxy;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class LoggingAnnotation {
/** Following is the definition for a pointcut to select
* all the methods available. So advice will be called
* for all the methods.
*/
@Pointcut("execution(* com.spring.aop.proxy.*.*(..))")
private void selectAll(){}
/**
* This is the method which I would like to execute
* before a selected method execution.
*/
@Before("selectAll()")
public void beforeAdvice(){
System.out.println("[beforeAdvice] Going to setup student profile.");
}
@Pointcut("execution(* com.spring.aop.proxy.Student.getAge(..))")
private void selectAge(){};
@After("selectAge()")
public void afterAdvice(){
System.out.println("[afterAdvice] Going to setup student profile.");
}
@Pointcut("execution(* com.spring.aop.proxy.Student.*(..))")
private void selectGetName(){}
@Around("selectGetName()")
public void aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
System.out.println("[aroundAdvice] Around advice");
Object[] args = proceedingJoinPoint.getArgs();
if(args.length>0){
System.out.print("[aroundAdvice] Arguments passed: " );
for (int i = 0; i < args.length; i++) {
System.out.print("[aroundAdvice] arg "+(i+1)+": "+args[i]);
}
}
Object result = proceedingJoinPoint.proceed(args);
System.out.println("[aroundAdvice] Returning " + result);
}
}
新建MainApp类:
package com.spring.aop.proxy;
import org.springframework.aop.aspectj.annotation.AspectJProxyFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Hello world!
*
*/
public class MainApp {
public static void main(String[] args) {
//TODO:代理模式实现Spring AOP
@SuppressWarnings("resource")
ApplicationContext context = new ClassPathXmlApplicationContext("proxy-spring-aop.xml");
Student student = (Student) context.getBean("student");
//Create the Proxy Factory
AspectJProxyFactory proxyFactory = new AspectJProxyFactory(student);
//Add Aspect class to the factory
proxyFactory.addAspect(LoggingAnnotation.class);
//Get the proxy object
Student proxyStudent = proxyFactory.getProxy();
//Invoke the proxied method.
proxyStudent.getAge();
proxyStudent.getName();
}
}
执行MainApp中的main方法,打印结果如下:
进阶:
自定义注解配置方法:
新建resource文件夹下新建myannotation-spring-aop.xml文件,配置如下:
新建LoggingAnnotation类:
package com.spring.aop.myannotation;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class LoggingAnnotation {
// @Pointcut("@annotation(Myannotation)")
// private void myannotation(){};
/**
* This is the method which I would like to execute
* before a selected method execution.
*/
@Before("@annotation(com.spring.aop.myannotation.MyAnnotation)")
public void beforeAdvice(){
System.out.println("[beforeAdvice] Going to setup student profile.");
}
}
新建Student类:
package com.spring.aop.myannotation;
public class Student {
private Integer age;
private String name;
public void setAge(Integer age) {
this.age = age;
}
public Integer getAge() {
System.out.println("Age : " + age );
return age;
}
public void setName(String name) {
this.name = name;
}
@MyAnnotation
public String getName() {
System.out.println("Name : " + name );
return name;
}
public void printThrowException(){
System.out.println("Exception raised");
throw new IllegalArgumentException();
}
}
新建MyAnnotation类:
package com.spring.aop.myannotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyAnnotation {
}
新建MainApp类:
package com.spring.aop.myannotation;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Hello world!
*
*/
public class MainApp {
public static void main(String[] args) {
@SuppressWarnings("resource")
ApplicationContext context = new ClassPathXmlApplicationContext("myannotation-spring-aop.xml");
Student student = (Student) context.getBean("student");
student.getName();
student.getAge();
}
}
执行MainApp中的main方法,打印结果如下:
至此,简单的自定义注解配置使用完毕。
以上为我个人学习SpringAop的一点记录,如有错漏,欢迎指正探讨。