给容器中注册组件的注解
@Controller
用在类上表明一个控制器类
@Service
用在类上表明一个Service层类
@Repository
用在类上表明一个Repository类
@Component
用在类上表明一个Bean对象,可以代替@Controller、@Service、@Repository,只是用不同的注解表明不同的业务逻辑层 语义更加清晰
@Configuration+@Bean
@Configuration
public class Conf {
@Bean
public Person person() {
return new Person();
}
}
@Configuration用在类上表明一个配置类,@Bean用在方法上,表明要注入的组件,配置类本身也会被注入到IOC中。
FactoryBean接口
public class Shape {
private String type;
public Shape() {
}
public Shape(String type) {
this.type = type;
}
}
public class ShapeFactoryBean implements FactoryBean<Shape> {
@Override
public boolean isSingleton() {
return false;
}
@Override
public Shape getObject() throws Exception {
return new Shape();
}
@Override
public Class<?> getObjectType() {
return Shape.class;
}
}
@Configuration
@ComponentScan(value = "com.exapmle.service.test12")
public class Conf {
@Bean
public ShapeFactoryBean shapeFactoryBean() {
return new ShapeFactoryBean();
}
}
public class test12 {
public static void main(String[] args) {
AnnotationConfigApplicationContext ioc = new AnnotationConfigApplicationContext(com.exapmle.service.test12.Conf.class);
String[] beanDefinitionNames = ioc.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
}
Object shapeFactoryBean = ioc.getBean("shapeFactoryBean");
System.out.println(shapeFactoryBean);
}
}
//...忽略其他容器本身创建的bean
conf
shapeFactoryBean
com.exapmle.service.test12.Shape@64c87930
可以看到获取bean的名称的时候是shapeFactoryBean,但是实际的bean是Shape对象,所以getBean()的时候实际获取的是调用shapeFactoryBean对象getObject()方法创建的bean对象
那如果获取工厂bean的本身?
public class test12 {
public static void main(String[] args) {
AnnotationConfigApplicationContext ioc = new AnnotationConfigApplicationContext(com.exapmle.service.test12.Conf.class);
// 给bean的名称加上&
Object shapeFactoryBean = ioc.getBean("&shapeFactoryBean");
// com.exapmle.service.test12.ShapeFactoryBean@64c87930
System.out.println(shapeFactoryBean);
}
}
可以看BeanFactory源码中,就默认给工厂bean名称加上&前缀
@Import
@Configuration
@Import({AAA.class, BBB.class})
public class Conf {
@Bean
public Person person() {
return new Person();
}
}
@Import用于导入第三方库中的类,因为第三方类可能并没有加注解,就用全限定类名导入。包括自定义的组件也可以用Import方式导入。组件的名称是全限定类名。
ImportSelector接口
ImportSelector的类也可以用于导入组件
/**
* 自定义需要返回的组件
*/
public class MyImportSelector implements ImportSelector {
/**
* 返回导入到容器中的组件全类名
* @param importingClassMetadata 当前标注了@Import注解的类的所有注解信息
* @return
*/
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{"com.exapmle.test13.Blue", "com.exapmle.test13.Red"};
}
}
@Configuration
@Import({MyImportSelector.class})
public class Conf {
}
// 组件的名称
com.exapmle.test13.Blue
com.exapmle.test13.Red
ImportBeanDefinitionRegistrar接口
ImportBeanDefinitionRegistrar接口的功能更加多,可以自定义组件的名称,可以根据不同的条件选择要注入的bean
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
/**
*
* @param importingClassMetadata 当前类的注解信息
* @param registry BeanDefinition注册类
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 注意这里的组件名称,如果是用Import注解导入的或者
// 是实现了ImportSelector接口的类导入的,则应该是全限定类名作为
// 类的名称,这里我用的是@ComponentScan+@Component导入的bean
boolean red = registry.containsBeanDefinition("red");
boolean blue = registry.containsBeanDefinition("blue");
// 如果容器中已经注入了red和blue组件,则把color组件注入进去
if(red && blue) {
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Color.class);
registry.registerBeanDefinition("color", rootBeanDefinition);
}
}
}
@Configuration
@ComponentScan("com.exapmle.test13")
@Import({MyImportBeanDefinitionRegistrar.class})
public class Conf {
}
// 组件的名称
conf
blue
red
color
@Conditional+Condition接口
按照条件判断给容器中注入组件,用在类上,只有满足条件,这个类中的所有bean才能注入到IOC中,作用在方法上,只有满足条件,这个方法生成的bean才能注入到IOC中
public class LinuxCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment environment = context.getEnvironment();
String property = environment.getProperty("os.name");
assert property != null;
if(property.contains("Linux")) {
return true;
}
return false;
}
}
public class WindowsCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment environment = context.getEnvironment();
String property = environment.getProperty("os.name");
assert property != null;
if(property.contains("Windows 10")) {
return true;
}
return false;
}
}
public class SysType {
private String name;
private Integer version;
public SysType(String name, Integer version) {
this.name = name;
this.version = version;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", version=" + version +
'}';
}
}
@Configuration
public class Conf {
@Conditional({WindowsCondition.class})
@Bean
public SysType sysWin() {
return new SysType("windows", 10);
}
@Conditional({LinuxCondition.class})
@Bean
public SysType sysLinux() {
return new SysType("linux", 5);
}
}
public class Test14 {
public static void main(String[] args) {
AnnotationConfigApplicationContext ioc = new AnnotationConfigApplicationContext(com.exapmle.service.test14.Conf.class);
ConfigurableEnvironment environment = ioc.getEnvironment();
String os = environment.getProperty("os.name");
System.out.println(os);
String[] beanDefinitionNames = ioc.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
}
}
}
只有名称为sysWin的bean被注入到IOC中了:
Windows 10
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
conf
sysWin
指定包扫描的注解
@ComponentScan
@Configuration
@ComponentScan(value="cn.test", excludeFilters={
@Filter(type=FilterType.ANNOTATION, classes={Controller.class, Service.class})
})
排除Controller和Service注解
@Configuration
@ComponentScan(value="cn.test", includeFilters={
@Filter(type=FilterType.ANNOTATION, classes=
{Controller.class, Service.class},
useDefaultFilters=false)
})
只包含Controller和Service注解,useDefaultFilters=false表示禁用默认过滤规则,因为默认是全部扫描
@ComponentScans
用@ComponentScans指定多种包扫描规则:
@Configuration
@ComponentScans(
value={
@ComponentScan(value="cn.test", includeFilters={
@Filter(type=FilterType.ANNOTATION, classes=
{Controller.class, Service.class},
useDefaultFilters=false)
})
}
)
指定Bean的作用域和懒加载
@Scope
@Scope设置bean的作用域,常用取值:prototype表明多实例的,只要在使用到类的时候才会创建,每次都会重新创建对象;singleton表明单实例的(默认的),在容器启动的时候就会创建,以后使用直接从容器中取同一个对象;request作用在web中表明同一个请求创建一个实例;session作用在web中表明同一个session创建一个实例。
@Configuration
public class Conf {
@Bean
@Scope("prototype")
public Person person() {
return new Person();
}
}
@Lazy
@Lazy针对单实例bean的懒加载策略,只要在bean第一次使用的时候才创建,以后都用的是同一个对象
@Configuration
public class Conf {
@Bean
@Lazy
public Person person() {
return new Person();
}
}
指定Bean的生命周期
方式一:
bean的生命周期由IOC容器管理,我们可以自定义初始化和销毁方法,由@Bean注解的initMethod 和destroyMethod指定,容器负责进行调用
public class Color {
public void init() {
System.out.println("Color---init");
}
public void destory() {
System.out.println("Color---destory");
}
}
@Configuration
@ComponentScan(value = "com.exapmle.service.test14")
public class Conf {
@Bean(initMethod = "init", destroyMethod = "destory")
public Color color() {
return new Color();
}
}
public class Test14 {
public static void main(String[] args) {
AnnotationConfigApplicationContext ioc = new AnnotationConfigApplicationContext(com.exapmle.service.test14.Conf.class);
Object color = ioc.getBean("color");
System.out.println(color);
ioc.close();
}
}
Color---init
com.exapmle.service.test14.Color@1e67a849
Color---destory
对于单实例的bean,容器关闭的时候bean就会被销毁,但是对于多实例的bean,销毁是由GC管理的
方式二:
实现InitializingBean和DisposableBean接口,有容器自动调用
public class Color implements InitializingBean, DisposableBean {
@Override
public void destroy() throws Exception {
System.out.println("DisposableBean-destory");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("InitializingBean-afterPropertiesSet");
}
public void selfInit() {
System.out.println("selfInit");
}
public void selfDestory() {
System.out.println("selfDestory");
}
}
InitializingBean-afterPropertiesSet
selfInit
com.exapmle.service.test14.Color@667a738
DisposableBean-destory
selfDestory
方式三:
JSR250提供的两个注解:@PostConstruct和@PreDestory
public class Color implements InitializingBean, DisposableBean {
@Override
public void destroy() throws Exception {
System.out.println("DisposableBean-destory");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("InitializingBean-afterPropertiesSet");
}
public void selfInit() {
System.out.println("selfInit");
}
public void selfDestory() {
System.out.println("selfDestory");
}
@PostConstruct
public void init2() {
System.out.println("init2---@PostConstruct");
}
@PreDestroy
public void destory2() {
System.out.println("init2---@PreDestroy");
}
}
init2---@PostConstruct
InitializingBean-afterPropertiesSet
selfInit
com.exapmle.service.test14.Color@400cff1a
init2---@PreDestroy
DisposableBean-destory
selfDestory
给属性赋值的注解@Value、@PropertySource
public class Person{
@Value("张三")
private String name;
@Value("#{10*2}")
private Integer age;
}
// pro.properties
person.name = "张三"
@PropertySource(value={"classpath:/pro.properties"})
@Configuration
public class Conf {
@Bean
@Scope("prototype")
public Person person() {
return new Person();
}
}
public class Person{
@Value("${person.name}")
private String name;
}
@Value注解的三种用法:
- 基本数值 @Value(“张三”)
- SpEL表达式 @Value("#{10*2}")
- ${}取运行环境变量中的值,结合@PropertySource注解指定配置文件的路径,读取配置文件中的键值保存到运行环境中
自动装配的注解
自动装配Spring利用DI完成对IOC容器中各个组件的依赖关系的赋值
@Autowired
@Autowired:自动按照类型注入,如何匹配上多个,再按照属性的名称名称和bean的id进行匹配。
该注解除了用在属性上,还可以作用在方法上、构造器、参数上:
@Component
public class Boss {
Car car;
// 自动获取IOC容器中的Car
@Autowired
public Boss(Car car) {
this.car = car;
}
@Autowired
public void setCar(Car car) {
this.car = car;
}
public void setCar(@Autowired Car car) {
this.car = car;
}
}
@Autowired(required=false) // 表示如果没有找到组件,可以不装配
@Qualifier
@Qualifier:按照类型注入的基础上再按照指定的bean的id匹配,不能单独用在类的属性上(要和@Autowired注解一起使用),但是可以单独用于方法参数注入。
属性:value用于指定注入bean的id
@Primary
@Primary:这个注解不是放在属性上,而是放在配置类的打上了@Bean注解的方法上,表明如果使用@Autowired装配bean的时候,首选这个bean进行装配。
上面三个注解是Spring规范中的注解,下面这两个是Java规范的注解
@Resource
@Resource:name属性用于指定注入bean的id;type属性用于指定注入bean的类型;name和type可以同时使用,也可以单独使用,也可以都不使用,默认先按照类型注入,如果匹配上多个,再按照属性名称和bean的id进行匹配。
它的默认方式和@Autowired的方式不同的地方在于,对于@Resource方式,@Primary注解是不起作用的,他也不支持类型@Autowired中required=false的功能
@Inject
需要导入依赖,和@Autowired功能一样
Aware子接口
如果自定义组件要使用Spring容器底层的组件,比如ApplicationContext、BeanFactory,只需要让我们自定义组件实现Aware的子接口并重写方法就行,容器创建这个组件的时候,会判断这个组件是否实现了相关接口,如果是,会自动调用这个方法完成注入。
以ApplicationContextAware接口举例:
@Component
public class Color implements ApplicationContextAware {
public ApplicationContext applicationContext;
/**
* 容器创建bean的时候会自动调用这个方法
* @param applicationContext
* @throws BeansException
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
@Profile
Spring提供的可以根据当前运行环境,动态的激活和切换一系列组件的功能。比如我们需要根据开发环境、测试环境、生成环境引入不同的数据库组件。
@Profile可以作用在方法上和类上,作用在类上,表明只有含有指定运行环境变量时,这个类才会生效。
@Configuration
@ComponentScan("com.exapmle.test13")
public class Conf {
@Profile("blue")
@Bean
public Blue blue() {
return new Blue();
}
@Profile("red")
@Bean
public Red red() {
return new Red();
}
}
public class Test {
public static void main(String[] args) {
AnnotationConfigApplicationContext ioc = new AnnotationConfigApplicationContext(Conf.class);
String[] beanDefinitionNames = ioc.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
}
}
}
当前不会注册blue和red组件,添加运行环境变量-Dspring.profiles.active=blue
:
也可以通过下面这种方式添加运行环境变量:
public class Test {
public static void main(String[] args) {
AnnotationConfigApplicationContext ioc = new AnnotationConfigApplicationContext();
ConfigurableEnvironment environment = ioc.getEnvironment();
environment.setActiveProfiles("blue");
ioc.register(Conf.class);
ioc.refresh();
String[] beanDefinitionNames = ioc.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
}
}
}