通过XML配置注册Bean
spring-config.xml
<!--方式一:声明自定义的bean,没有设置id,id默认为全类型#编号-->
<bean id="cat" class="com.rzg.entity.Cat"/>
<bean class="com.rzg.entity.Cat"/>
public class SpringApp {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
String[] beanNames = context.getBeanDefinitionNames();
for (String beanName : beanNames) {
System.out.println(beanName);
}
}
}
使用@Component注解创建bean
用@Component注解创建beanm,前提是开启包扫描,开启包扫描有两种方式:xml注解开启
配置类开启
xml注解开启
spring-config.xml
<context:component-scan base-package="com.rzg"/>
Dog.java
@Component
public class Dog {
private String dog;
}
启动类
public class SpringApp {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
String[] beanNames = context.getBeanDefinitionNames();
for (String beanName : beanNames) {
System.out.println(beanName);
}
}
}
Spring配置类开启
SpringConfig.java
配置类
@ComponentScan("com.rzg")//开启包扫描
public class SpringConfig2 {
}
Dog.java
@Component
public class Dog {
private String dog;
}
启动类
public class SpringApp02 {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig2.class);
String[] beanNames = context.getBeanDefinitionNames();
for (String beanName : beanNames) {
System.out.println(beanName);
}
}
}
AnnotationConfigApplicationContext(SpringConfig2.class);方式启动的IOC容器,SpringConfig2可以不添加@Configuration注解和@Component注解,Spring会自动吧SpringConfig2加载到IOC容器中
@Component修饰的类或者类似上面SpringConfig配置的类,都可以在类中使用@Bean配置一个新的Bean,但是在这个新创建的Bean所在类中,无法再使用@Bean。
这里的@Component可以使xml注解开启的包扫描,也可以是XML配置开启的包扫描,用Spring配置类来开启包扫描,举例如下:
使用配置类的方式创建IOC容器
public class SpringApp02 {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig2.class);
String[] beanNames = context.getBeanDefinitionNames();
for (String beanName : beanNames) {
System.out.println(beanName);
}
}
}
在SpringConfig2中,使用@Bean创建了一个Bean。毫无疑问,配置类中使用@Bean可以帮我们创建一个bean。
@ComponentScan("com.rzg")
public class SpringConfig2 {
@Bean
public Rabbit rabbit(){
return new Rabbit();
}
}
由于这个配置类开启了ComponentScan,下面我们使用@Component创建一个Dog Bean
@Component
public class Dog {
private String dog;
@Bean
public Cat cat(){
return new Cat();
}
}
在这个@Component创建的Bean的所在类中,仍然可以使用@Bean创建新的Bean。那么在这个Cat Bean所在类中,是否还能用@Bean创建bean呢?答案是不能
。
补充
FactoryBean
spring提供了一个接口FactoryBean,也可以用于声明bean,只不过实现了FactoryBean接口的类造出来的对象不是当前类的对象,而是FactoryBean接口泛型指定类型的对象。如下列,造出来的bean并不是DogFactoryBean,而是Dog。有什么用呢?可以在对象初始化前做一些事情,下例中的注释位置就是让你自己去扩展要做的其他事情的。
启动类
public class SpringApp02 {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig2.class);
String[] names = context.getBeanDefinitionNames();
for (String name : names) {
System.out.println(name);
}
System.out.println(context.getBean("dogFactory").getClass().getName());
}
}
输出结果:
springConfig2
dogFactory
com.rzg.entity.Dog
配置类
定义了一个DogFactory
public class SpringConfig2 {
@Bean
public DogFactory dogFactory(){
return new DogFactory();
}
}
DogFactory.java
public class DogFactory implements FactoryBean<Dog> {
@Override
public Dog getObject() throws Exception {
Dog dog = new Dog();
dog.setName("小黑");
/* 其他的操作 */
return dog;
}
@Override
public Class<?> getObjectType() {
return Dog.class;
}
@Override
public boolean isSingleton() {
return FactoryBean.super.isSingleton();
}
}
FactoryBean也是一个bean,只不过它用于生产其他的bean。需要注意的是,IOC容器中并没有这个工厂bean,只有其生产的Bean
@ImportResource
原有的xml配置的Spring项目怎么转换成Java配置类转换的项目。使用@ImportResource放在配置类上,传入XML的文件路径即可
配置类
@ImportResource("spring-config.xml")
public class SpringConfig2 {
}
启动类
public class SpringApp02 {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig2.class);
String[] beanNames = context.getBeanDefinitionNames();
for (String beanName : beanNames) {
System.out.println(beanName);
}
}
}
@Configuration
前面的例子中用到了@Configuration这个注解,当我们使用AnnotationConfigApplicationContext加载配置类的时候,配置类可以不添加这个注解。但是这个注解有一个更加强大的功能,它可以保障配置类中使用方法创建的bean的唯一性。为@Configuration注解设置proxyBeanMethods属性值为true即可,由于此属性默认值为true,所以很少看见明确书写的,除非想放弃此功能。
看下面的例子
public class Cat {
}
@Configuration(proxyBeanMethods = true)
public class SpringConfig2 {
@Bean
public Cat cat(){
return new Cat();
}
}
public class SpringApp02 {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig2.class);
SpringConfig2 bean = context.getBean(SpringConfig2.class);
System.out.println(bean.cat());
System.out.println(bean.cat());
System.out.println(bean.cat());
}
}
输出结果:
com.rzg.entity.Cat@7fe8ea47
com.rzg.entity.Cat@7fe8ea47
com.rzg.entity.Cat@7fe8ea47
输出结果为什么会不一样呢。
如果配置类是一个普通类SpringConfig2 ,那么每调用一次cat方法,会产生一个新的cat对象。但是SpringConfig2作为普通类,并将proxyBeanMethods 设置为true(这也是默认值),那么获取SpringConfig2的Bean后调用cat方法,会由Spring代理,每次调用都会返回同一个IOC容器中的bean。如果proxyBeanMethods设置为false,则不使用代理,每次调用cat方法会像普通方法一样,返回一个全新的cat对象。
@Import注入Bean
在配置类上使用@Import可以精确导入指定的Bean,只需要给@Import传入指定的类对象数组,同样也可以导入工厂类,只不过这个工厂生产出来的Bean Name为工厂类的全类名。
public class Cat {
}
public class DogFactory implements FactoryBean<Dog> {
@Override
public Dog getObject() throws Exception {
Dog dog = new Dog();
dog.setName("小黑");
/* 其他的操作 */
return dog;
}
@Override
public Class<?> getObjectType() {
return Dog.class;
}
@Override
public boolean isSingleton() {
return FactoryBean.super.isSingleton();
}
}
@Import({Cat.class,Cat.class,DogFactory.class})
public class SpringConfig2 {
}
public class SpringApp02 {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig2.class);
String[] names = context.getBeanDefinitionNames();
for (String name : names) {
System.out.println(name);
}
System.out.println("----------------");
Object bean = context.getBean("com.rzg.entity.DogFactory");
System.out.println(bean.getClass().getName());
}
}
输出结果:
springConfig2
com.rzg.entity.Cat
com.rzg.entity.DogFactory
----------------
com.rzg.entity.Dog
如果生产多个相同类型的Bean,后面的Bean会把前面的Bean覆盖掉
还需要注意 @Import导入的类可以作为一个配置类。看下面例子
我们知道在通过配置类启动的Spring程序,可以在配置类上省略@Configuration注解,Spring仍然可以作为一个配置类使用。比如在配置类上使用@ComponentScan标签。
public class SpringApp02 {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig2.class);
String[] names = context.getBeanDefinitionNames();
for (String name : names) {
System.out.println(name);
}
System.out.println("----------------");
}
}
@ComponentScan("com.rzg.entity")
public class SpringConfig2 {
@Bean Cat cat(){
return new Cat();
}
}
使用@Component方式加载的bean和@Import加载的bean仍然可以作为配置类,像下面这样
public class SpringApp02 {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig2.class);
String[] names = context.getBeanDefinitionNames();
for (String name : names) {
System.out.println(name);
}
System.out.println("----------------");
}
}
@ComponentScan("com.rzg.entity") //会将Rabbit 扫描到,但是不会扫描到SpringConfig
public class SpringConfig2 {
}
@Component//使用@Component方式导入的bean,仍然可以用作配置类
@ComponentScan("com.rzg.config")//会扫描到SpringConfig
public class Rabbit {
}
@Component
public class SpringConfig {
}
输出结果:
springConfig2
rabbit
springConfig
@Import方式
public class SpringApp02 {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig2.class);
String[] names = context.getBeanDefinitionNames();
for (String name : names) {
System.out.println(name);
}
System.out.println("----------------");
}
}
@Import({Rabbit.class})
public class SpringConfig2 {
}
@Component
@ComponentScan("com.rzg.config")
public class Rabbit {
}
@Component
public class SpringConfig {
}
输出结果:
springConfig2
springConfig
com.rzg.entity.Rabbit
暂且将@Bean方式产生的bean作为普通bean,@Component和@Import方式产生的Bean作为配置Bean。因为@Component和@Import方式产生的bean可以放@ComponentScan(“”)注解,@Bean方式产生的Bean,放@ComponentScan没有作用。
那么@Import导入的类可以作为配置类的话,在这个类上是否可以继续用@Import导入其他Bean,答案是当然可以。
public class SpringApp02 {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig2.class);
String[] names = context.getBeanDefinitionNames();
for (String name : names) {
System.out.println(name);
}
System.out.println("----------------");
}
}
public class SpringConfig2 {
@Bean
public Rabbit rabbit(){
return new Rabbit();
}
}
@Component
@ComponentScan("com.rzg.config")
public class Rabbit {
}
@Component
public class SpringConfig {
//在com.rzg.config包下,但是不会产生类,因为给@Bean普通类添加@ComponentScan没作用
}
输出结果:
springConfig2
rabbit
编程方式加载Bean
在ioc容器产生之后,可以动态的添加bean
public class SpringConfig2 {
}
public class SpringApp02 {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig2.class);
String[] names = context.getBeanDefinitionNames();
for (String name : names) {
System.out.println(name);
}
System.out.println("----------------");
context.register(Rabbit.class);
names = context.getBeanDefinitionNames();
for (String name : names) {
System.out.println(name);
}
}
}
主要输出结果:
springConfig2
----------------
springConfig2
rabbit
ImportSelector
实现了ImportSelector的类,作为一个ImportSelector,可以一次性导入多个Bean。实现这个接口中的selectImports方法,返回String数组,数组中放置要导入的全类名。ImportSelector和工厂类似
,但是注意实现ImportSelector接口的类,只能通过@Import注解来导入。@Import应该是和ImportSelector配合使用的,@Import导入/ImportSelector导入时选择嘛。
public class SpringApp02 {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig2.class);
String[] names = context.getBeanDefinitionNames();
for (String name : names) {
System.out.println(name);
}
System.out.println("----------------");
}
@Import(MyImportSelector.class)
public class SpringConfig2 {
}
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
//importingClassMetadata存放了一些配置类SpringConfig2的一些信息,包括是否有某些注解
return new String[]{"com.rzg.entity.Cat","com.rzg.entity.Dog"};
}
@Override
public Predicate<String> getExclusionFilter() {
return ImportSelector.super.getExclusionFilter();
}
}
主要输出结果:
springConfig2
com.rzg.entity.Cat
com.rzg.entity.Dog
----------------
ImportBeanDefinitionRegistrar接口
bean的加载不是一个简简单单的对象,spring中定义了一个叫做BeanDefinition的东西,它才是控制bean初始化加载的核心。BeanDefinition接口中给出了若干种方法,可以控制bean的相关属性。说个最简单的,创建的对象是单例还是非单例,在BeanDefinition中定义了scope属性就可以控制这个。如果你感觉方式六没有给你开放出足够的对bean的控制操作,那么方式七你值得拥有。我们可以通过定义一个类,然后实现ImportBeanDefinitionRegistrar接口的方式定义bean,并且还可以让你对bean的初始化进行更加细粒度的控制,不过对于新手并不是很友好。忽然给你开放了若干个操作,还真不知道如何下手。
public class MyRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Cat.class).getBeanDefinition();
registry.registerBeanDefinition("cat",beanDefinition);
}
}
@Import(MyRegistrar.class)
public class SpringConfig2 {
}
public class SpringApp02 {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig2.class);
String[] names = context.getBeanDefinitionNames();
for (String name : names) {
System.out.println(name);
}
System.out.println("----------------");
}
}
主要运行结果:
springConfig2
cat
----------------
BeanDefinitionRegistryPostProcessor
如果在一个项目开发中,很多开发者在自己模块定义一些Bean,那么造成一些冲突怎么办,Bean的定义有前后顺序的话,岂不是很难控制。BeanDefinitionRegistryPostProcessor就是帮我们在一些bean定义之后再去定义一些Bean,他的定义是在以上几种Bean定义方式之后。
public class SpringApp02 {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig2.class);
String[] names = context.getBeanDefinitionNames();
for (String name : names) {
System.out.println(name);
}
System.out.println("----------------");
}
}
@Import(MyPostProcessor.class)
public class SpringConfig2 {
}
public class MyPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
BeanDefinition beanDefinition =
BeanDefinitionBuilder.rootBeanDefinition(Cat.class).getBeanDefinition();
beanDefinitionRegistry.registerBeanDefinition("cat",beanDefinition);
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
}
}
springConfig2
com.rzg.entity.MyPostProcessor
cat
----------------