前言:最近几个月很忙,都没有时间写文章了,今天周末刚好忙完下班相对早点(20:00下班)就在家把之前想总结的知识点写出来,于是就有了这篇文章。虽无很高深的技术,但小技巧有大用处。
有时我们经常需要将实现了某个基类或某个接口的所有Bean进行分类管理,在需要用到的时候按需获取实现了某个基类或某个接口的Bean实例对象,那么我们就需要Bean管理类工厂(即:工厂模式),实现Bean管理类工厂我总结了目前已知且常用的实现方式,敬请各位看官欣赏,如是不足或更好建议欢迎评论区留言指正,谢谢!
为了便于演示,我先自定义如下接口:
/**
* @author zuowenjun
* <pre>www.zuowenjun.cn</pre>
*/
public interface IDemo {
String getValue();
int doFor();
}
然后定义3个实现了上述接口的Service Bean类:(注意到Bean类上方还有@DemoFactoryNeedBean这个先不用管,后面的方式中会有用到)
@Service
public class DemoService1 implements IDemo {
@Override
public String getValue() {
return "DemoService1.getValue by 梦在旅途 zuowj.cnblogs.com";
}
@Override
public int doFor() {
return 1;
}
}
@DemoFactoryNeedBean
@Service
public class DemoService2 implements IDemo {
@Override
public String getValue() {
return "DemoService2.getValue by 梦在旅途 zuowj.cnblogs.com";
}
@Override
public int doFor() {
return 2;
}
}
@DemoFactoryNeedBean
@Service
public class DemoService3 implements IDemo {
@Override
public String getValue() {
return "DemoService3.getValue by 梦在旅途 zuowj.cnblogs.com";
}
@Override
public int doFor() {
return 3;
}
}
下面直接无废话列举各种实现方式
实现方式一:直接使用集合的依赖注入方式(利用spring注入时会判断是否为集合,若为集合则获取所有实现了该类的BEAN集合并进行注入)
/**
* @author zuowenjun
* <pre>www.zuowenjun.cn</pre>
*/
@Service
public class DemoFactory1 {
@Autowired
private List<IDemo> demos;
public IDemo getOne(int index){
return demos.stream().filter(d->d.doFor()==index).findFirst().orElseThrow(()->new IllegalArgumentException("not found demo bean"));
}
}
单元测试【DemoFactory1】BEAN管理工厂用法及结果:
@Autowired
private DemoFactory1 demoFactory1;
@Test
public void testDemoFactory1(){
for (int i=1;i<=3;i++){
IDemo demo = demoFactory1.getOne(i);
System.out.printf("testDemoFactory1--bean class: %s , getValue:%s, doFor:%d %n", demo.getClass().getSimpleName(), demo.getValue(), demo.doFor());
}
}
运行结果:
testDemoFactory1--bean class: DemoService1 , getValue:DemoService1.getValue by 梦在旅途 zuowj.cnblogs.com, doFor:1
testDemoFactory1--bean class: DemoService2 , getValue:DemoService2.getValue by 梦在旅途 zuowj.cnblogs.com, doFor:2
testDemoFactory1--bean class: DemoService3 , getValue:DemoService3.getValue by 梦在旅途 zuowj.cnblogs.com, doFor:3
实现方式二:通过实现BeanPostProcessor接口,利用每个BEAN实例化后均会调用postProcessAfterInitialization方法的特点,直接在postProcessAfterInitialization方法中收集所需的BEAN实例并添加到集合中
/**
* @author zuowenjun
* <pre>www.zuowenjun.cn</pre>
*/
@Service
public class DemoFactory2 implements BeanPostProcessor {
private List<IDemo> demos=new ArrayList<>();
public IDemo getOne(int index){
return demos.stream().filter(d->d.doFor()==index).findFirst().orElseThrow(()->new IllegalArgumentException("not found demo bean"));
}
@Override
@Nullable
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof IDemo) {
System.out.printf("postProcessAfterInitialization->bean class:%s",bean.getClass().getSimpleName());
demos.add((IDemo) bean);
}
return bean;
}
}
单元测试【DemoFactory2】BEAN管理工厂用法及结果:
@Autowired
private DemoFactory2 demoFactory2;
@Test
public void testDemoFactory2(){
for (int i=1;i<=3;i++){
IDemo demo= demoFactory2.getOne(i);
System.out.printf("testDemoFactory2--bean class: %s , getValue:%s, doFor:%d %n",demo.getClass().getSimpleName(),demo.getValue(),demo.doFor());
}
}
运行结果:
testDemoFactory2--bean class: DemoService1 , getValue:DemoService1.getValue by 梦在旅途 zuowj.cnblogs.com, doFor:1
testDemoFactory2--bean class: DemoService2 , getValue:DemoService2.getValue by 梦在旅途 zuowj.cnblogs.com, doFor:2
testDemoFactory2--bean class: DemoService3 , getValue:DemoService3.getValue by 梦在旅途 zuowj.cnblogs.com, doFor:3
实现方式三:通过实现ApplicationRunner、ApplicationContextAware接口,以便在setApplicationContext能获取到上下文实例对象并保存,然后在spring初始化完成执行run方法中使用上下文实例对象获取指定类型的BEAN实例集合。当然也可以不用实现ApplicationRunner接口,而是在工厂方法获取BEAN对象第一次时才用上下文实例对象获取指定类型的BEAN实例集合(即:初始化一次)如代码中的getOneForLazy方法所示。
/**
* @author zuowenjun
* <pre>www.zuowenjun.cn</pre>
*/
@Service
public class DemoFactory3 implements ApplicationRunner, ApplicationContextAware {
private ApplicationContext context;
@Autowired
private List<IDemo> demos;
public IDemo getOne(int index) {
return demos.stream().filter(d -> d.doFor() == index).findFirst().orElseThrow(() -> new IllegalArgumentException("not found demo bean"));
}
public IDemo getOneForLazy(int index) {
if (CollectionUtils.isEmpty(demos)){
demos = new ArrayList<>(context.getBeansOfType(IDemo.class).values());
}
return demos.stream().filter(d -> d.doFor() == index).findFirst().orElseThrow(() -> new IllegalArgumentException("not found demo bean"));
}
@Override
public void run(ApplicationArguments args) throws Exception {
demos = new ArrayList<>(context.getBeansOfType(IDemo.class).values());
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.context = applicationContext;
}
}
单元测试【DemoFactory3】BEAN管理工厂用法及结果:
@Autowired
private DemoFactory3 demoFactory3;
@Test
public void testDemoFactory3(){
for (int i=1;i<=3;i++){
IDemo demo= demoFactory3.getOne(i);
System.out.printf("testDemoFactory3--bean class: %s , getValue:%s, doFor:%d %n",demo.getClass().getSimpleName(),demo.getValue(),demo.doFor());
}
}
运行结果:
testDemoFactory3--bean class: DemoService1 , getValue:DemoService1.getValue by 梦在旅途 zuowj.cnblogs.com, doFor:1
testDemoFactory3--bean class: DemoService2 , getValue:DemoService2.getValue by 梦在旅途 zuowj.cnblogs.com, doFor:2
testDemoFactory3--bean class: DemoService3 , getValue:DemoService3.getValue by 梦在旅途 zuowj.cnblogs.com, doFor:3
实现方式四:此为组合模式,先定义注入ApplicationContext上下文对象,然后定义一个枚举类,在枚举类中为每个枚举项都指明BEAN的实现类型,最后需要获取BEAN实例时,直接根据上下文对象获取指定类型的BEAN实例即可。
/**
* @author zuowenjun
* <pre>www.zuowenjun.cn</pre>
*/
@Service
public class DemoFactory4 {
private final ApplicationContext context;
public DemoFactory4(ApplicationContext context) {
this.context = context;
}
public IDemo getOne(DemoFactory4Enum factory4Enum) {
return context.getBean(factory4Enum.getBeanClass());
}
public enum DemoFactory4Enum {
Demo1(1, DemoService1.class),
Demo2(2, DemoService2.class),
Demo3(3, DemoService3.class),
;
private final Class<? extends IDemo> beanClass;
private final int index;
DemoFactory4Enum(int i, Class<? extends IDemo> beanClass) {
this.index = i;
this.beanClass = beanClass;
}
public Class<? extends IDemo> getBeanClass() {
return beanClass;
}
public int getIndex() {
return index;
}
public static DemoFactory4Enum parse(int i){
return Arrays.stream(values()).filter(d->d.getIndex()==i).findFirst().orElseThrow(()->new IllegalArgumentException("not found enum item!"));
}
}
}
单元测试【DemoFactory4】BEAN管理工厂用法及结果:(演示了2种方式,当然本质都是先确定枚举项,再获取BEAN对象)
@Autowired
private DemoFactory4 demoFactory4;
@Test
public void testDemoFactory4(){
// for (DemoFactory4.DemoFactory4Enum enumItem:DemoFactory4.DemoFactory4Enum.values()){
// IDemo demo= demoFactory4.getOne(enumItem);
// System.out.printf("testDemoFactory4--bean class: %s , getValue:%s, doFor:%d %n",demo.getClass().getSimpleName(),demo.getValue(),demo.doFor());
// }
for (int i=1;i<=3;i++){
IDemo demo= demoFactory4.getOne(DemoFactory4.DemoFactory4Enum.parse(i));
System.out.printf("testDemoFactory4--bean class: %s , getValue:%s, doFor:%d %n",demo.getClass().getSimpleName(),demo.getValue(),demo.doFor());
}
}
运行结果:
testDemoFactory4--bean class: DemoService1 , getValue:DemoService1.getValue by 梦在旅途 zuowj.cnblogs.com, doFor:1
testDemoFactory4--bean class: DemoService2 , getValue:DemoService2.getValue by 梦在旅途 zuowj.cnblogs.com, doFor:2
testDemoFactory4--bean class: DemoService3 , getValue:DemoService3.getValue by 梦在旅途 zuowj.cnblogs.com, doFor:3
实现方式五:此为组合模式,与实现方式四有点类似,但又有不同,仍然是先定义注入ApplicationContext上下文对象,然后定义一个抽象枚举类(有一个抽象方法,如:getBean),在枚举类中为每个枚举项都实现这个抽象方法,在抽象方法中通过静态上下文对象字段来获取指定类型的BEAN实例,最后需要获取BEAN实例就比较简单了,只要得到枚举项,就可以直接获取到对应的BEAN实例。
/**
* @author zuowenjun
* <pre>www.zuowenjun.cn</pre>
*/
@Service
public class DemoFactory5 {
private static ApplicationContext context;
public DemoFactory5(ApplicationContext context) {
DemoFactory5.context = context;
}
public enum DemosEnum {
Demo1(1) {
@Override
public IDemo getBean() {
return context.getBean(DemoService1.class);
}
},
Demo2(2) {
@Override
public IDemo getBean() {
return context.getBean(DemoService2.class);
}
},
Demo3(3) {
@Override
public IDemo getBean() {
return context.getBean(DemoService3.class);
}
},
;
private final int index;
DemosEnum(int index) {
this.index = index;
}
public int getIndex() {
return index;
}
public abstract IDemo getBean();
public static DemosEnum parse(int i){
return Arrays.stream(values()).filter(d->d.getIndex()==i).findFirst().orElseThrow(()->new IllegalArgumentException("not found enum item!"));
}
}
}
单元测试【DemoFactory5】BEAN管理工厂用法及结果:(演示了2种方式,当然本质都是先确定枚举项,再获取BEAN对象)
@Test
public void testDemoFactory5(){
// for (DemoFactory5.DemosEnum demosEnum:DemoFactory5.DemosEnum.values()){
// IDemo demo= demosEnum.getBean();
// System.out.printf("testDemoFactory5--bean class: %s , getValue:%s, doFor:%d %n",demo.getClass().getSimpleName(),demo.getValue(),demo.doFor());
// }
for (int i=1;i<=3;i++){
IDemo demo= DemoFactory5.DemosEnum.parse(i).getBean();
System.out.printf("testDemoFactory5--bean class: %s , getValue:%s, doFor:%d %n",demo.getClass().getSimpleName(),demo.getValue(),demo.doFor());
}
}
运行结果:
testDemoFactory5--bean class: DemoService1 , getValue:DemoService1.getValue by 梦在旅途 zuowj.cnblogs.com, doFor:1
testDemoFactory5--bean class: DemoService2 , getValue:DemoService2.getValue by 梦在旅途 zuowj.cnblogs.com, doFor:2
testDemoFactory5--bean class: DemoService3 , getValue:DemoService3.getValue by 梦在旅途 zuowj.cnblogs.com, doFor:3
实现方式六:其实本质还是实现方式一的灵活应用,通过自定义标注了@Qualifier注解的过滤注解类(如:@DemoFactoryNeedBean),然后在对应的BEAN类上加上该自定义的过滤注解,最后在工厂类的内部集合依赖注入字段上同样增加自定义的过滤注解,这样就可以在原有的基础上(BEAN的基类或接口)增加过滤必需包含指明了自定义过滤注解的BEAN实例集合。
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface DemoFactoryNeedBean {
}
/**
* @author zuowenjun
* <pre>www.zuowenjun.cn</pre>
*/
@Service
public class DemoFactory1 {
@DemoFactoryNeedBean
@Autowired
private List<IDemo> demos;
public IDemo getOne(int index){
return demos.stream().filter(d->d.doFor()==index).findFirst().orElseThrow(()->new IllegalArgumentException("not found demo bean"));
}
public boolean hasBean(int index){
return demos.stream().anyMatch(d->d.doFor()==index);
}
}
然后再看文章开头定义的3个BEAN类,其中:DemoService2、DemoService3是有加@DemoFactoryNeedBean注解的,最后再次单元测试【DemoFactory1】BEAN管理工厂用法及结果:
@Test
public void testDemoFactory1(){
for (int i=1;i<=3;i++){
if (demoFactory1.hasBean(i)) {
IDemo demo = demoFactory1.getOne(i);
System.out.printf("testDemoFactory1--bean class: %s , getValue:%s, doFor:%d %n", demo.getClass().getSimpleName(), demo.getValue(), demo.doFor());
}
}
}
运行结果:(少了DemoService1 的BEAN)
testDemoFactory1--bean class: DemoService2 , getValue:DemoService2.getValue by 梦在旅途 zuowj.cnblogs.com, doFor:2
testDemoFactory1--bean class: DemoService3 , getValue:DemoService3.getValue by 梦在旅途 zuowj.cnblogs.com, doFor:3
好了, 以上就是全部的实现方式了,至于哪种更好,我认为在不同的场景下选择合适的实现方式即可,没有所谓的最好,存在即有意义,最后期待我下次再写新的博文吧!~
我正在使用i18n从头开始构建一个多语言网络应用程序,虽然我自己可以处理一大堆yml文件,但我说的语言(非常)有限,最终我想寻求外部帮助帮助。我想知道这里是否有人在使用UI插件/gem(与django上的django-rosetta不同)来处理多个翻译器,其中一些翻译器不愿意或无法处理存储库中的100多个文件,处理语言数据。谢谢&问候,安德拉斯(如果您已经在rubyonrails-talk上遇到了这个问题,我们深表歉意) 最佳答案 有一个rails3branchofthetolkgem在github上。您可以通过在Gemfi
在railstutorial中,作者为什么选择使用这个(代码list10.25):http://ruby.railstutorial.org/chapters/updating-showing-and-deleting-usersnamespace:dbdodesc"Filldatabasewithsampledata"task:populate=>:environmentdoRake::Task['db:reset'].invokeUser.create!(:name=>"ExampleUser",:email=>"example@railstutorial.org",:passwo
我试图获取一个长度在1到10之间的字符串,并输出将字符串分解为大小为1、2或3的连续子字符串的所有可能方式。例如:输入:123456将整数分割成单个字符,然后继续查找组合。该代码将返回以下所有数组。[1,2,3,4,5,6][12,3,4,5,6][1,23,4,5,6][1,2,34,5,6][1,2,3,45,6][1,2,3,4,56][12,34,5,6][12,3,45,6][12,3,4,56][1,23,45,6][1,2,34,56][1,23,4,56][12,34,56][123,4,5,6][1,234,5,6][1,2,345,6][1,2,3,456][123
我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i
我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden
question的一些答案关于redirect_to让我想到了其他一些问题。基本上,我正在使用Rails2.1编写博客应用程序。我一直在尝试自己完成大部分工作(因为我对Rails有所了解),但在需要时会引用Internet上的教程和引用资料。我设法让一个简单的博客正常运行,然后我尝试添加评论。靠我自己,我设法让它进入了可以从script/console添加评论的阶段,但我无法让表单正常工作。我遵循的其中一个教程建议在帖子Controller中创建一个“评论”操作,以添加评论。我的问题是:这是“标准”方式吗?我的另一个问题的答案之一似乎暗示应该有一个CommentsController参
我安装了ruby版本管理器,并将RVM安装的ruby实现设置为默认值,这样'哪个ruby'显示'~/.rvm/ruby-1.8.6-p383/bin/ruby'但是当我在emacs中打开inf-ruby缓冲区时,它使用安装在/usr/bin中的ruby。有没有办法让emacs像shell一样尊重ruby的路径?谢谢! 最佳答案 我创建了一个emacs扩展来将rvm集成到emacs中。如果您有兴趣,可以在这里获取:http://github.com/senny/rvm.el
是否可以为特定(或所有)项目使用多个布局?例如,我有几个项目,我想对其应用两种不同的布局。一个是绿色的,一个是蓝色的(但是)。我想将它们编译到我的输出目录中的两个不同文件夹中(例如v1和v2)。我一直在玩弄规则和编译block,但我不知道这是怎么回事。因为,每个项目在编译过程中只编译一次,我不能告诉nanoc第一次用layout1编译,第二次用layout2编译。我试过这样的东西,但它导致输出文件损坏。compile'*'doifitem.binary?#don’tfilterbinaryitemselsefilter:erblayout'layout1'layout'layout2'
是否有简单的方法来更改默认ISO格式(yyyy-mm-dd)的ActiveAdmin日期过滤器显示格式? 最佳答案 您可以像这样为日期选择器提供额外的选项,而不是覆盖js:=f.input:my_date,as::datepicker,datepicker_options:{dateFormat:"mm/dd/yy"} 关于ruby-on-rails-事件管理员日期过滤器日期格式自定义,我们在StackOverflow上找到一个类似的问题: https://s
华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o