什么是IOC容器?
控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。
- IOC底层原理
- IOC生存周期
- IOC操作管理bean(基于xml和基于注解)
1.IOC底层原理
通过引入IOC容器,利用依赖关系注入的方式,实现对象之间的解耦。ioc思想通过ioc容器完成,ioc容器底层就是对象工厂。大体步骤有xml 解析、工厂模式、反射 。
(1) 写xml配置文件,配置需要创建的对象
<bean id="dao" class="cheerful.UserDao"></bean>
<!--class是所需要的bean对象的包名-->
class UserFactory{
public static UserDao getDao(){
String classValue=属性值;//xml解析
Class clazz=Class.forName(classValue);//通过发射创建对象
return (UserDao)Clazz.newInstance();
}
}
spring提供IOC容器实现的两个接口:
(1)BeanFactory:Spring 内部的使用接口,不提供开发人员进行使用 ,加载配置文件时候不会创建对象,在获取对象(使用)才去创建对象
(2)ApplicationContext:BeanFactory接口的子接口,提供更多的功能,一般开发人员使用。 加载配置文件时候就会把在配置文件对象进行创建 。
ApplicationContext接口的实现类
2.IOC操作bean基于xml(依赖注入)
现在有这样的一个UserDao对象,我们可以写xml文件用bean标签实现对象的创建,再写一个测试类测试是否能获取到这个对象。
package cheerful.dao;
public class UserDao {
public void add(){
System.out.println("add.");
}
}
<?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="userDao" class="cheerful.dao.UserDao"></bean>
</beans>
public class test {
@Test
public void test(){
//1.LOAD bean1.xml
ApplicationContext context=new ClassPathXmlApplicationContext("bean1.xml");
//2.GET BEAN
UserDao userDao = (UserDao) context.getBean("userDao");
System.out.println(userDao);
userDao.add();
}
}
这样就基本实现了ioc的一个hello world!那么假如UserDao里面还有一些成员变量又该怎样通过bean注入呢?答案是再bean里面写一个property。话不多说直接上代码。
public class UserDao {
private String username;
public void setUsername(String username) {
this.username = username;
}
@Override
public String toString() {
return "UserDao{" +
"username='" + username + '\'' +
'}';
}
public void add(){
System.out.println("add.");
}
}
<bean id="userDao" class="cheerful.dao.UserDao">
<property name="username" value="马化腾666"></property>
</bean>
public class test {
@Test
public void test(){
//1.LOAD bean1.xml
ApplicationContext context=new ClassPathXmlApplicationContext("bean1.xml");
//2.获取对象
UserDao userDao = (UserDao) context.getBean("userDao");
System.out.println(userDao);
userDao.add();
}
}
运行之后得到这样的结果,说明通过xml管理bean是可行的~
以此类推,别的一些特殊情况或数据结构也差不多是这样只不过用到一些不同的标签,就不再多累赘主要写写如何通过注解管理bean。
3.bean的生命周期
bean的生命周期有五部或者七部。
(1)创建 bean 实例(无参数构造)
(2)为 bean 的属性设置值调用 set 方法)
(3)bean 的初始化
把 bean 实例传递 bean 后置处理器的方法 postProcessBeforeInitialization
(4)bean对象获取到了
把 bean 实例传递 bean 后置处理器的方法 postProcessAfterInitialization
(5)bean 的销毁
然后我们可以写个例子来证明一下(在生命周期的每一步的方法上加上一些输出)。下面MyPostBean类是我们重写后置处理器的方法的类,order是我们用于测试的类,以及配置文件bean.xml和测试代码。
package cheerful.bean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class MyPostBean implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("使用了后置处理器postProcessAfterInitialization方法");
return null;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("使用了后置处理器postProcessBeforeInitialization的方法");
return null;
}
}
package cheerful.bean;
public class Orders {
private String oname;
public Orders() {
System.out.println("调用无参构造");
}
public void setOname(String oname) {
this.oname = oname;
System.out.println("调用set方法");
}
@Override
public String toString() {
return "Orders{" +
"oname='" + oname + '\'' +
'}';
}
public void init(){
System.out.println("bean初始化方法");
}
public void destory(){
System.out.println("bean销毁方法");
}
}
<?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">
<!--init-method设置初始化方法,destroy-method设置销毁方法-->
<bean id="order" class="cheerful.bean.Orders" init-method="init" destroy-method="destory">
<property name="oname" value="monica"></property>
</bean>
<!--这里配置后置处理器-->
<!--后置处理器会将当前xml文件里面的所有bean都添加后置处理器作用-->
<bean id="myBeanPost" class="cheerful.bean.MyPostBean"></bean>
</beans>
@Test
public void testBean(){
ApplicationContext context=new ClassPathXmlApplicationContext("bean4.xml");
Orders order = context.getBean("order",Orders.class);
System.out.println("对象已得到");
// System.out.println(order);
// ApplicationContext的子接口ClassPathXmlApplicationContext才有close方法
((ClassPathXmlApplicationContext) context).close();
}
最后的运行界面居然和上面的bean生命周期完全吻合!!
4.基于注解管理bean
比起xml那么麻烦的东西,当然有注解能够避免这些事情呀!Java提供了一些注解来免去写xml文件这样繁琐的事情。只不过会多需要一个jar包(spring-aop-5.2.6.RELEASE.jar)。
Spring 针对 Bean 管理中创建对象提供的注解 :
(1)@Component
(2)@Service
(3)@Controller
(4)@Repository
这四个注解都可以用于创建bean,只是通常用的地方不一样
下面我们就开始用注解形式的hello world:
这里的xml文件要开始组件扫描,就是xmlns: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: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.xsd">
<!--开启组件扫描-->
<!--base-package指扫描的包路径-->
<context:component-scan base-package="cheerful.bean"></context:component-scan>
</beans>
package cheerful.bean;
import org.springframework.stereotype.Component;
@Component(value = "stu")
public class Stu {
private String name;
@Override
public String toString() {
return "Stu{" +
"name='" + name + '\'' +
'}';
}
public void test(){
System.out.println("注解测试成功");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@Test
public void textXml6(){
ApplicationContext context=new ClassPathXmlApplicationContext("bean6.xml");
Stu stu = context.getBean("stu", Stu.class);
stu.test();
System.out.println(stu);
}
这里我们同样获得了创建的Stu对象。说明用注解操作bean也是可行的,而且还比xml方便不少!
5、基于注解方式实现属性注入
(1)@Autowired:根据属性类型进行自动装配
(2)@Qualifier:根据名称进行注入
(3)@Resource:可以根据类型注入,也可以根据名称注入
(4)@Value:注入普通类型属性
自动装配就是表示根据属性类型等自动注入value。如果是根据对象类型注入的话,假如是UserDao类型就直接是userDao。
@Autowired
private UserDao userDao;
@Autowired // 根据类型进行注入
@Qualifier(value = "userDaoImpl1") // 根据名称进行注入
private UserDao userDao;
//@Resource // 根据类型进行注入
@Resource(name = "userDaoImpl1") // 根据名称进行注入
private UserDao userDao;
@Value(value = "abc")
private String name;
6、完全注解开发
(1)创建配置类,替代 xml 配置文件
package cheerful;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component(value = "book")
public class Book {
@Value(value = "围城")
private String name;
public void setName(String name) {
this.name = name;
}
public Book(String name) {
this.name = name;
}
@Override
public String toString() {
return "Book{" +
"name='" + name + '\'' +
'}';
}
public Book() {
}
}
@Configuration // 作为配置类,替代 xml 配置文件
@ComponentScan(basePackages = {"cheerful"})
public class SpringConfig { }
(2)编写测试类
@Test
public void test(){
ApplicationContext context=new AnnotationConfigApplicationContext(SpringConfig.class);
Book book = context.getBean("book", Book.class);
System.out.println(book);
}
如运行结果所示,这样能不用编写xml文件成功得到对象,更加省时间(推荐使用)!