Spring系列第33篇:ProxyFactoryBean创建AOP代理

}

需求

在spring容器中注册上面这个类的bean,名称为service1,通过代理的方式来对这个bean进行增强,来2个通知

一个前置通知:在调用service1中的任意方法之前,输出一条信息:准备调用xxxx方法

一个环绕通知:复制统计所有方法的耗时。

下面是代码的实现

package com.javacode2018.aop.demo8.test1;

import org.aopalliance.intercept.MethodInterceptor;

import org.aopalliance.intercept.MethodInvocation;

import org.springframework.aop.MethodBeforeAdvice;

import org.springframework.aop.framework.ProxyFactoryBean;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.lang.Nullable;

import java.lang.reflect.Method;

@Configuration

public class MainConfig1 {

//注册目标对象

@Bean

public Service1 service1() {

return new Service1();

}

//注册一个前置通知

@Bean

public MethodBeforeAdvice beforeAdvice() {

MethodBeforeAdvice advice = new MethodBeforeAdvice() {

@Override

public void before(Method method, Object[] args, @Nullable Object target) throws Throwable {

System.out.println(“准备调用:” + method);

}

};

return advice;

}

//注册一个后置通知

@Bean

public MethodInterceptor costTimeInterceptor() {

MethodInterceptor methodInterceptor = new MethodInterceptor() {

@Override

public Object invoke(MethodInvocation invocation) throws Throwable {

long starTime = System.nanoTime();

Object result = invocation.proceed();

long endTime = System.nanoTime();

System.out.println(invocation.getMethod() + “,耗时(纳秒):” + (endTime - starTime));

return result;

}

};

return methodInterceptor;

}

//注册ProxyFactoryBean

@Bean

public ProxyFactoryBean service1Proxy() {

//1.创建ProxyFactoryBean

ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();

//2.设置目标对象的bean名称

proxyFactoryBean.setTargetName(“service1”);

//3.设置拦截器的bean名称列表,此处2个(advice1和advice2)

proxyFactoryBean.setInterceptorNames(“beforeAdvice”, “costTimeInterceptor”);

return proxyFactoryBean;

}

}

下面启动spring容器,并获取代理对象

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig1.class);

//获取代理对象,代理对象bean的名称为注册ProxyFactoryBean的名称,即:service1Proxy

Service1 bean = context.getBean(“service1Proxy”, Service1.class);

System.out.println(“----------------------”);

//调用代理的方法

bean.m1();

System.out.println(“----------------------”);

//调用代理的方法

bean.m2();

运行输出


准备调用:public void com.javacode2018.aop.demo8.test1.Service1.m1()

我是 m1 方法

public void com.javacode2018.aop.demo8.test1.Service1.m1(),耗时(纳秒):8680400


准备调用:public void com.javacode2018.aop.demo8.test1.Service1.m2()

我是 m2 方法

public void com.javacode2018.aop.demo8.test1.Service1.m2(),耗时(纳秒):82400

从输出中可以看到,目标对象service1已经被增强了。

ProxyFactoryBean中的interceptorNames


interceptorNames用来指定拦截器的bean名称列表,常用的2种方式。

  • 批量的方式

  • 非批量的方式

批量的方式

使用方法

proxyFactoryBean.setInterceptorNames(“需要匹配的bean名称*”);

需要匹配的bean名称后面跟上一个*,可以用来批量的匹配,如:interceptor*,此时spring会从容器中找到下面2中类型的所有bean,bean名称以interceptor开头的将作为增强器

org.springframework.aop.Advisor

org.aopalliance.intercept.Interceptor

这个地方使用的时候需要注意,批量的方式注册的时候,如果增强器的类型不是上面2种类型的,比如下面3种类型通知,我们需要将其包装为Advisor才可以,而MethodInterceptorInterceptor类型的,可以不用包装为Advisor类型的。

MethodBeforeAdvice(方法前置通知)

AfterReturningAdvice(方法后置通知)

ThrowsAdvice(异常通知)

下面来个案例感受一下。

案例

下面批量注册2个增强器。

package com.javacode2018.aop.demo8.test2;

import com.javacode2018.aop.demo8.test1.Service1;

import org.aopalliance.intercept.MethodInterceptor;

import org.aopalliance.intercept.MethodInvocation;

import org.springframework.aop.Advisor;

import org.springframework.aop.MethodBeforeAdvice;

import org.springframework.aop.framework.ProxyFactoryBean;

import org.springframework.aop.support.DefaultPointcutAdvisor;

import org.springframework.context.annotation.Bean;

import org.springframework.lang.Nullable;

import java.lang.reflect.Method;

public class MainConfig2 {

//注册目标对象

@Bean

public Service1 service1() {

return new Service1();

}

//定义一个增强器:interceptor1,内部是一个前置通知,需要将其包装为Advisor类型的

@Bean

public Advisor interceptor1() {

MethodBeforeAdvice advice = new MethodBeforeAdvice() {

@Override

public void before(Method method, Object[] args, @Nullable Object target) throws Throwable {

System.out.println(“准备调用:” + method);

}

};

DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor();

advisor.setAdvice(advice);

return advisor;

}

//定义一个增强器:interceptor2

@Bean

public MethodInterceptor interceptor2() {

MethodInterceptor methodInterceptor = new MethodInterceptor() {

@Override

public Object invoke(MethodInvocation invocation) throws Throwable {

long starTime = System.nanoTime();

Object result = invocation.proceed();

long endTime = System.nanoTime();

System.out.println(invocation.getMethod() + “,耗时(纳秒):” + (endTime - starTime));

return result;

}

};

return methodInterceptor;

}

//注册ProxyFactoryBean

@Bean

public ProxyFactoryBean service1Proxy() {

//1.创建ProxyFactoryBean

ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();

//2.设置目标对象的bean名称

proxyFactoryBean.setTargetName(“service1”);

//3.设置拦截器的bean名称列表,此处批量注册

proxyFactoryBean.setInterceptorNames(“interceptor*”); //@1

return proxyFactoryBean;

}

}

上面定义了2个增强器:

interceptor1:前置通知,包装为Advisor类型了

interceptor2:环绕通知,MethodInterceptor类型的

测试代码

@Test

public void test2() {

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig2.class);

//获取代理对象,代理对象bean的名称为注册ProxyFactoryBean的名称,即:service1Proxy

Service1 bean = context.getBean(“service1Proxy”, Service1.class);

System.out.println(“----------------------”);

//调用代理的方法

bean.m1();

System.out.println(“----------------------”);

//调用代理的方法

bean.m2();

}

运行输出


准备调用:public void com.javacode2018.aop.demo8.test1.Service1.m1()

我是 m1 方法

public void com.javacode2018.aop.demo8.test1.Service1.m1(),耗时(纳秒):10326200


准备调用:public void com.javacode2018.aop.demo8.test1.Service1.m2()

我是 m2 方法

public void com.javacode2018.aop.demo8.test1.Service1.m2(),耗时(纳秒):52000

非批量的方式

用法

非批量的方式,需要注册多个增强器,需明确的指定多个增强器的bean名称,多个增强器按照参数中指定的顺序执行,如

proxyFactoryBean.setInterceptorNames(“advice1”,“advice2”);

advice1、advice2对应的bean类型必须为下面列表中指定的类型,类型这块比匹配的方式范围广一些

MethodBeforeAdvice(方法前置通知)

AfterReturningAdvice(方法后置通知)

ThrowsAdvice(异常通知)

org.aopalliance.intercept.MethodInterceptor(环绕通知)

org.springframework.aop.Advisor(顾问)

下面来个案例。

案例

这次给service1来3个通知:前置、环绕、后置

package com.javacode2018.aop.demo8.test3;

import com.javacode2018.aop.demo8.test1.Service1;

import org.aopalliance.intercept.MethodInterceptor;

import org.aopalliance.intercept.MethodInvocation;

import org.springframework.aop.AfterReturningAdvice;

import org.springframework.aop.MethodBeforeAdvice;

import org.springframework.aop.framework.ProxyFactoryBean;

import org.springframework.context.annotation.Bean;

import org.springframework.lang.Nullable;

import java.lang.reflect.Method;

public class MainConfig3 {

//注册目标对象

@Bean

public Service1 service1() {

return new Service1();

}

//定义一个前置通知

@Bean

public MethodBeforeAdvice methodBeforeAdvice() {

MethodBeforeAdvice methodBeforeAdvice = new MethodBeforeAdvice() {

@Override

public void before(Method method, Object[] args, @Nullable Object target) throws Throwable {

System.out.println(“准备调用:” + method);

}

};

return methodBeforeAdvice;

}

//定义一个环绕通知

@Bean

public MethodInterceptor methodInterceptor() {

MethodInterceptor methodInterceptor = new MethodInterceptor() {

@Override

public Object invoke(MethodInvocation invocation) throws Throwable {

long starTime = System.nanoTime();

Object result = invocation.proceed();

long endTime = System.nanoTime();

System.out.println(invocation.getMethod() + “,耗时(纳秒):” + (endTime - starTime));

return result;

}

};

return methodInterceptor;

}

//定义一个后置通知

@Bean

public AfterReturningAdvice afterReturningAdvice() {

AfterReturningAdvice afterReturningAdvice = new AfterReturningAdvice() {

@Override

public void afterReturning(@Nullable Object returnValue, Method method, Object[] args, @Nullable Object target) throws Throwable {

System.out.println(method + “,执行完毕!”);

}

};

return afterReturningAdvice;

}

//注册ProxyFactoryBean

@Bean

public ProxyFactoryBean service1Proxy() {

//1.创建ProxyFactoryBean

ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();

//2.设置目标对象的bean名称

proxyFactoryBean.setTargetName(“service1”);

//3.设置拦截器的bean名称列表,此处批量注册

proxyFactoryBean.setInterceptorNames(“methodBeforeAdvice”, “methodInterceptor”, “afterReturningAdvice”);

return proxyFactoryBean;

}

}

测试代码

@Test

public void test3() {

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig3.class);

//获取代理对象,代理对象bean的名称为注册ProxyFactoryBean的名称,即:service1Proxy

Service1 bean = context.getBean(“service1Proxy”, Service1.class);

System.out.println(“----------------------”);

//调用代理的方法

bean.m1();

System.out.println(“----------------------”);

//调用代理的方法

bean.m2();

}

运行输出


准备调用:public void com.javacode2018.aop.demo8.test1.Service1.m1()

我是 m1 方法

public void com.javacode2018.aop.demo8.test1.Service1.m1(),执行完毕!

public void com.javacode2018.aop.demo8.test1.Service1.m1(),耗时(纳秒):12724100


准备调用:public void com.javacode2018.aop.demo8.test1.Service1.m2()

我是 m2 方法

public void com.javacode2018.aop.demo8.test1.Service1.m2(),执行完毕!

public void com.javacode2018.aop.demo8.test1.Service1.m2(),耗时(纳秒):76700

源码解析


重点在于下面这个方法

org.springframework.aop.framework.ProxyFactoryBean#getObject

源码:

public Object getObject() throws BeansException {

//初始化advisor(拦截器)链

initializeAdvisorChain();

//是否是单例

if (isSingleton()) {

//创建单例代理对象

return getSingletonInstance();

}

else {

//创建多例代理对象

return newPrototypeInstance();

}

}

initializeAdvisorChain方法,用来初始化advisor(拦截器)链,是根据interceptorNames配置,找到spring容器中符合条件的拦截器,将其放入创建aop代理的配置中

private synchronized void initializeAdvisorChain() throws AopConfigException, BeansException {

if (!ObjectUtils.isEmpty(this.interceptorNames)) {

// 轮询 interceptorNames

for (String name : this.interceptorNames) {

//批量注册的方式:判断name是否以*结尾

if (name.endsWith(GLOBAL_SUFFIX)) {

//@1:从容器中匹配查找匹配的增强器,将其添加到aop配置中

addGlobalAdvisor((ListableBeanFactory) this.beanFactory,

name.substring(0, name.length() - GLOBAL_SUFFIX.length()));

}

else {

//非匹配的方式:按照name查找bean,将其包装为Advisor丢到aop配置中

Object advice;

//从容器中查找bean

advice = this.beanFactory.getBean(name);

//@2:将advice添加到拦截器列表中

addAdvisorOnChainCreation(advice, name);

}

}

}

}

@1:addGlobalAdvisor批量的方式Advisor,看一下源码,比较简单

/**

* 添加所有全局拦截器和切入点,

* 容器中所有类型为Advisor/Interceptor的bean,bean名称prefix开头的都会将其添加到拦截器链中

*/

private void addGlobalAdvisor(ListableBeanFactory beanFactory, String prefix) {

//获取容器中所有类型为Advisor的bean

String[] globalAdvisorNames =

BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, Advisor.class);

//获取容器中所有类型为Interceptor的bean

String[] globalInterceptorNames =

BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, Interceptor.class);

List beans = new ArrayList<>(globalAdvisorNames.length + globalInterceptorNames.length);

Map<Object, String> names = new HashMap<>(beans.size());

for (String name : globalAdvisorNames) {

Object bean = beanFactory.getBean(name);

beans.add(bean);

names.put(bean, name);

}

for (String name : globalInterceptorNames) {

Object bean = beanFactory.getBean(name);

beans.add(bean);

names.put(bean, name);

}

//对beans进行排序,可以实现Ordered接口,排序规则:order asc

AnnotationAwareOrderComparator.sort(beans);

for (Object bean : beans) {

String name = names.get(bean);

//判断bean是否已prefix开头

if (name.startsWith(prefix)) {

//将其添加到拦截器链中

addAdvisorOnChainCreation(bean, name);

}

}

}

@2:addAdvisorOnChainCreation

private void addAdvisorOnChainCreation(Object next, String name) {

//namedBeanToAdvisor用来将bean转换为advisor

Advisor advisor = namedBeanToAdvisor(next);

//将advisor添加到拦截器链中

addAdvisor(advisor);

}

namedBeanToAdvisor方法

private AdvisorAdapterRegistry advisorAdapterRegistry = new DefaultAdvisorAdapterRegistry();

private Advisor namedBeanToAdvisor(Object next) {

//将对象包装为Advisor对象

return this.advisorAdapterRegistry.wrap(next);

}

AdvisorAdapterRegistry不清楚的,看一下上一篇文章:Spring系列第32篇:AOP核心源码、原理详解

advisorAdapterRegistry#wrap方法将adviceObject包装为Advisor对象,代码如下,比较简单

public Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException {

if (adviceObject instanceof Advisor) {

return (Advisor) adviceObject;

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
img

最后

面试是跳槽涨薪最直接有效的方式,马上金九银十来了,各位做好面试造飞机,工作拧螺丝的准备了吗?

掌握了这些知识点,面试时在候选人中又可以夺目不少,暴击9999点。机会都是留给有准备的人,只有充足的准备,才可能让自己可以在候选人中脱颖而出。

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
img

也去过华为、OPPO等大厂,18年进入阿里一直到现在。**

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-RSpkoaWY-1712871999904)]
[外链图片转存中…(img-p95nrL36-1712871999905)]
[外链图片转存中…(img-6tj0awbi-1712871999905)]
[外链图片转存中…(img-0NhyVoif-1712871999906)]
[外链图片转存中…(img-TJn5y8j4-1712871999906)]
[外链图片转存中…(img-YofgjcH4-1712871999906)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
[外链图片转存中…(img-0krcyUA0-1712871999906)]

最后

面试是跳槽涨薪最直接有效的方式,马上金九银十来了,各位做好面试造飞机,工作拧螺丝的准备了吗?

掌握了这些知识点,面试时在候选人中又可以夺目不少,暴击9999点。机会都是留给有准备的人,只有充足的准备,才可能让自己可以在候选人中脱颖而出。

[外链图片转存中…(img-aW20VMq2-1712871999907)]

[外链图片转存中…(img-pqZ42h0n-1712871999907)]

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
[外链图片转存中…(img-YI7ifv1q-1712871999907)]

  • 25
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值