最近公众号受邀获取了留言和赠送模板的权限,小开心(欢迎去公众号JackieZheng围观)。

  我们大致的了解了Spring这个框架对于依赖注入的使用和诠释可谓是淋漓尽致。因为有了Spring的这个IOC也好DI也好,我们把上街买菜的事情变成了菜主动送上门的活,这样的“生活方式”大大的提高了我们对于Spring框架的用户体验。

  今天主要说两件事,想必凡是稍稍接触过Spring框架开发的对于这些场景肯定都是眼熟透了——Spring如何使用多个外部属性文件以及基于注解方式配置Bean。

1. Spring使用多个外部属性文件

  这个截图并不稀奇,甚至完全看不出什么逻辑,下面分别贴出各个配置文件的内容

  beans.xml

...
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <property name="driverClassName" value="${jdbc.driver}"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
</bean>
...

  

  jdbc.properites

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/shake?useUnicode=true&characterEncoding=utf-8
jdbc.username=root
jdbc.password=admin

  没错,这就是你绝对见过的,在Spring中最常见对于数据源的配置

  1. 你完全可以把jdbc.driver的值写在beans.xml文件中,但是对于大型项目,某一天你需要该其中的配置,那就必须从庞大而臃肿的beans.xml文件找到你要修改的位置,并且胆战心惊的确认是不是还有遗漏的地方。
  2. 你完全可以把这些针对性的配置提取到一个外部属性文件当中。写成jdbc.properties的模样,这样修改起来,省时省心省力。
  3. 光有以上的配置还是无法工作的,因为spring的beans.xml文件并不知道该去哪里查找相应的变量,并为变量赋值。所以还需要在beans.xml中添加如下标签<context:property-placeholder location="jdbc.properties"/>

  测试代码

  写上如下的测试方法,可以用来验证上述配置是否正确

@Test
public void testJDBCConfiguration() throws SQLException {
    ApplicationContext act=new ClassPathXmlApplicationContext("beans.xml");
    DataSource dataSource = (DataSource) act.getBean("dataSource");
    System.out.println(dataSource.getConnection());
}

  

  显然我们得到了理想的结果

  那么问题来了,如果我们需要使用多个外部属性文件,怎么做?

  直接按照上面的套路再拷贝一份试试

  beans.xml

...
<context:property-placeholder location="test.properties"/>
<bean id="dataSource1" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <property name="driverClassName" value="${test.driver}"/>
    <property name="url" value="${test.url}"/>
    <property name="username" value="${test.username}"/>
    <property name="password" value="${test.password}"/>
</bean>
...

  

  test.properties

test.driver = com.mysql.jdbc.Driver
test.url = jdbc:mysql://localhost:3306/shake
test.username=root
test.password=admin

  

  测试方法

@Test
public void testJDBCConfiguration() throws SQLException {
    ApplicationContext act=new ClassPathXmlApplicationContext("beans.xml");
    DataSource dataSource = (DataSource) act.getBean("dataSource1");
    System.out.println(dataSource.getConnection());
}

  以下是报错信息

org.springframework.beans.factory.BeanDefinitionStoreException: Invalid bean definition with name 'dataSource1' defined in class path resource [beans.xml]: Could not resolve placeholder 'test.driver' in string value "${test.driver}"; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'test.driver' in string value "${test.driver}"
	at org.springframework.beans.factory.config.PlaceholderConfigurerSupport.doProcessProperties(PlaceholderConfigurerSupport.java:211)
	at org.springframework.beans.factory.config.PropertyPlaceholderConfigurer.processProperties(PropertyPlaceholderConfigurer.java:223)
	at org.springframework.beans.factory.config.PropertyResourceConfigurer.postProcessBeanFactory(PropertyResourceConfigurer.java:86)
	at ...org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:83)
	at com.jackie.springmvc.TestCollections.testJDBCConfiguration(TestCollections.java:186)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:74)
	at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:211)
	at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:67)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'test.driver' in string value "${test.driver}"
	at org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:174)
	at ...
	at org.springframework.beans.factory.config.PlaceholderConfigurerSupport.doProcessProperties(PlaceholderConfigurerSupport.java:208)
	... 35 more

  

  报错的主要原因来源于不识别test.driver这个变量。

  究其原因是因为beans.xml对于<context:property-placeholder location="jdbc.properties"/>这样的标签,如果有多个这样的定义,只会生效第一个,后面的都会忽略,这就造成了spring没有办法找到test.driver是在哪个文件中定义的。

解决方法

  采用通配符的方式,只定义一次,但是可以匹配多个外部属性文件

<context:property-placeholder location="classpath*:*.properties"/>

  这样就能够正常运行上面的测试方法。

2.基于注解的方式配置Bean

  与之经常同时出场的还有基于XML的方式配置Bean,我想大家都见过或了解autowired=byName和autowired=byType。这两种都是基于XML方式对于Bean采用基于名字和基于类型进行匹配的。

  但是这种方式有他的不足之处,所以在实际的项目中应用的不多。

  •   在 Bean 配置文件里设置 autowire 属性进行自动装配将会装配 Bean 的所有属性. 然而, 若只希望装配个别属性时, autowire 属性就不够灵活了.
  •   autowire 属性要么根据类型自动装配, 要么根据名称自动装配, 不能两者兼而有之.
  •   一般情况下,在实际的项目中很少使用自动装配功能,因为和自动装配功能所带来的好处比起来,明确清晰的配置文档更有说服力一些

  所以你看到以及用到比较多的应该是基于注解的方式配置Bean

  beans.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:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">

	<context:component-scan base-package="com.jackie.spring.annotation.generic"></context:component-scan>

</beans>

  

  该spring配置文件及其简洁,我们甚至看不到声明bean,只有Context:component-scang该标签意为spring会扫描com.jackie.spring.annotation.generic包下面的所有相关类。相关类是指具有以下字样的注解:

    @Component: 基本注解, 标识了一个受 Spring 管理的组件

    @Respository: 标识持久层组件

    @Service: 标识服务层(业务层)组件

    @Controller: 标识表现层组件

  标注了如上注解的类都是受Spring管辖的。

  同时我们还需要如下几个类

  BaseBao.java

public class BaseDao<T> {

	public void save(T entity){
		System.out.println("Save:" + entity);
	}

}

  

  BaseService.java

public class BaseService<T> {

	private BaseDao<T> dao;

	public void addNew(T entity){
		System.out.println("addNew by " + dao);
		dao.save(entity);
	}

}

  

  UserBao.java

@Repository
public class UserDao extends BaseDao<User>{

}

  

  UserService.java

@Service
public class UserService extends BaseService<User>{

}

  

  Main.java

public class Main {

	public static void main(String[] args) {

		ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");

		UserService userService = (UserService) ctx.getBean("userService");
		userService.addNew(new User());
	}

}

  

  1. 这里BaseBao和UserBao以及BaseService和UserService有一个简单的继承关系。

  2. UserBao类上加上了注解@Repository表示其为持久层的bean,UserService类上加上了注解@Service表示其为业务层的bean

  3. 这时候执行main方法,会报错

    Exception in thread "main" addNew by null

    java.lang.NullPointerException

    at com.jackie.spring.annotation.generic.BaseService.addNew(BaseService.java:12)

    at com.jackie.spring.annotation.generic.Main.main(Main.java:13)

  原因很简单,BaseService中不识别BaseBao这个bean,因为我们并没有声明过这个类,也没有注入,这时候需要在该类前加上注解

@Autowired

private BaseDao<T> dao;

  加上@Autowired表示Spring装配了该bean,从而就不会报空指针异常了。最终执行结果:

addNew by com.jackie.spring.annotation.generic.UserDao@32d2fa64

Save:com.jackie.spring.annotation.generic.User@1d8d30f7

  4. Spring 还支持 @Resource 和 @Inject 注解,这两个注解和 @Autowired 注解的功用类似

至此,我们熟悉了不能再熟悉的两大场景

  • Spring如何调用外部属性文件
  • Spring如何调用多个外部属性文件
  • Spring基于注解的方式注入bean的使用场景(反正我是一直在用,你们呢???)

如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是我最大的写作动力!如果您想持续关注我的文章,请扫描二维码,关注JackieZheng的微信公众号,我会将我的文章推送给您,并和您一起分享我日常阅读过的优质文章。



友情赞助

如果你觉得博主的文章对你那么一点小帮助,恰巧你又有想打赏博主的小冲动,那么事不宜迟,赶紧扫一扫,小额地赞助下,攒个奶粉钱,也是让博主有动力继续努力,写出更好的文章^^。

    1. 支付宝                              2. 微信

                      

学习Spring——两个你熟悉的不能再熟悉的场景使用的更多相关文章

  1. 学习Spring——依赖注入

    前言: 又开始动笔开了“学习Spring”系列的头…… 其实一开始写“学习SpringMVC”的几篇文章是出于想系统的了解下Spring以及SpringMVC,因为平时在公司中虽然每天都在使用Spri ...

  2. 菜鸟学习Spring——60s配置XML方法实现简单AOP

    一.概述. 上一篇博客讲述了用注解的形式实现AOP现在讲述另外一种AOP实现的方式利用XML来实现AOP. 二.代码演示. 准备工作参照上一篇博客<菜鸟学习Spring--60s使用annota ...

  3. 深入浅出学习Spring框架(四):IoC和AOP的应用——事务配置

    在前文 深入浅出学习Spring框架(一):通过Demo阐述IoC和DI的优势所在. 深入浅出学习Spring框架(三):AOP 详解 分别介绍了Spring的核心功能——IoC和AOP,光讲知识远远 ...

  4. [原创]java WEB学习笔记109:Spring学习---spring对JDBC的支持:使用 JdbcTemplate 查询数据库,简化 JDBC 模板查询,在 JDBC 模板中使用具名参数两种实现

    本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...

  5. 一步步学习 Spring Data 系列之JPA(二)

    继上一篇文章对Spring Data JPA更深( )一步剖析. 上一篇只是简单的介绍了Spring Data JPA的简单使用,而往往在项目中这一点功能并不能满足我们的需求.这是当然的,在业务中查询 ...

  6. [原创]java WEB学习笔记109:Spring学习---spring中事物管理

    博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱好 ...

  7. [原创]java WEB学习笔记103:Spring学习---Spring Bean配置:基于注解的方式(基于注解配置bean,基于注解来装配bean的属性)

    本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...

  8. 准备学习Spring MVC

    这一系列笔记将带你一步一步的进入Spring MVC,高手勿喷. 首先你得安装以下的工具: JDK,虽然JDK8已经发布了一段时间了,但是由于我们并不会使用到里面的新特性,所以JDK6以上版本皆可以( ...

  9. Spring 4 官方文档学习 Spring与Java EE技术的集成

    本部分覆盖了以下内容: Chapter 28, Remoting and web services using Spring -- 使用Spring进行远程和web服务 Chapter 29, Ent ...

随机推荐

  1. 如何下载和安装CocoaPods

    朋友自己学习了一段时间就去公司实习了去了之后公司用的是CocoaPods,他一脸茫然的向我求助,我这才想起来写着一遍为了帮助更多的朋友 CocoaPods是什么? 当你开发iOS应用时,会经常使用到很 ...

  2. 美妙的 CSS3 动画!一组梦幻般的按钮效果

    今天给大家带来的是五款梦幻般的动画按钮效果.下面是在线演示,把鼠标放在按钮上试试,有惊喜哦!CSS3 引入了众多供功能强大的新特性,让设计和开发人员能够轻松的创作出各种精美的界面效果. 温馨提示:为保 ...

  3. IOS第11天(2:UIPickerView自定义国旗选择)

    国旗选择 #import "HMViewController.h" #import "HMFlag.h" #import "HMFlagView.h& ...

  4. Date类型-演示JS中的日期

    <script type="text/javascript"> /* *演示JS中的日期 */ var date = new Date(); document.writ ...

  5. set集合,是一个无序且不重复的元素集合

    set集合,是一个无序且不重复的元素集合 class set(object):     """     set() -> new empty set object ...

  6. 一:解决VirtualBox只能安装32位系统的问题

    发现自己的笔记本(Thinkpad E440)里的 VirtualBox 只能安装 32位 的系统,如下图所示: 经过一番查资料,发现这玩意需要到BIOS里设置一下,方可安装 64位 系统,操作如下: ...

  7. memcached 缓存服务器

    Memcached 缓存服务器 Memcached 是高性能的分布式内存缓存服务器. 一般的使用目的是,通过缓存数据库查询结果,减少数据库访问次数,以提高动态web应用的速度.提高可扩展性. 主要特点 ...

  8. mysql错误:“ Every derived table must have its own alias”(每个派生出来的表都必须有一个自己的别名)

    自我感悟: 由此可以延伸,我们得到一个结果集,可以通过as XXX的方式,把结果集给当作一张表来用,以实现子查询: 一般在多表查询时,会出现此错误. 因为,进行嵌套查询的时候子查询出来的的结果是作为一 ...

  9. 在Linux中打印函数调用堆栈【原创】

    本人学习笔记,代码参考如下网址 参考http://www.cnblogs.com/dma1982/archive/2012/02/08/2342215.html zhangbh@prolin-srv: ...

  10. valuestack,stackContext,ActionContext.之间的关系

    者之间的关系如下图所示: relation ActionContext 一次Action调用都会创建一个ActionContext 调用:ActionContext context = ActionC ...