Spring学习(7)-AOP面向切面编程

1 AOP

1.1 AOP是什么

如果说 IoC 是 Spring 的核心,那么面向切面编程就是 Spring 最为重要的功能之一了,在数据库事务中切面编程被广泛使用。
AOPAspect Oriented Program 面向切面编程,它将业务逻辑的各个部分进行隔离,使开发人员在编写业务逻辑时可以专心于核心业务,从而提高了开发效率。

首先,在面向切面编程的思想里面,把功能分为核心业务功能,和周边功能。

  • 所谓的核心业务,比如登陆,增加数据,删除数据都叫核心业务(主业务)
  • 所谓的周边功能,比如性能统计,日志,事务管理等等(辅助业务)

核心业务在 Spring 的面向切面编程AOP思想里,即被定义为切入点
周边功能在 Spring 的面向切面编程AOP思想里,即被定义为切面

在面向切面编程AOP的思想里面,核心业务功能和切面功能分别独立进行开发,然后把切面功能和核心业务功能 “编织” 在一起,这就叫AOP

1.2 AOP 的目的

  • AOP能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码降低模块间的耦合度并有利于未来的可拓展性和可维护性。

1.3 AOP 当中的概念

名称说明
Joinpoint(连接点)指那些被拦截到的点,在 Spring 中,可以被动态代理拦截目标类的方法。
Pointcut(切入点)指要对哪些 Joinpoint 进行拦截,即被拦截的连接点。
Advice(通知)指拦截到 Joinpoint 之后要做的事情,即对切入点增强/通知的内容。
Target(目标)指代理的目标对象。
Weaving(织入)把切面(Aspect)加入到目标对象(target),并创建出代理对象(proxy)的过程,由Spring 来完成。
Proxy(代理)指生成的代理对象。
Aspect(切面)切入点+通知的结合。通俗点就是:在什么时机,什么地方,做什么增强/通知!

1.4 Spring aop所需要的包:

  • Spring四大基础包(core、beans、context、expression
  • 日志包:commons-logging.jar
  • aop扩展包(用来环绕通知):aopalliance.jar(aop联盟)
    在这里插入图片描述
    提供一个百度云连接:链接提取码:5nnr

1.5 Spring aop常用增强/通知方法

类型名称说明
前置通知org.springframework.aop.MethodBeforeAdvice在方法之前自动执行的通知称为前置通知,可以应用于权限管理等功能。
后置通知org.springframework.aop.AfterReturningAdvice在方法之后自动执行的通知称为后置通知,可以应用于关闭流、上传文件、删除临时文件等功能。
环绕通知org.aopalliance.intercept.MethodInterceptor在方法前后自动执行的通知称为环绕通知,可以应用于日志、事务管理等功能。
异常通知org.springframework.aop.ThrowsAdvice在方法抛出异常时自动执行的通知称为异常通知,可以应用于处理异常记录日志等功能。
引介通知org.springframework.aop.IntroductionInterceptor在目标类中添加一些新的方法和属性,可以应用于修改旧版本程序(增强类)。

2 AOP实例

2.1 idea 建立一个普通的web 4.0项目,并导入包

项目目录
在这里插入图片描述

2.2 代码

一个买手机的小例子,增加一些趣味性
BuyPhone接口

package com.cheng.spring.service;
//主业务,购买手机服务
public interface BuyPhone {//编写业务方法public void giveMeAphone(String name, String pass);
}

BuyPhoneImpl实现类

package com.cheng.spring.serviceImpl;
import com.cheng.spring.service.BuyPhone;
public class BuyPhoneImpl implements BuyPhone {@Overridepublic void giveMeAphone(String name, String pass) {System.out.println("我的身份只有8848钛金手机才配得上,8848你值得拥有");}
}

三种AOP增强/通知方法
前置通知
BuyPhoneBeforeAdvice

package com.cheng.spring.advice;import java.lang.reflect.Method;import org.springframework.aop.MethodBeforeAdvice;//购买手机业务的前置通知
//前置通知需要实现MethodBeforeAdvice接口
public class BuyPhoneBeforeAdvice implements MethodBeforeAdvice {//前置通知,做用户身份校验@Overridepublic void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {//arg1是主业务当中提供的参数String name = arg1[0].toString();String pass = arg1[1].toString();if(name.equals("此成")&&pass.equals("123")){System.out.println(name+"同学,欢迎来本站购物---前置通知");}else{throw new RuntimeException("对不起,您不是本站会员,购物前请先注册");}}
}

后置通知
BuyPhoneAfterAdvice

package com.cheng.spring.advice;import java.lang.reflect.Method;import org.springframework.aop.AfterReturningAdvice;//后置通知需要实现AfterReturningAdvice接口
public class BuyPhoneAfterAdvice implements AfterReturningAdvice {@Overridepublic void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable {System.out.println("自从用了8848,身体倍儿棒,吃嘛嘛香。---后置通知");}
}

环绕通知
BuyPhoneAroundAdvice

package com.cheng.spring.advice;import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;//环绕通知需要实现MethodInterceptor接口
//环绕通知:在主业务的前,后执行服务功能
public class BuyPhoneAroundAdvice implements MethodInterceptor {@Overridepublic Object invoke(MethodInvocation invocation) throws Throwable {//环绕通知的前置System.out.println("此成同学,您定制的钛金手机给您送来了,赶紧拍照发朋友圈---环绕前置");//执行核心业务,作为环绕的前后通知分割点invocation.proceed();System.out.println("用了两天就坏了,智商税还交的不够啊!---环绕后置");//环绕通知的后置return null;}
}

Spring Bean配置 (applicationContext.xml)
Spring 创建一个 AOP 代理的基本方法是使用代理工厂:
org.springframework.aop.framework.ProxyFactoryBean,这个类对应的切入点和通知提供了比较完整的控制能力,并可以生成指定的内容。
ProxyFactoryBean 的常用属性

属性名称描 述
target代理的目标对象
proxyInterfaces代理要实现的接口,如果有多个接口,则可以使用以下格式赋值:
proxyTargetClass是否对类代理而不是接口,设置为 true 时,使用 CGLIB 代理
interceptorNames需要植入目标的 Advice
singleton返回的代理是否为单例,默认为 true(返回单实例)
optimize当设置为 true 时,强制使用 CGLIB

有两种方式
第一种:最基础的AOP配置


<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="buyPhoneImpl" class="com.cheng.spring.serviceImpl.BuyPhoneImpl"/><bean id="buyPhoneBeforeAdvice" class="com.cheng.spring.advice.BuyPhoneBeforeAdvice"/><bean id="buyPhoneAfterAdvice" class="com.cheng.spring.advice.BuyPhoneAfterAdvice"/><bean id="buyPhoneAroundAdvice" class="com.cheng.spring.advice.BuyPhoneAroundAdvice"/><bean id="factoryBean" class="org.springframework.aop.framework.ProxyFactoryBean"><property name="proxyInterfaces" value="com.cheng.spring.service.BuyPhone"/><property name="target" ref="buyPhoneImpl" /><property name="interceptorNames" value="buyPhoneAroundAdvice,buyPhoneBeforeAdvice,buyPhoneAfterAdvice" /><property name="proxyTargetClass" value="false" />bean>
beans>

第二种:使用p标签配置,需要在中引用p标签


<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:p="http://www.springframework.org/schema/p"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="buyPhoneImpl" class="com.cheng.spring.serviceImpl.BuyPhoneImpl"/><bean id="buyPhoneBeforeAdvice" class="com.cheng.spring.advice.BuyPhoneBeforeAdvice"/><bean id="buyPhoneAfterAdvice" class="com.cheng.spring.advice.BuyPhoneAfterAdvice"/><bean id="buyPhoneAroundAdvice" class="com.cheng.spring.advice.BuyPhoneAroundAdvice"/><bean id="factoryBean" class="org.springframework.aop.framework.ProxyFactoryBean"p:proxyInterfaces="com.cheng.spring.service.BuyPhone"p:target-ref="buyPhoneImpl"p:interceptorNames="buyPhoneBeforeAdvice,buyPhoneAfterAdvice,buyPhoneAroundAdvice"p:proxyTargetClass="false"/>
beans>

Test主函数

package com.cheng.spring.test;import com.cheng.spring.service.BuyPhone;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class Test {public static void main(String[] args) {//如何通过context模块加载配置文件,获取bean实例ApplicationContext context = newClassPathXmlApplicationContext("applicationContext.xml");//如果要启动主业务,我需要获取哪个bean的实例?BuyPhone buyPhone = (BuyPhone)context.getBean("factoryBean");buyPhone.giveMeAphone("此成", "123");}
}

2.3 执行结果

在这里插入图片描述
可以看到只执行了buyPhone的一个方法,三个增强/通知也出现在控制台了,这便是AOP的面向切面的编程。

在这次执行中,环绕通知前置在前置通知前,环绕通知后置在后置通知后,是不是说明环绕通知的优先级高呢?
我们将buyPhoneAroundAdvice放到最后一个再运行试试

 <bean id="factoryBean" class="org.springframework.aop.framework.ProxyFactoryBean"p:proxyInterfaces="com.cheng.spring.service.BuyPhone"p:target-ref="buyPhoneImpl"p:interceptorNames="buyPhoneBeforeAdvice,buyPhoneAfterAdvice,buyPhoneAroundAdvice"/>

结果:
在这里插入图片描述
可以看到,优先级跟上一次运行的不同了,所以他们的优先级取决于p:interceptorNames配置的先后


文毕,本文如有助,赞后吾更嗨~
下一篇:Spring学习(8)-AOP之ProxyFactoryBean、RegexMethodPointcutAdvisor、BeanNameAutoProxyCreator


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部