目录
Spring 框架的 AOP
- 跨一个应用程序的多个点的功能被称为横切关注点
- 这些横切关注点在概念上独立于应用程序的业务逻辑
- 有各种各样的常见的很好的方面的例子,如日志记录、审计、声明式事务、安全性和缓存等。
- Spring AOP 模块提供拦截器来拦截一个应用程序,例如,当执行一个方法时,你可以在方法执行之前或之后添加额外的功能。
AOP 术语
项 | 描述 |
---|---|
Aspect (切面) | 一个模块具有一组提供横切需求的 APIs。例如,一个日志模块为了记录日志将被 AOP 方面调用。应用程序可以拥有任意数量的方面,这取决于需求。 |
Join point (连接点) | 在实际的应用程序中,其中一个操作将使用 Spring AOP 框架。 |
Advice(通知) | 实际行动之前或之后执行的方法,这是在程序执行期间通过 Spring AOP 框架实际被调用的代码。 |
Pointcut(切入点) | 这是一组一个或多个连接点,通知应该被执行。你可以使用表达式或模式指定切入点。 |
Introduction(引用) | 引用允许你添加新方法或属性到现有的类中。 |
Target object(目标对象) | 被一个或者多个方面所通知的对象,这个对象永远是一个被代理对象。也称为被通知对象。 |
Weaving(编织) | Weaving 把方面连接到其它的应用程序类型或者对象上,并创建一个被通知的对象。这些可以在编译时,类加载时和运行时完成。 |
通知的类型
通知 | 描述 |
---|---|
前置通知 | 在一个方法执行之前,执行通知。 |
后置通知 | 在一个方法执行之后,不考虑其结果,执行通知。 |
返回后通知 | 在一个方法执行之后,只有在方法成功完成时,才能执行通知。 |
抛出异常后通知 | 在一个方法执行之后,只有在方法退出抛出异常时,才能执行通知。 |
环绕通知 | 在建议方法调用之前和之后,执行通知。 |
实现自定义方面
方法 | 描述 |
---|---|
XML Schema based | 方面是使用常规类以及基于配置的 XML 来实现的。 |
@AspectJ based | @AspectJ 引用一种声明方面的风格作为带有 Java 5 注释的常规 Java 类注释。 |
基于 AOP 的 XML架构(XML Schema based)
为了在本节的描述中使用 aop 命名空间标签,你需要导入 spring-aop j架构,如下所述:
<?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 ">
<!-- bean definition & AOP specific configuration -->
</beans>
我这里用的是maven工具,所以可以直接导入相关依赖
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
</dependency>
</dependencies>
创建切面类
package com.demo.point;
public class Logging {
/**
* 前置通知,在一个方法执行之前,执行通知
*/
public void beforeAdvice(){
System.out.println("Going to setup student profile");
}
/**
* 后置通知,在一个方法执行之后,不考虑其结果,执行通知
*/
public void afterAdvice(){
System.out.println("Student profile has been setup");
}
/**
* 返回后通知,在一个方法执行之后,只有在方法成功完成时,才能执行通知。
*/
public void afterReturningAdvice(Object retVal){
System.out.println("Returning:" + retVal.toString() );
}
/**
* 抛出异常后通知,在一个方法执行之后,只有在方法退出抛出异常时,才能执行通知
* @param ex
*/
public void afterThrowingAdvice(IllegalArgumentException ex){
System.out.println("There has been an exception: " + ex.toString());
}
}
创建student
package com.demo.dao;
public class Student {
private Integer age;
private String name;
public Integer getAge() {
System.out.println("Age : " + age );
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getName() {
System.out.println("Name : " + name );
return name;
}
public void setName(String name) {
this.name = name;
}
/**
* 塞入一个异常,使抛出异常后通知方法生效
*/
public void printThrowException(){
System.out.println("Exception raised");
throw new IllegalArgumentException();
}
}
spring-confg配置
<?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:config>
<aop:aspect id="log" ref="logging">
<!-- execution(* com.demo.dao.*.*(..)) 表示 所有修饰符的所有返回值类型 com.demo.dao 包下的所有方法-->
<aop:pointcut id="selectAll" expression="execution(* com.demo.dao.*.*(..))"/>
<aop:before pointcut-ref="selectAll" method="beforeAdvice" />
<aop:after pointcut-ref="selectAll" method="afterAdvice"/>
<aop:after-returning pointcut-ref="selectAll" returning="retVal" method="afterReturningAdvice" />
<aop:after-throwing pointcut-ref="selectAll" throwing="ex" method="afterThrowingAdvice"/>
</aop:aspect>
</aop:config>
<!-- 配置bean -->
<bean id="student" class="com.demo.dao.Student">
<property name="name" value="张三"/>
<property name="age" value="24"/>
</bean>
<!-- 将切面类交与Spring容器管理 -->
<bean id="logging" class="com.demo.point.Logging"/>
</beans>
测试类
import com.demo.dao.Student;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringTest {
public static void main(String[] args){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-config.xml");
Student student = (Student) applicationContext.getBean("student");
student.getName();
student.getAge();
student.printThrowException();
}
}
运行
Going to setup student profile
Name : 张三
Student profile has been setup
Returning:张三
Going to setup student profile
Age : 24
Student profile has been setup
Returning:24
Going to setup student profile
Exception raised
Student profile has been setup
There has been an exception: java.lang.IllegalArgumentException
.....
other exception content
如果你想要在一个特殊的方法之前或者之后执行你的建议,你可以通过替换使用真实类和方法名称的切入点定义中的星号(*)来定义你的切入点来缩短你的执行
<?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:config>
<aop:aspect id="log" ref="logging">
<!-- execution(* com.demo.*(..)) 表示 所有修饰符的所有返回值类型 com.demo 包下的所有方法-->
<aop:pointcut id="selectAll" expression="execution(* com.demo.dao.Student.getName(..))"/>
<aop:before pointcut-ref="selectAll" method="beforeAdvice" />
<aop:after pointcut-ref="selectAll" method="afterAdvice"/>
<aop:after-returning pointcut-ref="selectAll" returning="retVal" method="afterReturningAdvice" />
<aop:after-throwing pointcut-ref="selectAll" throwing="ex" method="afterThrowingAdvice"/>
</aop:aspect>
</aop:config>
<!-- 配置bean -->
<bean id="student" class="com.demo.dao.Student">
<property name="name" value="张三"/>
<property name="age" value="24"/>
</bean>
<!-- 将切面类交与Spring容器管理 -->
<bean id="logging" class="com.demo.point.Logging"/>
</beans>
运行结果
Going to setup student profile
Name : 张三
Student profile has been setup
Returning:张三
Age : 24
Exception raised
基于 AOP 的 @AspectJ(@AspectJ based)
要想使用@AspectJ,首先需要再spring-config.xml中配置,也需要导入aspectjweaver依赖包
<!-- 使用注解自动生成代理对象 -->
<aop:aspectj-autoproxy/>
例:
<?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:aspectj-autoproxy/>
<!-- 配置bean -->
<bean id="student" class="com.demo.dao.Student">
<property name="name" value="张三"/>
<property name="age" value="24"/>
</bean>
<!-- 将切面类交与Spring容器管理 -->
<bean id="logging" class="com.demo.point.Logging"/>
</beans>
package com.demo.point;
import org.aspectj.lang.annotation.*;
/**
* @Title:
* @auther: raohr
* @Date: 2021/2/23 10:18
* @param:
* @Description:
* @return:
* @throws:
*/
@Aspect
public class Logging {
@Pointcut("execution(* com.demo.dao.Student.*(..))")
public void selectAll(){}
/**
* 前置通知,在一个方法执行之前,执行通知
*/
@Before("selectAll()")
public void beforeAdvice(){
System.out.println("Going to setup student profile");
}
/**
* 后置通知,在一个方法执行之后,不考虑其结果,执行通知
*/
@After("selectAll()")
public void afterAdvice(){
System.out.println("Student profile has been setup");
}
/**
* 返回后通知,在一个方法执行之后,只有在方法成功完成时,才能执行通知。
*/
@AfterReturning(pointcut = "selectAll()",returning = "retVal")
public void afterReturningAdvice(Object retVal){
System.out.println("Returning:" + retVal.toString() );
}
/**
* 抛出异常后通知,在一个方法执行之后,只有在方法退出抛出异常时,才能执行通知
* @param ex
*/
@AfterThrowing(pointcut = "selectAll()",throwing = "ex")
public void afterThrowingAdvice(IllegalArgumentException ex){
System.out.println("There has been an exception: " + ex.toString());
}
}
import com.demo.dao.Student;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringTest {
public static void main(String[] args){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-config.xml");
Student student = (Student) applicationContext.getBean("student");
student.getName();
student.getAge();
student.printThrowException();
}
}
运行
Going to setup student profile
Name : 张三
Student profile has been setup
Returning:张三
Going to setup student profile
Age : 24
Student profile has been setup
Returning:24
Going to setup student profile
Exception raised
Student profile has been setup
There has been an exception: java.lang.IllegalArgumentException
...
以上难以理解,可以参考这篇,更通俗易懂
详解spring AOP