1.2 AOP相关的概念
1.2.1 AOP的概述
什么是AOP的技术?
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程
- AOP是一种编程范式,隶属于软工范畴,指导开发者如何组织程序结构
- AOP最早由AOP联盟的组织提出的,制定了一套规范.Spring将AOP思想引入到框架中,必须遵守AOP联盟的规范
- 通过预编译方式或者运行期动态代理实现程序功能的统一维护的一种技术
- AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型
- 利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率
AOP:面向切面编程.(思想.—解决OOP遇到一些问题)
AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码(性能监视、事务管理、安全检查、缓存)
为什么要学习AOP,可以在不修改源代码的前提下,对程序进行增强!!
1.2.2 AOP的优势
运行期间,不修改源代码的情况下对已有的方法进行增强
优势
- 减少重复的代码
- 提供开发的效率
- 维护方便
1.2.3 AOP的底层原理
- JDK的动态代理技术
- 为接口创建代理类的字节码文件
- 使用ClassLoader将字节码文件加载到JVM
- 创建代理类实例对象,执行对象的目标方法
- cglib代理技术
1.3 Spring的AOP技术-配置文件方式
1.3.1 AOP相关的术语
- Joinpoint(连接点) 所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点
- Pointcut(切入点) – 所谓切入点是指我们要对哪些Joinpoint进行拦截的定义
- Advice(通知/增强)-- 所谓通知是指拦截到Joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能)
- Target(目标对象)-- 代理的目标对象
- Weaving(织入)-- 是指把增强应用到目标对象来创建新的代理对象的过程
- Proxy(代理)-- 一个类被AOP织入增强后,就产生一个结果代理类
- Aspect(切面)-- 是切入点和通知的结合,以后咱们自己来编写和配置的
1.3.2 切入点的表达式
再配置切入点的时候,需要定义表达式,具体展开如下:
切入点表达式的格式如下:
- execution([修饰符] 返回值类型 包名.类名.方法名(参数))
- 修饰符可以省略不写,不是必须要出现的。
- 返回值类型是不能省略不写的,根据你的方法来编写返回值。可以使用 * 代替。
- 包名例如:com.tx.demo3.BookDaoImpl
- 首先com是不能省略不写的,但是可以使用 * 代替
- 中间的包名可以使用 * 号代替
- 如果想省略中间的包名可以使用 …
- 类名也可以使用 * 号代替,也有类似的写法:*DaoImpl
- 方法也可以使用 * 号代替
- 参数如果是一个参数可以使用 * 号代替,如果想代表任意参数使用 …
1.3.3 AOP的通知类型
- 前置通知 目标方法执行前,进行增强。
- 最终通知 目标方法执行成功或者失败,进行增强。
- 后置通知 目标方法执行成功后,进行增强。
- 异常通知 目标方法执行失败后,进行增强。
- 环绕通知 目标方法执行前后,都可以进行增强。目标对象的方法需要手动执行。
示例:
<?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">
<bean id = "us" class = "com.qcby.demo1.UserServiceImpl"/>
<bean id = "myXmlAspect" class = "com.qcby.demo1.MyXmlAspect"/>
<aop:config>
<aop:aspect ref = "myXmlAspect">
<!--前置通知:UserServiceImpl的save方法执行前,会增强-->
<aop:before method="log" pointcut="execution(public * com.qcby.demo1.UserServiceImpl.save())"/>
<!--后置通知-->
<aop:after-returning method="afterReturning" pointcut="execution(public * com.qcby.demo1.UserServiceImpl.save())"/>
<!--异常通知-->
<aop:after-throwing method="afterThrowing" pointcut="execution(public * com.qcby.demo1.UserServiceImpl.save())"/>
<!--最终通知-->
<aop:after method="after" pointcut="execution(public * com.qcby.demo1.UserServiceImpl.save())"/>
</aop:aspect>
</aop:config>
</beans>
切面类MyXmlAspect.java:
package com.qcby.demo1;
/**
*
*自定义切面类 = 切入点(表达式) + 通知(增强的代码)
*/
public class MyXmlAspect {
/**
* 前置通知
*/
public void log(){
// 发送手机短信
// 发送邮件/记录日志/事务管理
System.out.println("前置增强的方法执行了...");
}
/**
* 最终通知
*/
public void after(){
System.out.println("最终通知的方法...");
}
/**
* 后置通知
*/
public void afterReturning(){
System.out.println("后置通知的方法...");
}
/**
* 异常通知
*/
public void afterThrowing(){
System.out.println("异常通知的方法...");
}
}
运行结果:
环绕通知:
<!--环绕通知-->
<aop:around method="around" pointcut="execution(public * com.qcby.demo1.UserServiceImpl.save())"/>
/**
* 环绕通知
*/
public void around(ProceedingJoinPoint joinPoint){
try {
// 前置与后置,相当于开始事务与提交事务
System.out.println("前置增强的方法执行了...");
// 在这里需要手动执行目标对象的方法
joinPoint.proceed();
System.out.println("后置通知的方法...");
} catch (Throwable throwable){
throwable.printStackTrace();
// 出现异常
System.out.println("异常通知的方法...");
} finally {
// 无论如何都要执行
System.out.println("最终通知的方法...");
}
}
1.4 Spring的AOP技术-注解方式
1.4.1 注解+配置文件的方式
MyAnnoAspect.java:
package com.qcby.demo2;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
/**
* 切面类
*/
@Component // 普通类交给Spring管理
@Aspect // 切面类
public class MyAnnoAspect {
/**
* 通知的方法
* @Before
* @AfterReturning
* @AfterThrowing
* @After()
* @Around()
*/
@Before("execution(public void com.qcby.demo2.OrderServiceImpl.save())")
public void log(){
System.out.println("前置通知的方法...");
}
}
beans_demo2.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">
<!--开启注解扫描-->
<context:component-scan base-package="com.qcby.demo2"/>
<!--开启自动代理-->
<aop:aspectj-autoproxy/>
</beans>
DemoTest2.java:
package com.qcby.demo;
import com.qcby.demo2.OederService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
// 配置文 classpath
@ContextConfiguration("classpath:beans_demo2.xml")
// 配置类 classes
//@ContextConfiguration(classes=SpringConfig.class)
public class DemoTest2 {
@Autowired
private OederService oederService;
/**
* 测试
* 注解+配置文件
*/
@Test
public void run(){
oederService.save();
}
}
1.4.2 纯注解的方式
配置类SpringConfig.java:
package com.qcby.demo2;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration // 配置类
@ComponentScan(value = "com.qcby.demo2") // 扫描包
@EnableAspectJAutoProxy // 开启自动代理 == <aop:aspectj-autoproxy />
public class SpringConfig {
}