目录
IoC(Inversion of Control)控制反转思想
Spring技术对IoC思想的实现
DI(Dependency Injection)依赖注入
目标
最终效果
IoC入门案例
传统方法,不使用IoC思想调用dao层
使用IoC思想调用dao层
第一步:导入Spring坐标
第二步:创建Spring配置文件
第三步:初始化IoC容器,并通过容器来获取bean对象
不使用IoC思想调用service层
使用IoC思想调用service层
总结:如何将对象交给spring容器管理
Bean的基础配置
标签中的name属性:给一个bean变量起别名
标签中的scope属性:控制bean变量是单例还是多例,默认是单例(多次从容器获取的bean对象是同一个)
适合交给IoC容器管理的bean
不适合交给IoC容器管理的bean
Bean的实例化
第一种:通过无参构造,初始化IoC容器时就会调用所有bean对象的无参构造方法,创建每个bean对象
第二种:通过静态工厂
第三种:通过实例化工厂类
依赖注入方式
第一种setter注入
第二种:构造器注入
IoC(Inversion of Control)控制反转思想
使用对象时,由主动的new产生对象转换成由外部提供对象,此过程对象控制权由程序转移到外部,此思想称为控制反转。
Spring技术对IoC思想的实现
spring提供了一种容器,叫做IoC容器,用来充当IoC思想的"外部”
IoC容器负责对象的创建,初始化一系列工作,被创建或被管理的对象在IoC容器中都叫做Bean
DI(Dependency Injection)依赖注入
在容器中建立bean和bean之间的依赖关系的整个过程,称为依赖注入
目标
- 在IoC容器中管理bean
- 在IoC容器中将有依赖关系bean进行绑定(DI)
最终效果
使用对象时不仅可以直接从IoC容器中获取,并且获取到的bean都已经绑定了所有的依赖关系
IoC入门案例
传统方法,不使用IoC思想调用dao层
public class BookDaoImpl implements BookDao {
@Override
public void save() {
System.out.println("bookDao save");
}
}
不使用IoC思想(自己new一个对象,缺点:接口与实现类的耦合性太高)
/*
传统方法
*/
public class Book {
public static void main(String[] args) {
BookDao bookDao=new BookDaoImpl();
bookDao.save();
}
}
使用IoC思想调用dao层
第一步:导入Spring坐标
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
第二步:创建Spring配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="bookDao" class="com.hhh.dao.Impl.BookDaoImpl"></bean>
id是为这个实现类起别名
</beans>
第三步:初始化IoC容器,并通过容器来获取bean对象
public class App {
public static void main(String[] args) {
//初始化容器,加载配置文件
ApplicationContext ctx=new ClassPathXmlApplicationContext("springContext.xml");
BookDao bookDao = (BookDao) ctx.getBean("bookDao");//从容器中new一个id为bookDao的实现类对象,并依赖注入到这
bookDao.save();
}
}
我们可以发现使用IoC思想可以把接口和实现类的耦合度降低很多,由于这个实现类在IoC容器里注册为了Bean对象,所以我们可以直接通过容器来获取接口对应的Bean对象(实现类对象)
不使用IoC思想调用service层
BookServiceImpl
public class BookServiceImpl implements BookService {
BookDao bookDao;
@Override
public void save() {
System.out.println("bookService");
bookDao.save();
}
@Override
public void setBookDao(BookDao bookDao) {
this.bookDao=bookDao;
}
}
public class Test {
public static void main(String[] args) {
BookService bookService=new BookServiceImpl();
BookDao bookDao=new BookDaoImpl();
bookService.setBookDao(bookDao);
bookService.save();
}
}
使用IoC思想调用service层
springContext.xml文件的配置,由于bookServiceImpl实现类里有一个dao层的成员变量,所以在这个<bean>中要给这个dao层的成员变量单独配置
<bean id="bookService" class="com.hhh.service.impl.BookServiceImpl">
<!-- name属性是实现类中dao层的成员变量的名字
ref属性是id-->
<property name="bookDao" ref="bookDao"></property>
</bean>
调用
public class App {
public static void main(String[] args) {
//初始化容器
ApplicationContext ctx=new ClassPathXmlApplicationContext("springContext.xml");
BookService bookService = (BookService) ctx.getBean("bookService");
bookService.save();
}
}
总结:如何将对象交给spring容器管理
- 配置文件使用<bean>标签配置
- 初始化IoC容器,加载配置文件
Bean的基础配置
<bean>标签中的name属性:给一个bean变量起别名
<bean id="bookService" name="service service2" class="com.hhh.service.impl.BookServiceImpl">
<!-- name属性是实现类中dao层的成员变量的名字
ref属性是id-->
<property name="bookDao" ref="bookDao"></property>
</bean>
如图:给bookServiceImpl实现类Bean对象起了两个别名,可以使用空格,也可以使用, ; 这两种符号来间隔别名
public class App {
public static void main(String[] args) {
//初始化容器
ApplicationContext ctx=new ClassPathXmlApplicationContext("springContext.xml");
//BookService bookService = (BookService) ctx.getBean("bookService");
BookService bookService = (BookService) ctx.getBean("service");
bookService.save();
}
}
<bean>标签中的scope属性:控制bean变量是单例还是多例,默认是单例(多次从容器获取的bean对象是同一个)
public class App {
public static void main(String[] args) {
//初始化容器
ApplicationContext ctx=new ClassPathXmlApplicationContext("springContext.xml");
//BookService bookService = (BookService) ctx.getBean("bookService");
BookService bookService1 = (BookService) ctx.getBean("service");
BookService bookService2 = (BookService) ctx.getBean("service");
System.out.println(bookService1);
System.out.println(bookService2);
//com.hhh.service.impl.BookServiceImpl@4c1d9d4b
//com.hhh.service.impl.BookServiceImpl@4c1d9d4b
}
}
可以这两个Bean对象都是同一个,接下来我们修改一下
<bean id="bookService" name="service service2" class="com.hhh.service.impl.BookServiceImpl"
scope="prototype"
>
<!-- name属性是实现类中dao层的成员变量的名字
ref属性是id-->
<property name="bookDao" ref="bookDao"></property>
</bean>
可以发现修改之后这两个bean对象就是不同的
适合交给IoC容器管理的bean
- dao层对象
- service层对象
- web层对象
- 工具对象
不适合交给IoC容器管理的bean
封装实体的域对象(实体类)
Bean的实例化
第一种:通过无参构造,初始化IoC容器时就会调用所有bean对象的无参构造方法,创建每个bean对象
第二种:通过静态工厂
创建静态工厂类
public class StaticBookDaoFactory {
public static BookDao getBookDao(){
return new BookDaoImpl();
}
}
配置
<bean id="bookDao" class="com.hhh.factory.StaticBookDaoFactory" factory-method="getBookDao"> </bean>
通过factory-method属性来指定通过静态工厂的静态成员方法来返回的对象与id="bookDao"对应
相当于:
BookDao bookDao= StaticBookDaoFactory.getBookDao();
第三种:通过实例化工厂类
public class InstanceBookDaoFactory {
public BookDao getBookDao(){
return new BookDaoImpl();
}
}
配置
<bean id="factoryBean" class="com.hhh.factory.InstanceBookDaoFactory"></bean>
<bean id="bookDao" factory-bean="factoryBean" factory-method="getBookDao"></bean>
先配置实例化工厂类的bean对象,再通过factory-bean和factory-method来获取BookDaoImpl实现类的对象与id对应,相当于
InstanceBookDaoFactory instanceBookDaoFactory = new InstanceBookDaoFactory();
BookDao bookDao1 = instanceBookDaoFactory.getBookDao();
但是这样相当的麻烦,还要先配置实例化工厂类的bean对象,所以我们可以让实例化工厂类实现FactoryBean接口,让程序知道不是获取这个实例化工厂类的bean对象,而是通过它里面的方法来获取指定的bean对象
public class InstanceBookDaoFactory implements FactoryBean {
@Override
public Object getObject() throws Exception {
return new BookDaoImpl();
}
@Override
public Class<?> getObjectType() {
return null;
}
}
<bean id="bookDao" class="com.hhh.factory.InstanceBookDaoFactory"></bean>
因为InstanceBookDaoFactory实现了FactoryBean接口,所以不会获取InstanceBookDaoFactory的bean对象,而是获取getObject()方法返回对象的bean对象
依赖注入方式
第一种setter注入
- 简单类型
- 引用类型(自定义类型)
public class BookServiceImpl implements BookService {
BookDao bookDao;
Integer num;
@Override
public void save() {
System.out.println("bookService");
System.out.println("num="+num);
bookDao.save();
}
@Override
public void setBookDao(BookDao bookDao) {
this.bookDao=bookDao;
}
@Override
public void setNum(Integer num) {
this.num=num;
}
}
<bean id="bookDao" class="com.hhh.dao.Impl.BookDaoImpl"></bean>
<bean id="bookService" class="com.hhh.service.impl.BookServiceImpl">
<property name="bookDao" ref="bookDao"></property>
<property name="num" value="10"></property>
</bean>
简单类型使用value属性进行赋值(依赖注入)
结果:
如果没有setXxx()方法,就会报错:Bean property 'num' is not writable or has an invalid setter method.
第二种:构造器注入
- 简单类型
- 引用类型(自定义类型)
public class BookServiceImpl implements BookService {
BookDao bookDao;
Integer num;
public BookServiceImpl(BookDao bookDao, Integer num) {
this.bookDao = bookDao;
this.num = num;
}
@Override
public void save() {
System.out.println("bookService");
System.out.println("num="+num);
bookDao.save();
}
}
<bean id="bookDao" class="com.hhh.dao.Impl.BookDaoImpl"></bean>
<bean id="bookService" class="com.hhh.service.impl.BookServiceImpl">
<constructor-arg name="bookDao" ref="bookDao"></constructor-arg>
<constructor-arg name="num" value="20"></constructor-arg>
</bean>
<constructor-arg>name属性是有参构造函数里面的参数名字
结果
或者也可以这样配置
<bean id="bookDao" class="com.hhh.dao.Impl.BookDaoImpl"></bean>
<bean id="bookService" class="com.hhh.service.impl.BookServiceImpl">
<constructor-arg type="com.hhh.dao.BookDao" ref="bookDao"></constructor-arg>
<constructor-arg index="1" value="20"></constructor-arg>
</bean>
type就是参数的类型,index就是有参构造函数里参数位置的索引
依赖自动装配,<autowire>
public class BookServiceImpl implements BookService {
BookDao bookDao;
@Override
public void save() {
System.out.println("bookService");
bookDao.save();
}
@Override
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
}
<bean id="bookDao" class="com.hhh.dao.Impl.BookDaoImpl"></bean>
<bean id="bookService" class="com.hhh.service.impl.BookServiceImpl" autowire="byType">
</bean>
byType是容器通过类型匹配来查看自己是否有与BookServiceImpl类的成员变量相同类型的bean对象,如果有就进行注入,没有就空指针异常
也可以使用byName,通过成员变量的名字来寻找,必须与id的名字一样
集合注入
public class ResourceServiceImpl implements ResourceService {
private String[] array;
private List<String> list;
private Set<String>set;
private Map<String,String>map;
private Properties properties;
@Override
public String toString() {
return "ResourceServiceImpl{" +
"array=" + Arrays.toString(array) +
", list=" + list +
", set=" + set +
", map=" + map +
", properties=" + properties +
'}';
}
public void setArray(String[] array) {
this.array = array;
}
public void setList(List<String> list) {
this.list = list;
}
public void setSet(Set<String> set) {
this.set = set;
}
public void setMap(Map<String, String> map) {
this.map = map;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
}
配置
<bean id="resourceService" class="com.hhh.service.impl.ResourceServiceImpl">
<property name="array">
<array>
<value>array1</value>
<value>array2</value>
<value>array3</value>
</array>
</property>
<property name="list">
<list>
<value>list1</value>
<value>list2</value>
<value>list3</value>
</list>
</property>
<property name="set">
<set>
<!-- set会去重-->
<value>set1</value>
<value>set1</value>
<value>set3</value>
</set>
</property>
<property name="map">
<map>
<entry key="mapKey1" value="value1"></entry>
<entry key="mapKey2" value="value2"></entry>
</map>
</property>
<property name="properties">
<props>
<prop key="propKey1">value1</prop>
<prop key="propKey2">value2</prop>
</props>
</property>
</bean>
public class App {
public static void main(String[] args) {
//初始化容器
ApplicationContext ctx=new ClassPathXmlApplicationContext("springContext.xml");
ResourceService resourceService = (ResourceService) ctx.getBean("resourceService");
System.out.println(resourceService);
}
}
结果:
Bean的生命周期
public class BookDaoImpl implements BookDao {
public void init(){
System.out.println("bookDao init");
}
@Override
public void save() {
System.out.println("bookDao save");
}
public void destroy(){
System.out.println("bookDao destroy");
}
}
配置
<bean id="bookDao" class="com.hhh.dao.Impl.BookDaoImpl" init-method="init" destroy-method="destroy"></bean>
public class App {
public static void main(String[] args) {
//初始化容器
ClassPathXmlApplicationContext ctx=new ClassPathXmlApplicationContext("springContext.xml");
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
bookDao.save();
ctx.registerShutdownHook();//使用完自己关闭,写在哪都可以
//暴力关闭
//ctx.close();
}
}
结果:
我们可以发现自己写init方法,和destroy方法还要再bean标签中自己配置,太过麻烦,我们可以实现spring提供的接口来重写方法
public class BookDaoImpl implements BookDao, InitializingBean, DisposableBean {
@Override
public void save() {
System.out.println("bookDao save");
}
@Override
public void destroy() throws Exception {
System.out.println("bean destroy");
}
@Override
//根据名字我们可以发现是先依赖注入再初始化bean对象
public void afterPropertiesSet() throws Exception {
System.out.println("bean init");
}
}
根据方法名字我们可以发现是先依赖注入再初始化bean对象
这样一来我们就不用自己配置了
<bean id="bookDao" class="com.hhh.dao.Impl.BookDaoImpl"></bean>