目录
一、Spring的AOP的简介
1.AOP思想最早是由AOP联盟组织提出的,Spring是使用这种思想最好的框架:
(1)Spring的AOP有自己的实现方法(非常繁琐)。
(2)使用第三方的AspectJ是一个AOP的扩建,Spring引入AspectJ作为自身AOP的开发。
(3)Spring的AOP开发方式:
- Spring传统的方式(弃用)。
- Spring基于AspentJ的AOP开发(使用广泛)。
二、Spring的AOP开发的相关术语
1.通知(Advice):就是你想要的功能,也就是上面说的安全,事物,日志等。你给先定义好,然后在想用的地方用一下,也就是去增强别人的方法。
2.连接点(JoinPoint):spring允许你使用通知的地方,基本每个方法的前,后(两者都有也行),或抛出异常时都可以是连接点,spring只支持方法连接点和方法有关的前后(抛出异常),都是连接点,也就是说,我们可以把需要增强的方法看作是一个连接点。
3.切入点(Pointcut):上面说的连接点的基础上,来定义切入点,你的一个类里,有5个方法,那就有5个连接点了,但是你并不想在所有方法附近都使用通知(使用叫织入),你只想让其中的几个,在调用这几个方法之前,之后或者抛出异常时干点什么,那么就用切入点来定义这几个方法,让切入点来筛选连接点,选中那几个你想要的方法。
4.切面(Aspect):切面是通知和切入点的结合。通知说明了干什么和什么时候干(什么时候通过方法名中的before,after,around等就能知道),而切入点说明了在哪干(指定到底是哪个方法),这就是一个完整的切面定义。简单来说,切面就是增强类。
5.引入(introduction):允许我们向现有的类添加新的方法属性。就是把切面(也就是新方法属性,通知定义的)用到目标类中。
6.目标(target):引入中所提到的目标类,也就是要被通知的对象,也就是真正的业务逻辑,他可以在毫不知情的情况下,被咋们织入切面。而自己专注于业务本身的逻辑,也就是我们需要被增强的对象。
7.代理(proxy):怎么实现整套aop机制的,就是通过代理,织入增强功能后产生的代理对象。
8.织入(weaving):把切面应用到目标对象来创建新的代理对象的过程。有3种方式,spring采用的是运行时。也就是我们在切入点前面或者后面使用通知的过程就叫做织入。
三、使用AOP编程完成功能增强
1.需要在配置文件种添加约束头:
(1)约束头地址:XML Schemas :: Spring Framework
2.在配置文件写一个<aop:config></aop:config>标签。
3.在<aop:config></aop:config>标签之间完成我们相应的操作。
(1)获取需要被增强的那个方法,专业术语叫切入点:使用<aop:pointcut><aop:pointcut>标签获取,这个标签需要写两个属性,一个是id,一个是expression,id属性的值可以随便写,expression属性的值是一个表达式:
- expression的值:execution(* 要增强的方法所在的包名加类名.要增强的方法(..)):
- 上面的这个表达式代表使用这种方法获取到了这个要增强的方法。
- 而方法一般都是有返回值的,我们表达式中的*号是一个通配符,就是用来匹配所有类型的返回值的。
- 而后面要增强的方法的括号中的两个点代表了这个方法中传入的参数,也就是用来表示参数列表。
(2)配置切面类,把切面类中的增强方法织入在需要被增强的方法的前面或者后面:
- 使用<aop:aspect></aop:aspect>标签来配置,这个标签中会有要给ref标签属性,这个ref标签属性是用来关联切面类的:
- 格式:ref="切面类在Bean缓存池中的id"。
(3)织入方法:
- 使用<aop:before method="要增强切入点的增强方法" pointcut-ref="前面获取切入点的标签id"></aop:before>标签在切入点前面添加增强切入点的方法。
- 使用<aop:after-returning method="要增强切入点的增强方法的方法名" pointcut-ref="前面获取切入点的标签id"></aop:after-returning>标签在切入点后面添加增强切入点的方法。
4.案例:
<?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 https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--在这个文件种使用spring的aop技术让两个不相干的类联系起来
底层就是使用的动态代理-->
<aop:config>
<!--首先获取被增强的那个add()方法,专业术语叫切入点
*号是通配符,表示通配方法前的返回值void或Object
.. 表示方法的参数列表的所有情况(方法中有参数和无参数)
-->
<aop:pointcut id="p1" expression="execution(* com.spring.service.impl.UserServiceImpl.add(..))"></aop:pointcut>
<!--配置切面类:把切面类MyAspect中的check()织入在add()之前执行-->
<aop:aspect ref="myAspect">
<!--下面的动作的专业术语叫:织入-->
<aop:before method="check" pointcut-ref="p1"></aop:before>
</aop:aspect>
</aop:config>
</beans>
四、Spring的AOP开发的通知类型
1.前置通知-before:在目标方法执行之前的操作,可以获取切入点信息:
(1)格式:<aop:before method="要增强切入点的增强方法的方法名" pointcut-ref="前面获取切入点的标签id"></aop:before>。
2.后置通知-after-returning:在目标方法执行之后的操作:
(1)格式:<aop:after-returning method="要增强切入点的增强方法的方法名" pointcut-ref="前面获取切入点的标签id"></aop:after-returning>。
3.带返回值的后置通知-after-returning:在目标方法执行之后的操作,并获取目标方法的返回值,这个可以在两个完全没有联系的类之间进行数据的传递:
(1)格式:<aop:after-returning method="要增强切入点的增强方法的方法名" pointcut-ref="前面获取切入点的标签id" returning="要增强切入点的增强方法所定义的形参名字"></aop:after-returning>。
4.环绕通知-around:在目标方法执行之前和之后进行操作:
(1)格式:<aop:around method="要增强切入点的增强方法的方法名" pointcut-ref="前面获取切入点的标签id"></aop:around>。
(2)注意:这个环绕通知需要在要增强切入点的增强方法中写一个ProceedingJoinPoint类型的形参,可以通过形参名.proceed()来调用切入点的方法,然后可以在这个方法的前后去完成我们的增强逻辑。
(3)环绕通知扩展:
- 带参数的环绕通知:可以在要增强切入点的增强方法中使用ProceedingJoinPoint类型的形参.getArgs()就可以获取到需要被增强的方法的参数列表所传入的参数。
5.异常抛出通知-after-throwing:在目标方法抛出异常时的操作(比如出现异常后进行回滚):
(1)格式:<aop:after-throwing method="要增强切入点的增强方法的方法名" pointcut-ref="前面获取切入点的标签id" throwing="要增强切入点的增强方法的形参名字,这个形参是Exception类型"></aop:after-throwing>。
6.最终通知-after:无论是否有异常都会执行的,相当于try-catch-finally中的finally块:
(1)格式:<aop:after method="要增强切入点的增强方法的方法名" pointcut-ref="前面获取切入点的标签id"></aop:after>。
7.案例:
(1)spring.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"
xsi:schemaLocation="
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.spring.service"></context:component-scan>
<context:component-scan base-package="com.spring.aspect"></context:component-scan>
</beans>
(2)aspect.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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--在这个文件种使用spring的aop技术让两个不相干的类联系起来
底层就是使用的动态代理-->
<aop:config>
<!--首先获取被增强的那个add()方法,专业术语叫切入点
*号是通配符,表示通配方法前的返回值void或Object
.. 表示方法的参数列表的所有情况(方法中有参数和无参数)
-->
<aop:pointcut id="p1" expression="execution(* com.spring.service.impl.UserServiceImpl.add(..))"></aop:pointcut>
<aop:pointcut id="p2" expression="execution(* com.spring.service.impl.UserServiceImpl.select(..))"></aop:pointcut>
<aop:pointcut id="p3" expression="execution(* com.spring.service.impl.UserServiceImpl.addReturn(..))"></aop:pointcut>
<aop:pointcut id="p4" expression="execution(* com.spring.service.impl.UserServiceImpl.delete(..))"></aop:pointcut>
<aop:pointcut id="p5" expression="execution(* com.spring.service.impl.UserServiceImpl.select2(..))"></aop:pointcut>
<aop:pointcut id="p6" expression="execution(* com.spring.service.impl.UserServiceImpl.deleteParam(..))"></aop:pointcut>
<!--配置切面类:把切面类MyAspect中的check()织入在add()之前执行-->
<aop:aspect ref="myAspect">
<!--下面的动作的专业术语叫:织入-->
<aop:before method="check" pointcut-ref="p1"></aop:before>
<aop:after-returning method="back" pointcut-ref="p2"></aop:after-returning>
<!--带返回值的后置通知,obj这个名字要和MyAspect类中backReturn(Object obj)的参数名一致-->
<aop:after-returning method="backReturn" pointcut-ref="p3" returning="obj"></aop:after-returning>
<aop:around method="around" pointcut-ref="p4"></aop:around>
<!--在环绕通知中传参数-->
<aop:around method="aroundParam" pointcut-ref="p6"></aop:around>
<aop:after-throwing method="doException" pointcut-ref="p5" throwing="e"></aop:after-throwing>
<aop:after method="finallyDo" pointcut-ref="p5"></aop:after>
</aop:aspect>
</aop:config>
</beans>
(3)业务逻辑层的接口:
package com.spring.service;
public interface UserService {
void add();
void select();
String addReturn();
void delete();
void deleteParam(String name,String pwd);
void select2();
}
(4)业务逻辑层的接口的实现类:
package com.spring.service.impl;
import com.spring.service.UserService;
import org.springframework.stereotype.Service;
@Service //与@Component注解一样,表示把当前类交给spring管理
public class UserServiceImpl implements UserService {
//希望在当前这个add()方法被调用前,对其进行身份校验(增强)
@Override
public void add() { //切入点
System.out.println("新增数据操作");
}
//希望在当前这个select()方法被调用之后,对其进行功能的增强
@Override
public void select() {
System.out.println("查询操作");
}
@Override
public String addReturn() {
System.out.println("带返回值的方法addReturn");
return "abc"; //这个返回值将被切面类接收到
}
/**
* 测试环绕通知
*/
@Override
public void delete() {
System.out.println("删除操作");
}
/**
* 含有参数列表的环绕增强
* @param name
* @param pwd
*/
@Override
public void deleteParam(String name, String pwd) {
System.out.println("带参数列表的deleteParam");
}
/**
* 异常抛出通知
*/
@Override
public void select2() {
System.out.println("查找操作");
System.out.println(1/0); //by zero异常
}
}
(5)增强类(切面类):
package com.spring.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.stereotype.Component;
import java.util.Arrays;
@Component
public class MyAspect {
public void check(){ //通知
System.out.println("进行身份的校验");
}
public void back(){
System.out.println("---之后的操作---");
}
public void backReturn(Object obj){
System.out.println("带返回值的后置通知" + (String) obj);
}
//这个point会接收切入点delete()的方法,是delete方法的代理对象
public void around(ProceedingJoinPoint point) throws Throwable {
System.out.println("-----------之前的操作");
point.proceed(); //运行delete方法
System.out.println("-----------之后的操作");
}
//这个point会接收切入点deleteParam(String s1,String s2)的方法,是deleteParam(String s1,String s2)方法的代理对象
public void aroundParam(ProceedingJoinPoint point) throws Throwable {
System.out.println("-----------之前的操作");
Object[] args = point.getArgs();
System.out.println("参数1" + (String)args[0]);
System.out.println("参数2" + (String)args[1]);
point.proceed(); //运行delete方法
System.out.println("-----------之后的操作");
}
//异常抛出通知:当前被增强的方法出现异常时,触发下面的方法执行
public void doException(Exception e){
System.out.println("有异常:" + e);
}
public void finallyDo(){
System.out.println("----总是会执行----");
}
}
(6)具体使用:
package com.spring.test;
import com.spring.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Demo {
public static void main(String[] args) {
//加载spring.xml和aspect.xml配置文件
ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml","aspect.xml");
UserService userServiceImpl = (UserService) ac.getBean("userServiceImpl");
// userServiceImpl.add();
// userServiceImpl.select();
// userServiceImpl.addReturn();
// userServiceImpl.delete();
// userServiceImpl.select2();
userServiceImpl.deleteParam("张三","123");
}
}
五、切入点表达式语法
1.基于execution的函数完成的。
2.语法:
(1)execution([访问修饰符] 方法的返回值 包名.类名.方法名(参数))。
3.案例:
(1)com.spring.service.UserServiceImpl.add(..)
(2)com.spring.service.UserServiceImpl.*(..)
- 这种方法是开发中最常用的方法,用的最多,这个方法是对当前类下所有的方法做增强处理(场景:事物处理)。
(3)com.springservice.impl.add(..)
(4)com.springservice.impl.*(..)
- 表示com.springservice.impl类下所有的方法被增强。
(5)* *.*.service.impl.add(..)
- 表示没有包可以用*代替。
(6)* com.spring.*.*(..)
- 表示com.spring包下所有的类,所有的方法都被增强。
六、Spring的AOP的注解开发
1.Spring的常用注解:
(1)@Aspect:这个注解表示,告诉spring容器,当前类是切面类,可以基于aspectj技术使用注解开发。
2.Spring注解的通知类型:
(1)开启注解的aop开发:<aop:aspectj-autoproxy></aop:aspectj-autoproxy>。
(2)前置通知:@Before(value="execution(* 包名.类名.方法名(..))")。
(3)不带参数的后置通知:@AfterReturning(value="execution(* 包名.类名.方法名(..))")。
(4)带参数的后置通知:@AfterReturning(value="execution(* 包名.类名.方法名(..))",returing="result")。
(5)环绕通知:@Around(value="execution(* 包名.类名.方法名(..))")。
(6)异常抛出通知:@AfterThrowing(value="execution(* 包名.类名.方法名(..))",throwing="e")。
(7)最终通知:@After(value="execution(* 包名.类名.方法名(..))")。
3.案例:
(1)spring.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"
xsi:schemaLocation="
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.spring.service"></context:component-scan>
<context:component-scan base-package="com.spring.aspect"></context:component-scan>
</beans>
(2)aspect.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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--开启AOP的注解开发-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
(3)业务逻辑层的接口:
package com.spring.service;
public interface UserService {
void add();
void select();
String addReturn();
void delete();
void deleteParam(String name,String pwd);
void select2();
}
(4)业务逻辑层的接口的实现类:
package com.spring.service.impl;
import com.spring.service.UserService;
import org.springframework.stereotype.Service;
@Service //与@Component注解一样,表示把当前类交给spring管理
public class UserServiceImpl implements UserService {
//希望在当前这个add()方法被调用前,对其进行身份校验(增强)
@Override
public void add() { //切入点
System.out.println("新增数据操作");
}
//希望在当前这个select()方法被调用之后,对其进行功能的增强
@Override
public void select() {
System.out.println("查询操作");
}
@Override
public String addReturn() {
System.out.println("带返回值的方法addReturn");
return "aaa"; //这个返回值将被切面类接收到
}
/**
* 测试环绕通知
*/
@Override
public void delete() {
System.out.println("删除操作");
}
/**
* 含有参数列表的环绕增强
* @param name
* @param pwd
*/
@Override
public void deleteParam(String name, String pwd) {
System.out.println("带参数列表的deleteParam");
}
/**
* 异常抛出通知
*/
@Override
public void select2() {
System.out.println("查找操作");
System.out.println(1/0); //by zero异常
}
}
(5)增强类(切面类):
package com.spring.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.util.Arrays;
@Component
@Aspect //这个注解表示,告诉spring容器,当前类是切面类,可以基于aspectj技术使用注解开发
public class MyAspect {
@Before(value = "execution(* com.spring.service.impl.UserServiceImpl.add(..))") //织入动作:把check方法放置在add()方法之前执行
public void check(){ //通知
System.out.println("进行身份的校验");
}
@AfterReturning(value = "execution(* com.spring.service.impl.UserServiceImpl.select(..))")
public void back(){
System.out.println("---之后的操作---");
}
@AfterReturning(value = "execution(* com.spring.service.impl.UserServiceImpl.addReturn(..))",returning = "obj")
public void backReturn(Object obj){
System.out.println("带返回值的后置通知" + (String) obj);
}
@Around(value = "execution(* com.spring.service.impl.UserServiceImpl.delete(..))")
//这个point会接收切入点delete()的方法,是delete方法的代理对象
public void around(ProceedingJoinPoint point) throws Throwable {
System.out.println("-----------之前的操作");
point.proceed(); //运行delete方法
System.out.println("-----------之后的操作");
}
@Around(value = "execution(* com.spring.service.impl.UserServiceImpl.deleteParam(..))")
//这个point会接收切入点deleteParam(String s1,String s2)的方法,是deleteParam(String s1,String s2)方法的代理对象
public void aroundParam(ProceedingJoinPoint point) throws Throwable {
System.out.println("-----------之前的操作");
Object[] args = point.getArgs();
System.out.println("参数1" + (String)args[0]);
System.out.println("参数2" + (String)args[1]);
point.proceed(); //运行delete方法
System.out.println("-----------之后的操作");
}
@AfterThrowing(value = "execution(* com.spring.service.impl.UserServiceImpl.select2(..))",throwing = "e")
//异常抛出通知:当前被增强的方法出现异常时,触发下面的方法执行
public void doException(Exception e){
System.out.println("有异常:" + e);
}
@After(value = "execution(* com.spring.service.impl.UserServiceImpl.select2(..))")
public void finallyDo(){
System.out.println("----总是会执行----");
}
}
(6)具体使用:
package com.spring.test;
import com.spring.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Demo {
public static void main(String[] args) {
//加载spring.xml和aspect.xml配置文件
ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml","aspect.xml");
UserService userServiceImpl = (UserService) ac.getBean("userServiceImpl");
// userServiceImpl.add();
// userServiceImpl.select();
// userServiceImpl.addReturn();
// userServiceImpl.delete();
userServiceImpl.select2();
// userServiceImpl.deleteParam("张三","123");
}
}
转载自 CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/m0_53499553/article/details/161324934




