问题1:
往一个类中传递数据的方式有哪些呢,其实,只有一种方式,即通过方法,但方法却有多种,一种是我们先前学到的通过set
方法(普通方法),另一种则是通过构造方法的方式。
问题2:
依赖注入是描述了容器建立Bean与Bean之间的依赖关系的过程,那么如果Bean运行时需要的是字符串或者其他数据类型而非引用类型(Bean)呢?
那么关于依赖注入的类型也要分为两种,分别是引用类型(Bean)以及String,int等。
Setter方式依赖注入
先前,我们已经使用了set方法来注入引用类型:
<bean id="bookservice" name="service,books1;bookl books" class="service.impl.BookServiceImpl" ><property name="bookdao" ref="bookdao"></property>
</bean>
public void setBookdao(BookDaoImpl bookdao) {this.bookdao=bookdao;}
那么,如果有多个引用类型呢,我们需要多个setter方法即可。
在BookServiceImpl中定义UserDao接口并实现
UserDao userdao;
public void setUserdao(UserDao userdao) {this.userdao = userdao;}
至于Spring的xml文件中只需要创建一个userdao,随后在bookservice中引用即可。
<bean id="userdao" class="dao.impl.UserDaoImpl" ></bean>
<bean id="bookservice" name="service,books1;bookl books" class="service.impl.BookServiceImpl" ><property name="bookdao" ref="bookdao"></property><property name="userdao" ref="userdao"></property>
</bean>
那么,该如何注入普通数据类型呢,我们在UserDaoImpl中定义两个私有成员变量:dataBaseName
与connectionNum
package dao.impl;
import dao.UserDao;
public class UserDaoImpl implements UserDao {private String dataBaseName;public void setDataBaseName(String dataBaseName) {this.dataBaseName = dataBaseName;}public void setConnectionNum(int connectionNum) {this.connectionNum = connectionNum;}private int connectionNum;@Overridepublic void save() {System.out.print("userdao ..."+connectionNum+","+dataBaseName+"\n");}
}
随后在xml文件中注入属性值:使用property标签中的value属性来注入基本数据类型。
<bean id="userdao" class="dao.impl.UserDaoImpl" ><property name="connectionNum" value="10"></property><property name="dataBaseName" value="mysql"></property>
</bean>
运行结果如下:
构造器方式依赖注入
接下来,上面是通过setter方法来进行依赖注入,另一种方法则是通过构造方法的形式进行依赖注入,事实上,两种写法很相似,但由于采用构造方法进行依赖注入时会有些许问题,因此我们大多采用setter方法来进行依赖注入。
由于构造器传参的方式要求Spring中属性的name必须与构造函数中的形参保持一致,因此具有一定的耦合度,这就导致Spring中提供了根据type类型与index属性来进行注入,但依旧很不方便,因此我们多采用Setter方法的方式进行注入。然而,仍存在些许情况我们需要使用构造器注入的方式:
依赖自动装配
前面已经介绍了两种依赖注入方式,但这些都需要我们自己去写,那么,这个依赖注入的方法能够不写呢,当然可以,Spring由此提出了依赖自动装配。
IOC根据Bean所依赖的资源在容器中自动查找并注入到Bean的过程称为依赖自动装配。
有两种主要的自动装配方式,分别是按照类型装配以及按照类型装配,其中按照类型装配最为常用。
首先,在ServiceImpl中的set方法还是需要保留的,这也符合Spring的装配策略,保证不易出错。
按名称装配:
package service.impl;import dao.BookDao;
import dao.impl.BookDaoImpl;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import service.BookService;public class BookServiceImpl implements BookService, InitializingBean, DisposableBean {BookDao bookdao;UserDao userdao;public void save() {System.out.print("执行service...\n");bookdao.save();}public void setBookdao(BookDaoImpl bookdao) {this.bookdao=bookdao;}
}
可以看到使用按名称自动装配时只需要使用autowire属性即可,这里的名称值得是在service中定义的成员变量bookdao,事实上,其真正对应的是set方法去掉set后第一个字母小写的名称即bookdao。
<bean id="bookservice" class="service.impl.BookServiceImpl" autowire="byName"></bean>
而按类型装配则更为简单了,它甚至都不需要依赖类有名字,会装配所有需要的,符合的Bean,这种也是最常用的。注意:里面对应的setter方法是需要保留的。
<bean id="bookdao" class="dao.impl.BookDaoImpl" init-method="init" destroy-method="destory"></bean>
<bean class="dao.impl.UserDaoImpl" ><property name="connectionNum" value="10"></property><property name="dataBaseName" value="mysql"></property>
</bean>
<bean id="bookservice" class="service.impl.BookServiceImpl" autowire="byType"></bean>
集合注入
package dao.impl;
import dao.OrderDao;
import java.util.*;
public class OrderDaoImpl implements OrderDao {private List<String> list;private Map<String,String> map;private Set<String> set;private int[] array;public void setList(List<String> list) {this.list = list;}public void setMap(Map<String, String> map) {this.map = map;}public void setSet(Set<String> set) {this.set = set;}public void setArray(int[] index) {this.array = index;}public void setProperties(Properties properties) {this.properties = properties;}private Properties properties;@Overridepublic void show() { System.out.print(Arrays.toString(array)+"\n"+map+"\n"+set+"\n"+list+"\n"+properties+"\n");}
}
<bean id="orderdao" class="dao.impl.OrderDaoImpl"> <property name="map"><map><entry value="100" key="apple"></entry><entry value="40" key="orange"></entry><entry value="130" key="dog"></entry></map></property><property name="array"><array><value>100</value><value>200</value></array></property><property name="list"><list><value>dog</value><value>cat</value></list></property><property name="set"><set><value>100</value><value>100</value><value>200</value></set></property><property name="properties"><props><prop key="country">china</prop><prop key="city">beijing</prop></props></property>
</bean>
运行结果:
数据源等对象(第三方资源)管理
先前的都是我们对自己创建的对象Bean进行管理,那么对于一些第三方资源如何管理呢?
这里我们以数据库连接池c3p0为例,首先在pom.xml文件中加入c3p0的坐标:
<dependency><groupId>c3p0</groupId><artifactId>c3p0</artifactId><version>0.9.1.2</version>
</dependency>
随后在IOC容器中配置Bean,我们可以在浏览器中搜索c3p0的连接池Bean使用哪个类进行创建,随后我们进入该类中,按Alt+7调出该类的所有方法,从而确定我们是使用setter还是构造函数的方式来创建Bean。
<bean class="com.mchange.v2.c3p0.ComboPooledDataSource"></bean>
我们通过查看其方法发现其提供的是通过setter方法来传递参数,我们将其配置完成。
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"><property name="driverClass" value="com.mysql.jdbc.Driver"/><property name="jdbcUrl" value="jdbc\:mysql\:///ssm_takeout?characterEncoding=utf-8"/><property name="user" value="root"/><property name="password" value="root"/>
</bean>
随后运行一下:
ClassPathXmlApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext.xml");
DataSource dataSource= (DataSource) ctx.getBean("dataSource");
System.out.print(dataSource);
但是,我们直接将配置信息写在IOC容器中是不合适的,我们应该使用Properties文件来读取到Bean中进而生成配置。那么如何使用Spring加载外部文件呢?Spring 使用开启命名空间的方式来加载外部文件。
在头文件上加上下面三句代码:
1.开启context命名空间
<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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"
>
随后使用context命名空间加载properties文件并通过属性占位符$
读取数据
这里只要我们使用context加载完文件后,那么${jdbc.driver}
便可以在整个IOC容器中使用。
<context:property-placeholder location="db.properties"/>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"><property name="driverClass" value="${jdbc.driver}"/><property name="jdbcUrl" value="${jdbc.url}"/><property name="user" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/>
</bean>
此外,需要注意的是,在加载文件时,如果properties文件中的变量与一些系统变量重复了,那么由于系统变量优先级较高,则会覆盖我们设置的变量,此时,我们只需要在添加一个属性system-properties-mode="NEVER"
即可,代表不加载系统变量。
<context:property-placeholder location="db.properties" system-properties-mode="NEVER"/>
同时,如果想要加载多个properties文件,只需要在location属性中通过逗号连接即可。
<context:property-placeholder location="db.properties,db.properties1" system-properties-mode="NEVER"/>
但这样的加载方式也太过麻烦,我们可以使用*.properties
来匹配所有的配置文件。
<context:property-placeholder location="*.properties" system-properties-mode="NEVER"/>
但最规范的格式应加上classpath
<context:property-placeholder location="classpath:db.properties" system-properties-mode="NEVER"/>
但这种方式只能在自己的项目中读取,要想在引用的jar包中读取则可使用:
<context:property-placeholder location="classpath*:db.properties" system-properties-mode="NEVER"/>
容器
这里我们对容器内容进行补充:其中第一种是根据类路径加载配置文件,第二种则是根据绝对路径加载配置配件,我们多采用第一种。
需要注意的是,创建容器的方式有两种分别是通过BeanFactory与ApplicationContext,我们常用的是后者,其加载方式是立即加载,即在创建容器时便将Bean创建好了,而还有一种方式则是 在我们需要才创建Bean,称为延迟加载,如何实现呢?加上lazy-init
属性即可
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" lazy-init="true">