Spring学习(8)-AOP之ProxyFactoryBean、RegexMethodPointcutAdvisor、BeanNameAutoProxyCreator

前言:

上一篇学习了Spring AOP(面向切面编程)的基础,在Spring Bean 的配置文件中代理工厂ProxyFactoryBean 的写法似乎并没有见到切入点(pointcut),切面(aspect),织入(weaving)等等的英文所在,那是从哪里来的这些概念呢?这篇文章就告诉你aop的真正写法。

1 配置ProxyFactoryBean

上篇博客地址:Spring学习(7)-AOP面向切面编程

前言:

上一篇对于spring aop仅仅使用代理工厂ProxyFactoryBean ,这是最基础的spring aop写法,而且每一个通知/增强类都不是POJO(简单java对象),它都是实现了Spring提供的接口,复习一下:

  • 前置通知:MethodBeforeAdvice
  • 后置通知:AfterReturningAdvice
  • 环绕通知:MethodInterceptor(只有这个是aopalliance包内的,其他均为spring aop包)
  • 异常通知:ThrowsAdvice
  • 引介通知:IntroductionInterceptor

然后我们在xml文件中配置一个ProxyFactoryBean的 Bean,将实现接口的通知类放到代理工厂的interceptorNames属性中,再指定代理工厂代理的主业务接口实现主业务接口的目标类,将这些通知织入到目标类中,实现aop的思想。

我就再使用一下上次的例子:

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;
	}
}

BuyPhone(买手机的主业务接口)

package com.cheng.spring.service;

//主业务,购买手机服务
public interface BuyPhoneService {
	//编写业务方法
	public void giveMeAphone(String name, String pass);
}

BuyPhoneImpl (买手机的主业务实现类)

ackage com.cheng.spring.serviceImpl;
import com.cheng.spring.service.BuyPhoneService;

public class BuyPhoneServiceImpl implements BuyPhoneService{
	@Override
	public void giveMeAphone(String name, String pass) {
		System.out.println("我的身份只有8848钛金手机才配得上,8848你值得拥有");
	}
}

我想通过aop来将环绕通知类切入到我的买手机主业务实现类,需要在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:p="http://www.springframework.org/schema/p"
       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="buyPhoneService" class="com.cheng.spring.serviceImpl.BuyPhoneServiceImpl"/>

    <!-- 配置切面:环绕通知 -->
    <bean id="buyPhoneBeforeAdvice" class="com.cheng.spring.advice.BuyPhoneBeforeAdvice"/>
	
	<!-- 使用代理工厂将切面织入目标对象 -->
	 <bean id="factoryBean" class="org.springframework.aop.framework.ProxyFactoryBean"
          p:proxyInterfaces="com.cheng.spring.service.BuyPhoneService"
          p:target-ref="buyPhoneService"
          p:interceptorNames="buyPhoneAroundAdvice"
          p:proxyTargetClass="false"/>
</beans>

这样我们通过容器拿到代理工厂返回的接口,来调用它的方法

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");
	}
}

结果:
在这里插入图片描述

后语:

只配置代理工厂会对目标类的所有方法拦截

上方实现一个简单环绕通知,且是对买手机这个实现类的所有方法起作用的,什么意思呢?
我给接口和实现类加一个方法:

package com.cheng.spring.serviceImpl;
import com.cheng.spring.service.BuyPhoneService;

public class BuyPhoneServiceImpl implements BuyPhoneService{
	@Override
	public void giveMeAphone(String name, String pass) {
		System.out.println("我的身份只有8848钛金手机才配得上,8848你值得拥有");
	}

    @Override
    public void newMethod() {
        System.out.println("甩卖8848,只要998");
    }
}

我把新加的方法也运行一下

public class Test {
	public static void main(String[] args) {
		//通过context模块加载配置文件,获取bean实例
		ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
		//获得代理工厂返回的接口
		BuyPhone buyPhone = (BuyPhone)context.getBean("factoryBean");
		buyPhone.giveMeAphone("此成", "123");
		buyPhone.newMethod();
	}
}

结果:
在这里插入图片描述
我本来只想要giveMeAphone方法用这个环绕通知的,但这个实现类的所有方法都加上这个通知了,这并不是我想要的。

2 配置RegexMethodPointcutAdvisor

在spring aop 的support里有这个帮助类,帮助我们实现单个或多个指定方法的拦截(织入)

它相当于是一个过渡类,把目标对象的id先放到这个RegexMethodPointcutAdvisor的active属性内,再给patterns赋值告诉它你只需要什么方法被织入通知。最后把这个Bean的id再给原来的代理工厂,即可实现指定方法的切入。

<?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"
       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="buyPhoneService" class="com.cheng.spring.serviceImpl.BuyPhoneServiceImpl"/>

    <!-- 配置切面:环绕通知 -->
    <bean id="buyPhoneBeforeAdvice" class="com.cheng.spring.advice.BuyPhoneBeforeAdvice"/>
	
	<!-- 使用RegexpMethodPointcutAdvisor指定切入的是哪个方法 -->
    <!--patterns,如果有多个指定的值的话,可以使用,隔开,例如value=".*giveMeAphone,.*newMethod"-->
    <bean id="advisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"
          p:advice-ref="buyPhoneAroundAdvice"
          p:patterns=".*giveMeAphone"/>
          
	<!-- 使用代理工厂将切面织入目标对象 -->
	 <bean id="factoryBean" class="org.springframework.aop.framework.ProxyFactoryBean"
          p:proxyInterfaces="com.cheng.spring.service.BuyPhoneService"
          p:target-ref="buyPhoneService"
          p:interceptorNames="buyPhoneAroundAdvice"
          p:proxyTargetClass="false"/>
</beans>

再次运行一下:
在这里插入图片描述
完成我想要的单个方法的织入。

3 配置BeanNameAutoProxyCreator

它是帮助我们减少配置一堆代理工厂的类

比如说我上面配置的一个代理工厂的Bean,它只能帮助我把一个/多个切面织入到另一个目标对象中,如果我想多个目标对象都用这个切面,我就要配置多个代理工厂,这就造成了代码的重复。而且我要获得生产好的代理返回的Bean,还需要去配置里找代理工厂的id,实在有些麻烦。

使用BeanNameAutoProxyCreator可以方便的获得代理后的目标对象接口
这个类在Spring aop包中的framework中的autoproxy中

BeanNameAutoProxyCreator的属性:

  • p:beanNames=“你需要切入的主业务bean id” (可以写*Service,将自动找到以Service结尾的bean id)
  • p:interceptorNames=“切面id”

使用BeanNameAutoProxyCreator后,我们的配置变成这样

<?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"
       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="buyPhoneService" class="com.cheng.spring.serviceImpl.BuyPhoneServiceImpl"/>

    <!-- 配置切入面:环绕通知 -->
    <bean id="buyPhoneAroundAdvice" class="com.cheng.spring.advice.BuyPhoneAroundAdvice"/>

    <!-- 使用RegexpMethodPointcutAdvisor指定切入的是哪个方法 -->
    <!--patterns,如果有多个指定的值的话,可以使用,隔开,例如value=".*giveMeAphone,.*newMethod"-->
    <bean id="advisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"
          p:advice-ref="buyPhoneAroundAdvice"
          p:patterns=".*giveMeAphone"/>

    <!-- 自动返回代理工厂 -->
    <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"
          p:beanNames="*Service"
          p:interceptorNames="advisor"/>
</beans>

还是运行之前的主函数:
在这里插入图片描述
可以看到结果是一致的。
通过BeanNameAutoProxyCreator,我们可以配置多个目标对象来织入我们需要的切面。

4 总结

  1. ProxyFactoryBean :代理工厂,最经典基础的aop的配置,只使用它的话,会让切面(通知)织入到目标对象的所有方法内;
  2. RegexMethodPointcutAdvisor:方法切入,配合ProxyFactoryBean 使用,可以只让切面(通知)织入到目标对象的某几个方法内;
  3. BeanNameAutoProxyCreator:自动创建代理对象,如果需要一个切面织入到多个目标对象中,它是很棒的选择。

下一篇:Spring学习(9)-AOP之使用aop:config标签

文毕,如有助,赞之吾嗨~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值