注入自定義對象
上一篇文章說到了注入java內置對象的方法(Setter) 這次來說一說關于注入自定義類型對象的一些內容
Bean和注入
定義兩個Bean如下所示:
public class User {
private String name;
private String sex;
private Role role;
}
public class Role {
private String role_name;
}
兩個Bean里面都實現Get()和Set()方法 并且重寫toString()方法
xml主配置文件內使用如下配置進行測試:
<bean id="user" class="com.test.model.User">
<property name="name" value="wukong" />
<property name="sex" value="男" />
<!-- 此時name所指代的屬性 不再是簡單的數據類型 而是自定義的對象 -->
<!-- 再次使用bean標簽來進行實例化 -->
<property name="role">
<bean id="role" class="com.test.model.Role">
<property name="role_name" value="斗戰勝佛" />
</bean>
</property>
</bean>
在測試文件內(使用Junit)編寫測試方法:
@Test
public void GetUserMessige(){
ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml");
//User指代xml文件內的bean標簽的id
//User.class指明User的類路徑 利用框架映射幫助我們進行實例化
User user= ac.getBean("user",User.class);
System.out.println(user);
}
控制臺輸出:
user [name=wukong, sex=男, role=role [role_name=斗戰勝佛]]
但是這樣的寫法 會存在這樣一個大問題:
我們在xml文件內定義了兩個<bean> id分別為 user 和 role
但是我們只能獲取到id為user的bean內的內容,role我們獲取不到
進行如下測試:
xml配置文件內容保持上述不動 在Junit測試文件內新建測試方法 指向id為role的bean標簽
@Test
public void GetRoleMessige(){
ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml");
Role role= ac.getBean("role",Role.class);
System.out.println(role);
}
進行測試發現Junit報錯:
報錯內容為 : No bean named 'role' available
沒有名為role的bean被定義
出現這個報錯的原因很好理解
==>
我們的確是定義了兩個bean
但是它們是包含與被包含的關系
role在user的內層 其作用范圍在uesr內
我們發出了想要越過user來直接獲取內層bean的請求
自然是獲取不到
因此 我們在內層的bean上設置id 其實沒有任何意義
綜上 我們還有第二種解決方案
我們將role的配置單獨書寫 并將兩個bean建立聯系
代碼如下:
<bean id="user" class="com.test.model.User">
<property name="name" value="wukong" />
<property name="sex" value="男" />
<property name="role">
<!-- 使用<ref>標簽來使兩個獨立的bean建立聯系 -->
<ref bean="role"/>
</property>
</bean>
<bean id="role" class="com.test.model.Role">
<property name="role_name" value="斗戰勝佛" />
</bean>
</beans>
存在這樣一種簡寫方式:
<property name="role" ref="role"/>
<!--
name="role" 指代類中的屬性
ref="role" 指代獨立的bean的id值
-->
可以將上面配置聯系的代碼進行簡化書寫代碼
接口和注入
在開發當中我們經常會使用到接口 按照傳統的開發模式 現在我們有了新的包結構 如下圖:
在正常的開發模式下 我們需要在接口的實現類下進行接口回調 如下圖
現在我們可以嘗試通過注入的方式來完成操作
在xml文件內聲明兩個實現類:
<bean id="userDaoImpl" class="com.test.dao.impl.UserDaoImpl"/>
<bean id="userServiceImpl" class="com.test.service.impl.UserServiceImpl">
<!--
UserDaoImpl userdaoimpl = new UserDaoImpl();
UserDao userDao = userdaoimpl;
-->
<property name="userdao" ref="userDaoImpl"></property>
</bean>
可以看到 我們通過注入方式 利用框架幫助我們完成了接口回調的操作 具體實現邏輯 看上述代碼的注釋部分
此時 我們可以直接通過UsereService進行測試了:
@Test
public void 測試UserService(){
ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml");
UserService userService= ac.getBean("userServiceImpl",UserService.class);
userService.Test01();
}
注意:
在書寫這段代碼的時候 ac.getBean("userServiceImpl",UserService.class);
雙引號括起來的地方 指向Service層bean的id
后面的類路徑 需要書寫接口的類路徑 不是接口實現類(impl)的類路徑
代碼提示:其實本段代碼 類似于上面所列出的User和Role的例子 不過是把實體bean轉換成了接口及其實現類 一點都不復雜 千萬別迷糊 hhhhh
構造函數注入方式
在前文 我們主要使用Setter注入方式 類中只有存在set方法才能夠使用
下面我們來測試另一種注入方式:構造函數注入
我們更改User類里面的內容 如下:
private String name;
private String sex;
private Role role;
public User(){
System.out.println("構造方法");
}
public void setName( String name ) {
System.out.println("setName");
this.name = name;
}
public void setSex( String sex ) {
System.out.println("setSex");
this.sex = sex;
}
public void setRole( Role role ) {
System.out.println("setRole");
this.role = role;
}
然后重寫toString()方法
重新進行測試 結果如下:
可以看到具體的流程:
現在我們所寫的bean標簽的內容 是基于java默認為每一個類提供的無參的構造函數
每次加載bean標簽 都會首先去尋找所引用的類中的構造方法 然后通過公開的Set()方法 往實例化好的對象里面添加值
所以我們每個類當中 都要存在一個無參的構造方法來進行控制反轉
現在我們添加一個有參的構造函數:
public User( String name , String sex ) {
super();
this.name = name;
this.sex = sex;
}
在xml配置文件內添加如下代碼:
<bean id="user1" class="com.test.model.User">
<constructor-arg name="name" value="八戒"/>
<constructor-arg name="sex" value="男"/>
</bean>
新建bean標簽做注入 <constructor-arg>標簽對應類中的構造函數
因為在User類中的有參構造函數有兩個參數 所以<constructor-arg>標簽要寫兩個 分別對應兩個參數 并給予初始值
此時新建測試方法:
@Test
public void 測試User構造函數(){
ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml");
User user= ac.getBean("user1",User.class);
System.out.println(user);
控制臺輸出:
產生 構造方法 SetName 等結果
是因為我們同一個xml文件中配置了使用無參構造函數 使用setter方式進行注入
xml文件一被加載 內部所有的bean都會被進行實例化
別驚慌hhhh
值得注意的是 在 <constructor-arg>標簽內
存在index屬性 用來標識形參的位置 起始值為1
存在type屬性 指明參數類型
如剛才我們對構造函數進行的操作,完整內容應為如下:
<bean id="user1" class="com.test.model.User">
<constructor-arg name="name" index="0" type="java.lang.String" value="八戒"/>
<constructor-arg name="sex" index="1" type="java.lang.String" value="男"/>
</bean>
關于構造函數注入自定義類型 類似于下面的語句就可以完成:
指明類中屬性 指明引用的bean(我們在上文曾新建了一個專門用來實例化Role類型的bean)
<constructor-arg name="role" ref="role" />