第一时间获取干货文章,关注微信公众号:不会说话的刘同学
最近入有个同学入职了一家新公司,以为只是维护下公司里的老项目做做需求迭代,没想到项目经理却要他按照需求搭建一个新的Spring项目,这位同学顿时就懵了,心里有一万只草泥马在奔腾,都两个月没有写代码了,现在突然要重新搭建一个新的项目,搭得好就好,搭得不好可能就要滚蛋了,万般无耐的情况下跑过来问我,这种小事怎么能难得到我呢,不一会儿就给他搭建好了,这位同学立马投来仰慕的眼光来了一句“卧槽,你太厉害了吧,可以考虑写一篇博客了”。我想了想,那也不是不可以
什么是Spring
在搭建之前,我们来了解下什么是Spring
Spring是一个开源的Java EE框架,它能够让你的应用开发变得更容易,它主要提供了IOC和AOP两大核心功能,能够在复杂的软件开发中更好的解耦,除此之外,在不同的模块里,它还提供了大量的特性,比如Spring MVC、Spring JDBC,同时也提供了一系列的扩展点,能够帮助我们将程序更好的与Spring进行整合
在上面的概述中有两个特别重要的概念--IOC和AOP,我来详说一下
IOC,全称为inversion of control, 译为控制反转,我们在写项目的时候一般都是自己手动通过 new 关键字来创建一个对象,但是这种手动创建对象的方式在无形之间增加了开发成本以及造成了程序之间的重度依赖,为了解决这种问题,IOC这个概念就应运而生了,我们把需要手动创建的对象通过配置的方式交给Spring去创建及管理,当我们需要用到的对象的时候直接从Spring容器里获取就行了,按需所取,这样在解决了程序之间的依赖的同时也还提高了开发效率
AOP,我们先来看看维基百科是怎么描述的
面向切面的程序设计(Aspect-oriented programming,AOP,又译作面向方面的程序设计、剖面导向程序设计),是计算机科学中的一种程序设计思想,旨在将横切关注点与业务主体进行进一步分离,以提高程序代码的模块化程度。通过在现有代码基础上增加额外的通知(Advice)机制,能够对被声明为“切点(Pointcut)”的代码块进行统一管理与装饰,比如说:“对所有方法名以set*开头的方法添加后台日志”。该思想使得开发人员能够将与代码核心业务逻辑关系不那么密切的功能(如日志功能)添加至程序中,同时又不降低业务代码的可读性。面向切面的程序设计思想也是面向切面软件开发的基础。 ——摘自维基百科
这种描述还是比较的抽象,我来用人话说下吧
我们有时候需要增强我们写的应用程序程序,我们需要写一些增强的代码,比如打印一些日志信息,但是如果直接改代码的话那么一定会造成不必要的程序bug,为了解决这种问题让我们的程序更强壮,让增强代码与应用程序代码进行分离,起初是直接使用的动态代理及字节码增强技术来实现实现AOP,但Spring整合了动态代理,让我们也可以通过配置的方式对代码进行增强,从而减少直接对代码的修改
Spring除了提供IOC和AOP之外,还提供了一些其他的功能特性,它这些功能特性都进行了模块化处理,让我们在使用的过程中能够按需引用对应的模块,Spring一共分为了二十个模块,主要模块如下:
spring-aop : 主要用于对AOP的支持
spring-aspects : 主要对aspects的一个支持,我们在使用aop过程中经常使用到的Aspect语法
spring-beans : 对ioc的支持
spring-contex : 对ioc的支持
spring-core : spring的核心模块
spring-expression : 对Spel表达式的支持
spring-instrument : 对Java装配的支持
spring-jcl : spring自己的一套日志模块,主要用来统一日志管理
spring-jdbc : spring对jdbc的一个整合
spring-jms : 用来处理java消息的模块,比如activeMQ、RabbmitMQ等消息中间件
spring-messaging : 主要是统一消息消息中间件
spring-orm : 对于Hibernate、JPA的整合
spring-oxm : 用于XML的序列化和反序列化
spring-test : 提供对程序测试的一个组件
spring-tx : 对事务的控制
spring-web : 对于Java web的支持
spring-webflux : 对webflux的支持
spring-websocket : 对websocket的支持
如何在项目中使用Spring
我们再来看看如何来使用Spring,在我看来使用Spring主要分为三大步骤:引入模块 -> Bean的定义和配置 -> 通过容器获取所需要的Bean
引入模块
我们如果只需要用到IOC的话,那么只需要引入 spring-context 这个模块就可以了,以5.2.2版本为例
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.2.RELEASE</version>
</dependency>
Bean的定义和配置
引入完模块之后,接下来就是 Bean的定义和配置了,这里两种方式去定义和配置我们所需要的Bean,XML配置和JavaConfig配置
XML配置Bean
XML配置要稍微的复杂点,我们需要写一个XML文件,在文件里通过 bean 节点来配置
那么说到这里,在创建完 XML 文件之后,XML的开头描述怎么写呢,这里你们可能已经默默的打开百度然后复制粘贴了,但是作为一个有经验的程序员,还是得要体现的有水平点,因此我们应该要直接去官网找对应的XML开头描述
官网地址: docs.spring.io/spring-fram…
我们需要在项目的 resources 下新建个ioc-appliction.xml文件,然后再找到官方示例,直接复制粘贴了
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<bean id="user" class="com.spring.ioc.dependency.domain.User"></bean>
</beans>
这样的复制粘贴是不是体现的更有水准一点呢,哈哈
我们再在XML配置一个自定义的类,这样我们的XML配置就完成了
那么如果你嫌弃配置XML很麻烦,我们可以使用JavaConfig的方式来配置
JavaConfig配置Bean
我们需要建一个Java类,比如JavaConfig,在JavaConfig里配置User类
package com.spring.ioc.dependency.config;
import com.spring.ioc.dependency.domain.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class JavaConfig {
@Bean
public User user(){
return new User();
}
}
就这个几个注解就搞定了,是不是很方便呢
这里可能有会有疑问了,项目中有这么多的类,不可能一个一个来配置吧,不然反而增加负担
Spring包扫描机制
这里Spring提供了一个包扫描机制,我们只需要在类加上一个注解,然后配置一个扫描的包路径,这样只要在 这个路径下的类就可以被扫描到并创建 Bean (是一定要加了注解类)
配置包扫描路径也有两种方式----XML和JavaConfig
如果是使用XML来配置的Bean,那么我们只需要在XML文件中填下一个配置就行了
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 包扫描路径 -->
<context:component-scan base-package="com.spring.ioc.dependency"/>
</beans>
这样就可以不用去一个一个配置Java类了,配置完之后,如果想要类被扫描到并创建Bean,一定要扫描包路径下的类上加上一个@Service 注解
当然除了 @Service 注解外,用 @Component 、 @Repository 两个注解也可以
如果是使用JavaConfig的方式来配置的话,那么只只需要在JavaConfig中加个 @ComponentScan 注解,然后在注解中添加包扫描的路径
package com.spring.ioc.dependency.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@ComponentScan("com.spring.ioc.dependency")
@Configuration
public class JavaConfig {
}
JavaConfig方式配置同样也是需要在扫描路径下的类添加注解才能被扫描到,这里跟XML一样
这里要注意一点,XML配置和JavaConfig配置只能二选一,不要两个都去配置哦,不然项目经理可能就要打人了,哈哈
通过容器获取所需要的Bean
这里的容器主要是指BeanFactory,这是Spring提供的一个底层容器接口,它有两个主要实现子类--- ClassPathXmlApplicationContext 和 AnnotationConfigApplicationContext,不同的Bean配置就使用不同的子类实现去获取Bean
ClassPathXmlApplicationContext : 看名字就应该知道了,如果是XML配置的话就直接使用这个子类去获取Bean
AnnotationConfigApplicationContext : 如果是JavaConfig配置的话就用这个子类去获取Bean
这两个是比较重要的两个子类,要记下来,要考的!!!
在使用这两个子类的时候,其构造方法需要传入对应的配置文件
public static void main(String[] args) {
BeanFactory classPathXmlApplicationContext = new ClassPathXmlApplicationContext("classpath:/ioc-application.xml");
User user1 = (User)classPathXmlApplicationContext.getBean("user");
System.out.println(user1);
BeanFactory annotationConfigApplicationContext = new AnnotationConfigApplicationContext(JavaConfig.class);
User user2 = (User)annotationConfigApplicationContext.getBean("user");
System.out.println(user2);
}
当在你的控制台输出了这两个对象的时候,Spring的搭建就完成了,可以在你的项目中畅想Spring带给你的乐趣了
Spring对于Bean的依赖查找及依赖注入
先来看看什么是依赖查找和依赖注入吧
依赖查找: 通过BeanFactory来手动获取一个Bean对象,这个不同于依赖注入的是,依赖查找是主动的去容器里获取Bean对象
依赖注入:通过配置的方式,来将属性或特定对象注入到某个对象中,这个不同于依赖查找的是,依赖注入是通过Spring自动的将属性或特定对象注入到对象中
依赖查找
对于依赖查找我们也可以分为单一类型的依赖查找、集合类型依赖查找、层次性依赖查找、延迟依赖查找、安全依赖查找
我们在上面就讲了BeanFactory的两个子类---ClassPathXmlApplicationContext和AnnotationConfigApplicationContext
这次我们只拿ClassPathXmlApplicationContext来进行讲述
单一类型查找
我们在上面的一段程序中就是一种典型的单一类型查找,那就是通过Bean的id或名字来获取
public static void main(String[] args) {
// 单一类型查找,通过Bean的id
User user1 = (User)classPathXmlApplicationContext.getBean("user");
}
在 ClassPathXmlApplicationContext 中 getBean() 是一个重载方法,这里除了直接根据id获取Bean之外,还可以通过类型来获取
public static void main(String[] args) {
// 单一类型查找,通过类型获取
User user1 = (User)classPathXmlApplicationContext.getBean(User.class);
}
还可以通过类型和id组合的形式来获取
public static void main(String[] args) {
// 单一类型查找,通过类型和id组合形式获取
User user1 = (User)classPathXmlApplicationContext.getBean("user",User.class);
}
除了以上三个方法之外,还有一些其他的方式,我就不一一来演示了,具体看下面的方法描述:
getBean(String name) : 通过Bean的id来获取
getBean(String name,Class<T> requiredType):通过Bean的id和类型组合的形式来获取
getBean(Class<T> requiredType) : 通过Bean的类型来获取
getBean(Class<T> requiredType,Object... args) : 通过Bean的类型和多一个id组合获取
getBeanProvider(Class<T> requiredType) : 通过类型获取延迟加载Bean
集合类型查找
BeanFactory并没有提供集合类型查找相关的API,但是它的一个子接口提供了集合查找的方法----ListableBeanFactory
我们只需要将ClassPathXmlApplicationContext强制转为 ListableBeanFactory,就可以使用对应的API了
public static void main(String[] args) {
ListableBeanFactory listableBeanFactory = (ListableBeanFactory)classPathXmlApplicationContext;
// 集合类型查找,通过类型查找
Map<String, User> beansOfType = listableBeanFactory.getBeansOfType(User.class);
}
这里返回的并不是一个List集合,而是一个Map,其中Bean的id为key,Bean对象为value
除了通过类型查找之外,还可以通过注解的形式来查找,比如我在某个类上加了个 @Super 注解,那么就可以通过这个方法来获取
public static void main(String[] args) {
ListableBeanFactory listableBeanFactory = (ListableBeanFactory)classPathXmlApplicationContext;
// 集合类型查找,通过注解形式查找
Map<String, User> beansOfType = listableBeanFactory.getBeansWithAnnotation(Super.class);
}
层次性依赖查找
这种查找说白了就是获取Bean的时候先从父容器里查找,如果父容器里没有就直接从子容器里查找,有点像双亲委派机制
为了更好的讲明白层次性依赖查找,先准备两个XML文件:ioc-application-sub.xml和ioc-application-parent.xml
我在 ioc-application-sub.xml 配置了一个User类,在ioc-application-parent.xml配置了一个Order类
ioc-application-sub.xml :
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<bean id="user" class="com.spring.ioc.dependency.domain.User"></bean>
</beans>
ioc-application-parent.xml :
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<bean id="order" class="com.spring.ioc.dependency.domain.Order"></bean>
</beans>
配置完成之后,再创建一个父容器和一个子容器分配读取ioc-application-parent.xml和ioc-application-sub.xml文件,然后
public static void main(String[] args) {
// 子容器 读取ioc-application-sub.xml文件
ClassPathXmlApplicationContext subClassPathXmlApplicationContext = new ClassPathXmlApplicationContext("classpath:/ioc-application-sub.xml");
ConfigurableListableBeanFactory subBeanFactory = subClassPathXmlApplicationContext.getBeanFactory();
// 父容器 读取ioc-application-parent.xml文件
ClassPathXmlApplicationContext parentBeanFactory = new ClassPathXmlApplicationContext("classpath:/ioc-application-parent.xml");
// 将父容器设置到子容器中
subBeanFactory.setParentBeanFactory(parentBeanFactory);
// 从子容器从获取order对象,可以直接获取
System.out.println("从父容器获取oder对象:"+subClassPathXmlApplicationContext.getBean("order"));
System.out.println("从子容器获取user对象:"+subClassPathXmlApplicationContext.getBean("user"));
}
当我们要从子容器中获取order对象的时候,它会直接从父容器中获取并直接返回
这其实也很好理解,因为父容器读取的ioc-application-parent.xml里就只配置了Order类,子容器里读取的ioc-application-sub.xml里就只配置了User类,因此在子容器没有找到的order对象那就只可能存在父容器中,那就直接去父容器里获取
延迟依赖查找
这里的延迟查找就是当去容器里获取Bean对象的时候,如果容器里不存在就会创建一个
这里在获取Bean对象的时候实际获取的是一个包装器对象,然后再通过包装器对象去获取实际的对象
Spring提供了 ObjectProvider 接口,这个接口就相当于是包装器,对于这个包装器对象的获取 ClassPathXmlApplicationContext 也提供了相关的API来获取
public static void main(String[] args) {
BeanFactory classPathXmlApplicationContext = new ClassPathXmlApplicationContext("classpath:/ioc-application.xml");
ObjectProvider<Order> beanProvider = classPathXmlApplicationContext.getBeanProvider(Order.class);
Order order = beanProvider.getIfAvailable(() -> new Order());
System.out.println(order);
}
读取的XML文件没有配置Order类,这里就是当去获取Order对象的时候,容器里不存在,就会去创建一个Order对象,从而达到延迟加载
这种方法一般在开发中很少用,了解下即可
安全依赖查找
我们用 ClassPathXmlApplicationContext 获取某个Bean的时候,如果这个Bean不存在,那么就会报错,如下
public static void main(String[] args) {
BeanFactory classPathXmlApplicationContext = new ClassPathXmlApplicationContext("classpath:/ioc-application.xml");
System.out.println(classPathXmlApplicationContext.getBean("order"));
}
那么这种是极其不友好的,为了查找的安全性,Spring 提供了一个安全的查找方式,还是通过 ObjectProvider 接口的API来查找
public static void main(String[] args) {
ObjectProvider<Order> beanProvider = classPathXmlApplicationContext.getBeanProvider(Order.class);
System.out.println(beanProvider.getIfAvailable());
}
这个跟延迟查找的方法一样的,不过里面没有传入对应的参数
getIfAvailable()方法如果没有获取的话就会直接返回null,不会报错
依赖注入
依赖注入也是Spring IOC 中很重要的一功能点,我们可以通过配置的方式来进行属性的 输入,但是实际开发的过程中,我们一般都是使用 @Autowired 注解来实现自动注入
依赖注入也有很多种方式,一般分为Setter方式注入、构造器注入、字段注入、方法注入、接口回调注入
Setter方式注入
这里我还是以XML配置为例
这种方式是最基本的注入方式,如果要使用这种方式那就需要在类里提供Setter方法,
public class User {
private String id;
private String name;
public void setId(String id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"id='" + id + ''' +
", name='" + name + ''' +
'}';
}
}
在配置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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<bean id="user" class="com.spring.ioc.dependency.domain.User">
<!-- 配置对应的属性值 -->
<property name="name" value="zhangsan"/>
<property name="id" value="1"/>
</bean>
</beans>
再通过 ClassPathXmlApplicationContext 去获取对应的Bean对象
除了这种基本类型的注入之外,还可以对象注入
我在User类里再新增个Order属性
public class User {
private Order order
public void setOrder(Order order) {
this.order = order;
}
}
在XML可以这样来注入
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<bean id="order" class="com.spring.ioc.dependency.domain.Order">
<property name="orderId" value="1561234123"/>
<property name="orderName" value="1号订单" />
</bean>
<bean id="user" class="com.spring.ioc.dependency.domain.User">
<!-- 配置对应的属性值 -->
<property name="order" ref="order"/>
</bean>
</beans>
再去获取Bean对象,order对象已经注入进来了
这里需要注意一点,在XML里这样去配置,如果类里没有提供Setter方法的话,就会报错
构造器注入
这种方式也是必将常用的注入方式,使用这种方式注入需要在类里提供一个有参构造方法
public class User {
private String id;
private String name;
private Order order;
public User(String id, String name, Order order) {
this.id = id;
this.name = name;
this.order = order;
}
@Override
public String toString() {
return "User{" +
"id='" + id + ''' +
", name='" + name + ''' +
", order=" + order +
'}';
}
}
在配置文件里面的配置跟Setter方式注入的配置稍微有点不一样
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<bean id="order" class="com.spring.ioc.dependency.domain.Order">
<property name="orderId" value="1561234123"/>
<property name="orderName" value="1号订单" />
</bean>
<bean id="user" class="com.spring.ioc.dependency.domain.User">
<constructor-arg name="name" value="zhangsan"></constructor-arg>
<constructor-arg name="id" value="123456"></constructor-arg>
<constructor-arg name="order" ref="order"></constructor-arg>
</bean>
</beans>
我们再去获取Bean对象的时候,可以正常注入
字段注入
字段注入方式是Spring自动注入的一种实现,我们平时在项目中使用的 @Autowired @Resource 注解
这种方式相比于XML配置来说,可以说是非常的简单
在XML需要配置下包扫描
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 配置包扫描 -->
<context:component-scan base-package="com.spring.ioc.dependency"/>
</beans>
我们还是以User类为例
@Service
public class User {
@Autowired
private Order order;
public Order getOrder() {
return order;
}
public void setOrder(Order order) {
this.order = order;
}
@Override
public String toString() {
return "User{" +
"order=" + order +
'}';
}
}
在order属性字段上添加了个 @Autowired 注解
仍然还是通过 ClassPathXmlApplicationContext 去获取
这里字段注入只注入了一个对象,但如果是一些基本类型的注入该怎么办呢
Spring给我们提供了个 yml 外部化配置,我们可以把这种基本类型的值配置到yml中
然后再通过 @Value 注解进行注入
public class User {
@Value("${user.id}")
private String id;
@Value("${user.name}")
private String name;
}
这种方式比较的方便,也比较符合我们的开发需要,因此还是比较推荐这种方式来进行注入
方法注入
这里的方法注入只针对于JavaConfig配置,我们在JavaConfig里配置Bean对象的时候,是需要在方法上加上 @Bean 注解的
还是以User类为例,我们在方法里添加一个 order 参数,那么只要方法上添加了 @Bean 注解,它就会从容器里查找这个Oder对象,然后通过方法传参的方式注入进来
@Bean
public User user(Order order){
User user = new User();
user.setOrder(order);
return user;
}
这里我们将注入进来的Order对象设置到User里
再通过 AnnotationConfigApplicationContext 这个类去获取User对象
public static void main(String[] args) {
BeanFactory annotationConfigApplicationContext = new AnnotationConfigApplicationContext(DependencyLookupDemo.class);
System.out.println(annotationConfigApplicationContext.getBean("user"));
}
这种方式在项目中也是比较常用的,特别是在SpringBoot兴起之后,大量的中间件与Spring整合都会使用这种方式来进行注入
接口回调注入
Spring 为了更好的扩展性提供了一系列的接口,比如 BeanFacotryAware、ApplicationContextAware等
假如我们在写程序的过程中需要拿到BeanFacotry对象的话,那么我们就可以通过实现 BeanFacotryAware 接口里的 setBeanFactory(BeanFactory beanFactory) 方法,方法里它传入了一个BeanFacoty对象,在 Spring 扫描到这个 Bean 的时候回去回调 setBeanFactory 这个方法并将 BeanFacotry 注入进来
@Service
public class BeanFactoryAwareDemo implements BeanFactoryAware {
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("获取到BeanFactory对象:"+ beanFactory);
}
}
这里我们只需要启动容器,他就会去调用了
public static void main(String[] args) {
// 启动容器
BeanFactory annotationConfigApplicationContext = new AnnotationConfigApplicationContext(DependencyLookupDemo.class);
}
看到这里,你们可能会有点懵逼,就 new 了个 AnnotationConfigApplicationContext 对象,容器就启动了吗
没错, 在启动容器是需要调用 refresh() 方法的,这里之所以没有手动去调用这个方法是因为ApplicationContext 被创建完之后内部就已经自动调用了
这种方式的注入一般也是在与其他的中间件整合的时候才会使用,在实际开发项目的过程中很少会用到,了解下即可
BeanFactory or ApplicationContext
在上面的代码演示过程中,我一直在强调 BeanFactory、 AnnotationConfigApplicationContext 、 ClassPathXmlApplicationContext,现在我们再来屡屡这三个类之间的关系吧
BeanFacotry是Spring容器的顶层接口,我们之前所使用的 getBean() 方法都是这个接口提供的,除了 getBean() 方法之外,还有涉及到延迟查找的方法 getBeanProvider() 等
我们再来看看 BeanFacotry 有哪些子接口
ListableBeanFactory: 这个接口我之前有讲过,当我们需要获取一个Bean的集合的时候需要用到这个接口
AutowireCapableBeanFactory: 这个接口主要就是提供属性自动注入的功能
HierarchicalBeanFactory: 这个主要就是是提供了了 BeanFacotry 父子容器的功能
除了 BeanFacotry 之外,还有一个特别重要的接口,那就是 ConfigurableApplicationContext,AnnotationConfigApplicationContext和ClassPathXmlApplicationContext 都是间接实现了 ConfigurableApplicationContext
那我们再来看看 ConfigurableApplicationContext 继承了哪些接口
通过继承关系图可以看出来,ConfigurableApplicationContext 除了继承了最基础的 ListableBeanFactory、AutowireCapableBeanFactory和HierarchicalBeanFactory HierarchicalBeanFactory接口之外,还继承了 MessageSource(解析消息资源) 、 EnvironmentCapable(解析环境变量)、ResourcePatternResolver(资源转换)、ApplicationEvenPublisher(用于事件发布) 等
因此 ConfigurableApplicationContext 接口所提供的功能比上面所讲的 BeanFactory 还要强大, 现在知道为啥要直接使用 AnnotationConfigApplicationContext和ClassPathXmlApplicationContext 了吧
不管是AnnotationConfigApplicationContext和ClassPathXmlApplicationContext,还是 ConfigurableApplicationContext ,他们都直接或间接继承了 ApplicationContext 接口
但是我们用的时候又是用的 AnnotationConfigApplicationContext和ClassPathXmlApplicationContext
那么 BeanFacotry 和 ApplicationContext 谁才是真正的底层IOC容器呢
这里毋庸置疑肯定是BeanFacotry
为什么呢,这里官网给出了答案
In short, the
BeanFactoryprovides the configuration framework and basic functionality, and theApplicationContextadds more enterprise-specific functionality. TheApplicationContextis a complete superset of theBeanFactoryand is used exclusively in this chapter in descriptions of Spring’s IoC container --- 摘自Sring官网
我们来翻译下
简而言之,BeanFactory提供了配置框架和基本功能,而ApplicationContext添加了更多特定于企业的功能。ApplicationContext是BeanFactory的一个完整的超集.
上面说到了 ApplicationContext 是 BeanFactory 的一个超集,这里的意思就是 ApplicationContext 其实就只是整合了 BeanFactory 及其他的一些功能特性(比如事件发布等),真正实现功能的仍然还是BeanFacotry
那么啥时候用ApplicationContext ,啥时候用 BeanFacotry 呢
当我们在使用的过程中如果需要用到其他的一些功能特性,比如事件发布,消息资源解析,那么可以使用ApplicationContext , 如果就只是使用下IOC容器管理Bean,那么我们可以用BeanFacotry
到了这里,你们对于Spring的IOC特性是不是有了一个很清醒的认知呢,现在来简简单单搭建一个项目应该不是问题吧,哈哈
后言
如果觉得这篇技术文章写的可以的话,还请多多点赞支持一下哦