一:Spring AOP的简介
AOP 意思为面向切面编程,是 OOP 的延续,底层是由动态代理技术实现的!
作用:在程序运行期间,在不修改源码的情况下对目标方法进行功能增强
优势:减少重复代码,提高开发效率,并且便于维护
1.1:jdk 的动态代理
1.2:cglib的动态代理
注:cglib动态代理: 在高版本的spring,已经不需要额外导jar坐标了,已经集成在spring-core中了!
二:Spring AOP中的术语
Aop的相关概念!
1.Target(目标对象)
2.Proxy(代理对象)
3.joinpoint(连接点):可以被增强的方法叫做连接点!
4.pointcut(切点):被增强的方法
5.Advice(通知/增强): 所谓通知是指拦截到Joinpoint之后要做的事情就是通知
6.Aspect(切面): 是切点+通知
7.Weaving(织入): 是切点+通知结合的过程!!!
注:连接点和切点的关系,就好像所有的女生都可以嫁人这是连接点,但是只有嫁过人的女生才是切点
三:基于XML的AOP开发
步骤:
1.导入spring-aop和aspectjweaver的jar坐标
2.创建目标接口和目标类(内部有切点)
3.创建切面类
4.将目标类和切面类交给spring进行管理
5.在spring配置中,配置AOP
6.使用spring集成junit的方式来测试
- 1、 导入spring-aop和aspectjweaver的jar坐标
注: spring-context 中有spring-aop,但是还需要aspectjweaver
<dependencies>
<!--AOP: sprint-aop和aspectj-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
</dependencies>
-
2、创建目标接口和目标类(内部有切点)
-
目标接口
package com.wzj.aop.xml;
public interface TargetInterface {
void save();
}
- 目标类
package com.wzj.aop.xml;
public class Target implements TargetInterface{
@Override
public void save() {
System.out.println("save()...");
}
}
- 3、创建切面类
public class MyAspect {
//前置通知
public void before(){
System.out.println("前置通知:");
}
//后置通知
public void afterReturning(){
System.out.println("后置通知:");
}
//环绕通知
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("环绕通知前:");
Object obj = pjp.proceed();
System.out.println("环绕通知后:");
return obj;
}
//异常抛出通知: 只有修饰的切点表达式中的切点在运行过程中抛出了异常后,才会触发!
public void afterThrowing(){
System.out.println("异常抛出通知");
}
//最终通知: 不管修饰的切点表达式,运行的时候有没有发生异常,最终都会执行,类似finally关键字
public void after(){
System.out.println("最终通知");
}
}
- 4、将目标类和切面类交给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">
<!--1.配置目标类-->
<bean id="target" class="com.wzj.aop.xml.Target"></bean>
<!--2.配置切面类: 其实就是个普通的bean,需要指定才能成为切面类,要不然spring哪知道-->
<bean id="myAspect" class="com.wzj.aop.xml.MyAspect"></bean>
<!--3.配置织入 = 切点+通知结合的过程, 注:需要引入aop的命名空间和xml约束-->
<aop:config>
<!--4.ref:指定该类为切面类, 切面 = 切点 + 通知-->
<aop:aspect ref="myAspect">
<!--
5.告诉spring,哪些方法(切点)需要哪些通知/增强(5种)
method:指定切面类中的哪个方法作为通知/增强, pointcut:指定目标对象中的哪些方法作为切点
-->
<aop:before method="before" pointcut="execution(* com.wzj.aop.xml.Target.*(..))"></aop:before>
<aop:after-returning method="afterReturning" pointcut="execution(* com.wzj.aop.xml.Target.*(..))"></aop:after-returning>
<aop:around method="around" pointcut="execution(* com.wzj.aop.xml.Target.*(..))"></aop:around>
<aop:after-throwing method="afterReturning" pointcut="execution(* com.wzj.aop.xml.Target.*(..))"></aop:after-throwing>
<aop:after method="after" pointcut="execution(* com.wzj.aop.xml.Target.*(..))"></aop:after>
</aop:aspect>
</aop:config>
</beans>
- 5、使用spring集成junit的方式来测试
使用之前记得导入spring-test和junit的jar坐标
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AopXmlModeTest {
@Autowired
private TargetInterface target; //依赖倒转原则
@Test
public void testXml(){
target.save();
}
}
- 控制台
前置通知:
环绕通知前:
save()...
最终通知
环绕通知后:
后置通知:
3.6:切点表达式的抽取
- 优化后
3.7:通知的XML类型
- 通知的配置语法
<aop:通知类型 method=“切面类中方法名” pointcut=“切点表达式"></aop:通知类型>
3.8:切点表达式的写法
- 表达式语法
execution(修饰符 返回值类型 包名.类名.方法名(参数))
注:切点表达式确实就是针对方法来的,距离方法括号最近的一个是方法名,然后是类名!!
1.访问修饰符可以省略
2.返回值类型、包名、类名、方法名可以使用星号*代表任意
3.参数列表可以使用两个点 .. 表示任意个数,任意类型的参数列表
4.包名与类名之间一个点.代表当前包下的类,两个点..表示当前包及其子包下的类
- 例子
1.execution(public void com.wzj.aop.Target.method())
表示com.wzj.aop包下的Target类的被public修饰的无参无返回值的method方法,被选中为切点!!
2.execution(void com.wzj.aop.Target.*(..))
表示com.wzj.aop.Target包下的Target类下的任意参数的无返值的方法,被选中为切入点!!
3.execution(* com.wzj.aop.*.*(..))
表示com.wzj.aop包下的所有类的所有方法!!,这点是比较常用的!!
四:基于注解的AOP开发
步骤:
1.导入spring-aop和aspectjweaver的jar坐标
2.创建目标接口和目标类(内部有切点)
3.使用@Aspect来指定切面类(内部有通知/增强),使用@Before,@AfterReturning,@Around,@AfterThrowing,@After来指定通知/增强
4.使用@Component注解的方式将目标类和切面类交给spring进行管理
5.在spring配置中,进行包扫描,和开启Aop的自动代理
6.使用spring集成junit的方式来测试
- 1、导入spring-aop和aspectjweaver的jar坐标
参照基于xml的aop开发的第一点
- 2、创建目标接口和目标类(内部有切点)
package com.wzj.aop.anno;
@Component
public class Target implements TargetInterface{
@Override
public void save() {
System.out.println("save()...");
}
}
public interface TargetInterface {
void save();
}
- 3、使用
@Aspect
来指定切面类(内部有通知/增强),使用@Before
,@AfterReturning
,@Around
,@AfterThrowing
,@After
来指定通知/增强
@Component
@Aspect
public class MyAspect {
@Before("execution(* com.wzj.aop.anno.Target.*(..))")
public void before(){
System.out.println("前置通知:");
}
@AfterReturning("execution(* com.wzj.aop.anno.Target.*(..))")
public void afterReturning(){
System.out.println("后置通知:");
}
@Around("execution(* com.wzj.aop.anno.Target.*(..))")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("环绕通知前:");
Object obj = pjp.proceed();
System.out.println("环绕通知后:");
return obj;
}
@AfterThrowing("execution(* com.wzj.aop.anno.Target.*(..))")
public void afterThrowing(){
System.out.println("异常抛出通知");
}
@After("execution(* com.wzj.aop.anno.Target.*(..))")
public void after(){
System.out.println("最终通知");
}
}
- 4、使用@Component注解的方式将目标类和切面类交给spring进行管理
注:类上都加了@Component注解
- 5、在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:context="http://www.springframework.org/schema/context"
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/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--使用组件扫描,将@Component这类注解实例化的bean,放到spring容器中进行管理-->
<context:component-scan base-package="com.wzj.aop.anno"></context:component-scan>
<!--开启Aop的自动代理,不然使用通知的时候会没有效果-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
注:默认为false,表示使用jdk动态代理织入增强,当配为<aop:aspectj-autoproxy poxy-target-class=“true”/>时,表示使用CGLib动态代理技术织入增强
- 6、使用spring集成junit的方式来测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext-anno.xml")
public class AopAnnoModeTest {
@Autowired
private TargetInterface target;
@Test
public void testAnno(){
target.save();
}
}
- 控制台
环绕通知前:
前置通知:
save()...
环绕通知后:
最终通知
后置通知:
4.1:切点表达式的抽取
- 优化后
4.2:通知的注解类型
来自:虽然帅,但是菜的cxy