@Configuration
声明当前类是一个配置类(相当于一个Spring配置的xml文件)
@Configuration不可以是final类型;
@Configuration不可以是匿名类;
嵌套的configuration必须是静态类。
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
@ComponentScan
- 自定扫描路径下边带有@Controller,@Service,@Repository,@Component注解加入spring容器
- 通过includeFilters加入扫描路径下没有以上注解的类加入spring容器
- 通过excludeFilters过滤出不用加入spring容器的类
- 自定义增加了@Component注解的注解方式
package com.org.config;
import org.springframework.context.annotation.ComponentScan;
@ComponentScan(value = "com.org.ums.controller")
public class BeanConfig {
}
@ComponentScans
可包含多个@ComponentScan
示例:@ComponentScans(value = {@ComponentScan,@ComponentScan,@ComponentScan})
@ComponentScans({
@ComponentScan(basePackages = {
"com.org.sxd.controller"
}, includeFilters = {
// 仅仅使用了 @RestController 注解声明的类
@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = { RestController.class })
}, useDefaultFilters = false),
@ComponentScan(basePackages = {
"com.org.springboot.controller2"
}, excludeFilters = {
// 过滤使用了 @MyAnnotation 注解的类
@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = { MyAnnotation.class })
})
})
public class MyConfig {
}
@Lazy
实际开发过程中并不是所有的bean都需要一开始就被创建,有些我们可以在使用的时候进行创建,此时可以使用@Lazy实现懒加载。
单实例bean:默认在容器启动的时候创建对象;
懒加载:容器启动不创建对象。第一次使用(获取)Bean创建对象,并初始化;
@Lazy注解作用于类上时,通常与@Component及其衍生注解配合使用。
@Lazy注解作用于方法上时,通常与@Bean注解配合使用。
//@Lazy与@component配合使用
@Component
@Lazy
public class UserService {
public UserService(){
System.out.println("userService创建了");
}
}
@Configuration
@ComponentScan(basePackages = "lazyDemo")
public class SpringConfig {
@Bean
@Lazy
// @Lazy与@Bean注解配合使用
public UserService userService1(){
return new UserService();
}
}
public class TestLazyDemo {
private AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
@Test
public void testLazyDemo() throws SQLException {
System.out.println("容器初始化完成");
UserService userService = (UserService) context.getBean("userService");
UserService userService1 = (UserService) context.getBean("userService1");
}
}
@Conditional
它的逻辑语义可以作为"If…then…else…"来对bean的注册起作用。
SpringBoot 模块大量的使用@Conditional 注释,我们可以将Spring的@Conditional注解用于以下场景:
- 可以作为类级别的注解直接或者间接的与@Component相关联,包括@Configuration类;
- 可以作为元注解,用于自动编写构造性注解;
- 作为方法级别的注解,作用在任何@Bean方法上。
Condition接口
我们需要一个类实现Spring提供的Condition接口,它会匹配@Conditional所符合的方法,然后我们可以使用我们在@Conditional注解中定义的类来检查。
public class MqttCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
System.out.println("MqttCondition。。。。");
//1、能获取到ioc使用的beanfactory
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
//2、获取类加载器
ClassLoader classLoader = context.getClassLoader();
//3、获取当前环境信息
Environment environment = context.getEnvironment();
String isOpen = environment.getProperty("mqtt.isOpen");
return Boolean.valueOf(isOpen);
}
}
@Configuration
public class MqttConfig {
@Autowired
private MqttAcceptClient mqttAcceptClient;
@Conditional(MqttCondition.class)
@Bean
public MqttAcceptClient getMqttAcceptClient(){
mqttAcceptClient.connect();
//mqttAcceptClient.subscribe("test_queue",0);
return mqttAcceptClient;
}
}
@Conditional 注解可以作用在类上,表示此类下面所有的bean满足条件后都可以进行注入,通常与@Configuration注解一起使用。
上图满足MqttCondition.class的matches方法为true的时候注入相应的Bean。
@Value
该注解的作用是将我们配置文件的属性读出来,有@Value(“${}”)和@Value(“#{}”)两种方式。
区别:@Value(“${}”)注入的为配置文件的内容,@Value(“#{}”)则为SpEL表达式对应的内容。 那个default_value,就是前面的值为空时的默认值。注意二者的不同,#{}里面那个obj代表对象。
@Getter
@Setter
@Component
@Configuration
public class MqttConfiguration {
@Value("${spring.mqtt.broker}")
private String host;
@Value("${spring.mqtt.clientId}")
private String clientId;
@Value("${spring.mqtt.username}")
private String username;
@Value("${spring.mqtt.password}")
private String password;
@Value("${spring.mqtt.timeout}")
private int timeout;
@Value("${spring.mqtt.KeepAlive}")
private int KeepAlive;
@Value("${spring.mqtt.topics}")
private String topics;
@Value("${spring.mqtt.isOpen}")
private Boolean isOpen;
@Value("${spring.mqtt.qos}")
private int qos;
}
spring:
mqtt:
broker: tcp://192.168.0.168:1883
clientId: mqtt_client #客户端的id
username: admin
password: public
timeout: 2000
KeepAlive: 20
topics: test_queue #主题
qos: 1 #心跳包级别
isOpen: true
@Value(“#{}”)在使用的时候我们需要准备一个实体类并注入到Bean当中,再通过@Value(“#{}”)去获取相应的属性值。
这里需要注意一点:
最好将配置文件交给sping加载,不要交给springMVC加载 避免出现错误,因为web.xml配置时spring的监听先启动,springMVC的Dispatcherservlet接收到请求时初始化springMVC的配置文件。
@Autowired
默认按类型装配,找不到或者找到多个则报错。如果要按名称装配,需要结合Spring另外一个注解Qualifier("name")使用。默认必须装配requred=true,如果可以为空,可以设置为false,在Spring4+结合jdk8+的情况下还可以使用Optional和false同等的效果,如:
@Autowired
private Optional<UserService> userService;
需要注意的是@Autowired用在service上,但是实际注入的是实现类,通过bytype的方式进行装配。
这里顺便简单说一下@Resource和@Inject作为了解。虽然他们不是spring的注解
**@Resource可以根据name和type进行装配
**@Inject和@Autowired差不多,可以实现完全替代,没有required属性,而且必须有bean存在。如果使用byname装配方式则要搭配@Named注解使用
@Scope
@Scope注解是springIoc容器中的一个作用域,在 Spring IoC 容器中具有以下几种作用域:基本作用域singleton(单例)、prototype(多例),Web 作用域(reqeust、session、globalsession),自定义作用域。
singleton
单例模式(默认):全局有且仅有一个实例prototype
原型模式:每次获取Bean的时候会有一个新的实例request
: request表示该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前http request内有效session
:session作用域表示该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前http session内有效global session
: global session作用域类似于标准的HTTP Session作用域,不过它仅仅在基于portlet的web应用中才有意义
直接使用字符串容易出问题,spring有默认的参数:
ConfigurableBeanFactory.SCOPE_PROTOTYPE
,即“prototype”ConfigurableBeanFactory.SCOPE_SINGLETON
,即“singleton”WebApplicationContext.SCOPE_REQUEST
,即“request”WebApplicationContext.SCOPE_SESSION
,即“session”
使用方法:
@Scope直接加在bean对象方法上,如:
/**
* 定义一个bean对象
* @return
*/
@Scope //@Scope(value = "prototype")
@Bean(value = "user0", name = "user0", initMethod = "initUser", destroyMethod = "destroyUser")
public User getUser() {
System.out.println("创建user实例");
return new User("张三", 18);
}
大多数业务中使用singleton就可以了,感觉这也是为啥他默认设置为单例类型的原因,
singleton保证了全局是一个实例,但是如果实例为非静态变量的时候,会导致线程安全的问题。
当设置为类型为prototype的时候,每次连接请求,都会生成一个bean实例,也会导致一个问题,当请求数越多,性能会降低,因为创建的实例,导致GC频繁,gc时长会增加。
直接在controller上设置多例是正常的,但是如果在controller层调用service层,其中controller层是默认的单例的话,service层设置多例会导致多例失效。因为:
虽然Service是多例的,但是Controller是单例的。如果给一个组件加上@Scope("prototype")
注解,每次请求它的实例,spring的确会给返回一个新的。问题是这个多例对象Service是被单例对象Controller依赖的。而单例服务Controller初始化的时候,多例对象Service就已经注入了;当你去使用Controller的时候,Service也不会被再次创建了(注入时创建,而注入只有一次)。
@Profile
可以实现不同环境(开发、测试、部署等)使用不同的配置。
在spring使用DI来依赖注入的时候,能够根据当前制定的运行环境来注入相应的bean。最常见的就是使用不同的DataSource。
当我们指定组件在哪个环境的情况下才能被注册到容器中,不指定,任何环境下都能注册这个组件
- 1)加了环境标识的bean,只有这个环境被激活的时候才能注册到容器中。默认是default环境
- 2)写在配置类上,只有是指定的环境的时候,整个配置类里面的所有配置才能开始生效
- 3)没有标注环境标识的bean,在任何环境下都是加载的;