Spring框架(六)—— Spring AOP

一、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();
		}
	}
}
发布了75 篇原创文章 · 获赞 10 · 访问量 2869

猜你喜欢

转载自blog.csdn.net/baidu_27414099/article/details/104440554