-
xml方式实例化bean
-
注解方式实例化bean
-
java方式实例化bean
-
ClassPathXmlApplication和AnnotationConfigApplicationContext
-
Bean存放到了哪里?
-
选取哪一种方式合适?
-
bean的名称
-
延迟加载
-
bean加载顺序
-
bean范围
-
bean的回调函数
-
组件扫描
-
内部类实例化
-
静态工厂方法
-
实例工厂方法
-
BeanPostProcessor
-
BeanFactoryPostProcessor
-
FactoryBean
阅读须知
1 本文核心内容来自 https://docs.spring.io/spring/docs/5.2.1.RELEASE/spring-framework-reference/core.html
2 在我自己阅读文档并写出test后,为了提高本文的可读性和连续性,我决定本文不按照spring文档目录顺序排版。将IOC部分拆分为 实例化bean,为bean注入属性,spring的其他特性三个方面。
3 文章中所包含的test都经过测试。git地址为 https://gitee.com/zumengjie/springcode-core-ioc
4 文章中对于spring的使用方式和使用技巧,仅代表我个人。如有错误,或不足,请留言。
5 文章环境为jdk8,springboot2.2.2.RELEASE
6 文章适用于使用过spring系列框架的朋友,或者准备学习spring的朋友。
7 最后,这篇博客从第一遍看spring文档到手稿,到整理,三周,一个个test敲出来。对我自身是有很大提升的,spring对于ioc部分的处理十分细腻,本文也只是根据我所看到的整理出来了。在原文中,还有很多东西没有被我挖掘出来。我觉得对于每一个学spring用spring的人来讲都应该去看一遍官方文档。在我看文档前,我担心自己英语不好,看不懂。在一篇博客上,有个前辈说,你看不懂的不是英文文档,而是文档。确实,一篇文档,如果没有一点根基,就算他是中文的,我们也不一定看一遍就懂,看一遍就记住。spring官方文档,值得我们耐着性子看,然后自己写test。只看文档和边看边写还是有很大差距的。下一章资源,会在后续更新。
实例化bean
1 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/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- class:哪个bean需要被实例化,就用哪个bean的路径
id:告诉spring被实例化这个bean叫什么名字-->
<bean class="com.datang.springcode.xmlRegisterBean.Computer" ></bean>
</beans>
2 注解方式实例化bean
package com.datang.springcode.annoRegisterBean;
import org.springframework.stereotype.Component;
//@Component标识这个类,需要被实例化
@Component
public class Pear {
}
3 java方式实例化bean
package com.datang.springcode.javaRegisterBean;
//需要被实例化的bean
public class Banana {
}
package com.datang.springcode.javaRegisterBean;
import org.springframework.context.annotation.Bean;
//注意:此处RegisterBeanConfig类上我并未使用@Configuration注解,是因为我使用的AnnotationConfigApplicationContext对象时,
// 传递的是RegisterBeanConfig.class。否则,RegisterBeanConfig类必须也要使用xml方式或@Component配置成bean。
public class RegisterBeanConfig {
//这个注解才是真正的java实例化bean的关键,可以指定多个value值,第一个往后,为别名
@Bean(value = {"banana", "banana2", "banana3"})
public Banana getBanana() {
return new Banana();
}
}
4 ClassPathXmlApplication和AnnotationConfigApplicationContext
@Test
public void t1() {
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:xmlRegisterBean.xml");
Computer computer = context.getBean("computer", Computer.class);
}
@Test
public void t2() {
ApplicationContext context = new AnnotationConfigApplicationContext(Pear.class);
Pear bean = context.getBean(Pear.class);
}
@Test
public void t3() {
ApplicationContext context = new AnnotationConfigApplicationContext(RegisterBeanConfig.class);
Object getBanana1 = context.getBean("getBanana");
}
ClassPathXmlApplicationContext对象用来加载xml配置文件,AnnotationConfigApplicationContext用来加载一个类。spring通过抽象抽取了一个用户使用的对象ApplicationContext,此次ioc部分我们最常用的方法就是getBean()
ClassPathXmlApplicationContext有多个构造函数重载,这里提一种常见的 ClassPathXmlApplicationContext(String... configLocations)参数接收可变长度的字符串,也就是说,ClassPathXmlApplicationContext可以同时加载多个xml配置文件。
AnnotationConfigApplicationContext也有多个构造函数重载,最常用的是AnnotationConfigApplicationContext(Class<?>... componentClasses)参数接收可变长度的Class类对象,可以同时加载多个配置对象类。
getBean()方法的可重载方式很多,接收一个字符串根据bean的id查询,接收Class根据类型查询,还可以同时接收id和class。
5 Bean存放到了哪里?
上边介绍的三种方式无论哪一种最终都会将pojo实例化到spring容器中。那么我们经常说的spring容器到底是什么?通过debug查看ApplicationContext对象。在spring上下文中有一个beanFactory对象,该对象实现类DefaultListableBeanFactory。依次再找父类AbstractAutowireCapableBeanFactory-->AbstractBeanFactory-->FactoryBeanRegistrySupport-->DefaultSingletonBeanRegistry中有一个Map,registeredSingletons这个Map就是用来
存放所有单例模式的bean。关于bean的模式后边内容会说到。
6 选取哪一种方式合适?
以上三种实例化bean的方式,无论使用哪一种,最终的bean都会存到到registeredSingletons这个Map中。我们可以三种方式混着用,也可以只用一种方式。使用过springmvc框架的朋友一般都有过这样的配置。
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
ContextLoaderListener实现了ServletContextListener接口,所以能够监听Tomcat启动和停止。而在ContextLoaderListener中则读取了。classpath:applicationContext.xml。在这个xml中我们是不是了实例化bean,例如dataSource。
而使用springboot的朋友一般极少使用xml实例化的方式,就算有需要配置的也会在application.properties中配置。如果是我们用户自己的pojo,那使用注解方式实例化bean最方便,如果有一些系统pojo需要统一规范的配置,那就选取xml方式实例化bean,便于管理和修改,而且不容易配破坏,大家都不会随便修改xml的内容嘛。最后如果你使用的是第三方的jar,那此时使用java的实例化方式是最合适的了,毕竟你也无法在源码上加@Component注解。
7 bean的名称
7.1 默认名称
无论哪种方式实例化bean,被实例化到map中的bean都有一个唯一的名称。我们通过这个名称来找到bean,如getBean(),包括在注入属性时,我们也需要使用到bean的名称来找到bean。以上test中使用xml实例化bean时,我们使用id属性来指定bean的名称,不设置id也有默认的名称。而使用java方式和注解方式时,我们则没有指定bean的名称。为了演示方便我们将xml中实例化bean的id属性去掉。
package com.datang.springcode.javaRegisterBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ImportResource;
//扫描包下的注解
@ComponentScan(value = "com.datang.springcode")
//加载xml
@ImportResource(value = "classpath:xmlRegisterBean.xml")
public class RegisterBeanConfig {
//这个注解才是真正的java实例化bean的关键。
@Bean
public Banana getBanana(){
return new Banana();
}
}
在上边代码中,使用@ComponentScan和@ImportResource注解分别扫描包中的注解和加载xml,为的就是我们能在一个Map中看到三个bean。xml方式实例化bean默认的id是类的全路径拼接#和编号。而@Component注解方式则是使用类名,首字母换成了小写。使用@Bean方式实例化bean时,bean的名称是@Bean所注释的方法名。
7.2 设置名称
package com.datang.springcode.javaRegisterBean;
import org.springframework.context.annotation.Bean;
public class RegisterBeanConfig {
//这个注解才是真正的java实例化bean的关键。value指定bean的名称
@Bean(value = "banana")
public Banana getBanana(){
return new Banana();
}
}
package com.datang.springcode.annoRegisterBean;
import org.springframework.stereotype.Component;
//@Component标识这个类,需要被实例化.value指定bean的名称
@Component(value = "pear")
public class Pear {
}
<?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">
<!-- class:哪个bean需要被实例化,就用哪个bean的路径
id:告诉spring被实例化这个bean叫什么名字-->
<bean class="com.datang.springcode.xmlRegisterBean.Computer" ></bean>
</beans>
通过以上代码我们给每一个bean设置了名称。
7.3 设置别名
package com.datang.springcode.javaRegisterBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ImportResource;
//扫描包下的注解
@ComponentScan(value = "com.datang.springcode")
//加载xml
@ImportResource(value = "classpath:xmlRegisterBean.xml")
public class RegisterBeanConfig {
//这个注解才是真正的java实例化bean的关键,可以指定多个value值,第一个往后,为别名
@Bean(value = {"banana", "banana2", "banana3"})
public Banana getBanana() {
return new Banana();
}
}
<?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">
<!-- class:哪个bean需要被实例化,就用哪个bean的路径
id:告诉spring被实例化这个bean叫什么名字
通过name设置别名,可以用 , 号分割-->
<bean class="com.datang.springcode.xmlRegisterBean.Computer" ></bean>
<!-- alias也可以给bean起别名,但是一个alias标签仅能指定一个别名 -->
<alias name="computer" alias="computer4"/>
</beans>
通过别名也可以获取到bean。另外别名不可以重复,否则会被替换。在beanFactory下有aliasMap,记录的就是当前所有bean的别名。@Component注解实例化bean无法设置别名。
8 延迟加载
bean的延迟加载也就是说,在spring上下文启动时,不会立刻将bean实例化到registeredSingletonsMap中,而是在第一次使用getBean()时才会实例化。
@Component
@Lazy
public class Bookrack {
}
package com.datang.springcode.lazyBean;
public class Schoolbag {
}
package com.datang.springcode.lazyBean;
public class Stool {
}
<?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 class="com.datang.springcode.lazyBean.Stool" ></bean>
</beans>
package com.datang.springcode.lazyBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Lazy;
@ComponentScan(value = "com.datang.springcode.lazyBean")
public class LazyConfig {
@Bean(value = "schoolbag")
@Lazy
public Schoolbag getSchoolbag(){
return new Schoolbag();
}
}
以上代码通过三种方式实例化bean,并且通过三种方式实行懒加载。结果如下。在spring启动成功后,并singletonObects中并没有出现。Bookrack,Schoolbag,Stool三个类的实例对象。
但实际开发中我们又不会直接使用getBean()方法,多数会使用属性注入。比如@Autowried。如果Abean不是lazy,Bbean是lazy。Abean需要注入Bbean,那么在实例化Abean时也会把Bbean实例化了。
9 bean加载顺序
假设一个场景,我们有一个全局配置类sitting,里边有默认的几个静态全局变量。同时我们在程序的各个地方调用通过static.的方式调用。但是,这里的参数有些在项目启动后需要从数据库重新取值,对此我们创建了一个专门重新赋值的刷新类,该类初始化过程中,从数据库取值,然后在重新赋值到sitting类中。在此场景中,散布在程序各处的类和从数据库取值的刷新类没有明显依赖注入关系,但是有弱依赖关系。也就是说,spring必须先把刷新类实例化到Map中,然后才能实例化其他各个业务类。
package com.datang.springcode.depends_on;
import org.springframework.stereotype.Component;
@Component(value = "cap")
public class Cap {
public Cap(){
System.out.println("cap");
}
}
package com.datang.springcode.depends_on;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Component;
@Component
@DependsOn(value = {"cap"})
public class Clothes {
public Clothes(){
System.out.println("clothes:我依赖于cap");
}
}
package com.datang.springcode.depends_on;
import org.springframework.stereotype.Component;
@Component(value = "bed")
public class Bed {
public Bed(){
System.out.println("bed");
}
}
package com.datang.springcode.depends_on;
public class Quilt {
public Quilt(){
System.out.println("quilt:我依赖于bed");
}
}
<?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 class="com.datang.springcode.depends_on.Quilt" ></bean>
</beans>
package com.datang.springcode.depends_on;
public class Phone {
public Phone() {
System.out.println("phone:我依赖于charger");
}
}
package com.datang.springcode.depends_on;
public class Charger {
public Charger() {
System.out.println("charger");
}
}
package com.datang.springcode.depends_on;
import org.springframework.context.annotation.*;
@Configuration
@ImportResource(value = {"classpath:depends_on.properties.xml"})
@ComponentScan(value = {"com.datang.springcode.depends_on"})
public class DependsOnConfig {
@Bean(value = {"phone"})
@DependsOn(value = {"charger"})
public Phone getPhone() {
return new Phone();
}
@Bean(value = {"charger"})
public Charger getCharger() {
return new Charger();
}
}
以上代码,演示了三种实例化bean的方式分别使用不同的方式指定bean的加载顺序。xml实例化方式使用depends-on属性,java实例化方式和注解实例化方式使用@DependsOn注解。两种方式都可以指定多个值。
10 bean范围
在实例化bean时,我们可以选择bean的活动范围。spring默认实例化单例bean,我们可以自定义选择注入prototype原型bean(有会讲多实例bean)。在web环境还有request,session,application和websocket。
package com.datang.springcode.scope;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Component
@Scope(value = "prototype")
public class Refrigerator {
}
package com.datang.springcode.scope;
public class Television {
}
<?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 class="com.datang.springcode.scope.Television" />
</beans>
package com.datang.springcode.scope;
public class Spoon {
}
package com.datang.springcode.scope;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ImportResource;
import org.springframework.context.annotation.Scope;
@ComponentScan(value = {"com.datang.springcode.scope"})
@ImportResource(value = {"classpath:scope.xml"})
public class ScopeConfig {
@Bean(value = "spoon")
@Scope(value = "prototype")
public Spoon getSpoon(){
return new Spoon();
}
}
@Test
public void t6(){
ApplicationContext context = new AnnotationConfigApplicationContext(ScopeConfig.class);
Refrigerator refrigerator = context.getBean(Refrigerator.class);
Refrigerator refrigerator1 = context.getBean(Refrigerator.class);
Assert.assertFalse(refrigerator==refrigerator1);
Television television = context.getBean(Television.class);
Television television1 = context.getBean(Television.class);
Assert.assertFalse(television==television1);
Spoon spoon = context.getBean(Spoon.class);
Spoon spoon1 = context.getBean(Spoon.class);
Assert.assertFalse(spoon==spoon1);
}
上边测试使用三种实例化bean的方式,实例化了三个原型bean。在singletonObjectsMap中并没有上边所实例化的三个单例bean。
另外我们也可以在。beanFactory下的beanDefinitionMap中查看所有bean的描述信息。在spring实例化bean的过程中,有一个步骤会将收集到的每个pojo信息存到到beanDefinitionMap中,等到以后使用。例如在实例化时,会先查看这个pojo是否是singleton,如果不是则不会实例化。
11 bean的回调函数
在spring实例化bean后,和销毁bean之前有两个钩子函数。spring有三种方式实现这两个钩子函数。分别时xml配置,注解,和实现接口。
package com.datang.springcode.beanCallBack;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
//此处标记类需要被扫描
@Component
public class House implements InitializingBean, DisposableBean {
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("接口形式的回调after");
}
@Override
public void destroy() throws Exception {
System.out.println("接口形式的回调destroy");
}
public House() {
System.out.println("house的构造函数");
}
public void xmlInit() {
System.out.println("xml形式初始化回调");
}
public void xmlDes() {
System.out.println("xml形式销毁回调");
}
@PostConstruct
public void annInit() {
System.out.println("注解形式的初始化回调");
}
@PreDestroy
public void annDes() {
System.out.println("注解形式的销毁回调");
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
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
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 扫描包,注意导入命名空间 -->
<context:component-scan base-package="com.datang.springcode.beanCallBack"/>
<bean class="com.datang.springcode.beanCallBack.House" />
</beans>
注意这三种方式同时使用时的顺序。注解优先,实现接口其次,xml配置最后。
12 组件扫描
12.1 @Component @Controller @Service @Repository @Configuration
@Component只能被标记在类上,被@Component标记表示这个类需要被spring实例化。
package com.datang.springcode.scanPack;
import org.springframework.stereotype.Component;
@Component
public class Milk {
}
package com.datang.springcode.scanPack;
public class Fruit {
}
@ComponentScan(value = {"com.datang.springcode.scanPack"})
public class ScanPackConfig {
}
上边代码片段,Fruit类没有被@Component标记,所以没有被实例化到singletonObjects这个Map中。@Controller @Service @Repository @Configuration 注解都是和@Component组合的注解。也就是说,这4个注解和@Component有同等的效果。其中 @Controller @Service @Repository 在语义化上代表服务层,业务层,持久层。而 @Configuration则一般和@Bean使用。也只有@Configuration是有特殊的功能。
12.2 @Bean@Configuration的精简模式和完全模式
略微修改Milk类。
package com.datang.springcode.scanPack;
public class Milk {
public Fruit fruit;
//使其构造函数需要持有一个Fruit实例
public Milk(Fruit fruit) {
this.fruit = fruit;
}
}
package com.datang.springcode.scanPack;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@ComponentScan(value = {"com.datang.springcode.scanPack"})
@Configuration
public class ScanPackConfig {
@Bean(value = "milk")
public Milk getMilk() {
//这种写法完全没有问题。构造函数需要一个Fruit而getFruit()方法返回的就是一个fruit
return new Milk(getFruit());
}
@Bean(value = "fruit")
public Fruit getFruit() {
return new Fruit();
}
}
看结果,两个fruit是同一个对象。这种结果就是完全模式。再看@Configuration
默认是true。我们修改成false试试。
package com.datang.springcode.scanPack;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@ComponentScan(value = {"com.datang.springcode.scanPack"})
@Configuration(proxyBeanMethods = false)
public class ScanPackConfig {
@Bean(value = "milk")
public Milk getMilk() {
//这种写法完全没有问题。构造函数需要一个Fruit而getFruit()方法返回的就是一个fruit
return new Milk(getFruit());
}
@Bean(value = "fruit")
public Fruit getFruit() {
return new Fruit();
}
}
现在发现两个fruit就不是同一个对象了。但是在singletonObjects中只有一个fruit
这就是@Bean的精简模式。也是@Configuration和@Component的区别。
12.3 开启组件扫描
spring提供两种方式的组件扫描,注意组件扫描只会扫描类上标记有@Component注解或和@Component的组合注解如@Service
package com.datang.springcode.scanPack2;
import org.springframework.stereotype.Component;
@Component
public class Reality {
}
package com.datang.springcode.scanPack2;
import org.springframework.stereotype.Component;
@Component
public class Exist {
}
package com.datang.springcode.scanPack2;
public class Dream {
}
package com.datang.springcode.scanPack2;
import org.springframework.context.annotation.ComponentScan;
@ComponentScan(value = "com.datang.springcode.scanPack2")
public class ScanPack2Config {
}
使用@ComponentScan注解扫描Dream类没有标记@Component所以没有被实例化出来。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
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
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 扫描包,注意导入命名空间 -->
<context:component-scan base-package="com.datang.springcode.scanPack2"/>
</beans>
使用xml方式配置扫描器,结果也是一样的。
12.4默认的扫描路径
即使我们使用@ComponentScan注解时,不指定value或者basePackages属性,spring会默认扫描,该配置类的平级以及自包中所有@Component。
package com.datang.springcode.filterScan1;
public class Clerk extends Employee {
}
package com.datang.springcode.filterScan1;
public class Employee {
}
package com.datang.springcode.filterScan1;
public class Manager extends Employee {
}
package com.datang.springcode.filterScan1;
import org.springframework.stereotype.Component;
@Component
public class Smoke {
}
package com.datang.springcode.filterScan1.child;
import org.springframework.stereotype.Component;
@Component
public class Mask {
}
package com.datang.springcode.filterScan1;
import org.springframework.context.annotation.ComponentScan;
@ComponentScan
public class FilterScan1Config {
}
同级中的@Component被扫描到,子包中的@Component也可以被扫描到。这也就是springboot启动类以下的包可以被扫描到的原因。
12.5 扫描过滤
在组件扫描过程中,除了扫描@Component标记的类,我们也可以额外的设置扫描规则。
在@ComponentScan注解的属性中,有includeFilters()和excludeFilters()两个属性,分别是包含和排除,而这两个属性可接收多个Filter。我们来看看Filter中的type()属性,有哪些可选值。
FilterType下有五种匹配模式,顺序依次是。注解匹配,类型匹配,AspectJ表达式匹配,正则表达式匹配,自定义匹配器。
package com.datang.springcode.filterScan1;
public class Employee {
}
package com.datang.springcode.filterScan1;
public class Manager extends Employee {
}
package com.datang.springcode.filterScan1;
public class Clerk extends Employee {
}
package com.datang.springcode.filterScan1;
import org.springframework.stereotype.Component;
@Component
public class Smoke {
}
package com.datang.springcode.filterScan1.child;
import org.springframework.stereotype.Component;
@Component
public class Mask {
}
package com.datang.springcode.filterScan1;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
@ComponentScan(includeFilters = {@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {Employee.class})})
public class FilterScan1Config {
}
以上代码片段,Employee Clerk Manager 三个类没有被@Component注解。但我们在扫描组件时使用 includeFilters 并且按照 FilterType.ASSIGNABLE_TYPE Class类型 值是 Employee.class 如此Employee类及其子类可以被spring实例化。
下边我们在试试excludeFilters,此时@Component标记的类没有被注入。
package com.datang.springcode.filterScan1;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Component;
@ComponentScan(includeFilters = {@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {Employee.class})},
excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Component.class)})
public class FilterScan1Config {
}
使用xml配置方式也可以做到过滤扫描,需要注意的是,在xml中assignable代表class类型,和Filter中的ASSIGNABLE_TYPE作用一样。<context:include-filter/>必须在<context:exclude-filter/>之前
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
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
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 扫描包,注意导入命名空间 -->
<context:component-scan base-package="com.datang.springcode.filterScan1">
<!-- 加载Employee类 -->
<context:include-filter type="assignable" expression="com.datang.springcode.filterScan1.Employee"/>
<!-- 排除配置类 -->
<context:exclude-filter type="assignable" expression="com.datang.springcode.filterScan1.FilterScan1Config"/>
<!-- 排除@Component注解-->
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Component"/>
</context:component-scan>
</beans>
13 内部类实例化
在使用xml实例化bean时,内部类必须是static的,否则报错
package com.datang.springcode.innerBean;
public class Teacher {
static class Student {
}
}
<?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 class="com.datang.springcode.innerBean.Teacher$Student" />
</beans>
@Component方式,外部类必须要是一个springbean,内部类必须是静态的。
package com.datang.springcode.innerBean;
import org.springframework.stereotype.Component;
@Component
public class Family {
@Component
static class Person{
}
}
package com.datang.springcode.innerBean;
import org.springframework.context.annotation.ComponentScan;
@ComponentScan(basePackages = {"com.datang.springcode.innerBean"})
public class InnerBeanConfig {
}
java方式实例化bean则比较灵活
package com.datang.springcode.innerBean;
public class Food {
class Bread {
}
}
package com.datang.springcode.innerBean;
import org.springframework.context.annotation.Bean;
public class InnerBeanConfig2 {
@Bean
public Food.Bread getBread() {
return new Food().new Bread();
}
}
14 静态工厂方法
package com.datang.springcode.staticFactoryBean;
public class Conditioner {
private Conditioner() {
}
private static Conditioner conditioner = new Conditioner();
public static Conditioner getConditioner() {
return conditioner;
}
}
<?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 class="com.datang.springcode.staticFactoryBean.Conditioner" />
</beans>
注意,静态工厂,本身无法实例化,需要提供一个静态的方法,用来返回bean。其实静态工厂方法,如果您需要的是单例对象。不使用springbean完全可以。
延用刚才的pojo。我们用@Bean注入,其实这种做法就有点脱裤子放屁的感觉了,感受下。
package com.datang.springcode.staticFactoryBean;
import org.springframework.context.annotation.Bean;
public class StaticFactoryBeanConfig {
@Bean
public Conditioner getConditioner() {
return Conditioner.getConditioner();
}
}
@Component注解方式无法创建静态工厂bean。因为构造方法私有化。
15 实例工厂方法
只有xml配置支持实例工厂方法,要配置由实例工厂返回的pojo,需要先配置工厂类。
<?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 class="com.datang.springcode.newBeanFacotry.Mask" id="mask"/> <bean id="glove" factory-bean="mask" factory-method="getGlove"/> </beans>