Spring(三)spring核心技术——aop
一、AOP简介
AOP是Aspect Orient Programming的缩写,即面向切面编程。基于动态代理的,可以使用jdk和cglib两种代理方式。
-
Aspect: 切面,给你的目标类增加的功能,就是切面。像上面用的日志,事务都是切面。切面的特点:一般都是非业务方法,独立使用的。
-
Orient:面向,对着。
-
Programming:编程
AOP就是动态代理的规范化,把动态代理的实现步骤,方式都定义好了,让开发人员用一种统一的方式,使用动态代理。
作用:
-
在目标类不修改的情况下增加功能
-
减少代码的重复
-
使开发人员专注业务功能的实现
-
解耦合:业务功能和日志、事务等非业务功能的耦合
二、动态代理的实现方式
- jdk动态代理
使用jdk中的Proxy,Method,InvocaitonHanderl创建代理对象。jdk动态代理要求目标类必须实现接口
- cglib动态代理
第三方的工具库,创建代理对象,原理是继承。 通过继承目标类,创建子类。子类就是代理对象。要求目标类不能是final的,方法也不能是final的。
三、AOP中名词概念
-
aspect(切面):表示给业务方法增加的功能,一般为*输出、事务、权限检查等
-
JoinPoint:连接点,连接业务方法和切面的位置,就某类中的业务方法
-
pointcut(切入点):是一个或多个JoinPoint的集合,表示切面功能执行的位置
-
目标对象:给哪个类的方法增加功能,这个类就是目标对象
-
advice(通知):也叫增强。表示切面执行的时间,在方法前或后
四、何时使用AOP
-
某项目功能类不完善,需要增加功能,但是没有源代码
-
给项目的多个类需要增加相同功能
-
为业务功能增加事务、日志输出
-
…
五、AOP的实现
1、Spring
Spring在内部实现了aop规范,能做aop的工作。spring主要在事务处理时使用aop,在项目开发中很少使用spring的aop实现,因为spring的aop比较笨重。
2、使用Aspectj框架实现AOP
一个开源的专门做aop的框架。spring框架中集成了aspectj框架,通过spring就能使用aspectj的功能。
aspectJ框架实现aop有两种方式:
1)使用xml的配置文件 : 配置全局事务
2)使用注解,项目中要做aop功能,一般都使用注解
-
使用相应注解确定切面执行的时间
-
使用切面表达式确定切面执行的位置
切面位置的切入点表达式:execution(修饰符 返回值 包名.类名.方法名(方法参数) 异常)。用来指定切面执行的位置。
aspectj中常用注解如下:
- @Aspect:声明该类是切面类
/*
* @Aspect:是Aspectj框架中的注解
* 作用:声明该类是切面类
* 切面类:为目标类增加功能的类,包含切面的功能代码
* 位置:在类的上面
* */
@Aspect
public class MyAspect {}
- @Before:前置通知,在目标方法之前执行切面的功能
@Aspect
public class MyAspect {
/*
* 定义方法,是实现切面功能的方法
* 方法定义要求:
* 1、方法时公共的
* 2、方法名自定义
* 3、方法没有返回值
* 4、可以有参数,也可以无参数
* 如果有参数,参数不是自定义的
* @Before:前置通知注解
* 属性:value,是切入点表达式,表明切面的功能执行的位置
* 位置:在方法的上面
* 特点:
* 1、在目标方法前执行
* 2、不会改变和影响目标方法的执行
* */
@Before(value = "execution(public void cn.krain.ba01.SomeServiceImpl.doSome(String,Integer))")
public void myBefore(){
System.out.println("前置通知,切面功能,在目标方法执行前输出执行的时间:"+new Date());
}
- @AfterReturning:后置通知,在目标方法之后执行切面的功能,能获取返回值
@Aspect
public class MyAspect {
/*
* @AfterReturning定义方法,是实现切面功能的方法
* 方法定义要求:
* 1、方法时公共的
* 2、方法名自定义
* 3、方法没有返回值
* 4、方法有参数
* @AfterReturning:后置通知
* 属性:1、value:切入点表达式
* 2、returning:自定义变量,目标方法的返回值
* 自定义变量名称和通知方法的形参名一致
* 位置:在方法定义的上面
* 特点:
* 1、在目标方法之后执行
* 2、能获取目标方法的返回值
* 3、可修改这个返回值
* */
@AfterReturning(value = "execution(* cn.krain.ba02.SomeServiceImpl.doOther(..))",
returning="res")
public void myAfterReturning(Object res){
System.out.println("后置通知,切面功能,在目标方法执行前输出执行的时间:"+res);
if (res!=null){
res = "Hello";
}
}
}
- @Around:环绕通知,能在目标方法前后增强功能,能够控制目标方法的执行,修改返回值
@Aspect
public class MyAspect {
/*
* 环绕通知的定义格式
* 方法定义要求:
* 1、方法时公共的
* 2、方法名自定义
* 3、必须有返回值
* 4、方法有固定的参数:proceedingJoinPoint
* @Around:环绕通知
* 属性:value 切入点表达式
* 位置:在方法的定义上面
* 特点:
* 1、是功能最强的通知
* 2、能够在目标方法前后增强功能
* 3、控制目标方法是否被执行
* 4、修改原来目标方法的执行结果,影响最后的调用结果
* */
@Around(value = "execution(* *..SomeServiceImpl.doFirst(..))" )
public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable {
//获取第一个参数的值
String name = null;
Object args [] = joinPoint.getArgs();
if (args!=null && args.length>=1){
Object arg = args[0];
name = (String) arg;
}
Object result = null;
//实现环绕通知
System.out.println("环绕通知,在目标方法之前,输出时间:"+new Date());
//1、目标方法调用
//控制目标方法的执行
if (name.equals("张三")){
result = joinPoint.proceed(); //等同于执行目标方法
}
System.out.println("环绕通知,在目标方法之后,提交事务");
//修改目标函数返回结果
if (result!=null){
result = "Hello Aop";
}
return result;
}
}
- @AfterThrowing:异常通知,在目标方法抛出异常后执行
@Aspect
public class MyAspect {
/* 异常通知的定义格式
* 方法定义要求:
* 1、方法时公共的
* 2、方法名自定义
* 3、方法没有返回值
* 4、固定参数Exception,如果还有参数则是:JoinPoint
* 5、throwing的值要与Exception的参数名相同
* @AfterThrowing:异常通知
* 属性:1、value 切入点表达式
* 2、throwing 自定义变量,表示目标方法抛出的异常对象
* 如果有异常,通过邮件、短信通知
* 特点:
* 1、在目标方法出现异常时执行
* 2、监控目标方法是否存在异常
*
* 执行过程:
* try{
* SomeServiceImpl.doSecond();
* }catch(Exception e){
* myAfterThrowing(e);
* }
* */
@AfterThrowing(value = "execution(* *..SomeServiceImpl.doSecond())",
throwing = "ex")
public void myAfterThrowing(Exception ex){
System.out.println("异常通知,方法执行异常,执行:"+ex.getMessage());
//发送短信
}
}
- @After:最终通知,总是会执行
@Aspect
public class MyAspect {
/* 最终通知的定义格式
* 方法定义要求:
* 1、方法时公共的
* 2、方法名自定义
* 3、方法没有返回值
* 4、没有参数,如果有参数则是:JoinPoint
* */
/*
* @After 最终通知
* 属性:value 切入表达式
* 位置:在方法的上面
*
* 特点:
* 1、总是会执行
* 2、在目标方法之后执行
*
* 执行过程:
* try{
* //目标方法
* }catch(){
*
* }finally{
* myAfter();
* }
* */
@After(value = "execution(* *..SomeServiceImpl.doThird(..))")
public void myAfter(){
System.out.println("执行最终通知,总是会被执行的代码");
}
}
- @Pointcut:定义和管理切入点的辅助注解
@Aspect
public class MyAspect {
@After(value = "myPT()")
public void myAfter(){
System.out.println("执行最终通知,总是会被执行的代码");
}
@Before(value = "myPT()")
public void myBefore(){
System.out.println("执行前置通知");
}
/*
* @Pointcut:定义和管理切入点表达式,如果项目中的切入点表达式是重复的,可使用Pointcut
* 属性:value 切入点表达式
* 位置:在自定义方法上面
* 为切入点表达式定义别名,使用"方法名称()"代替切入点表达式
* */
@Pointcut(value = "execution(* *..SomeServiceImpl.doThird(..))")
private void myPT(){
//无功能代码
}
}
- 编写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: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 https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--把对象交给spring,有spring统一创建和管理-->
<!--创建SomeService目标对象-->
<bean name="someService" class="cn.krain.ba.SomeServiceImpl" />
<!--创建aspect切面对象-->
<bean name="myAspect" class="cn.krain.ba.MyAspect" />
<!--
声明自动代理生成器:使用aspectj的内部功能,创建目标对象的代理对象。
创建代理对象是在内存中实现的,修改目标对象在内存中的结构,创建成代理对象
因此,目标对象就是被修改后的代理对象
-->
<aop:aspectj-autoproxy />
<!--
如果项目中有接口时,仍希望使用CGHLIB动态代理
proxy-target-class="true":告诉框架使用cglib动态代理
-->
<aop:aspectj-autoproxy proxy-target-class="true" />
</beans>
- 测试类MyTest.java
public class MyTest {
public void Test(){
String config = "applicationContext.xml";
ApplicationContext ac = new ClassPathXmlApplicationContext(config);
//根据对象名获取代理对象
SomeService proxy = (SomeService) ac.getBean("someService");
//proxy."目标方法名"();
}
}
本文地址:https://blog.csdn.net/ccccc_chuang/article/details/107389467
推荐阅读
-
Spring Boot 入门(五):集成 AOP 进行日志管理
-
02Spring基于xml的IOC配置--实例化Bean的三种方式
-
Spring 学习指南 第三章 bean的配置 (未完结)
-
Spring AOP创建AroundAdvice实例
-
Spring AOP创建BeforeAdvice和AfterAdvice实例
-
spring aop action中验证用户登录状态的实例代码
-
设计模式:代理模式是什么,Spring AOP还和它有关系?
-
Spring-AOP本质原理分析(七)
-
原理解密 → Spring AOP 实现动态数据源(读写分离),底层原理是什么
-
Spring AOP 实现原理与 CGLIB 应用