Spring系列第14篇:单例bean中使用多例bean,你未必会玩

ServiceB.java

package com.javacode2018.lesson001.demo13.lookupmethod;

public class ServiceB {

public void say() {

ServiceA serviceA = this.getServiceA();

System.out.println(“this:” + this + “,serviceA:” + serviceA);

}

public ServiceA getServiceA() { //@1

return null;

}

}

注意上面的@1,这个方法中返回了一个null对象,下面我们通过spring来创建上面2个bean对象,然后让spring对上面的getServiceA方法进行拦截,返回指定的bean,如下:

lookupmethod.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”

xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-4.3.xsd">

注意上面的配置,重点在于这行配置:

当我们调用serviceB中的getServiceA方法的时候,这个方法会拦截,然后会按照lookup-method元素中bean属性的值作为bean的名称去容器中查找对应bean,然后作为getServiceA的返回值返回,即调用getServiceA方法的时候,会从spring容器中查找id为serviceA的bean然后返回。

测试用例

LookupMethodTest中加个方法,如下:

@Test

public void lookupmethod() {

String beanXml = “classpath:/com/javacode2018/lesson001/demo13/lookupmethod.xml”;

ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(beanXml);

System.out.println(context.getBean(com.javacode2018.lesson001.demo13.lookupmethod.ServiceA.class)); //@1

System.out.println(context.getBean(com.javacode2018.lesson001.demo13.lookupmethod.ServiceA.class)); //@2

System.out.println(“serviceB中的serviceA”);

com.javacode2018.lesson001.demo13.lookupmethod.ServiceB serviceB = context.getBean(com.javacode2018.lesson001.demo13.lookupmethod.ServiceB.class); //@3

serviceB.say();

serviceB.say();

}

运行看看效果:

com.javacode2018.lesson001.demo13.lookupmethod.ServiceA@619713e5

com.javacode2018.lesson001.demo13.lookupmethod.ServiceA@708f5957

serviceB中的serviceA

this:com.javacode2018.lesson001.demo13.lookupmethod.ServiceB E n h a n c e r B y S p r i n g C G L I B EnhancerBySpringCGLIB EnhancerBySpringCGLIBaca8be5a@68999068,serviceA:com.javacode2018.lesson001.demo13.lookupmethod.ServiceA@7722c3c3

this:com.javacode2018.lesson001.demo13.lookupmethod.ServiceB E n h a n c e r B y S p r i n g C G L I B EnhancerBySpringCGLIB EnhancerBySpringCGLIBaca8be5a@68999068,serviceA:com.javacode2018.lesson001.demo13.lookupmethod.ServiceA@2ef3eef9

注意最后2行的输出,serviceA是调用this.getServiceA()方法获取 ,源码中这个方法返回的是null,但是spring内部对这个方法进行了拦截,每次调用这个方法的时候,都会去容器中查找serviceA,然后返回,所以上面最后2行的输出中serviceA是有值的,并且是不同的serviceA实例。

lookup-method:看其名字,就知道意思:方法查找,调用name属性指定的方法的时候,spring会对这个方法进行拦截,然后去容器中查找lookup-method元素中bean属性指定的bean,然后将找到的bean作为方法的返回值返回。

这个地方底层是使用cglib代理实现的,后面有篇文章会详细介绍代理的2种实现,到时候大家注意下,spring中很多牛逼的功能都是靠代理实现的。

spring提供的还有一个功能,同样可以可以解决上面单例bean中用到多例bean的问题,也就是下面我们要说的replaced-method。

replaced-method:方法替换


replaced-method:方法替换,比如我们要调用serviceB中的getServiceA的时候,我们可以对serviceB这个bean中的getServiceA方法进行拦截,把这个调用请求转发到一个替换者处理。这就是replaced-method可以实现的功能,比lookup-method更强大更灵活。

replaced-method的使用3个步骤
步骤一:定义替换者

自定义一个替换者,替换者需要实现spring中的MethodReplacer接口,看一下这个接口的定义:

package org.springframework.beans.factory.support;

import java.lang.reflect.Method;

public interface MethodReplacer {

/**

* @param obj 被替换方法的目标对象

* @param method 目标对象的方法

* @param args 方法的参数

* @return return value for the method

*/

Object reimplement(Object obj, Method method, Object[] args) throws Throwable;

}

当调用目标对象需要被替换的方法的时候,这个调用请求会被转发到上面的替换者的reimplement方法进行处理。

如:

package com.javacode2018.lesson001.demo14;

import org.springframework.beans.BeansException;

import org.springframework.beans.factory.support.MethodReplacer;

import org.springframework.context.ApplicationContext;

import org.springframework.context.ApplicationContextAware;

import java.lang.reflect.Method;

import java.util.Map;

/**

* servieB的方法替换者

*/

public class ServiceBMethodReplacer implements MethodReplacer, ApplicationContextAware {

@Override

public Object reimplement(Object obj, Method method, Object[] args) throws Throwable {

return this.context.getBean(ServiceA.class);

}

private ApplicationContext context;

@Override

public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {

this.context = applicationContext;

}

}

步骤二:定义替换者bean
步骤二:通过replaced-method元素配置目标bean需要被替换的方法

注意上面的replaced-method元素的2个属性:

name:用于指定当前bean需要被替换的方法

replacer:替换者,即实现了MethodReplacer接口的类对应的bean

上面配置中当调用serviceB的getServiceA的时候,会自动调用serviceAMethodReplacer这个bean中的reimplement方法进行处理。

案例
ServiceA.java

package com.javacode2018.lesson001.demo14;

public class ServiceA {

}

ServiceB.java

package com.javacode2018.lesson001.demo14;

public class ServiceB {

public void say() {

ServiceA serviceA = this.getServiceA();

System.out.println(“this:” + this + “,serviceA:” + serviceA);

}

public ServiceA getServiceA() { //@1

return null;

}

}

上面getServiceA需要返回一个ServiceA对象,此处返回的是null,下面我们通过spring对这个方法进行替换,然后从容器中获取ServiceA然后返回,下面我们来看看替换者的代码。

替换者ServiceBMethodReplacer.java

这个替换者会替换ServiceB中的getServiceA方法

package com.javacode2018.lesson001.demo14;

import org.springframework.beans.BeansException;

import org.springframework.beans.factory.support.MethodReplacer;

import org.springframework.context.ApplicationContext;

import org.springframework.context.ApplicationContextAware;

import java.lang.reflect.Method;

import java.util.Map;

/**

* servieB的方法替换者

*/

public class ServiceBMethodReplacer implements MethodReplacer, ApplicationContextAware {

@Override

public Object reimplement(Object obj, Method method, Object[] args) throws Throwable {

return this.context.getBean(ServiceA.class);

}

private ApplicationContext context;

@Override

public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {

this.context = applicationContext;

}

}

spring中bean配置文件:replacedmethod.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”

xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-4.3.xsd">

测试用例ReplacedMethodTest

package com.javacode2018.lesson001.demo14;

import org.junit.Test;

import org.springframework.context.support.ClassPathXmlApplicationContext;

/**

* 公众号:路人甲Java,工作10年的前阿里P7分享Java、算法、数据库方面的技术干货!坚信用技术改变命运,让家人过上更体面的生活!

  • replaced-method:方法替换

*/

public class ReplacedMethodTest {

@Test

public void replacedmethod() {

String beanXml = “classpath:/com/javacode2018/lesson001/demo14/replacedmethod.xml”;

ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(beanXml);

System.out.println(context.getBean(ServiceA.class)); //@1

System.out.println(context.getBean(ServiceA.class)); //@2

System.out.println(“serviceB中的serviceA”);

ServiceB serviceB = context.getBean(ServiceB.class); //@3

serviceB.say();

serviceB.say();

}

}

运行输出

com.javacode2018.lesson001.demo14.ServiceA@7722c3c3

com.javacode2018.lesson001.demo14.ServiceA@2ef3eef9

serviceB中的serviceA

this:com.javacode2018.lesson001.demo14.ServiceB E n h a n c e r B y S p r i n g C G L I B EnhancerBySpringCGLIB EnhancerBySpringCGLIB21bb8912@243c4f91,serviceA:com.javacode2018.lesson001.demo14.ServiceA@291ae

this:com.javacode2018.lesson001.demo14.ServiceB E n h a n c e r B y S p r i n g C G L I B EnhancerBySpringCGLIB EnhancerBySpringCGLIB21bb8912@243c4f91,serviceA:com.javacode2018.lesson001.demo14.ServiceA@61df66b6

从输出中可以看出结果和lookup-method案例效果差不多,实现了单例bean中使用多例bean的案例。

输出中都有CGLIB这样的字样,说明这玩意也是通过cglib实现的。

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

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

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

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

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

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

最后

由于篇幅有限,这里就不一一罗列了,20道常见面试题(含答案)+21条MySQL性能调优经验小编已整理成Word文档或PDF文档

MySQL全家桶笔记

还有更多面试复习笔记分享如下

Java架构专题面试复习

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门即可获取!
1711860006858)]

[外链图片转存中…(img-1NkhJLon-1711860006859)]

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

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

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

最后

由于篇幅有限,这里就不一一罗列了,20道常见面试题(含答案)+21条MySQL性能调优经验小编已整理成Word文档或PDF文档

[外链图片转存中…(img-oUhb9Ek4-1711860006859)]

还有更多面试复习笔记分享如下

[外链图片转存中…(img-9BWOaaOo-1711860006860)]

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门即可获取!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值