AOP
aop概念
首先定义一些重要的 AOP 概念和术语。这些术语不是特定于 Spring 的…不幸的是,AOP 术语不是特别直观。但是,如果使用 Spring 自己的术语,将会更加令人困惑。
Aspect *:关注点的模块化,跨多个类。事务 Management 是企业 Java 应用程序中横切关注的一个很好的例子。在 Spring AOP 中,方面是使用常规类(schema-based approach)或使用@Aspect注解(@AspectJ style)Comments 的常规类实现的。
连接点:程序执行期间的一个点,例如方法执行或异常处理。在 Spring AOP 中,连接点“总是”表示方法的执行。
忠告:方面在特定的连接点处采取的操作。不同类型的建议包括“周围”,“之前”和“之后”建议。 (建议类型将在下面讨论.)包括 Spring 在内的许多 AOP 框架都将建议建模为* interceptor ,并在连接点附近保持一系列拦截器。
Pointcut *:匹配连接点的谓词。建议与切入点表达式关联,并在与该切入点匹配的任何连接点处运行(例如,执行具有特定名称的方法)。切入点表达式匹配的连接点的概念是 AOP 的核心,并且 Spring 默认使用 AspectJ 切入点表达语言。
简介:代表类型声明其他方法或字段。 Spring AOP 允许您向任何建议对象引入新接口(和相应的实现)。例如,您可以使用简介使 Bean 实现IsModified接口,以简化缓存。 (在 AspectJ 社区中,介绍被称为类型间声明.)
目标对象:一个或多个方面建议的对象。也称为“建议”对象。由于 Spring AOP 是使用运行时代理实现的,因此该对象将始终是代理对象。
AOP 代理*:由 AOP 框架创建的一个对象,用于实现方面协定(建议方法执行等)。在 Spring 框架中,AOP 代理将是 JDK 动态代理或 CGLIB 代理。
编织:将方面与其他应用程序类型或对象链接以创建建议的对象。这可以在编译时(例如,使用 AspectJ 编译器),加载时或在运行时完成。像其他纯 Java AOP 框架一样,Spring AOP 在运行时执行编织。
咨询类型:
Before advisor *:在连接点之前执行的建议,但是它不能阻止执行流前进到连接点(除非它引发异常)。
返回建议之后:连接点正常完成后要执行的建议:例如,如果某个方法返回而没有引发异常。
抛出建议后:如果方法因抛出异常而退出,则执行建议。
(最终)建议之后:无论连接点退出的方式如何(正常或特殊返回),都将执行建议。
围绕建议:围绕连接点的建议,例如方法调用。这是最有力的建议。周围建议可以在方法调用之前和之后执行自定义行为。它还负责选择是返回连接点还是通过返回其自身的返回值或引发异常来捷径建议的方法执行。
围绕建议是最通用的建议。由于 Spring AOP 与 AspectJ 一样,提供了各种建议类型,因此我们建议您使用功能最弱的建议类型,以实现所需的行为。例如,如果您只需要使用方法的返回值更新缓存,则最好使用返回后的建议而不是周围的建议,尽管周围的建议可以完成相同的事情。使用最具体的建议类型可以提供更简单的编程模型,并减少出错的可能性。例如,您不需要在用于周围建议的JoinPoint上调用proceed()方法,因此不会失败。
在 Spring 2.0 中,所有建议参数都是静态类型的,因此您可以使用适当类型的建议参数(例如,从方法执行返回的值的类型)而不是Object数组。
连接点的概念以及切入点是 AOP 的关键,它使 AOP 与仅提供拦截功能的旧技术区分开。切入点使建议的目标独立于面向对象的层次结构。例如,提供声明式事务 Management 的环绕建议可以应用于跨越多个对象(例如服务层中的所有业务操作)的一组方法。
AOP核心概念
1、横切关注点
对哪些方法进行拦截,拦截后怎么处理,这些关注点称之为横切关注点
2、切面(aspect)
类是对物体特征的抽象,切面就是对横切关注点的抽象
3、连接点(joinpoint)
被拦截到的点,因为Spring只支持方法类型的连接点,所以在Spring中连接点指的就是被拦截到的方法,实际上连接点还可以是字段或者构造器
4、切入点(pointcut)
对连接点进行拦截的定义
5、通知(advice)
所谓通知指的就是指拦截到连接点之后要执行的代码,通知分为前置、后置、异常、最终、环绕通知五类
6、目标对象
代理的目标对象
7、织入(weave)
将切面应用到目标对象并导致代理对象创建的过程
8、引入(introduction)
在不修改代码的前提下,引入可以在运行期为类动态地添加一些方法或字段
Spring对AOP的支持
Spring中AOP代理由Spring的IOC容器负责生成、管理,其依赖关系也由IOC容器负责管理。因此,AOP代理可以直接使用容器中的其它bean实例作为目标,这种关系可由IOC容器的依赖注入提供。Spring创建代理的规则为:
1、默认使用Java动态代理来创建AOP代理,这样就可以为任何接口实例创建代理了
2、当需要代理的类不是代理接口的时候,Spring会切换为使用CGLIB代理,也可强制使用CGLIB
AOP编程其实是很简单的事情,纵观AOP编程,程序员只需要参与三个部分:
1、定义普通业务组件
2、定义切入点,一个切入点可能横切多个业务组件
3、定义增强处理,增强处理就是在AOP框架为普通业务组件织入的处理动作
所以进行AOP编程的关键就是定义切入点和定义增强处理,一旦定义了合适的切入点和增强处理,AOP框架将自动生成AOP代理,即:代理对象的方法=增强处理+被代理对象的方法。
Spring中aop实现案例(基于xml实现)
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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
</beans>
如果是第一次引入spring的xml文件,可能会爆红
解决办法:
在setting-schemas中年添加链接
aop需要引入的依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.20.RELEASE</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.28</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.21.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
</dependencies>
实现代码
Dao
package com.wcr.dao;
import org.springframework.stereotype.Component;
public class TeacherDao {
public void Test(){
System.out.println("this is a test");
}
}
Service
package com.wcr.service;
import com.wcr.dao.TeacherDao;
public class TeacherService implements TeacherApi{
private TeacherDao teacherDao;
public void setTeacherDao(TeacherDao teacherDao) {
this.teacherDao = teacherDao;
}
public void test(){
teacherDao.Test();
}
}
这里需要实现接口,实现接口之后他会自动使用jdk的代理生成一个代理的bean注入,之后getBean强转出来的bean也是该接口类型的bean
package com.wcr.service;
public interface TeacherApi {
void test();
}
需要一个类提供增强的方法,并且在xml文件中进行配置
package com.wcr.advice;
public class TeacherAdvice {
public void before(){
System.out.println("this is before");
}
public void after(){
System.out.println("this is after");
}
}
controller测试
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TeacherController {
public void test(){
//实例化容器
ApplicationContext context = new ClassPathXmlApplicationContext("spring-comment.xml");
TeacherApi teacherService = (TeacherApi) context.getBean("teacherService");
teacherService.test();
}
public static void main(String[] args) {
new TeacherController().test();
}
}
运行结果: