关于SpringAop学习的一点记录
AOP思想是一种面向切面编程思想,是将已经封装好的对象“切开”,将内部与业务无关的却又被多个业务模块所共同调用的部分进行抽取、封装。提高代码的重用性,降低各模块之间的耦合度(解耦),提高系统的可维护性。
举个栗子:
在系统管理过程中,经常会需要打印日志来对用户操作进行留痕处理,这个时候就可以将业务逻辑处理中的日志打印代码进行提取封装,以达到简化日志相关代码,降低各个模块之间耦合度的目的。下面用实际代码做简要说明。
Eclipse创建一个Maven项目:
pom.xml 配置如下:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.1.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.1.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.1.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.8</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
方案一:
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文件如下:
<?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"
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 ">
<!-- XML配置方式实现AOP需要AOP相关配置,切点表达式,方法名 -->
<aop:config>
<aop:aspect id="log" ref="logging">
<aop:pointcut id="selectAll" expression="execution(* com.spring.aop.xml.*.*(..))"/>
<aop:before pointcut-ref="selectAll" method="beforeAdvice"/>
<aop:after pointcut-ref="selectAll" method="afterAdvice"/>
<aop:after-returning pointcut-ref="selectAll" returning="obj" method="afterReturningAdvice"/>
<aop:after-throwing pointcut-ref="selectAll" throwing="ex" method="AfterThrowingAdvice"/>
</aop:aspect>
</aop:config>
<bean id="student" class="com.spring.aop.xml.Student">
<property name="name" value="Maxsu" />
<property name="age" value="21"/>
</bean>
<!-- XML配置方式实现AOP需要关联配置方式对应的切面类 -->
<bean id="logging" class="com.spring.aop.xml.LoggingAnnotation"/>
</beans>
注:配置文件名称与MainApp中的加载资源文件名一致;
Student类路径与创建的Student类路径一致;
<aop:config>中表达式的扫描路径和切面类的加载路径需同步修改。
执行MainApp中的main方法打印日志如下:
方案二:
注解方式实现AOP
新建resource文件夹下,新建annotation-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:aop="http://www.springframework.org/schema/aop"
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 ">
<!-- 该配置表示默认使用jdk代理方式,即基于接口的代理 -->
<aop:aspectj-autoproxy/>
<!-- Definition for student bean -->
<bean id="student" class="com.spring.aop.annotation.Student">
<property name="name" value="Maxsu" />
<property name="age" value="21"/>
</bean>
<!-- 注解方式实现AOP需要关联到相应切面类 -->
<bean id="logging" class="com.spring.aop.annotation.LoggingAnnotation"/>
</beans>
新建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文件,配置如下:
<?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"
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 ">
<!-- 代理方式实现AOP需要添加AOP代理配置 -->
<!-- 其中proxy-target-class="true/false"属性,
决定是基于接口的还是基于类的代理被创建。
如果proxy-target-class 属性值被设置为true,
那么基于类的代理将起作用(这时需要cglib库)。
如果proxy-target-class属值被设置为false或者这个属性被省略,
那么标准的JDK 基于接口的代理将起作用。
-->
<aop:config proxy-target-class="true"></aop:config>
<!-- Definition for student bean -->
<bean id="student" class="com.spring.aop.proxy.Student">
<property name="name" value="Maxsu" />
<property name="age" value="21"/>
</bean>
<!-- 注解方式实现AOP需要关联到相应切面类 -->
<bean id="logging" class="com.spring.aop.proxy.LoggingAnnotation"/>
</beans>
新建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文件,配置如下:
<?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"
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 ">
<!-- 代理方式实现AOP需要添加AOP代理配置 -->
<!-- 其中proxy-target-class="true/false"属性,
决定是基于接口的还是基于类的代理被创建。
如果proxy-target-class 属性值被设置为true,
那么基于类的代理将起作用(这时需要cglib库)。
如果proxy-target-class属值被设置为false或者这个属性被省略,
那么标准的JDK 基于接口的代理将起作用。
-->
<!-- <aop:config proxy-target-class="true"></aop:config> -->
<aop:aspectj-autoproxy/>
<!-- Definition for student bean -->
<bean id="student" class="com.spring.aop.myannotation.Student">
<property name="name" value="Maxsu" />
<property name="age" value="21"/>
</bean>
<!-- 注解方式实现AOP需要关联到相应切面类 -->
<bean id="logging" class="com.spring.aop.myannotation.LoggingAnnotation"/>
</beans>
新建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的一点记录,如有错漏,欢迎指正探讨。