AOP
AOP 面向切面编程,全称 Aspect Oriented Programming ,它是 OOP 的补充。OOP 关注的核心是对象,AOP 的核心是切面(Aspect)。AOP 可以在不修改功能代码本身的前提下,使用运行时动态代理的技术对已有代码逻辑增强。AOP 可以实现组件化、可插拔式的功能扩展,通过简单配置即可将功能增强到指定的切入点。
AOP概念
切面(Aspect)
: Aspect 声明类似于 Java 中的类声明,事务管理是AOP一个最典型的应用。在AOP中,切面一般使用 @Aspect
注解来使用,在XML 中,可以使用 <aop:aspect>
来定义一个切面。
连接点(Join Point)
: 一个在程序执行期间的某一个操作,就像是执行一个方法或者处理一个异常。在Spring AOP中,一个连接点就代表了一个方法的执行。
通知(Advice):
在切面中(类)的某个连接点(方法出)采取的动作,会有四种不同的通知方式: **around(环绕通知),before(前置通知),after(后置通知), exception(异常通知),return(返回通知)**。许多AOP框架(包括Spring)将建议把通知作为为拦截器,并在连接点周围维护一系列拦截器。
切入点(Pointcut):
表示一组连接点,通知与切入点表达式有关,并在切入点匹配的任何连接点处运行(例如执行具有特定名称的方法)。由切入点表达式匹配的连接点的概念是AOP的核心,Spring默认使用AspectJ切入点表达式语言。
介绍(Introduction):
introduction可以为原有的对象增加新的属性和方法。例如,你可以使用introduction使bean实现IsModified接口,以简化缓存。
目标对象(Target Object):
由一个或者多个切面代理的对象。也被称为”切面对象”。由于Spring AOP是使用运行时代理实现的,因此该对象始终是代理对象。
AOP代理(AOP proxy):
由AOP框架创建的对象,在Spring框架中,AOP代理对象有两种:JDK动态代理和CGLIB代理
织入(Weaving):
是指把增强应用到目标对象来创建新的代理对象的过程,它(例如 AspectJ 编译器)可以在编译时期,加载时期或者运行时期完成。与其他纯Java AOP框架一样,Spring AOP在运行时进行织入。
Spring AOP 中通知的分类
- 前置通知(Before Advice): 在目标方法被调用前调用通知功能;相关的类
org.springframework.aop.MethodBeforeAdvice
- 后置通知(After Advice): 在目标方法被调用之后调用通知功能;相关的类
org.springframework.aop.AfterReturningAdvice
- 返回通知(After-returning): 在目标方法成功执行之后调用通知功能;
- 异常通知(After-throwing): 在目标方法抛出异常之后调用通知功能;相关的类
org.springframework.aop.ThrowsAdvice
- 环绕通知(Around): 把整个目标方法包裹起来,在被调用前和调用之后分别调用通知功能相关的类
org.aopalliance.intercept.MethodInterceptor
Spring AOP 中织入的三种时期
编译期:
切面在目标类编译时被织入,这种方式需要特殊的编译器。AspectJ 的织入编译器就是以这种方式织入切面的。
类加载期:
切面在目标类加载到 JVM 时被织入,这种方式需要特殊的类加载器( ClassLoader ),它可以在目标类引入应用之前增强目标类的字节码。
运行期:
切面在应用运行的某个时期被织入。一般情况下,在织入切面时,AOP容器会为目标对象动态创建一个代理对象,Spring AOP 采用的就是这种织入方式。
流程:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| package pojo; @Component("landlord") public class Landlord { public void service() { System.out.println("签合同"); System.out.println("收房租"); } }
package aspect; @Component @Aspect class Broker { @Before("execution(* pojo.Landlord.service())") public void before(){ System.out.println("带租客看房"); System.out.println("谈价格"); } @After("execution(* pojo.Landlord.service())") public void after(){ System.out.println("交钥匙"); } }
<?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">
<context:component-scan base-package="aspect" /> <context:component-scan base-package="pojo" />
<aop:aspectj-autoproxy/>
</beans>
package test; public class TestSpring { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); Landlord landlord = (Landlord) context.getBean("landlord", Landlord.class); landlord.service(); } }
|
@Before
前置通知,在连接点方法前调用
@Around
环绕通知,它将覆盖原有方法,但是允许你通过反射调用原有方法
@After
后置通知,在连接点方法后调用
@AfterReturning
返回通知,在连接点方法执行并正常返回后调用,要求连接点方法在执行过程中没有发生异常
@AfterThrowing
异常通知,当连接点方法异常时调用
另外两种方法实现aop
我们编写两个 , 一个前置增强 一个后置增强,通过继承的方法进行前置后置
1 2 3 4 5 6 7 8 9 10
| public class Log implements MethodBeforeAdvice {
@Override public void before(Method method, Object[] objects, Object o) throws Throwable { System.out.println( o.getClass().getName() + "的" + method.getName() + "方法被执行了"); } }
|
1 2 3 4 5 6 7 8 9 10 11 12
| public class AfterLog implements AfterReturningAdvice { @Override public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { System.out.println("执行了" + target.getClass().getName() +"的"+method.getName()+"方法," +"返回值:"+returnValue); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| <?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 id="userService" class="com.kuang.service.UserServiceImpl"/> <bean id="log" class="com.kuang.log.Log"/> <bean id="afterLog" class="com.kuang.log.AfterLog"/>
<aop:config> <aop:pointcut id="pointcut" expression="execution(* com.kuang.service.UserServiceImpl.*(..))"/> <aop:advisor advice-ref="log" pointcut-ref="pointcut"/> <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/> </aop:config>
</beans>
|
通过自定义类实现aop
1 2 3 4 5 6 7 8
| public class DiyPointcut { public void before(){ System.out.println("---------方法执行前---------"); } public void after(){ System.out.println("---------方法执行后---------"); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13
|
<bean id="diy" class="com.kuang.config.DiyPointcut"/>
<aop:config> <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>
|