2.1AOP概述
什么是AOP?
AOP全称Aspect Oriented Programming,翻译过来就是:面向切面编程。
- AOP是一种编程的规范
- AOP最早由AOP联盟的组织提出的,制定了一套规范.Spring将AOP思想引入到框架中,必须遵守AOP联盟的规范
- 通过预编译方式或者运行期动态代理实现程序功能的统一维护的一种技术
- AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型
- 利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率
总结:AOP就是在不修改源代码的前提下,对程序的代码进行增强。
AOP优势
AOP是在运行期间在不修改源代码的情况下对代码进行增强的
AOP的底层原理
jdk动态代理技术:
1.为接口创建代理类的字节码文件
2.使用ClassLoader将字节码文件加载到jvm
3.创建代理类的实现对象执行目标方法
2.2 Spring的AOP技术-配置文件的形式
2.2.1 AOP相关术语
Joinpoint(连接点) 所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点
Pointcut(切入点) -- 所谓切入点是指我们要对哪些Joinpoint进行拦截的定义
Advice(通知/增强)-- 所谓通知是指拦截到Joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能)
Target(目标对象)-- 代理的目标对象
Weaving(织入)-- 是指把增强应用到目标对象来创建新的代理对象的过程
Proxy(代理)-- 一个类被AOP织入增强后,就产生一个结果代理类
Aspect(切面)-- 是切入点和通知的结合,以后咱们自己来编写和配置的
2.2.2 AOP配置文件的方式
创建项目,并导入依赖
<?xml version="1.0" encoding="UTF-8"?>
<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>org.example</groupId>
<artifactId>Spring03</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
<!--有单元测试的环境,Spring5版本,Junit4.12版本-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!--连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
<!--mysql驱动包-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<!-- Spring整合Junit测试的jar包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
<scope>test</scope>
</dependency>
<!-- AOP联盟 -->
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<!-- Spring Aspects -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!-- aspectj -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.3</version>
</dependency>
</dependencies>
</project>
创建Spring的配置文件,并导入约束
<?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>
创建包的结构
编写具体的接口和实现类
接口
/**
* 用户模块业务层的接口
*/
public interface UserService {
public void save();
}
接口的实现类
/**
* 用户模块业务层实现业务类
* 实现保存方法
*/
public class UserServiceImpl implements UserService{
@Override
public void save() {
System.out.println("保存方法执行了!!!");
}
}
<!-- 将UserServiceImpl交给Spring去管理 -->
<bean id="userService" class="com.qcby.demo1.UserServiceImpl"/>
<!-- 将切面类交给Spring去管理 -->
<bean id="myXmlAspect" class="com.qcby.demo1.MyXmlAspect"/>
<!-- AOP的配置 -->
<!--配置aop的增强-->
<aop:config>
<!-- MyXmlAspect被定义为切面类 -->
<!-- 配置切面=切入点+通知 -->
<aop:aspect ref="myXmlAspect">
<!-- execution() 固定写法
public 可以省略不写
方法的返回值类型 int String等 可以写* 但是不能不写
包名+类名 不能省略不写,可以写*
方法名
-->
<!-- <aop:before method="log" pointcut="execution(public void com.qcby.demo1.UserServiceImpl.save())"/>-->
<aop:after-returning method="log" pointcut="execution(public void com.qcby.demo1.UserServiceImpl.save())"/>
</aop:aspect>
</aop:config>
切面类
/**
* 定义一个自己的切面类
* 切面类=切入点(表达式)+通知(增强的代码)
*/
public class MyXmlAspect {
public void log(){
//发送短信、记录日志、事务管理
System.out.println("执行增强方法");
}
}
在配置文件中完成aop的配置
<!-- AOP的配置 -->
<!--配置aop的增强-->
<aop:config>
<!-- MyXmlAspect被定义为切面类 -->
<!-- 配置切面=切入点+通知 -->
<aop:aspect ref="myXmlAspect">
<!-- execution() 固定写法
public 可以省略不写
方法的返回值类型 int String等 可以写* 但是不能不写
包名+类名 不能省略不写,可以写*
方法名
-->
<!-- <aop:before method="log" pointcut="execution(public void com.qcby.demo1.UserServiceImpl.save())"/>-->
<aop:after-returning method="log" pointcut="execution(public void com.qcby.demo1.UserServiceImpl.save())"/>
</aop:aspect>
</aop:config>
编写测试用例
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:ApplicationContext.xml")
public class Demo1 {
@Autowired
private UserService userService;
@Test
public void run1(){
userService.save();
}
}
输出结果
2.2.3 切点表达式的写法
再配置切入点的时候,需要定义表达式,具体展开如下:
切入点表达式的格式如下:
· execution([修饰符] 返回值类型 包名.类名.方法名(参数))
· 修饰符可以省略不写,不是必须要出现的。
· 返回值类型是不能省略不写的,根据你的方法来编写返回值。可以使用 * 代替。
· 包名例如:com.tx.demo3.BookDaoImpl
o 首先com是不能省略不写的,但是可以使用 * 代替
o 中间的包名可以使用 * 号代替
o 如果想省略中间的包名可以使用 ..
· 类名也可以使用 * 号代替,也有类似的写法:*DaoImpl
· 方法也可以使用 * 号代替
参数如果是一个参数可以使用 * 号代替,如果想代表任意参数使用
2.3.4 通知的类型
-
前置通知 目标方法执行前,进行增强。
-
最终通知 目标方法执行成功或者失败,进行增强。
-
后置通知 目标方法执行成功后,进行增强。
-
异常通知 目标方法执行失败后,进行增强。
-
环绕通知 目标方法执行前后,都可以进行增强。目标对象的方法需要手动执行。
<aop:aspect ref="myXmlAspect">
aop:before method="log" pointcut="execution(* com.qcbyjy..ServiceImpl.save(..))" />
最终通知:目标方法执行成功或者失败,进行增强。
<aop:after method="log" pointcut="execution( com.qcbyjy..ServiceImpl.save(..))" />
后置通知:目标方法执行成功后,进行增强。
<aop:after-returning method="log" pointcut="execution( com.qcbyjy..ServiceImpl.save(..))" />
异常通知:目标方法执行失败后,进行增强。
<aop:after-throwing method="log" pointcut="execution( com.qcbyjy..ServiceImpl.save(..))" />
环绕通知:目标方法执行前后,都可以进行增强。目标对象的方法需要手动执行。
<aop:around method="aroundLog" pointcut="execution( com.qcbyjy.*.ServiceImpl.save(..))" />
</aop:aspect>
</aop:config>