文章目录
一、Spring AOP
1、Spring AOP 概述
(1)AOP(Aspect Oriented Programming),即面向切面编程,AOP是对OOP(Object Oriented Programming,面向对象编程)的补充和完善。
(2)OOP允许开发者定义纵向的关系,但并不适合定义横向的关系,例如日志功能。日志代码往往横向地散布在所有对象层次中,而与它对应的对象的核心功能毫无关系。对于其他类型的代码,如安全性、异常处理、事务管理等等,在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。
(3)AOP技术恰恰相反,它利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。
如下图:
没有使用AOP之前:
使用AOP之后:
2、AOP 核心概念
(1)通知 Advice:
- 定义:通知指的就是指拦截到连接点之后要执行的代码(工作),在 AOP 术语中,切面的工作被称为通知。
- 工作内容:通知定义了切面是什么以及何时使用。除了描述切面要完成的工作,通知还解决何时执行这个工作。
- Spring 的 5 种通知类型:
- 前置Before——在方法调用之前调用通知
- 后置After-returning——在方法执行成功之后调用通知
- 异常After-throwing——在方法抛出异常后进行通知
- 最终After——在方法完成之后调用通知,无论方法执行成功与否
- Around——通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为
(2)连接点 JointPoint:
- 定义:连接点是一个应用执行过程中能够插入一个切面的点,连接点代表了时间,也就是何时去执行。
- 程序执行过程中能够应用通知的所有点。
(3)切点 Pointcut:
- 定义:切点表示一组连接点 JointPoint,如果通知定义了“什么”和“何时”。那么切点就定义了“何处”。
- 通常使用明确的类或者方法来指定这些切点。
- 作用:定义通知被应用的位置(在哪些连接点)
(4)切面 Aspect:
- 定义:切面是通知和切点的集合,通知和切点共同定义了切面的全部功能——它是什么,在何时何处完成其功能。
切面声明类似于 Java 中的类声明,在 Aspect 中会包含着一些 Pointcut 以及相应的 Advice。切面就是对何时做什么事情的抽象。
(5)引入:
- 引入允许我们向现有的类中添加方法或属性
(6)织入 weave:
- 织入是将切面应用到目标对象来创建的代理对象过程。
- 切面在指定的连接点被织入到目标对象中,在目标对象的生命周期中有多个点可以织入
- 编译期——切面在目标类编译时期被织入,这种方式需要特殊编译器。AspectJ的织入编译器就是以这种方式织入切面。
- 类加载期——切面在类加载到
- JVM ,这种方式需要特殊的类加载器,他可以在目标类被引入应用之前增强该目标类的字节码。AspectJ5 的 LTW 就支持这种织入方式
- 运行期——切面在应用运行期间的某个时刻被织入。一般情况下,在织入切面时候,AOP 容器会为目标对象动态的创建代理对象。Spring AOP 就是以这种方式织入切面。
二、AOP 的实现方式
1、Spring 的原生AOP实现方式
(1)定义业务层接口和业务层实现类
① 接口层
public interface ProductsService {
public void insert() throws Exception;
}
② 实现层
public class ProductsServiceImp implements ProductsService{
@Override
public void insert() throws Exception {
System.out.println("执行新增业务操作");
}
}
(2)定义通知类
public class TransactionAdvice implements MethodBeforeAdvice,AfterReturningAdvice{
@Override
public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable {
//后置增强
System.out.println("执行后置增强");
}
@Override
public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {
//前置增强
System.out.println("执行前置增强");
}
}
重要API:
MethodBeforeAdvice
:前置增强接口,会在代理对象执行目标对象方法之前执行AfterReturningAdvice
:后置增强接口,会在代理对象执行目标对象方法之后执行MethodInterceptor
:方法拦截器,目标对象的方法被嵌入到该类的方法中,所以可以更灵活的实现各种不同的增强处理
(3)在 applicationContext.xml 中完成AOP配置
<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context" 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" xmlns:p="http://www.springframework.org/schema/p" xmlns:util="http://www.springframework.org/schema/util" xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:cache="http://www.springframework.org/schema/cache"
xsi:schemaLocation="
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-4.0.xsd">
<!-- 创建要被代理的目标对象 -->
<bean id="service" class="com.spring.demo5_aop.service.imp.ProductsServiceImp"></bean>
<!-- 创建通知类对象 -->
<bean id="transactionAdvice" class="com.spring.demo5_aop.advice.TransactionAdvice"></bean>
<!-- 定义切入点 -->
<bean id="pointCut" class="org.springframework.aop.support.JdkRegexpMethodPointcut">
<property name="pattern" value="com.spring.demo5_aop.service.ProductsService.insert"></property>
</bean>
<!-- 配置切面,描述何时做什么事情 -->
<bean id="transAspect" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="advice" ref="transactionAdvice"></property>
<property name="pointcut" ref="pointCut"></property>
</bean>
<!-- 创建动态代理对象 -->
<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 目标对象 -->
<property name="target" ref="service"></property>
<!-- 使用切面 -->
<property name="interceptorNames" value="transAspect"></property>
<!-- 代理类需要实现的接口 -->
<property name="proxyInterfaces" value="com.spring.demo5_aop.service.ProductsService"></property>
</bean>
</beans>
- 解析xml文件获取代理对象调用方法
在上面的创建动态代理对象是手动创建,也可以使用下面的代码替换手动创建动态代理对象的那一段代码,从而实现在 xml 文件中使用自动动态代理构建器,Spring 会检查切入点与 bean 的关系,在创建对象时自动生成代理对象。
<!-- 自动动态代理构建器 -->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"></bean>
(4)测试类
public class Test {
@SuppressWarnings("resource")
public static void main(String[] args) {
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
ProductsService service = (ProductsService) ac.getBean("proxy"); //注意这里是"proxy"
System.out.println(service);
try {
service.insert();
} catch (Exception e) {
e.printStackTrace();
}
}
}
2、整合 Aspect 框架实现AOP(使用xml文件)
(1)定义业务层接口和业务层实现类
① 接口层
public interface ProductsService {
public void insert() throws Exception;
}
② 实现层
public class ProductsServiceImp implements ProductsService{
@Override
public void insert() throws Exception {
System.out.println("执行新增业务操作");
}
}
(2)定义通知类,该通知类不需要实现任何接口或者继承任何类,方法名称完全自定义
public class TransactionAdvice {
public void start() {
System.out.println("开启事务");
}
public void commit() {
System.out.println("提交事务");
}
public void rollback() {
System.out.println("回滚事务");
}
public void close() {
System.out.println("关闭事务");
}
//获取代理对象调用方法
public Object around(ProceedingJoinPoint pj) {
Object result=null;
try {
start();
result=pj.proceed(pj.getArgs());
commit();
} catch (Throwable e) {
rollback();
}finally {
close();
}
return result;
}
}
ProceedingJoinPoint在
的依赖包
(3)在 applicationContext.xml 文件中完成织入(通过切入点、通知、目标对象创建代理对象的过程)
① 创建通知、目标对象
<!-- 创建要被代理的目标对象 -->
<bean id="service" class="com.spring.demo5_aop.service.imp.ProductsServiceImp"></bean>
<!-- 创建通知类对象 -->
<bean id="transactionAdvice" class="com.spring.demo5_aop.advice.TransactionAdvice"></bean>
- 也可以使用注解,在实现层和通知类添加注解,然后采用下面的配置代码替代上面两个配置
<context:component-scan base-package="com.spring.demo5_aop"></context:component-scan>
② 配置切入点
<aop:config>
//用于说明当前所织入的代理对象中通知类的对象
<aop:aspect ref="transactionAdvice"> //ref=”引用通知类的对象”
//用于描述切入点
<aop:pointcut expression="execution(* com.spring.demo5_aop.service.imp.*.*(..))" id="point"/>
//执行目标方法前置增强处理
<aop:before method="start" pointcut-ref="point"/>
//执行目标方法后异常增强处理
<aop:after-returning method="commit" pointcut-ref="point"/>
//正常执行目标方法后增强处理
<aop:after-throwing method="rollback" pointcut-ref="point"/>
//无论正常还是异常后置增强处理
<aop:after method="close" pointcut-ref="point"/>
</aop:aspect>
</aop:config>
- 为了完成上述功能可以也可以采用环绕式增强处理,不能再配置环绕增强的同时添加其他类型的增强方式。
<aop:config>
<aop:aspect ref="transactionAdvice">
<aop:pointcut expression="execution(* com.woniu.service.*.*(..))" id="point"/>
<aop:around method="around" pointcut-ref="point"/>
</aop:aspect>
</aop:config>
(4)测试类
public class Test {
@SuppressWarnings("resource")
public static void main(String[] args) {
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("spring5_1.xml");
ProductsService service = (ProductsService) ac.getBean("service");
System.out.println(service);
try {
service.insert();
} catch (Exception e) {
e.printStackTrace();
}
}
}
3、整合 Aspect 框架实现AOP(使用注解)
(1)定义业务层接口和业务层实现类
① 接口层
public interface ProductsService {
public void insert() throws Exception;
}
② 实现层
@Service
public class ProductsServiceImp implements ProductsService{
@Override
public void insert() throws Exception {
System.out.println("执行新增业务操作");
}
}
(2)定义通知类,并在通知类上添加注解,描述那些切入点应该执行哪种增强处理
@Component
@Aspect
public class TransactionAdvice {
@Before("execution(* com.spring.demo5_aop.service.imp.*.*(..))")
public void start() {
System.out.println("开启事务");
}
@AfterReturning("execution(* com.spring.demo5_aop.service.imp.*.*(..))")
public void commit() {
System.out.println("提交事务");
}
@AfterThrowing("execution(* com.spring.demo5_aop.service.imp.*.*(..))")
public void rollback() {
System.out.println("回滚事务");
}
@After("execution(* com.spring.demo5_aop.service.imp.*.*(..))")
public void close() {
System.out.println("关闭事务");
}
public Object around(ProceedingJoinPoint pj) {
Object result=null;
try {
start();
result=pj.proceed(pj.getArgs());
commit();
} catch (Throwable e) {
rollback();
}finally {
close();
}
return result;
}
}
或者
@Component
@Aspect
public class TransactionAdvice {
public void start() {
System.out.println("开启事务");
}
public void commit() {
System.out.println("提交事务");
}
public void rollback() {
System.out.println("回滚事务");
}
public void close() {
System.out.println("关闭事务");
}
@Around("execution(* com.spring.demo5_aop.service.imp.*.*(..))") //注意这里
public Object around(ProceedingJoinPoint pj) {
Object result=null;
try {
start();
result=pj.proceed(pj.getArgs());
commit();
} catch (Throwable e) {
rollback();
}finally {
close();
}
return result;
}
}
(3)在 applicationContext.xml 中开启aop注解自动代理方式,并同时扫描包。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
<context:component-scan base-package="com.spring.demo5_aop"></context:component-scan>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
(4)测试类
public class Test {
@SuppressWarnings("resource")
public static void main(String[] args) {
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
ProductsService service = (ProductsService) ac.getBean("productsServiceImp");
System.out.println(service);
try {
service.insert();
} catch (Exception e) {
e.printStackTrace();
}
}
}