IOC
前言
了解Spring IOC,及其它的XML和注解实现。
一、IOC
1、IOC底层原理
底层用XML解析+工厂设计模式+反射。
以前降低耦的方式,
1)写一个properties文件,然后load一下对于该文件的输入流,读取配置文件信息,然后通过getString("key")
来获取里面的内容,从而可改配置文件而不用重写编译。
2)工厂模式,解除两个类之间的耦合,但是工厂和两类之间都有高耦合。
以前学的反射,
1)通过classForName(classpath)
来动态创建实例。
有了前面的基础,就可以读取properties文件的类名,然后在工厂中使用反射来获取类实例,这样就进一步降低了耦合。而IOC使用的是XML解析而不是去加载properties文件。
2、IOC接口(BeanFactory)
IOC思想是基于IOC容器完成,IOC容器底层就是对象工厂。
IOC容器实现的两种方式(两种接口),BeanFactory、ApplicationContext。
1)BeanFactory
IOC的基本接口,用于Spring内部使用。
注:在加载配置文件时,不会去创建对象,在获取时才会去创建对象。
2)ApplicationContext
该为BeanFactory的子接口,提供更加强大的功能,提供给开发人员使用。
注:加载配置文件时,就会把配置文件配置的bean全部创建。
ApplicationContext的两个主要实现类,FileXmlApplicationContext(系统盘上的文件)、ClassPathXmlApplicationContext(src下,即类路径)
3、IOC操作Bean管理(基于xml)
Bean管理即,Spring创建什么对象?给创建的对象赋什么值?
1、 Bean标签的属性,
id,给该Bean取一个唯一的标识。
class,类的全路径。
name,类id的一个早期属性,可以加特殊字符。
2、xml注入基本属性,
DI,依赖注入,注入属性值。
1)setter方法去注入,
<!-- 配置一个User Bean -->
<bean id="user" class="com.xhu.spring5.User">
<property name="name" value="root">
</property>
</bean>
2)通过有参构造来注入,
<!-- 配置一个User Bean -->
<bean id="user" class="com.xhu.spring5.User">
<constructor-arg name="name" value="root">
</constructor-arg>
</bean>
3)p名称空间注入,用于简化太多的properties,
<?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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 配置一个User Bean -->
<bean id="user" class="com.xhu.spring5.User" p:name="root">
</bean>
</beans>
3、注入其它类型属性
null值,
<!-- 配置一个User Bean -->
<bean id="user" class="com.xhu.spring5.User" p:name="root">
<!--null值的设置 -->
<property name="name">
<null></null>
</property>
</bean>
特殊符号,
<!-- 配置一个User Bean -->
<bean id="user" class="com.xhu.spring5.User" p:name="root">
<!--null值的设置 -->
<property name="name">
<value>
<![CDATA[<<北京>]]>
</value>
</property>
</bean>
<!-- 配置一个User Bean -->
<bean id="user" class="com.xhu.spring5.User" p:name="root">
<!--null值的设置 -->
<property name="name">
<value>
<<北京>>
</value>
</property>
</bean>
</beans>
5、注入属性,外部Bean
<bean id="service" class="com.xhu.service.impl.ServiceImp">
<property name="dao" ref="dao"></property>
</bean>
<bean id="dao" class="com.xhu.dao.impl.DAOImpl"></bean>
注:service中必须有setter方法。
6、属性注入,内部bean和级联赋值,用于一对多的关系。
<bean id="emp" class="com.xhu.spring5.Emp">
<property name="name" value="root"></property>
<property name="gender" value="男"></property>
<property name="dept">
<bean id="dept" class="com.xhu.spring5.Dept">
<property name="name" value="IT部"></property>
</bean>
</property>
</bean>
<bean id="emp" class="com.xhu.spring5.Emp">
<property name="name" value="root"></property>
<property name="gender" value="男"></property>
<property name="dept" ref="dept"></property>
<property name="dept.name" value="IT部"></property>
</bean>
<bean id="dept" class="com.xhu.spring5.Dept">
</bean>
注:dept.name,需要生成获取dept的get方法。
7、属性注入,Array、List、Set、Map
package com.xhu.spring5;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class TestCollection {
private String[] names;
private List<String> list;
private Map<String,String> map;
private Set<String> set;
public TestCollection() {
}
public void setNames(String[] names) {
this.names = names;
}
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;
}
}
<bean id="testCollection" class="com.xhu.spring5.TestCollection">
<property name="names">
<array>
<value>a</value>
<value>b</value>
<value>c</value>
</array>
</property>
<property name="list">
<list>
<value>a</value>
<value>b</value>
<value>c</value>
</list>
</property>
<property name="map">
<map>
<entry key="root" value="root"></entry>
<entry key="root" value="root"></entry>
<entry key="root" value="root"></entry>
</map>
</property>
<property name="set">
<set>
<value>a</value>
<value>b</value>
<value>c</value>
</set>
</property>
</bean>
8、集合等+JavaBean
<property name="list">
<list>
<ref bean="bean1"></ref>
<ref bean="bean2"></ref>
<ref bean="bean3"></ref>
</list>
</property>
9、集合等的抽取,需引入名称空间util
<?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:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
<util:list id="list">
<value>1</value>
</util:list>
<bean id="testCollection" class="com.xhu.spring5.TestCollection">
<property name="list">
<ref bean="list"></ref>
</property>
</bean>
</beans>
10、工厂Bean
FactoryBean,不同于上面的普通bean,配置什么类型就返回什么类型,而FactoryBean可以返回不同的类型。
step)建立类实现FactoryBean,然后重写其方法,可以返回想要的类型。
package com.xhu.spring5;
import org.springframework.beans.factory.FactoryBean;
public class MyBean implements FactoryBean<User> {
@Override
public User getObject() throws Exception {
User user = new User();
user.setName("root");
return user;
}
@Override
public Class<?> getObjectType() {
return null;
}
@Override
public boolean isSingleton() {
return false;
}
}
<bean id="myBean" class="com.xhu.spring5.MyBean"></bean>
11、bean的作用域,可以设置配置的一个bean为单实例还是多实例。
<!-- singleton为单实例,prototype为多实例-->
<bean id="myBean" scope="singleton" class="com.xhu.spring5.MyBean"></bean>
<bean id="myBean2" scope="prototype" class="com.xhu.spring5.MyBean"></bean>
12、bean的生命周期
A)执行其无参构造方法
B)通过set方法来赋值
C)调用bean的初始化方法(需要进行配置)
D)bean的使用
E)容器在关闭时,调用bean的销毁方法(需要进行配置销毁的方法)
package com.xhu.spring5;
public class BeanLife {
private String name;
public BeanLife() {
System.out.println("第一步,调用无参构造器");
}
public void setName(String name) {
this.name = name;
System.out.println("第二步,执行赋值操作");
}
public void initMethod() {
System.out.println("第三步,执行初始化方法,需配置");
}
public void use() {
System.out.println("第四步,使用bean,需调用此方法");
}
public void destroy() {
System.out.println("第五步,执行销毁bean方法,需配置");
}
}
<bean id="beanLife" class="com.xhu.spring5.BeanLife" init-method="initMethod" destroy-method="destroy">
<property name="name" value="root"></property>
</bean>
注:销毁需要((ClassPathXmlApplicationContext)context).close()
注:如果加上后置处理器,还要在bean init方法的前调用两个方法,这两步都是把bean实例传入后置处理器方法中,
package com.xhu.spring5;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class MyBeanPost implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("初始化之前来操作一下bean");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("初始化之后来操作一下bean");
return bean;
}
}
<bean id="beanLife" class="com.xhu.spring5.BeanLife" init-method="initMethod" destroy-method="destroy">
<property name="name" value="root"></property>
</bean>
<bean id="beanPostProcessor" class="com.xhu.spring5.MyBeanPost"></bean>
13、自动装配,可以根据名字或者类型来注入属性值,
<!-- 通过bean 的id来看是否与属性名相等-->
<bean id="service" class="com.xhu.service.impl.ServiceImp" autowire="byName">
<!--<property name="dao" ref="dao"></property>-->
</bean>
<bean id="dao" class="com.xhu.dao.impl.DAOImpl"></bean>
<!-- 找到该类的bean,但是相同类型的bean不能定义多个-->
<bean id="service" class="com.xhu.service.impl.ServiceImp" autowire="byType">
<!--<property name="dao" ref="dao"></property>-->
</bean>
<bean id="dao" class="com.xhu.dao.impl.DAOImpl"></bean>
14、配置连接池,需要引入命名空间context
<?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:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
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/util http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/db"></property>
<property name="username" value="root"></property>
<property name="password" value="123"></property>
</bean>
<bean id="dataSource2" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${driverClassName}"></property>
<property name="url" value="${url}"></property>
<property name="username" value="${username}"></property>
<property name="password" value="${password}"></property>
</bean>
<context:property-placeholder location="classpath:druid.properties"></context:property-placeholder>
</beans>
4、IOC操作Bean管理(基于注解)
用注解来简化XML配置,Spring提供了以下几个创建bean的注解,
@Component、@Service、@Controller、@Repostory。
1、Step
1)需引入aop的jar包。
2)开启组件扫描,扫描到有组件的类就实例化。
3)创建类,并在其上面加上注解。
<?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:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
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/util http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 开启组件扫描,即类扫描,扫描注解,进行该类的bean管理-->
<!-- 多个package用逗号隔开,伙子直接用父包-->
<context:component-scan base-package="com.xhu.service,com.xhu.dao"></context:component-scan>
</beans>
package com.xhu.spring5;
import org.springframework.stereotype.Component;
/**
* 注解方式实现bean的生成,然后给spring管理
* value的方式可以取别名,否则默认类名且第一个字母小写,这里相当于xml中的id
*/
@Component(value = "annoClass")
public class AnnoClass {
}
注:扫描可以有配置的扫,设置默认filter为false,
<!-- 扫带Controller注解的类-->
<context:component-scan base-package="com.xhu.service,com.xhu.dao" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Component"/>
</context:component-scan>
4)属性-Bean注入
A)AutoWired,根据属性类型进行注入,对应XML的byType。
B)Qualifier,根据属性名称进行注入,对应XML的byName。如果一个接口有很多实现类,就需要用Qualifier来指定实现类。
C)Resource,即可以根据类型,也可以根据名称。
D)Value,针对普通类型,前三个是Bean的注入。
package com.xhu.service.impl;
import com.xhu.dao.DAO;
import com.xhu.service.Service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import javax.annotation.Resource;
public class ServiceImp implements Service {
@Autowired
private DAO dao;
public void update() {
dao.update();
}
@Autowired
@Qualifier(value = "daoImpl")
private DAO dao1;
@Resource(name = "daoImpl")
private DAO dao2;
@Resource
private DAO dao3;
@Value(value = "root")
private String name;
}
2、完全注解开发
都不用在XML中取扫描包,创建一个配置类取替代XML文件。
package com.xhu.spring5;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/**
* @Configuration 告诉spring这是一个配置类
*/
@Configuration
@ComponentScan(basePackages = {"com.xhu.service", "com.xhu.dao"})
public class SpringConfig {
}
@Test
public void annotation() {
//1.获取ApplicationContext对象来关联配置文件,为接下来的IOC做准备
ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfig.class);
//2.获取对象实例
User user = ac.getBean("user", User.class);
System.out.println(user);
}
总结
1)Spring IOC
参考文献
[1] Spring5 尚硅谷