要谈AOP,那么AOP究竟是什么呢?AOP即面向切面编程,相比OOP--面向对象编程,因为面向对象中最基本的单位是类,实例
,很天然咱们会想到AOP中最基本的单位可能就是所谓的切面
了,你可能会问,那切面
又是个什么东西,我想说,如今不懂不要紧,下面我会讲到。咱们先来看一段Spring
中关于AOP的定义:spring
面向切面——Spring提供了面向切面编程的丰富支持,容许经过分离应用的业务逻辑与系统级服务(例如审计(auditing)和事务(transaction)管理)进行内聚性的开发。应用对象只实现它们应该作的——完成业务逻辑——仅此而已。它们并不负责(甚至是意识)其它的系统级关注点,例如日志或事务支持。express
上面谈到,AOP能够分离系统的业务逻辑和系统服务(日志,安全等),这个功能我想是不难明白(原理是使用了代理模式
),但关键是为何要将这两种进行分离呢?或者说这样作有什么好处?apache
在平常的软件开发中,拿日志来讲,一个系统软件的开发都是必须进行日志记录的,否则万一系统出现什么bug,你都不知道是哪里出了问题。举个小栗子,当你开发一个登录功能,你可能须要在用户登录先后进行权限校验并将校验信息(用户名
,密码
,请求登录时间,ip地址
等)记录在日志文件中,当用户登陆进来以后,当他访问某个其余功能时,也须要进行合法性校验。想一想看,当系统很是地庞大,系统中专门进行权限验证的代码是很是多的,并且很是地散乱,咱们就想能不能将这些权限校验、日志记录等非业务逻辑功能的部分独立拆分开,而且在系统运行时须要的地方(链接点
)进行动态插入运行,不须要的时候就不理,所以AOP是可以解决这种情况的思想吧!编程
下图就很直观地展现这个过程:缓存
不得不说,AOP的概念是真的多且难以理解,不过不用担忧,聪明的你已经准备好打败它们了。安全
通知有5种类型:bash
Before
在方法被调用以前调用框架
After
在方法完成后调用通知,不管方法是否执行成功eclipse
After-returning
在方法成功执行以后调用通知maven
After-throwing
在方法抛出异常后调用通知
Around
通知了好、包含了被通知的方法,在被通知的方法调用以前后调用以后执行自定义的行为
咱们可能会问,那通知对应系统中的代码是一个方法、对象、类、仍是接口什么的呢?我想说一点,其实都不是,你能够理解通知就是对应咱们平常生活中所说的通知,好比‘某某人,你2019年9月1号来学校报个到’,通知更多地体现一种告诉咱们(告诉系统何)什么时候执行,规定一个时间,在系统运行中的某个时间点(好比抛异常啦!方法执行前啦!),并不是对应代码中的方法!并不是对应代码中的方法!并不是对应代码中的方法!
切点(Pointcut)
哈哈,这个你可能就比较容易理解了,切点在Spring AOP中确实是对应系统中的方法。可是这个方法是定义在切面中的方法,通常和通知一块儿使用,一块儿组成了切面。
链接点(Join point)
好比:方法调用、方法执行、字段设置/获取、异常处理执行、类初始化、甚至是 for 循环中的某个点
理论上, 程序执行过程当中的任什么时候点均可以做为做为织入点, 而全部这些执行时点都是 Joint point
但 Spring AOP 目前仅支持方法执行 (method execution) 也能够这样理解,链接点就是你准备在系统中执行切点和切入通知的地方(通常是一个方法,一个字段)
切面(Aspect)
切面是切点和通知的集合,通常单独做为一个类。通知和切点共同定义了关于切面的所有内容,它是何时,在什么时候和何处完成功能。
引入(Introduction)
引用容许咱们向现有的类添加新的方法或者属性
织入(Weaving)
组装方面来建立一个被通知对象。这能够在编译时完成(例如使用AspectJ编译器),也能够在运行时完成。Spring和其余纯Java AOP框架同样,在运行时完成织入。
首先AOP思想的实现通常都是基于代理模式,在JAVA中通常采用JDK动态代理模式,可是咱们都知道,JDK动态代理模式只能代理接口,若是要代理类那么就不行了。所以,Spring AOP 会这样子来进行切换,由于Spring AOP 同时支持 CGLIB、ASPECTJ、JDK动态代理,当你的真实对象有实现接口时,Spring AOP会默认采用JDK动态代理,不然采用cglib代理。
话说饼干能解渴吗?
链接点
package wokao666.club.aop.spring01;
public interface Subject {
//登录
public void login();
//下载
public void download();
}
复制代码
package wokao666.club.aop.spring02;
import wokao666.club.aop.spring01.Subject;
public class SubjectImpl implements Subject {
public void login() {
System.err.println("借书中...");
}
public void download() {
System.err.println("下载中...");
}
}
复制代码
package wokao666.club.aop.spring01;
import org.aspectj.lang.JoinPoint;
public class PermissionVerification {
/**
* 权限校验
* @param args 登录参数
*/
public void canLogin() {
//作一些登录校验
System.err.println("我正在校验啦!!!!");
}
/**
* 校验以后作一些处理(不管是否成功都作处理)
* @param args 权限校验参数
*/
public void saveMessage() {
//作一些后置处理
System.err.println("我正在处理啦!!!!");
}
}
复制代码
SpringAOP.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"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd">
<bean id="SubjectImpl1" class="wokao666.club.aop.spring02.SubjectImpl" />
<bean id="SubjectImpl2" class="wokao666.club.aop.spring02.SubjectImpl" />
<bean id="PermissionVerification" class="wokao666.club.aop.spring01.PermissionVerification" />
<aop:config>
<!-- 这是定义一个切面,切面是切点和通知的集合-->
<aop:aspect id="do" ref="PermissionVerification">
<!-- 定义切点 ,后面是expression语言,表示包括该接口中定义的全部方法都会被执行-->
<aop:pointcut id="point" expression="execution(* wokao666.club.aop.spring01.Subject.*(..))" />
<!-- 定义通知 -->
<aop:before method="canLogin" pointcut-ref="point" />
<aop:after method="saveMessage" pointcut-ref="point" />
</aop:aspect>
</aop:config>
</beans>
复制代码
pom.xml
文件<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>wokao666.club</groupId>
<artifactId>aop</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>aop</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring.version>4.3.0.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
<version>4.10</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.4</version>
</dependency>
</dependencies>
</project>
复制代码
package wokao666.club.aop.spring01;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
ApplicationContext ctx =
new ClassPathXmlApplicationContext("SpringAOP.xml");
Subject subject1 = (Subject)ctx.getBean("SubjectImpl1");
Subject subject2 = (Subject)ctx.getBean("SubjectImpl2");
subject1.login();
subject1.download();
System.err.println("==================");
subject1.login();
subject1.download();
}
}
复制代码
三月 13, 2018 4:59:44 下午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@31cefde0: startup date [Tue Mar 13 16:59:44 CST 2018]; root of context hierarchy
三月 13, 2018 4:59:45 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [SpringAOP.xml]
我正在校验啦!!!!
借书中...
我正在处理啦!!!!
我正在校验啦!!!!
下载中...
我正在处理啦!!!!
==================
我正在校验啦!!!!
借书中...
我正在处理啦!!!!
我正在校验啦!!!!
下载中...
我正在处理啦!!!!
复制代码
我想,上面的实现方式只是皮毛而已啦!若是想要深刻学习,那么能够看下JDK动态代理的源码分析(里边很是地巧妙,还运用了二级缓存,原本我想写出来的,就留给下一篇文吧!)、Spring AOP源码分析等等,固然上面的实现方式是基于老式的xml文件,不推荐咱们这样作,为何?我以为当你系统大了以后,那系统中将会有不少的bean
、xml
文件,这不容易维护和管理,我建议采用基于注解的方式进行AOP编程会更好!
好了,原本想结束的,可是在关掉eclipse前发现了一个小问题,既然切点是须要执行的方法,那么咱们如何获取链接点的数据(或者说是校验参数呢?),这里推荐一篇博文,我就不写啦!小伙伴们,一块儿加油吧!