學習Spring——兩個你熟悉的不能再熟悉的場景使用


  最近公眾號受邀獲取了留言和贈送模板的權限,小開心(歡迎去公眾號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. 微信

 

                      


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM