AOP概念
概念:Aspect Oriented Programming面向切面编程,是OOP的延续。
作用:在程序运行期间,在不修改源码情况下,对方法功能增强。
底层实现:Spring提供的动态代理技术实现:
- JKD代理:基于接口的动态代理技术(目标对象必须有接口)
- cglib代理:基于父类的动态代理技术(目标对象可以没有接口)
两者区别和联系:
- 两者都是通过一个类来包装另一个类实现代理
- cglib可以通过配置实现,而不用手动去定义。
AOP的相关术语:
- Target(目标对象):代理的目标对象
- Proxy(代理):一个完成代理的类,返回代理类
- Joinpoint(连接点):可以被增强的方法(可以被拦截到的方法)
- Poincut(切入点):被增强的方法(Joinpoint的一部分)
- Advice(通知/增强):增强方法
- Aspect(切面):切点+通知(名词)
- Weaving(织入):将切点和通知结合的过程(动词,创建代理对象的过程)
AOP重点属于:切点、通知、切面、织入
AOP需要编写的代码:
- 编写核心业务代码(目标类、目标方法,目标方法也叫切点)
- 编写切面类,切面类中有通知(增强方法)
- 在配置文件中,配置织入关系
AOP技术原理:
- Spring框架监控切入点,一旦监控到切入点在执行,动态创建代理对象,在代理对象的对应的位置,将通知方法的功能织入,运行代理对象。
AOP使用哪种代理方式:
- 框架根据目标类是否实现了接口来决定采用哪种代理方式。
XML实现AOP
1.导入坐标
<!-- Spring框架-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!-- aspectj团队实现的aop配置比较好-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.6</version>
</dependency>
2.创建目标类
创建目标类和接口,以及内部的方法(切点)
public class MyTarget {
public void save() {
System.out.println("save running...");
}
}
3.创建通知
创建切面类,以及增强方法(通知)
public class MyAspect {
public void before() {
System.out.println("前置增强...");
}
}
4.织入
织入:就是在applicationContext.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" >
<!--目标对象-->
<bean id="myTarget" class="com.aop.MyTarget"/>
<!--切面对象-->
<bean id="myAspect" class="com.aop.MyAspect"/>
<!--配置织入-->
<aop:config>
<aop:aspect ref="myAspect">
<aop:before method="before" pointcut="execution(public void com.aop.MyTarget.save())"/>
</aop:aspect>
</aop:config>
</beans>
XML配置详解
<bean id="myTarget" class="com.aop.MyTarget"/>
<bean id="myAspect" class="com.aop.MyAspect"/>
<aop:config>
<aop:aspect ref="myAspect">
<aop:before method="before" pointcut="execution(public void com.aop.MyTarget.save())"/>
</aop:aspect>
</aop:config>
-
通知的类型:
- aop:before 前置通知
- aop:after-returning 后置通知
- aop:around 环绕通知,前后都执行
- aop:throwing 异常处理通知,抛出异常时执行
- aop:after 最终通知,不管抛不抛异常
-
切点表达式:
execution(public void com.aop.MyTarget.save(参数))
-
访问限定符可以省略
-
返回值类型、包名、类名、方法名可以用*号代表任意
-
包名和类名之间可以一个点.表示当前包下所有类,两个点..表示当前及其子包下的所有类
-
参数列表可以使用两个点..表示任意个数,任意类型的参数列表
execution(public void com.aop.MyTarget.method()) execution(* com.aop.MyTarget.*(..)) // MyTarget下的所有方法 execution(* com.aop.*.*(..)) // aop包下的所有类的所有方法 execution(* com.aop..*.*(..)) // aop包下的所有包的所有类的所有方法
-
-
切点表达式抽取
<aop:aspect ref="aspect"> <aop:pointcut id="myPoint" expression="execution(* com.aop..*.*(..))"/> <aop:before method="before" pointcut-ref="myPoint"/> </aop:aspect>
注解实现AOP
导入坐标
<!-- Spring框架-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!-- aspectj团队实现的aop配置比较好-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.6</version>
</dependency>
<!-- spring-test-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
步骤:
-
创建目标接口和类,添加注解
@Component("xx")
,表示交给spring框架 -
创建切面类,添加注解
@Component("xxx")
,表示交给spring框架 -
在切面类中使用注解配置织入关系:类上加
@Aspect
表示切面,方法上添加注解类型@Component("myAspect") @Aspect public class MyAspect { @Before("execution(void com.aop.MyTarget.save())") public void before() { System.out.println("前置增强..."); } }
-
在applicationContext配置文件中开启组件扫描和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" 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/aop https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd" > <!-- 组件扫描--> <context:component-scan base-package="com.aop"/> <!-- aop自动代理--> <aop:aspectj-autoproxy/> </beans>
-
测试(创建一个目标类并执行)
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext-anno.xml") public class SpringTest { @Autowired private MyTarget target; @Test public void test01() throws SQLException { target.save(); } }
注解详解
-
通知的类型
@通知("切点表达式")
- Before, AfterReturning Around AfterThrowing After
-
切点表达式的抽取
@Component("myAspect") @org.aspectj.lang.annotation.Aspect public class Aspect { @Before("pointchut()") public void before() { System.out.println("before"); } @Poincut("execution(void baidu.proxy.cglib.Target.save())") public void pointcut(){} }