Spring AOP 详解与实现

AOP的概念

AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。

AOP,可以说是OOP(Object-Oriented Programing,面向对象编程)的补充和完善。OOP引入封装、继承和多态性等概念来建立一种对象层次结构,用以模拟公共行为的一个集合。当我们需要为分散的对象引入公共行为的时候,OOP则显得无能为力。也就是说,OOP允许你定义从上到下的关系,但并不适合定义从左到右的关系。例如日志功能。日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系。对于其他类型的代码,如安全性、异常处理和透明的持续性也是如此。这种散布在各处的无关的代码被称为横切(cross-cutting)代码,在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。

而AOP技术则恰恰相反,它利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其名为“Aspect”,即方面。所谓“方面”,简单地说,就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。AOP代表的是一个横向的关系,如果说“对象”是一个空心的圆柱体,其中封装的是对象的属性和行为;那么面向方面编程的方法,就仿佛一把利刃,将这些空心圆柱体剖开,以获得其内部的消息。而剖开的切面,也就是所谓的“方面”了。然后它又以巧夺天功的妙手将这些剖开的切面复原,不留痕迹。

AOP基础知识

  • 横切关注点:跨越应用程序多个模块的方法或功能。虽然与我们业务逻辑无关的,但是我们需要关注的部分,如日志 , 安全 , 缓存 , 事务等等 …
  • 切面(ASPECT):横切关注点 被模块化 的特殊对象。即,它是一个类。
  • 通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。
  • 目标(Target):被通知对象。
  • 代理(Proxy):向目标对象应用通知之后创建的对象
  • 切入点(PointCut):切面通知 执行的 “地点”的定义。
  • 连接点(JointPoint):与切入点匹配的执行点。

SpringAOP中,通过Advice定义横切逻辑,Spring中支持5种类型的Advice:

  • before advice, 在 join point 前被执行的 advice.
  • after return advice, 在一个 join point 正常返回后执行的 advice
  • after throwing advice, 当一个 join point 抛出异常后执行的 advice
  • after(final) advice, 无论一个 join point 是正常退出还是发生了异常, 都会被执行的 advice.
  • around advice, 在 join point 前和 joint point 退出后都执行的 advice. 这个是最常用的 advice.
  • introduction,introduction可以为原有的对象增加新的属性和方法。

即 Aop 在 不改变原有代码的情况下 , 去增加新的功能 .

Spring实现AOP

前置要求:实现AOP需要导入一个依赖包

<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>

Spring API 实现

首先编写业务接口和实现类

public interface UserService {
	public void add();
	public void delete();
	public void update();
	public void search();
}

public class UserServiceImpl implements UserService{
	@Override
	public void add() {
		System.out.println("增加用户");
	}
	@Override
	public void delete() {
		System.out.println("删除用户");
	}
	@Override
	public void update() {
		System.out.println("更新用户");
	}
	@Override
	public void search() {
		System.out.println("查询用户");
	}
}

编写通知(增强)类, 我们编写两个 , 一个前置通知 一个后置通知

public class Log implements MethodBeforeAdvice {
//method : 要执行的目标对象的方法
//objects : 被调用的方法的参数
//Object : 目标对象
	@Override
	public void before(Method method, Object[] objects, Object o) throws
	Throwable {
		System.out.println( o.getClass().getName() + "的" + method.getName()
		+ "方法被执行了");
	}
}

public class AfterLog implements AfterReturningAdvice {
//returnValue 返回值
//method被调用的方法
//args 被调用的方法的对象的参数
//target 被调用的目标对象
	@Override
	public void afterReturning(Object returnValue, Method method, Object[]
args, Object target) throws Throwable {
		System.out.println("执行了" + target.getClass().getName()
		+"的"+method.getName()+"方法,"
		+"返回值:"+returnValue);
	}
}

最后去spring的文件中注册 , 并实现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: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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
	<!--注册bean-->
	<bean id="userService" class="com.kuang.service.UserServiceImpl"/>
	<bean id="log" class="com.kuang.log.Log"/>
	<bean id="afterLog" class="com.kuang.log.AfterLog"/>
	<!--aop的配置-->
	<aop:config>
		<!--切入点 expression:表达式匹配要执行的方法-->
		<aop:pointcut id="pointcut" expression="execution(*
		com.kuang.service.UserServiceImpl.*(..))"/>
		<!--执行环绕; advice-ref执行方法 . pointcut-ref切入点-->
		<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
		<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
	</aop:config>
</beans>

测试

public class MyTest {
@Test
	public void test(){
		ApplicationContext context = new
		ClassPathXmlApplicationContext("beans.xml");
		UserService userService = (UserService)
		context.getBean("userService");
		userService.search();
}
}

自定义切面类实现

1.业务接口和实现类不变
2.编写切入类

public class DiyPointcut {
	public void before(){
		System.out.println("---------方法执行前---------");
	}
	public void after(){
		System.out.println("---------方法执行后---------");
	}
}

3.在配置文件中配置

<!--第二种方式自定义实现-->
<!--注册bean-->
<bean id="diy" class="com.kuang.config.DiyPointcut"/>
<!--aop的配置-->
	<aop:config>
<!--第二种方式:使用AOP的标签实现-->
		<aop:aspect ref="diy">
		<aop:pointcut id="diyPonitcut" expression="execution(*
		com.kuang.service.UserServiceImpl.*(..))"/>
		<aop:before pointcut-ref="diyPonitcut" method="before"/>
		<aop:after pointcut-ref="diyPonitcut" method="after"/>
		</aop:aspect>
	</aop:config>
</bean>

测试

public class MyTest {
	@Test
	public void test(){
		ApplicationContext context = new
		ClassPathXmlApplicationContext("beans.xml");
		UserService userService = (UserService)
		context.getBean("userService");
		userService.add();
	}
}

注解实现

编写一个注解实现的增强类

package com.kuang.config;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class AnnotationPointcut {
	@Before("execution(* com.kuang.service.UserServiceImpl.*(..))")
	public void before(){
		System.out.println("---------方法执行前---------");
	}
	@After("execution(* com.kuang.service.UserServiceImpl.*(..))")
	public void after(){
		System.out.println("---------方法执行后---------");
	}
	@Around("execution(* com.kuang.service.UserServiceImpl.*(..))")
	public void around(ProceedingJoinPoint jp) throws Throwable {
		System.out.println("环绕前");
		System.out.println("签名:"+jp.getSignature());
		//执行目标方法proceed,代表切入点方法执行了
		Object proceed = jp.proceed();
		System.out.println("环绕后");
		System.out.println(proceed);
	}
}

在Spring配置文件中,注册bean,并增加支持注解的配置

<!--第三种方式:注解实现-->
<bean id="annotationPointcut" class="com.kuang.config.AnnotationPointcut"/>
<aop:aspectj-autoproxy/>

aop:aspectj-autoproxy:说明:

通过aop命名空间的<aop:aspectj-autoproxy />声明,自动为spring容器中那些配置@aspectJ切面
的bean创建代理,织入切面。当然,spring 在内部依旧采用
AnnotationAwareAspectJAutoProxyCreator进行自动代理的创建工作,但具体实现的细节已经被
<aop:aspectj-autoproxy />隐藏起来了
<aop:aspectj-autoproxy />有一个proxy-target-class属性,默认为false,表示使用jdk动态
代理织入增强,当配为<aop:aspectj-autoproxy poxy-target-class="true"/>时,表示使用
CGLib动态代理技术织入增强。不过即使proxy-target-class设置为false,如果目标类没有声明接
口,则spring将自动使用CGLib动态代理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值