`
jinnianshilongnian
  • 浏览: 21441893 次
  • 性别: Icon_minigender_1
博客专栏
5c8dac6a-21dc-3466-8abb-057664ab39c7
跟我学spring3
浏览量:2406649
D659df3e-4ad7-3b12-8b9a-1e94abd75ac3
Spring杂谈
浏览量:2998851
43989fe4-8b6b-3109-aaec-379d27dd4090
跟开涛学SpringMVC...
浏览量:5632433
1df97887-a9e1-3328-b6da-091f51f886a1
Servlet3.1规范翻...
浏览量:257860
4f347843-a078-36c1-977f-797c7fc123fc
springmvc杂谈
浏览量:1593580
22722232-95c1-34f2-b8e1-d059493d3d98
hibernate杂谈
浏览量:249109
45b32b6f-7468-3077-be40-00a5853c9a48
跟我学Shiro
浏览量:5848574
Group-logo
跟我学Nginx+Lua开...
浏览量:698484
5041f67a-12b2-30ba-814d-b55f466529d5
亿级流量网站架构核心技术
浏览量:780961
社区版块
存档分类
最新评论

不重复配置——利用Spring通用化配置

 
阅读更多

还记得 如下这种配置吗:

 

1、struts2作用域:每一个Action我们必须设置scope为prototype   每次都做重复的配置,而且有时候忘记配置还会出现bug,想不想删掉它?

 

<bean id="**Action" class="***Action" scope="prototype">

 

2、在使用spring集成hibernate时,每次都必须注入sessionFactory,虽然可以用父子bean解决 但还是要写parent="abstractHibernateDao"之类的。

<bean id="***Dao" class="***DaoImpl">

    <property name="sessionFactory" ref="sessionFactory">

</bean>

 

受够了这种配置,得想法解决这个重复配置,怎么解决呢?

 

补充:

首先感谢downpour大哥的批评:

 

downpour 写道
在beans level上可以设置autowired的方式,sessionFactory这段配置从来就是可以省略的。
prototype属性不能也不该省略,配置是给人看的,要是人看不懂就是垃圾。
综上所述,此方案纯粹脱裤子放屁多此一举。

1、sessionFactory注入问题:

 如果是注解这个可以写个通用的基类就很容易搞定;

 如果是XML  也可以通过在beans标签上使用 default-autowire="byName" default-autowire-candidates="*Dao"   也能解决问题,当我们通过类似于如下方式时,必须在每个相关的配置文件中都写上。

 

 

  <context-param>
     <param-name>contextConfigLocation</param-name>
     <param-value>
        classpath:spring-common-config.xml,
        classpath:spring-budget-config.xml
     </param-value>
  </context-param>

2、struts2 Action scope问题

 如果使用StrutsPrepareAndExecuteFilter可以通过:

 

 

            <init-param>
			<param-name>actionPackages</param-name>
			<param-value>Action所在包前缀</param-value>
		</init-param> 

 scope会自动是prototype

 

 使用我说的这种设置方式:我觉得因为只要会Struts2+Spring集成都知道struts2的Action是prototype,可以用;『prototype属性不能也不该省略,配置是给人看的,要是人看不懂就是垃圾。』这个是这么回事,需要仔细考虑下;当然我可以考虑在配置文件中加上注释 说明一下 告诉其他人是怎么回事。

 

 另外这个功能我想可以改建为检查配置是否正确 类似于spring的依赖检查。欢迎大家拍砖。

 

思路:

在BeanFactory创建Bean之前查找所有我们需要通用化配置的Bean 然后修改BeanDefinition注入我们的通用数据就可以解决我们这个问题。

 

Spring提供了BeanFactoryPostProcessor扩展点,用于提供给我们修改BeanDefinition数据的。

 

还记得org.springframework.beans.factory.config.PropertyPlaceholderConfigurer吗? 替换占位符数据,它就是一个BeanFactoryPostProcessor的实现。

 

好了思路有了,接下来我们实现一下吧:

1、XML配置方式

 

 * 

 * 使用方法:<br/>

 * <pre>

 *  <bean class="cn.javass.common.spring.CommonConfigureProcessor">

       <property name="config">

            <map>

               <!--  aspectj表达式 选择所有Action结尾的Bean 注入scope数据 为 prototype  -->

               <entry key="cn.javass..*Action">

                   <props>

                       <prop key="scope">prototype</prop>

                   </props>

               </entry>

               <!-- aspectj表达式 选择所有的HibernateDaoSupport实现Bean 注入sessionFactory -->

               <entry key="org.springframework.orm.hibernate3.support.HibernateDaoSupport+">

                  <props>

                     <prop key="property-ref">sessionFactory=sessionFactory</prop>

                  </props>

               </entry>

            </map>          

       </property>

   </bean>

 * </pre>

 * 

 * 目前支持三种配置:

 *  scope:注入作用域

 *  property-ref:注入Bean引用 如之上的sessionFactory

 *     propertyName=beanName

 *  property-value:注入常量值

 *     propertyName=常量

 

2、CommonConfigureProcessor源码

 

package cn.javass.common.spring;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;

import org.aspectj.bridge.IMessageHandler;
import org.aspectj.weaver.ResolvedType;
import org.aspectj.weaver.World;
import org.aspectj.weaver.bcel.BcelWorld;
import org.aspectj.weaver.patterns.Bindings;
import org.aspectj.weaver.patterns.FormalBinding;
import org.aspectj.weaver.patterns.IScope;
import org.aspectj.weaver.patterns.PatternParser;
import org.aspectj.weaver.patterns.SimpleScope;
import org.aspectj.weaver.patterns.TypePattern;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.RuntimeBeanNameReference;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.util.StringUtils;
/**
 * 
 * 设置通用配置<br/>
 * 
 * 使用方法:<br/>
 * <pre>
 *  <bean class="cn.javass.common.spring.CommonConfigureProcessor">
       <property name="config">
            <map>
               <!--  aspectj表达式 选择所有Action结尾的Bean 注入scope数据 为 prototype  -->
               <entry key="cn.javass..*Action">
                   <props>
                       <prop key="scope">prototype</prop>
                   </props>
               </entry>
               <!-- aspectj表达式 选择所有的HibernateDaoSupport实现Bean 注入sessionFactory -->
               <entry key="org.springframework.orm.hibernate3.support.HibernateDaoSupport+">
                  <props>
                     <prop key="property-ref">sessionFactory=sessionFactory</prop>
                  </props>
               </entry>
            </map>          
       </property>
   </bean>
 * </pre>
 * 
 * 目前支持三种配置:
 *  scope:注入作用域
 *  property-ref:注入Bean引用 如之上的sessionFactory
 *     propertyName=beanName
 *  property-value:注入常量值
 *     propertyName=常量
 * 
 * @author Zhangkaitao
 * @version 1.0
 *
 */
public class CommonConfigureProcessor implements BeanFactoryPostProcessor {
    
    private Logger log = LoggerFactory.getLogger(CommonConfigureProcessor.class);

    private Map<String, Properties> config = new HashMap<String, Properties>();
    public void setConfig(Map<String, Properties> config) {
        this.config = config;
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory factory) throws BeansException {
        log.debug("apply common config start");
        for(Entry<String, Properties> entry : config.entrySet()) {
            String aspectjPattern = entry.getKey();
            Properties props = entry.getValue();

            List<BeanDefinition> bdList = findBeanDefinition(aspectjPattern, factory);
            
            apply(bdList, props);
        }
        log.debug("apply common config end");
    }


    private void apply(List<BeanDefinition> bdList, Properties props) {
        for(Entry<Object, Object> entry : props.entrySet()) {
            String key = (String) entry.getKey();
            String value = (String) entry.getValue();
            
            switch(SupportedConfig.keyToEnum(key)) {
                case scope : 
                    applyScope(bdList, value);
                    break;
                case propertyRef:
                    applyPropertyRef(bdList, value);
                    break;
                case propertyValue:
                    applyPropertyValue(bdList, value);
                    break;
                default:
                    throw new IllegalArgumentException(String.format("错误的配置:[%s]", key));
            }
            
            
        }
    }


    private void applyPropertyValue(List<BeanDefinition> bdList, String value) {
        for(BeanDefinition bd : bdList) {
            
            String propertyName = value.split("=")[0]; 
            String propertyValue = value.substring(propertyName.length()+1);
            bd.getPropertyValues().add(propertyName, propertyValue);
            
            log.debug("apply property value {} to {}", value, bd.getBeanClassName());
        }
    }

    private void applyPropertyRef(List<BeanDefinition> bdList, String value) {
        for(BeanDefinition bd : bdList) {
            
            String propertyName = value.split("=")[0]; 
            String propertyValue = value.substring(propertyName.length()+1);
            bd.getPropertyValues().addPropertyValue(propertyName, new RuntimeBeanReference(propertyValue));
            
            log.debug("apply property ref {} to {}", value, bd.getBeanClassName());
        }
    }

    private void applyScope(List<BeanDefinition> bdList, String value) {
        for(BeanDefinition bd : bdList) {
            bd.setScope(value);
            log.debug("apply scope {} to {}", value, bd.getBeanClassName());
        }
    }

    private List<BeanDefinition> findBeanDefinition(String aspectjPattern, ConfigurableListableBeanFactory factory) {
        List<BeanDefinition> bdList = new ArrayList<BeanDefinition>();
        
        for(String beanName : factory.getBeanDefinitionNames()) {
            BeanDefinition bd = factory.getBeanDefinition(beanName);
            
            if(matches(aspectjPattern, bd.getBeanClassName())) {
                bdList.add(bd);
            }
            
        }
        
        return bdList;
    }

    
    private boolean matches(String aspectjPattern, String beanClassName) {
        if(!StringUtils.hasLength(beanClassName)) {
            return false;
        }
        return new AspectJTypeMatcher(aspectjPattern).matches(beanClassName);
    }
    
    //支持的操作
    private static enum SupportedConfig {
        scope("scope"), 
        propertyRef("property-ref"),
        propertyValue("property-value"),
        
        error("error"); //出错的情况

        private final String key;
        private SupportedConfig(String key) {
            this.key = key;
        }
        
        public static SupportedConfig keyToEnum(String key) {
            if(key == null) {
                return error;
            }
            for(SupportedConfig config : SupportedConfig.values()) {
                if(config.key.equals(key.trim())) {
                    return config;
                }
            }
            return error;
        }
        
    }
    
    
    public static interface TypeMatcher {
        public boolean matches(String className);
    }
    
    static class AspectJTypeMatcher implements TypeMatcher {
        private final World world;
        private final TypePattern typePattern;
        
        public AspectJTypeMatcher(String pattern) {
         
            this.world = new BcelWorld(Thread.currentThread().getContextClassLoader(), IMessageHandler.THROW, null);
            this.world.setBehaveInJava5Way(true);
            PatternParser patternParser = new PatternParser(pattern);
            TypePattern typePattern = patternParser.parseTypePattern();
            typePattern.resolve(this.world);
            IScope scope = new SimpleScope(this.world, new FormalBinding[0]);
            this.typePattern = typePattern.resolveBindings(scope, Bindings.NONE, false, false);
        }
        @Override
        public boolean matches(String className) {
            ResolvedType resolvedType = this.world.resolve(className);
            return this.typePattern.matchesStatically(resolvedType);
        }
    }
    
    public static void main(String[] args) {
        //System.out.println(new AspectJTypeMatcher("cn.javass..*Action").matches("cn.javass.test.web.action.AbcAction"));
        //System.out.println(new AspectJTypeMatcher("com.opensymphony.xwork2.ActionSupport+").matches("cn.javass.erp.web.action.MoneyAction"));
    }
}
 

此类只实现基本的通用配置,欢迎大家提供想法并完善这个工具类。


11
2
分享到:
评论
21 楼 liwenjieifk 2014-07-13  
支持
20 楼 jinnianshilongnian 2012-11-06  
Inmethetiger 写道
Inmethetiger 写道
downpour 说话风格一向如此。呵呵。所以不必介怀。至少对我们这样的新手,你写的文章作用是大大滴!继续努力!如果你是菜鸟,让我们这些还没研究过源码,甚至还停留在用都不是很熟练的真正菜鸟情何以堪啊!
给你投票去

还有,你的头像笑得好灿烂!!!!

  爱笑的人运气不会很差  哈哈
19 楼 jinnianshilongnian 2012-11-06  
Inmethetiger 写道
downpour 说话风格一向如此。呵呵。所以不必介怀。至少对我们这样的新手,你写的文章作用是大大滴!继续努力!如果你是菜鸟,让我们这些还没研究过源码,甚至还停留在用都不是很熟练的真正菜鸟情何以堪啊!
给你投票去

呵呵,他说的是对的,而且很犀利 ,革命尚未成功,同志仍需努力;加油啦
18 楼 Inmethetiger 2012-11-06  
Inmethetiger 写道
downpour 说话风格一向如此。呵呵。所以不必介怀。至少对我们这样的新手,你写的文章作用是大大滴!继续努力!如果你是菜鸟,让我们这些还没研究过源码,甚至还停留在用都不是很熟练的真正菜鸟情何以堪啊!
给你投票去

还有,你的头像笑得好灿烂!!!!
17 楼 Inmethetiger 2012-11-06  
downpour 说话风格一向如此。呵呵。所以不必介怀。至少对我们这样的新手,你写的文章作用是大大滴!继续努力!如果你是菜鸟,让我们这些还没研究过源码,甚至还停留在用都不是很熟练的真正菜鸟情何以堪啊!
给你投票去
16 楼 jinnianshilongnian 2012-10-22  
iqeq00 写道
看了不少你的文章,对我还是有帮助,至少你的原创精神可贵。
不过真的,从我出道后每一个项目,xml方式的,都用了自动装配,
确实也从来没有在doa里配置过sessionFactory
也挺方便的,个人觉得还是应该很多项目都用吧。

而struts2的Action的scope为prototype的,我觉得在我感觉
是一种设计模式的体现,为什么这里要用prototype,这是我第一次看到的疑问,
后来查资料,才学到了点东西,如果这个取消了,确实是语意的缺失吧,
也许自己懂,但或多或少对别人、或者是新手有点...

而且任何东西,我觉得不到万不得已,我都不推荐修改源码...
小弟不才,对不住了,闲的没事也来和你冒下皮皮,
不过依旧支持你,探索的精神和原创的风格都是难得的...

谢谢,掉进坑里了,听了大家的意见 确实此类无用 在此留作纪念 时刻提醒自己有些东西可能有更简单的方案/而有些东西不应该使用所谓的简单方案。
15 楼 iqeq00 2012-10-22  
看了不少你的文章,对我还是有帮助,至少你的原创精神可贵。
不过真的,从我出道后每一个项目,xml方式的,都用了自动装配,
确实也从来没有在doa里配置过sessionFactory
也挺方便的,个人觉得还是应该很多项目都用吧。

而struts2的Action的scope为prototype的,我觉得在我感觉
是一种设计模式的体现,为什么这里要用prototype,这是我第一次看到的疑问,
后来查资料,才学到了点东西,如果这个取消了,确实是语意的缺失吧,
也许自己懂,但或多或少对别人、或者是新手有点...

而且任何东西,我觉得不到万不得已,我都不推荐修改源码...
小弟不才,对不住了,闲的没事也来和你冒下皮皮,
不过依旧支持你,探索的精神和原创的风格都是难得的...
14 楼 jinnianshilongnian 2012-10-17  
downpour 写道
在beans level上可以设置autowired的方式,sessionFactory这段配置从来就是可以省略的。

prototype属性不能也不该省略,配置是给人看的,要是人看不懂就是垃圾。

综上所述,此方案纯粹脱裤子放屁多此一举。


default-autowire="byName" default-autowire-candidates="*Dao" 当多配置文件时,在每个配置文件都得配置这句,你也是这样的吗?
13 楼 jinnianshilongnian 2012-10-17  
Mybeautiful 写道
虽然我不是博主发的每篇Spring的文章都喜欢,但是我觉得分享就是好的;大家看可以选择读或是不读;
就博主这篇文章而言,我虽然也许不会使用楼主的方案,但是后面很多兄弟的评论提出了自己的方案,不乏很好的,我或许会用上,那么我们是不是可以说博主的文章是有价值的?至少对我是有帮助的,不管是文章本身,还是因文章而引发的评论。

比如struts2的Action的scope为prototype,每次都重复配置,就想不重复配置,不知道你有什么思路。
12 楼 Mybeautiful 2012-10-17  
虽然我不是博主发的每篇Spring的文章都喜欢,但是我觉得分享就是好的;大家看可以选择读或是不读;
就博主这篇文章而言,我虽然也许不会使用楼主的方案,但是后面很多兄弟的评论提出了自己的方案,不乏很好的,我或许会用上,那么我们是不是可以说博主的文章是有价值的?至少对我是有帮助的,不管是文章本身,还是因文章而引发的评论。
11 楼 jinnianshilongnian 2012-10-17  
downpour 写道
在beans level上可以设置autowired的方式,sessionFactory这段配置从来就是可以省略的。

prototype属性不能也不该省略,配置是给人看的,要是人看不懂就是垃圾。

综上所述,此方案纯粹脱裤子放屁多此一举。

声明:实际做项目我没有这么用过,只是最近在想我天天重复配置 所以想出这个玩意来。
关于prototype的问题 我仔细考虑了一下 的确应该配置 代码写出来首先是人维护(因为只要做struts2基本都知道action是prototype,不知道大哥有没有什么好的思路[比如用@Action注解默认情况下就是prototype])。
因此我想可以把这个类改造成检查相关配置是否配置了,如果没有配置就告警,而不是通用这个配置?
10 楼 jinnianshilongnian 2012-10-17  
downpour 写道
在beans level上可以设置autowired的方式,sessionFactory这段配置从来就是可以省略的。

prototype属性不能也不该省略,配置是给人看的,要是人看不懂就是垃圾。

综上所述,此方案纯粹脱裤子放屁多此一举。

仔细考虑了一下
你应该通过candidates限定了。
default-autowire="byName" default-autowire-candidates=""
从这点可以看出sessionFactory没必要用这个。
9 楼 jinnianshilongnian 2012-10-17  
downpour 写道
所以我早说了你误入歧途,还一大堆理由。

Autowired有几个真正用过?你做过几个项目?我05年就开始用Autowired,到现在为止没有任何一个项目在DAO里面配置过sessionFactory。

XML配置的核心就是保证配置的语义,把一个attribute省略只能带来语义上的缺失。

像你这样的水平,我真的建议你少发点文章误导别人。你的很多想法还停留在6年前我们讨论最佳实践的水平,给出来的东西都还不是最好的方案。当然你为私塾在线做广告可以,但是千万不要嘴硬觉得自己有多了不起。

iteye就是被你这样的人搞得从一个原本还挺高端的讨论论坛,变成了一个充满错误观点,人气低落的社区。

我没有说我写的是对的,所以发上来探讨,而作为大哥的您觉得我水平不行应该开导,而不是打击, 没有人一开始就什么都会,都是经验和教训总结出来的,所以才发上来,没有讨论那就是自闭,所以你回复我我很感激,我写的东西是很入门 很基础,但我认真写了,现在也在认真的学习,从没有停止过。
我从来没认为我厉害,我水平本来就是很菜,希望像您这样的牛人指点。
每个人都有一个发展历程,就像面向对象一样,可能在您看来都悟透了,可对于我们这种刚刚出道的就没那么多经验/教训去体会为什么。所以每次看都有不同的收获。
我觉得像您这种牛人应该学会鼓励我们这种刚出道不久的如何去研究/思考,而不是打击。
我写的不对 就应该被批评。非常感谢您能回复。

引用
当然你为私塾在线做广告可以
既然为别人打工 就得做事,你可以看到 现在的博文都没有外链了,之前是为了获得点外链加的。 现在网站的pr也有了 就不用了。

Autowired我认为它的范围太大  对于所有setter都要查找并注入,所以很难排查错误原因,只有在运行期运行到那才会知道。 这是我的体会。

我写这个文章也是想表达一种我的一些思考,为什么每次我都要配置这个东西?而且还是重复的?为什么就不能不配?
8 楼 downpour 2012-10-17  
所以我早说了你误入歧途,还一大堆理由。

Autowired有几个真正用过?你做过几个项目?我05年就开始用Autowired,到现在为止没有任何一个项目在DAO里面配置过sessionFactory。

XML配置的核心就是保证配置的语义,把一个attribute省略只能带来语义上的缺失。

像你这样的水平,我真的建议你少发点文章误导别人。你的很多想法还停留在6年前我们讨论最佳实践的水平,给出来的东西都还不是最好的方案。当然你为私塾在线做广告可以,但是千万不要嘴硬觉得自己有多了不起。

iteye就是被你这样的人搞得从一个原本还挺高端的讨论论坛,变成了一个充满错误观点,人气低落的社区。
7 楼 jinnianshilongnian 2012-10-17  
downpour 写道
在beans level上可以设置autowired的方式,sessionFactory这段配置从来就是可以省略的。

prototype属性不能也不该省略,配置是给人看的,要是人看不懂就是垃圾。

综上所述,此方案纯粹脱裤子放屁多此一举。


比如我自己开发维护一个项目,为什么就不能使用呢? 就像spring的占位符替换, 大家都用了自然也就都会了。 当然如果项目是团队协作 而且不了解这个东西 肯定不推荐 如果我熟悉我觉得用用真无妨
6 楼 jinnianshilongnian 2012-10-17  
downpour 写道
在beans level上可以设置autowired的方式,sessionFactory这段配置从来就是可以省略的。

prototype属性不能也不该省略,配置是给人看的,要是人看不懂就是垃圾。

综上所述,此方案纯粹脱裤子放屁多此一举。

autowired 有几个真正用过? autowired会使项目更乱不是吗?

prototype不能也该省:是这样 但是如果有东西强制给你加上不是更好吗? 我不能保证我每次都不忘。

sessionFactory这段配置从来就是可以省略的。如果用注解是可以省略,如果用xml 1、做父子Bean;2、使用自动装配(不过这个技术有几个人用?)

如果用注解肯定不会存在这种情况。

5 楼 downpour 2012-10-17  
在beans level上可以设置autowired的方式,sessionFactory这段配置从来就是可以省略的。

prototype属性不能也不该省略,配置是给人看的,要是人看不懂就是垃圾。

综上所述,此方案纯粹脱裤子放屁多此一举。
4 楼 jinnianshilongnian 2012-10-17  
azheng270 写道
继承一个基类bean就可以了,这样写难喜欢控制

在没有注解的情况下 继承基类也是需要在xml注入数据 (此处不是讲注解的情况
3 楼 jinnianshilongnian 2012-10-17  
thc1987 写道
如果有多个数据源就不好管理了吧

这个可以控制的  只要写的符合某些规则即可  如果无规则那肯定不行 
因为此处使用的是aspectj语法
2 楼 thc1987 2012-10-17  
如果有多个数据源就不好管理了吧

相关推荐

Global site tag (gtag.js) - Google Analytics