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 {
	@Override
	public 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 {
	
	//前置通知,做用户身份校验
	@Override
	public 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 {
    @Override
    public 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 {
	@Override
	public 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代理要实现的接口,如果有多个接口,则可以使用以下格式赋值:
<list><value ></value><value ></value></list>
proxyTargetClass是否对类代理而不是接口,设置为 true 时,使用 CGLIB 代理
interceptorNames需要植入目标的 Advice
singleton返回的代理是否为单例,默认为 true(返回单实例)
optimize当设置为 true 时,强制使用 CGLIB

有两种方式
第一种:最基础的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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://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" />
        <!-- 如何生成代理,true:使用cglib,对于不是实现接口的类使用; 默认false :使用jdk动态代理,对于实现接口的类使用 -->
        <property name="proxyTargetClass" value="false" />
    </bean>
</beans>

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

<?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:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://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"/>

    <!-- 将前置通知织入到切入点 -->
    <!-- 织入过程通过spring代理类来进行实现:org.springframework.aop.framework.ProxyFactoryBean
        p                   表示对载入类属性的引用
        p:proxyInterfaces   指定需要织入的服务接口
        p:target-ref        指定织入的切入点
        p:interceptorNames  指定织入的切面(通知),通知配置的顺序根据先后关系来确定的
        p:proxyTargetClass  默认值为false,代理的是接口类,采用JDK进行动态代理
                            true:代理的不是接口类,使用CGLIB方式来进行代理。
                            false会自动判断,如果是要代理的是接口类就采用JDK进行动态代理,如果要代理的不是接口类就采用cglib代理
     -->
    <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 = new
		 ClassPathXmlApplicationContext("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

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值