武清做网站的/seo整站优化费用
上一篇并没有实际地带大家去看源码,而是介绍了两个概念:
- BeanDefinition
- BeanPostProcessor
当然,我介绍得非常笼统,不论是BeanDefinition还是BeanPostProcessor其实都有着较为复杂的继承体系,种类也很多。作为Spring系列第一篇,主要目的还是帮大家摆脱对Spring的刻板认知,刷新你们的三观,毕竟太多人对Spring的理解仅限于所谓的IOC和AOP。现在Spring5都出来了,好多人还停留在Spring2.5、Spring3的年代,还在使用XML。
今天我将会带大家复习Spring的基础,大致流程是:
- 复习XML方式开发
- 通过逐步暴露XML的弊端,引出Spring注解
- 最终完全舍弃XML,采用Spring注解开发
之所以推荐注解开发,原因有两点:
- XML配置太繁琐了
- 掌握Spring注解开发有助于后期学习SpringBoot
只有熟练使用Spring后,看源码时才能把应用和原理联系起来。
文章篇幅较长,建议看的时候先把下方目录截图,放在一旁做引导,防止自己看着看着不知道看到哪了。
主要内容:
- IOC与DI
- Spring的3种编程风格与2种注入方式
- 1️⃣XML配置开发:<bean>描述依赖关系
- 自动装配:让<bean>职责单一化
- 2️⃣XML+注解:XML+<context:component-scan>+@Component
- @Autowired的小秘密
- 2️⃣JavaConfig+注解:@Configuration+@ComponentScan+@Component
- 3️⃣JavaConfig方式:@Configuration+@Bean
- 大乱斗:@ImportResource、@Component、@Bean
IOC与DI
关于IOC的好处,推荐一篇文章,个人觉得写得很好:Spring IoC有什么好处呢?
大家不妨将IOC理解成一种思想,而DI是实现该思想的一种具体方式。Spring被称为IOC容器,它实现IOC的方式除了DI(Dependency Inject,依赖注入),其实还有DL(Dependency Look,依赖查找)。由于我们平时很少用到DL,所以这里只讨论DI(依赖注入)。
IOC与DI
Spring依赖注入的做法
首先,提供一些配置信息(比如XML)来描述类与类之间的关系,然后由IOC容器(Spring Context)去解析这些配置信息,继而维护好对象之间的关系。
<!-- 配置信息:在XML中定义Bean -->
<bean id="person" class="com.bravo.annotation.Person"><property name="car" ref="car"></property>
</bean><bean id="car" class="com.bravo.annotation.Car"></bean>
其次,还有一个很重要的前提是,除了配置信息,对象之间也要体现依赖关系。
public class Person {// Person类中声明了Car,表示Person依赖Carprivate Car car;// 由于上面XML使用了<property>标签,表示setter方法注入,所以必须提供setter方法public void setCar(Car car) {this.car = car;}
}
总结起来就是:
- 编写配置信息描述类与类之间的关系(XML/注解/Configuration配置类均可)
- 对象之间的依赖关系必须在类中定义好(一般是把依赖的对象作为成员变量)
- Spring会按照配置信息的指示,通过构造方法或者setter方法完成依赖注入
XML中bean标签的职责:1.定义bean 2.维护bean依赖关系,指导Spring完成依赖注入
Spring的3种编程风格与2种注入方式
按照Spring官方文档的说法,Spring的容器配置方式可以分为3种:
- Schema-based Container Configuration(XML配置)
- Annotation-based Container Configuration(注解)
- Java-based Container Configuration(@Configuration配置类)
Spring支持的2种注入方式:
- 构造方法注入
- setter方法注入
在Spring4之前,Spring还支持接口注入(很少用),这里不提及。
(这个分类还是有问题,后面分析源码时再解释)
大家必须要明确,所谓3种编程风格和2种注入方式到底指什么,之间又有什么联系?
我们从2种注入方式开始分析。
Q:Spring注入的是什么?
A:是Bean。
Q:这些Bean怎么来的?
A:IOC容器里的。
所以,所谓的3种编程风格其实指的是“将Bean交给Spring管理的3种方式”,可以理解为IOC,而2种注入方式即DI,是建立在IOC的基础上的。也就是说Spring的DI(依赖注入)其实是以IOC容器为前提。
3种编程风格其实指的是3种把Bean交给Spring管理的方式,而DI有2种方式:setter方法注入/构造方法注入
接下来,我们把3种编程风格分别用代码实验一下。
Spring系列文章我都会贴出完整、可运行的代码,所以建议大家一边看一边复制到本地调试,这样学得更快。
1️⃣XML配置开发:<bean>描述依赖关系
setter方法注入
pom.xml
<dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>4.3.12.RELEASE</version></dependency></dependencies>
配置信息(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"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><!-- 在xml中描述类与类的配置信息 --><bean id="person" class="com.bravo.xml.Person"><!-- property标签表示,让Spring通过setter方法注入--><property name="car" ref="car"></property></bean><bean id="car" class="com.bravo.xml.Car"></bean></bean
Person(这里偷懒,把后面要讲的构造器注入的准备工作也做了,对运行结果不影响)
public class Person {// Person依赖Carprivate Car car;// 无参构造public Person(){}// 有参构造public Person(Car car){this.car = car;System.out.println("通过构造方法注入...");}// setter方法public void setCar(Car car) {this.car = car;System.out.println("通过setter方法注入...");}@Overridepublic String toString() {return "Person{" +"car=" + car +'}';
}
Car
public class Car {
}
Test
public class Test {public static void main(String[] args) {// 由于是XML配置方式,对应的Spring容器是ClassPathXmlApplicationContext,传入配置文件告知Spring去哪读取配置信息ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-context.xml");// 从容器中获取PersonPerson person = (Person) applicationContext.getBean("person");System.out.println(person);}
}
目录结构
测试结果
由于XML中配置依赖信息时,使用了property标签,所以Spring会调用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"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><!-- 在xml中描述类与类的配置信息 --><bean id="person" class="com.bravo.xml.Person"><!-- constructor-arg标签表示,让Spring通过构造方法注入--><constructor-arg ref="car"></constructor-arg></bean><bean id="car" class="com.bravo.xml.Car"></bean></beans>
测试结果
其他代码都没变,仅仅改变配置信息,由property标签变为constructor-arg标签,Spring就知道要改为构造器注入了
至此,我们把XML配置下2种注入方式都实验过了,它们的区别是:
- XML配置<property> + 对象提供对应的setter方法
- XML配置<constructor-arg> + 对象提供对应的构造方法
改变XML配置的同时,需要对象提供对应的方法支持。如果你用了<property>,却没有在类中提供setter方法,则会报错。
自动装配:让<bean>职责单一化
我们会发现<bean>这个标签,其实承载着两个作用:
- 定义bean,告诉Spring哪个Bean需要交给它管理(放入容器)
- 维护bean与bean之间的依赖关系
接下来我们思考这样一个问题:
对于Person类
public class Person {// Person依赖Carprivate Car car;public void setCar(Car car) {this.car = car;}
}
上面代码其实已经很好地描述了Person和Car的依赖关系,此时在XML中继续用<property>或者<constructor-arg>反而成了累赘:
- 既然类结构本身包含了依赖信息,<bean>再用<property>等去描述就显得多余了
- 如果类结构变动,我们还需要额外维护<bean>的依赖信息,很麻烦。比如Person新增了一个shoes字段,那么<bean>又要写一个<property>表示shoes
所以,最好的做法是把让<bean>标签职责单一化,让它只负责定义bean,把bean与bean的依赖关系转交给类自身维护(有这个字段就说明有依赖)。
既然菜鸡的我们能想到,那么Spring肯定也想到了,于是它提出了“自动装配”的概念。很多人一听到自动装配,脑子里只有@Autowired。不算错,但其实XML也支持自动装配,而且真要论先来后到的话,肯定还是XML的自动装配在前。
XML实现自动装配可以分为两种:全局、局部。
全局自动装配(XML根标签<beans>末尾加default-autowire配置)
<?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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"default-autowire="byName"><!-- 在xml中只定义bean,无需配置依赖关系 --><bean id="person" class="com.bravo.xml.Person"></bean><bean id="car" class="com.bravo.xml.Car"></bean></beans>
所谓全局,就是在XML根标签末尾再加一个配置default-autowire="byName",那么在此XML中配置的每一个<bean>都遵守这个自动装配模式,可选值有4个:
- byName
- byType
- constructor
- no
default其实就是no
测试结果
我们会发现改用自动装配后,虽然没有了property标签,但是默认是调用setter方法
局部自动装配(每一个<bean>单独设置autowire)
<?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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><!-- 在xml中只定义bean,无需配置依赖关系 --><bean id="person" class="com.bravo.xml.Person" autowire="byName"></bean><bean id="car" class="com.bravo.xml.Car"></bean></bean
测试结果
小结:
- Spring支持自动装配(全局/局部),把原先<bean>标签的职责单一化,只定义bean,而依赖关系交给类本身维护
- 自动装配共4种,除了no,其他3种各自对应两种注入方式:byName/byType对应setter方法注入,constructor对应构造方法注入 (请自己动手证明)
1、XML+注解:XML+<context:component-scan>+@Component
原本<bean>标签有两个职责:
- 定义bean
- 描述依赖信息
上面通过自动装配,把依赖信息交给类本身维护,从此<bean>只负责bean定义。
现在,我们想想办法,能不能干脆把bean定义也剥离出来?这样就不需要在XML中写任何<bean>标签了。我早就看<bean>标签不爽了,这么一大坨,要是bean多了,就很臃肿。
怎么做呢?
我们先来回顾一下手上的牌面:
至此,我们已经成功调教Spring帮我们做了自动装配,也就是说IOC和DI中,DI已经实现自动化。我们接下来要考虑的是如何减少IOC配置的工作量。
原先是把<bean>写在XML中,再把XML喂给Spring:
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-context.xml");
既然现在打算消灭XML中的<bean>,则说明即使把XML喂给Spring,它也吃不到bean定义了。所以,必须要告诉Spring去哪可以吃到bean。
我们来看一下,当Spring吃下<bean>时,到底吃了什么:
<!-- 在xml中只定义bean,无需配置依赖关系 -->
<bean id="person" class="com.bravo.xml.Person" autowire="byName"></bean>
<bean id="car" class="com.bravo.xml.Car"></bean>
是的,<bean>只指定了类名和自动装配的模式。也就是说,要定义一个bean,只需要最基本的两样东西:
- 类名
- 装配模式(其实这个也不是必须的,默认no,不自动装配)
而类名其实很好得到,我们自己写的类不就有吗?至于自动装配的模式,也完全可以在类中通过注解指定。于是,我们找到了改造的方向:用带注解的类代替<bean>标签。
之前:XML中写好bean标签后,把XML喂给Spring,Spring就会把bean实例化加到容器
现在:消灭bean标签后,XML中已经没有bean,Spring必须自己去找bean定义
Spring2.5开始提供了一系列注解,比如@Component、@Service等,这些注解都是用来表示bean的。而@Service等注解底层其实还是@Component:
之所以做一层封装,是为了赋予它特殊的语义:定义Service层的bean。其余的这里不再赘述。总之我们暂时理解为,如果要使用注解表示bean定义,我们能用的只有@Component。
新建annotation包,把Car和Person移过去:
Person
@Component //带注解的类,我们希望用这种方式定义bean,并让Spring把它吃进去
public class Person {// Person依赖Carprivate Car car;@Overridepublic String toString() {return "Person{" +"car=" + car +'}';}
}
Car
@Component
public class Car {
}
XML(什么都没有配置,连自动装配模式也没指定,因为不在这里定义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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"></beans>
Test(不变)
public class Test {public static void main(String[] args) {// 由于是XML配置方式,对应的Spring容器是ClassPathXmlApplicationContext,传入配置文件告知Spring去哪读取配置信息ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-context.xml");// 从容器中获取PersonPerson person = (Person) applicationContext.getBean("person");System.out.println(person);}
}
测试结果
在Spring容器中找不到person
其实很好理解,我们传入了spring-context.xml告诉Spring去哪读取bean定义,但是实际上XML却没有配置任何<bean>,它是不可能把类实例化加入到容器的。
然而我们新定义的bean(@Component)Spring也没吃,怎么回事?
其实主要是因为我们的改变太突然了,Spring以前吃惯了XML中的<bean>,现在突然换成@Component这种注解类,它吃不惯,甚至不知道它能吃!
所以,必须通知Spring:
老哥,我们改用注解了,有@Component注解的类就是bean,和以前<bean>一样一样的。
如何通知?只要在XML中配置:
<context:component-scan base-package="com.bravo.annotation"/>
官方文档对这个标签的解释是:
The use of <context:component-scan> implicitly enables the functionality of <context:annotation-config>. There is usually no need to include the <context:annotation-config> element when using <context:component-scan>.
翻译过来就是:
使用<context:component-scan>隐式地启用了<context:annotation-config>的功能。<context:annotation-config>的作用是让Spring具备解析@Component等注解的功能。当使用<context:component-scan>时,通常不需要包含<context:annotation-config>元素。
这个标签的作用相当于什么呢?Spring一口吃下去,发现没有吃到<bean>,却吃出了一张小纸条,上面写着:赶紧去找标了@Component注解的类,那是新菜式!
所以,最终</context:component-scan>标签的作用有两个:
- 扫描:原先我们把写有bean定义的XML文件喂给Spring,现在则让Spring自己去指定路径下扫描bean定义
- 解析:让Spring具备解析注解的功能
所以,XML虽然不用配置<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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context-3.0.xsd"><context:component-scan base-package="com.bravo.annotation"/></beans
测试结果:
虽然能找到Person了,但是Car并没有被注入
又出幺蛾子了,怎么回事呢?我们回想一下XML的bean定义:
<!-- 在xml中只定义bean,无需配置依赖关系 -->
<bean id="person" class="com.bravo.xml.Person" autowire="byName"></bean>
<bean id="car" class="com.bravo.xml.Car"></bean>
我们设置了autowire属性,告诉Spring按什么方式自动装配。
现在我们改用注解了,@Component只是相当于<bean>标签,却没有指明自动装配的模式。如何在类中告诉Spring我们需要的装配方式呢?
方法有很多种:
- @Autowired(Spring提供的)
- @Resource(JSR-250定义)
- @Inject(JSR-330定义)
这里我们以@Autowired为例:
@Component
public class Person {// 用@Autowired告知Spring:请把Car装配进来@Autowiredprivate Car car;@Overridepublic String toString() {return "Person{" +"car=" + car +'}';}
}
测试结果
自动装配成功!
@Autowired的小秘密
上面我们有惊无险地从用@Component替换了<bean>,并且结识了@Autowired这个超棒的注解,用来完成自动装配。即:
- <context:component-scan>+@Component彻底解放IOC配置
- @Autowired完成自动装配
但是细心的小伙伴会发现,相较于<bean>中的autowire="byName",@Autowired虽然装配成功了,却没有显式地指定自动装配的模式。
只有一种解释:它有默认的装配方式。
在探究@Autowire默认的装配模式之前,关于bean的名称,要和大家先交代一下:
<!-- 在xml中只定义bean,无需配置依赖关系 -->
<bean id="person" class="com.bravo.xml.Person" autowire="byName"></bean>
<bean id="car" class="com.bravo.xml.Car"></bean>
在<bean>中,id即为最终bean在Spring容器的名字。
同样的,@Component也提供了给bean命名的方法:
@Component("bravo")
public class Person {// 用@Autowired告知Spring:请把Car装配进来@Autowiredprivate Car car;@Overridepublic String toString() {return "Person{" +"car=" + car +'}';}
}
如果不指定,则默认会把类名首字母小写后作为beanName。
铺垫结束,我们开始探究@Autowired到底默认是哪种装配模式:
- byName
- byType
- constructor
- no(已经装配成功,排除)
先来看看是不是byName
@Component
public class Person {// 用@Autowired告知Spring:请把Car装配进来@Autowiredprivate Car myCar;@Overridepublic String toString() {return "Person{" +"car=" + myCar +'}';}
}
测试结果
Car在Spring中bean的名字应该是car,而我把Person中的Car变量名改为myCar,仍旧注入成功,说明不是byName。
再来看看是不是byType。
这个稍微有点麻烦,因为我需要弄出至少两个同类型的bean。所以我打算把Car变成接口,然后创建Bmw和Benz两个实现类。这个接口只是为了试验,没有实际意义:
Car
//接口
public interface Car {
}//实现类Bmw
@Component
public class Bmw implements Car {
}//实现类Benz
@Component
public class Benz implements Car {
}
Person
@Component
public class Person {// 用@Autowired告知Spring:请把Car装配进来@Autowiredprivate Car car;@Overridepublic String toString() {return "Person{" +"car=" + car +'}';}
}
测试结果
熟悉的配方、熟悉的味道:expected single matching bean but found 2: BMW,benz
很明显,@Autowired默认采用byType的方式注入,由于当前Spring容器中存在两个Car类型的bean,所以注入时报错了,因为Spring无法替我们决定注入哪一个。
但是,有个神奇的现象是,你如果把变量名改为bmw或者benz,就会注入对应的bean:
@Component
public class Person {// 把变量名改为bmw@Autowiredprivate Car bmw;@Overridepublic String toString() {return "Person{" +"car=" + bmw +'}';}
}
也就是说,@Autowired默认采用byType模式自动装配,如果找到多个同类型的,会根据名字匹配。都不匹配,则会报错。
当然,有些人可能有强迫症,觉得我Car类型的变量必须叫car,但又想指定注入bmw,怎么办?我们先看看@Autowired能不能指定名字吧:
不能指定名字,因为Autowired只有一个属性:required,表示当前bean是否必须被注入
为了弥补@Autowired不能指定名字的缺憾,Spring提供了@Qualifier注解
@Qualifier("benz")
@Autowired
private Car car;
即使Spring容器中有两个Car类型的bean,也只会按名字注入benz。
其他的我就不测了,给个结论就好:
- @Autowired:默认byType,type相同则byName
- @Resource:和@Autowired几乎一样,但不能配合@Qualifier,因为它本身就可以指定beanName。但没有required属性
@Resource(name = "benz")
private Car car;
- @Inject:用的很少,不做讨论
2、JavaConfig+注解:@Configuration+@ComponentScan+@Component
有没有发现,上面标题还是2️⃣?因为接下来要介绍的,还是注解开发。
先复习一下前面两种方式:
- 纯XML(<bean>负责定义bean,Java类负责定义依赖,Spring完成自动装配)
<!-- 在xml中只定义bean,无需配置依赖关系 -->
<bean id="person" class="com.bravo.xml.Person" autowire="byName"></bean>
<bean id="car" class="com.bravo.xml.Car"></bean>
- 注解+XML(@Component+@Autowired,但我们发现注解并不能单独使用,必须要XML中配置开启注解扫描才能生效)
<context:component-scan base-package="com.bravo.annotation"/>
之前我在注解(上)讲过,注解的使用必须包含三步:定义注解、使用注解、解析注解。@Component是Spring定义、我们使用,也肯定是由Spring解析。但是这个解析必须由我们手动开启。这就是<context:component-scan>标签的意义。
到了这一步我们已经把<bean>标签完全消灭了。但是这种模式有点不伦不类。
你说它叫XML配置开发吧,它又有@Component注解。你说它是注解开发吧,XML中还有一个<context:component-scan>在那嘚瑟呢。所以如何才能完全消灭XML呢?
究其根本,我们发现无法消灭XML的原因在于:注解的读取和解析必须依赖于<context:component-scan>标签!因为我们要帮Spring开启注解扫描,不然他不知道去哪读取bean。
既然<bean>标签可以被@Component代替,那么<context:component-scan>标签应该也能找到对应的注解。
不错!这个注解就是@ComponentScan!如此一来我们就再也不需要spring-context.xml了。
但是转念一想,脊背发凉...ClassPathXmlApplicationContext这个类要求我们必须传一个XML,怎么办?别担心,Spring同样提供了一个注解@Configuration,目的是让我们可以把一个普通的Java类等同于一个XML文件,而这个Java类就是JavaConfig,我们习惯称之为配置类。
新建一个javaconfig包,把annotation包下的所有类移过来,并且新建AppConfig配置类
@Configuration //表示这个Java类充当XML配置文件
@ComponentScan(basePackages = "com.bravo.javaconfig") //相当于XML中的<context:component-scan>标签
public class AppConfig {}
这样,我们就可以把XML删除,用@ComponentScan来开启注解扫描。
目录结构
准备测试时,发现了大麻烦:
ClassPathXmlApplicationContext无法接受AppConfig配置类,它只认XML
所以,用AppConfig配置类替代XML只是我们的一厢情愿吗?
其实是我们选错了实现类。ApplicationContext的子类除了ClassPathXmlApplicationContext,还有一个专门针对注解开发的:AnnotationConfigApplicationContext。
新的Test
public class Test {public static void main(String[] args) {// AnnotationConfigApplicationContext是Spring用来专门针对注解开发的ApplicationContext子类ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);// 从容器中获取PersonPerson person = (Person) applicationContext.getBean("person");System.out.println(person);}
}
测试结果
稳得一批
至此,XML已经被我们完全消灭了。
3、JavaConfig方式:@Configuration+@Bean
严格来说,上面的做法并不是所谓的Java-based Container Configuration(@Configuration配置类)风格。我们虽然用到了@Configuration,但只是为了让Java配置类替代XML,最终消灭XML。这也太大材小用了...本质上,这还是@Component+@Autowired注解开发,只是开启注解扫描的方式从<context:component-scan>标签变为@ComponentScan。
实际上,真正的Java-based Container Configuration编程风格是这样的:
AppConfig(如果你不扫描@Component,则不需要@ComponentScan)
@Configuration
public class AppConfig {//new一个Benz对象,通过@Bean注解告知Spring把这个bean加到容器@Beanpublic Car benz(){return new Benz();}//new一个Bmw对象,通过@Bean注解告知Spring把这个bean加到容器@Beanpublic Car bmw(){return new Bmw();}//new一个Person对象,通过@Bean注解告知Spring把这个bean加到容器@Beanpublic Person person(){Person p = new Person();p.setCar(new Benz());return p;}}
Benz(去除@Component,那是注解开发方式)
public class Benz implements Car {
}
Bmw(去除@Component,那是注解开发方式)
public class Bmw implements Car {
}
Person(去除@Component,那是注解开发方式)
public class Person {private Car car;// setter方法。在@Bean场景下,手动调用setter方法设置成员变量public void setCar(Car car) {this.car = car;}@Overridepublic String toString() {return "Person{" +"car=" + car +'}';}
}
测试结果
小结
Java-based Container Configuration编程风格指的是:
- 用@Configuration把一个普通Java类变成配置类,充当XML
- 在配置类中写多个方法,加上@Bean把返回值对象加到Spring容器中
- 把配置类AppConfig喂给AnnotationConfigApplicationContext,让它像解析XML一样解析配置类
- 无需加@Component注解,因为我们可以手动new之后通过@Bean加入容器
大乱斗:@ImportResource、@Component、@Bean
其实XML、注解、JavaConfig三种方式相互兼容,并不冲突。
- XML的<bean>
- @Component注解和扫描(不论是<context:component-scan>还是@ComponentScan)
- @Configuration与@Bean
为了证实它们确实不冲突,我搞了很变态的,一个项目里三种编程方式混用:
- 两辆车子,bmw和benz交给@Bean(JavaConfig)
- Person交给@Component和@ComponentScan(注解)
- Student交给XML和@ImportResource(XML)
目录结构
AppConfig
@Configuration //JavaConfig方式,把当前Java类作为配置类
@ComponentScan(basePackages = "com.bravo.all")//注解方式,开启扫描
@ImportResource("spring-context.xml")//XML方式,导入bean定义
public class AppConfig {@Beanpublic Car benz(){return new Benz();}@Beanpublic Car bmw(){return new Bmw();}}
Car
public interface Car {
}
Benz(JavaConfig方式:@Bean加入Spring)
public class Benz implements Car {
}
Bmw(JavaConfig方式:@Bean加入Spring)
public class Bmw implements Car {
}
Person(注解方式:@ComponentScan扫描@Component加入Spring)
@Component
public class Person {// 用@Autowired告知Spring:请把Car装配进来@Qualifier("benz")@Autowiredprivate Car car;@Overridepublic String toString() {return "Person{" +"car=" + car +'}';}
}
Student(XML方式:使用<bean>定义)
public class Student {private Car bmw;//由于在下方XML配置中,我选用了byName自动装配,而byName/byType都要提供setter方法public void setBmw(Car bmw) {this.bmw = bmw;}@Overridepublic String toString() {return "Student{" +"car=" + bmw +'}';}
}
spring-context.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"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><!-- 在xml中描述类与类的配置信息 --><bean id="student" class="com.bravo.all.Student" autowire="byName"></bean></beans>
Test
public class Test {public static void main(String[] args) {// AnnotationConfigApplicationContext是Spring用来专门针对注解开发的ApplicationContext子类ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);// 从容器中获取PersonPerson person = (Person) applicationContext.getBean("person");System.out.println(person);// 从容器中获取StudentStudent student = (Student) applicationContext.getBean("student");System.out.println(student);}
}
测试结果
通常来说,我们日常开发一般是注解+JavaConfig混用。也就是
- @ComponentScan+@Configuration+@Component+@Bean
小结
- 纯XML配置开发:没有注解,全部<bean>标签,但也可以配置自动装配
- 注解开发不能单独存在,需要开启扫描。自动装配一般用@Autowired
- XML+注解:XML+<context:component-scan>+@Component
- JavaConfig+注解:@Configuration+@ComponentScan+@Component
- JavaConfig方式:@Configuration+@Bean
通常我们都会两两混用,比如XML+注解,或者JavaConfig+注解,但很少三种一起用。
本文目的是让大家知道:
- 3种编程风格:XML、注解、JavaConfig
- 2种注入方式:setter方法、构造方法
- 4种装配模式:byType、byName、constructor、no
然后,有一点需要和大家说明,本文关于所谓的2种注入方式和4种装配方式,在宏观上来说是对的,但是在源码层面上来说,还是太笼统了。有机会的话,后面专门写一篇自动装配相关的文章。
相关文章:

Spring基础(2):放弃XML,走向注解
上一篇并没有实际地带大家去看源码,而是介绍了两个概念: BeanDefinitionBeanPostProcessor 当然,我介绍得非常笼统,不论是BeanDefinition还是BeanPostProcessor其实都有着较为复杂的继承体系,种类也很多。作为Spring…...

【线性代数】分块矩阵总结
...

Redis-命令操作Redis->redis简介,redis的安装(Linux版本windows版本),redis的命令
redis简介redis的安装(Linux版本&windows版本)redis的命令 1.redis简介 Redis是一个开源(BSD许可),内存存储的数据结构服务器,可用作数据库,高速缓存和消息队列代理。 它支持字符串、哈…...

17、Python虚拟环境:为何要用虚拟环境、如何使用virtualenv
文章目录 在Python开发中,虚拟环境是一个独立的目录树,可以在其中安装Python模块。每个虚拟环境都有自己的Python二进制文件和一组安装的库。使用虚拟环境的主要原因是为了避免项目间的依赖冲突,允许每个项目有其特定的依赖,而不影响全局安装的模块。 为何要用虚拟环境 依…...

elasticSearch 接口实现查询热词统计
前面讲过使用elasticsearch可视化工具可以直接写语法查询如下: GET robot-demand/_search { "size":10, //查询多少条数据 "aggs":{ "hot_words":{ "terms":{ "field": "title" } }…...

10年测试经验分享:新手如何找到适合自己的软件测试项目?
每一个测试新手(特别是自学测试的人)来说,往往不知道到哪里去找项目练手,这应该是最大的困扰了。 实话讲,这个目前没有非常好的、直接的解决办法,不过在这我可以结合我自己之前的一些工作经历,…...

【MySQL】查询语句
文章目录 选择语句 / 子句比较运算符AND,OR,NOT运算符IN运算符BETWEEN运算符LIKE运算符REGEXP运算符 选择语句 / 子句 USE:选择使用的databaseSELECT:选择查询的列FROM:选择查询的表WHERE:条件查询ORDER B…...

金蝶云星空的网络控制设置
文章目录 金蝶云星空的网络控制设置说明网控参数加入网络控制清除网络控制清除网络控制(单个)清除网络控制(批量)清除网络控制(批量,参数是拼接好的业务对象) 金蝶云星空的网络控制设置 说明 …...

linux服务器国内源安装nvm,又快又方便
国内安装nvm的话,如果你的服务器不能访问github,那么使用gitee快速安装还是很方便的: # 能方位github的话,使用这条命令 curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.2/install.sh | bash# 不能访问github的话…...

noip模拟赛多校第八场 T3 遥控机器人 (最短路 + 技巧拆点)
题面 简要题意: 给你一个 n n n 个点 m m m 条边的图。边 i i i 有颜色 c i c_i ci。你可以选择一些边改变它们的颜色成为区间 [ 1 , m ] [1, m] [1,m] 中的任意颜色,改变一条边 i i i 一次的代价是 w i w_i wi。询问你能否在一些改变…...

高防IP的原理
高防IP,把域名解析到高防IP上(web事务只要把域名指向高防IP 即可。非web事务,把事务IP换成高防IP即可)一起在高防IP上设置转发规矩;所有公网流量都会走高防IP,通过端口协议转发的方法将用户的拜访通过高防IP转发到源站IP,一起将歹…...

Apache Doris (五十一): Doris数据缓存
🏡 个人主页:IT贫道_大数据OLAP体系技术栈,Apache Doris,Clickhouse 技术-CSDN博客 🚩 私聊博主:加入大数据技术讨论群聊,获取更多大数据资料。 🔔 博主个人B栈地址:豹哥教你大数据的个人空间-豹哥教你大数据个人主页-哔哩哔哩视频 目录 1....

一、配置环境
一、配置Java环境 确保安装了Java开发工具包(JDK),并且设置了JAVA_HOME环境变量。 二、配置FFmpeg环境 如果使用了FFmpeg相关的功能,需要确保系统中已经安装了FFmpeg,并且设置了FFMPEG_HOME环境变量。 ffmpeg安装教…...

各种 sql 语句
sql 语句: SELECT max(val) as level_max_val from (select greatest(level1,level2,level3,level4,level5,level6,level7,level8,level9,level10) as val from kbt_2020cv52_data) k;...

CentOS/RHEL7环境下更改网卡名称为CentOS6的传统命名规则
图片 CentOS/RHEL7网卡命名规则介绍 图片 传统的Linux服务器网卡的名称命名方式是从eth0,eth1,eth2....这种方式命名的,但是这个编号往往不一定准确对应网卡接口的物理顺序,常规模式下我们使用的服务器设备可能只有一张网卡,若网卡较多的情…...

飞书开发学习笔记(三)-利用python开发调试云文档和电子表格
飞书开发学习笔记(三)-利用python开发调试云文档和电子表格 一.建立Python飞书开发环境 首先还是进入开放平台下的API调试台 飞书开放平台:https://open.feishu.cn/app?langzh-CN 以获取"我的空间"下的文件清单为例,通过获取飞书API调试台提…...

爆火的正规号卡推广分销 流量卡分销代理平台
正规号卡推广和流量卡分销代理可以通过“聚量推客”申请 聚量推客上的号卡单价高 数据及时 结算快,你还可以搭配平台上的拉新产品各种推广场景,更值得拥有哦...

Gin框架入门实战系列教程之Gin环境搭建 Gin程序的热加载 Gin路由 GET POST PUT DELETE
Gin框架入门实战系列教程之Gin环境搭建 Gin程序的热加载 Gin路由 GET POST PUT DELETE 主讲教师:(大地) 在线文档见网盘下载: 百度网盘 请输入提取码 提取码:abcd 一、Gin介绍 Gin 是一个 Go (Golang) 编写的轻量级…...

浏览器自动播放音视频-前端实现方案
目录 前言 浏览器自动播放策略 策略详情: 实现方案 方案1: 互动后播放 方案2: 互动后出声 总结 前言 在开发中可能有遇到这样的需求,当用户打开页面后,需要自动播放视频或音频,按理说那就打开页面…...

HttpUtils工具类
作为Java开发程序员,需要我们经常写一些工具类来简化开发过程,我们自己肯定写过或者用过HttpUtils用来发送http请求,但是每次手写太繁琐了,于是就按照标准写了一个Http工具类,现在分享出来。 1.HTTP请求简介 HTTP(Hy…...

AI:59-基于深度学习的行人重识别
🚀 本文选自专栏:AI领域专栏 从基础到实践,深入了解算法、案例和最新趋势。无论你是初学者还是经验丰富的数据科学家,通过案例和项目实践,掌握核心概念和实用技能。每篇案例都包含代码实例,详细讲解供大家学习。 📌📌📌在这个漫长的过程,中途遇到了不少问题,但是…...

TCP编程及基础知识
一、端口号 为了区分一台主机接收到的数据包应该转交给哪个进程来进行处理,使用端口号来区分TCP端口号与UDP端口号独立端口用两个字节来表示 2byte(65535个) 众所周知端口:1~1023(1~255之间为众所周知端口ÿ…...

二百零一、Flink——Flink配置状态后端运行后报错:Can not create a Path from an empty string
一、目的 在尚硅谷学习用Flink配置状态后端的项目中,运行报错Exception in thread "main" java.lang.IllegalArgumentException: Can not create a Path from an empty string 二、Flink的状态后端(state backend)类型 (一)Memo…...

Python 爬虫基础
Python 爬虫基础 1.1 理论 在浏览器通过网页拼接【/robots.txt】来了解可爬取的网页路径范围 例如访问: https://www.csdn.net/robots.txt User-agent: * Disallow: /scripts Disallow: /public Disallow: /css/ Disallow: /images/ Disallow: /content/ Disallo…...

亚马逊云科技大语言模型的创新科技
陈老老老板🤴 🧙♂️本文专栏:生活(主要讲一下自己生活相关的内容)生活就像海洋,只有意志坚强的人,才能到达彼岸。 🧙♂️本文简述:亚马逊云科技大语言模型的创新科技 🧙♂️上…...

Qt 各种数据类型
目录 1. 基础类型 2. log 输出 3. 字符串类型 3.2 QByteArray 构造函数 数据操作 子字符串查找和判断 遍历 查看字节数 类型转换 3.3 QString 4. QVariant 4.1 标准类型 4.2 自定义类型 5. 位置和尺寸 5.1 QPoint 5.2 QLine 5.3 QSize 5.4 QRect 6. 日期和…...

电动车展示预约小程序的作用如何
电动车可以说是现在出行常见的方法,覆盖年龄广几乎是每家必备,也有不小大小品牌和经销商,市场需求较高,但在实际经营中,对经销商来时也面临着一些痛点: 1、品牌传播产品展示难 不同品牌竞争很大ÿ…...

「随笔」浅谈2023年云计算的发展趋势
在2023年,云计算的发展趋势将受到政治、经济、社会和科技四个维度的影响。以下是对这些维度的具体分析: 1.1 政治维度: 全球政策推动: 随着全球各国政策对云计算的重视程度不断提高,云计算服务将获得更广泛的市场准入…...

高性能三防工业平板电脑 防摔防爆电容屏工控平板
HT1000是一款高性能工业三防平板,10.1英寸超清大屏,厚度仅14.9mm,超薄机身,可轻松插入袋中,方便携带,搭载8核2.0GHz高性能CPU,行业领先的Android 11.0,设备性能大幅提升,…...

mac flutter pb解析报错:protoc-gen-dart: program not found or is not executable
在mac对pb文件转dart文件的时候报错:protoc-gen-dart: program not found or is not executable 原因是没有安装protoc-gen-dart或者protoc-gen-dart没有设置到环境变量中 解决办法: 1、安装protoc-gen-dart flutter pub global activate protoc_plu…...