Spring注解开发——1. 核心容器(组件注册)

先看一下之前我们怎么使用spring来做项目开发的

1)新建一个java工程

2)引入spring核心容器环境依赖

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>4.3.12.RELEASE</version>
</dependency>

3)配置文件

<?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"
	xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
	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
		http://www.springframework.org/schema/tx 
		http://www.springframework.org/schema/tx/spring-tx.xsd
		http://www.springframework.org/schema/aop 
		http://www.springframework.org/schema/aop/spring-aop.xsd
		">  
		
		<bean id="person" class="com.bjc.pojo.Person">
			<property name="name" value="张三 "></property>
			<property name="age" value="18 "></property>
		</bean>
		
</beans>

4)测试类

package com.bjc;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.bjc.pojo.Person;

public class MainTest {

	public static void main(String[] args) {
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
		Person p = applicationContext.getBean("person", Person.class);
		System.out.println(p);
	}
}

5)输出结果

那么,我们现在使用注解开发,就可以不需要编写xml配置文件了。

1. 组件注册

1.1 配置类——@Configuration + @Bean给容器中注册组件

        我们可以新建一个java类来代替之前的xml文件,用@Configuration标记该类,表示该类是一个配置类(beans标签),同时,在该类中可以定义一个一个的方法来代替xml中的bean标签,在方法上使用@Bean来表示。

例如:将上面xml改成如下的配置类

package com.bjc.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.bjc.pojo.Person;

// 配置类 == 配置文件
@Configuration	// 告诉spring这是一个配置类
public class MainConfig {
	
	// 给容器注册一个Bean,类型为返回值类型,id默认是方法名
	@Bean
	public Person person() {
		return new Person("李四",25);
	}

}

那么,怎么使用了?可以使用AnnotationConfigApplicationContext来代替ClassPathXmlApplicationContext,例如:

public static void main(String[] args) {
	ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
	// 通过类型获取
	// Person person = context.getBean(Person.class);
	// 通过id获取:因为在配置类中Person定义的方法名为person,所以,id = person
	Person person = context.getBean("person", Person.class);
	System.out.println(person);
}

@Bean注解

        如果我们想改变id的值,除了修改配置类中方法名之外,还可以给@Bean注解注入属性的方式来实现。

通过方法getBeanNamesForType来获取指定类型的name属性值,例如:

1.2 @ComponentScan-自动扫描组件

        我们在xml模式的开发中,通常会配置如下的配置来实现包扫描

<context:component-scan base-package="erp.web.controller"></context:component-scan>

那么,现在我们是不是也可以在配置类中通过注解的方式来实现该功能了,答案是肯定的。我们只需要在配置类上加上注解@ComponentScan并指定包路径即可。例如:

怎么验证该配置是否有效了,我们可以定义controller与service,然后通过方法getBeanDefinitionNames来查看,如图:

1.2.1 指定扫描规则——excludeFilters

        在xml中我们可以指定扫描某些包也可以指定排除某些包,在配置类中同样可以,通过属性includeFilters与excludeFilters来实现扫描指定的组件,例如:

// 配置类 == 配置文件
@Configuration	// 告诉spring这是一个配置类
@ComponentScan(value="com.bjc",excludeFilters = {
		// 以注解排除,排除掉Controller注解
		@Filter(type=FilterType.ANNOTATION,classes = Controller.class)  
})
public class MainConfig {
	
	// 给容器注册一个Bean,类型为返回值类型,id默认是方法名
	@Bean("personId")
	public Person person() {
		return new Person("李四",25);
	}

}

注意:@Filter注解的classes是一个数组,所以我们也可以用数组的形式表示排除多个

例如:

 

1.2.2 指定扫描规则——includeFilters

指定扫描的时候,只包含哪些组件

但是,需要注意,要让该配置生效,需要禁用掉默认规则生效。

例如:只扫描controller

// 配置类 == 配置文件
@Configuration	// 告诉spring这是一个配置类
@ComponentScan(value="com.bjc",includeFilters = {
		// 以注解排除,排除掉Controller注解
		@Filter(type=FilterType.ANNOTATION,classes = {Controller.class})  
},useDefaultFilters = false)
public class MainConfig {
	
	// 给容器注册一个Bean,类型为返回值类型,id默认是方法名
	@Bean("personId")
	public Person person() {
		return new Person("李四",25);
	}

}

1.2.3 扫描规则介绍

在FilterType中给定了很多规则

1)ANNOTATION:按照注解

2)ASSIGNABLE_TYPE:按照给定的类型

例如:

3)ASPECTJ:使用ASPECTJ表达式(基本上不会用)

4)REGEX:使用正则表达式

5)CUSTOM:自定义规则

1.2.4 自定义规则——CUSTOM

1)定义自定义规则类

package com.bjc.config;

import java.io.IOException;

import org.springframework.core.io.Resource;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter;

public class MyTypeFilter implements TypeFilter {

	/**
	 * metadataReader:读取到当前正在扫描的类的信息
	 * metadataReaderFactory:可以获取到其他任何类信息的工厂
	 * */
	@Override
	public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
			throws IOException {
		// 获取当前类注解信息
		AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
		// 获取当前正在扫描类的注解信息
		ClassMetadata classMetadata = metadataReader.getClassMetadata();
		// 获取当前类资源(类路径)
		Resource resource = metadataReader.getResource();
		
		String className = classMetadata.getClassName();
		System.out.println("-------> " + className);
		return false;
	}

}

2)使用自定义规则

package com.bjc.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Controller;

import com.bjc.pojo.Person;
import com.bjc.service.BookService;

// 配置类 == 配置文件
@Configuration	// 告诉spring这是一个配置类
@ComponentScan(value="com.bjc",includeFilters= {
		// 以注解排除,排除掉Controller注解
		//@Filter(type=FilterType.ANNOTATION,classes = {Controller.class}),  
		// 排除指定类型
		//@Filter(type=FilterType.ASSIGNABLE_TYPE,classes = {BookService.class}),
		// 由我们自定义的规则MyTypeFilter返回true或者false代表匹配或者不匹配
		@Filter(type=FilterType.CUSTOM,classes=MyTypeFilter.class)
},useDefaultFilters = false)
public class MainConfig {
	
	// 给容器注册一个Bean,类型为返回值类型,id默认是方法名
	@Bean("personId")
	public Person person() {
		return new Person("李四",25);
	}

}

运行结果如图:

因为我们现在自定义规则返回的是false,所以,全部给过滤掉了,只有mainConfig与personId注入到了spring容器中。接下来写一下我们自己的规则。

做一个简单的匹配规则,只要类名包含Service即匹配,例如:

运行结果如图,bookService匹配成功

1.3 @Scope——设置组件作用域

@Scope有4个取值:

1)singleton:单例,默认

注意:在单例模式下,在AOC容器被启动的时候,就会创建对象放到IOC容器中,以后每次获取就是直接从容器中拿

2)prototype:多例

注意:在多例模式中,IOC容器启动并不会创建对象,对象的创建是在每次获取对象的时候调用方法创建对象

3)request:同一个请求创建一个实例

4)session:同一个session创建一个实例

1.4 @Lazy——懒加载

所谓的懒加载就是在容器启动的时候,不要创建对象,等第一次使用(获取)Bean的时候,创建对象并初始化。

注意:懒加载是针对单例模式的。

例如:

测试类:

public static void main(String[] args) {
	ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig2.class);
	System.out.println("容器创建完成");
	// 通过类型获取
	//Person person = context.getBean(Person.class);
}

 

运行结果如下,“给容器注入person”并没有打印

然后,取消调用注释,再次运行,如图:

1.5 @Conditional——按照条件注册

        该注解的作用是按照一定条件进行判断,满足条件给容器中注册bean

例如:如果当前系统环境是Windows就注入window的bean,如果是Linux的就注入Linux的bean

1)定义条件

Windows条件

package com.bjc.conditation;

import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;

/** 条件
 * @author Administrator
 *
 */
public class WindowsCondication implements Condition{

	@Override
	public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
		// 获取系统环境变量
		Environment env = context.getEnvironment();
		String os = env.getProperty("os.name");
		if(os.contains("Windows"))
			return true;
		return false;
	}

}

Linux条件

package com.bjc.conditation;

import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;

/** 条件
 * @author Administrator
 *
 */
public class LinuxCondication implements Condition{

	@Override
	public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
		Environment env = context.getEnvironment();
		String os = env.getProperty("os.name");
		if(os.contains("Linux"))
			return true;
		return false;
	}

}

2)使用注解

3)测试

public static void main(String[] args) {
	ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig2.class);
	String[] names = context.getBeanDefinitionNames();
	for(String name : names) {
		System.out.println(name);
	}
}

输出结果:

通过输出结果可以知道,只有Windows的bean注入到容器中了,Linux的没有。

注意:@Conditional可以放在配置类上,也可以放在方法上

1.6 @Import

        通过前面的学习我们知道将一个类注册进spring容器有几种方法:

1)注解+包扫描

2)@Bean注册,可以将第三方的bean注册进容器中

        接下来,我们再来看看另一种方式,通过@Import给容器中快速的导入一个组件,既然有了@Bean为什么还需要@Import了,这是因为@Bean注册的时候,如果对于很简单的业务,比如仅仅是new一个对象,使用@Bean就显得比较麻烦了,这时候我们就可以使用户@Import来注册组件到容器中。

     查看该注解的源码,可以发现,该注册的使用有三种方式,如图:

1.6.1 给容器中快速导入一个组件

语法:@Import(要导入的类的Class)

使用起来很简单,如图:

测试:

注意:

1. @Import也可以导入多个

2. 使用import注册组件,容器中就会自动注册这个组件,id默认是类的全名称。

1.6.2 ImportSelector方式

        我们除了可以通过@Import快速导入组件之外,还可以实现ImportSelector接口,返回需要导入组件的全类名数组

例如;

1)实现ImportSelector接口

package com.bjc.conditation;

import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;

// 自定义逻辑返回需要导入的组件
public class MyImportSelector implements ImportSelector {

	// 返回值就是要导入到容器中的组件全类名
	// AnnotationMetadata:当前标注@Import注解的类的所有注解信息
	@Override
	public String[] selectImports(AnnotationMetadata importingClassMetadata) {
		return new String[] {"com.bjc.pojo.Blue","com.bjc.pojo.Yellow"};
	}
}

2)将该类注册进spring

3)测试运行

1.6.3 ImportBeanDefinitionRegistrar

        该方式是手动的将需要的类注册进spring容器中,使用起来也很简单,只需要实现ImportBeanDefinitionRegistrar接口并写入业务逻辑即可。

1)实现ImportBeanDefinitionRegistrar

package com.bjc.conditation;

import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;

import com.bjc.pojo.Red;

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
	/**
	 *   AnnotationMetadata: 当前类的注解信息
	 *   BeanDefinitionRegistry:     BeanDefinition注册类
	               把所有需要注册进容器中的bean,都可以通过BeanDefinitionRegistry的registerBeanDefinition方法手动注册
	 */
	@Override
	public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
		boolean b = registry.containsBeanDefinition("com.bjc.pojo.Blue");
		if(b) {
			// 注册一个bean并指定名字
			registry.registerBeanDefinition("red", new RootBeanDefinition(Red.class));
		}
	}
}

2)在@Import中引入该类

3)测试

1.7 FactoryBean

默认获取到的是工厂bean调用getObject创建的对象
要获取工厂Bean本身,我们需要给id前面加上&即可,例如:&colorFactoryBean,因为在FactoryBean的接口中有定义前缀为&

1)定义Factory实现FactoryBean

package com.bjc.conditation;

import org.springframework.beans.factory.FactoryBean;

import com.bjc.pojo.Colors;

public class ColorFactoryBean implements FactoryBean<Colors> {

	@Override
	public Colors getObject() throws Exception {
		return Colors.class.newInstance();
	}

	@Override
	public Class<?> getObjectType() {
		// TODO Auto-generated method stub
		return Colors.class;
	}

	/**
	 *  返回值true表示单例,false表示多例
	 */
	@Override
	public boolean isSingleton() {
		// TODO Auto-generated method stub
		return true;
	}

}

2)在配置类中配置factory 

@Bean
public ColorFactoryBean colorFactoryBean() {
	return new ColorFactoryBean();
}

3)测试

3.1 测试是否为单例

3.2 测试默认获取到的类是哪个

如图:显然是FactoryBean中返回的Color对象

3.3 测试获取FactoryBean本身

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值