spring注解编程-IOC
配置类&Bean注解
@Configuration
加了这个注解的类就相当于传统的一个applicationContext-xxx.xml ,告诉spring这是一个注解类
最初的Spring只支持xml方式配置Bean,从Spring 3.0起支持:基于Java类的配置方式,Java开发者可以从标签语法里解放了出来。
配置放在哪里
- 配置类要与启动类同包或者在其任意子包下,满足spring的扫描条件,配置才能被成功加载。
- 如果不满足上述同包或子包条件,需要如下办法:
在resources目录下新建META-INF目录,并在该目录下创建spring.factories文件。spring.factories文件内容如下:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.ft.xx.config.DruidConfig
第一行是spring自动配置的路径,第二行是我们自己要指定加载配置类的路径。
@Bean
@Bean注解的作用与<bean/>标签相同
在标注了@Configuration的类里面的方法上面打上@bean就相当于在applicationContext-xxx.xml配置的一个
<bean id="userDao" class="cn.itsource.dao.UserDao">
简单粗暴理解:@Configuration标注的类等同于一个xml文件,@Bean标注的方法等同于xml文件里的一个<bean/>标签
Bean的名字默认就是方法名,如果想改方法名使用@Bean(“beanName”)
@Configuration
public class MainConfig {
@Bean("userDao") //指定bean的名字
public UserDao userDao(){ // 方法必须是public。
return new UserDao();
}
}
通常来说,我们均会把@Bean标注的方法写在@Configuration标注的类里面来配合使用。
但其实,没有@Configuration注解的类中的@Bean也会被扫描到,只是一般不这么用。
是否通过@Configuration,官方管这两种模式分别叫:Full @Configuration和lite @Bean mode。
Full模式和Lite模式
Full模式和Lite模式均是针对于Spring配置类而言的,和xml配置文件无关。
- Full模式:
运行时会给该类生成一个CGLIB子类放进容器,有一定的性能、时间开销(这个开销在Spring Boot这种拥有大量配置类的情况下是不容忽视的,这也是Spring 5.2新增了proxyBeanMethods属性的最直接原因)
正因为被代理了,所以@Bean方法 不可以是private、不可以是final
- Lite模式:
官方定义为:在没有标注@Configuration的类里面有@Bean方法就称为Lite模式的配置。透过源码再看这个定义是不完全正确的,而应该是有如下case均认为是Lite模式的配置类:
类上标注有@Component注解
类上标注有@ComponentScan注解
类上标注有@Import注解
类上标注有@ImportResource注解
若类上没有任何注解,但类内存在@Bean方法
以上case的前提均是类上没有被标注@Configuration,在Spring 5.2之后新增了一种case也算作Lite模式:
标注有@Configuration(proxyBeanMethods = false),注意:此值默认是true,需要显示改为false才算是Lite模式。
自Spring5.2(对应Spring Boot 2.2.0)开始,内置的几乎所有的@Configuration配置类都被修改为了@Configuration(proxyBeanMethods = false),以此来降低启动时间,为Cloud Native继续做准备。
Lite模式下:
配置类本身不会被CGLIB增强,放进IoC容器内的就是本尊
不能直接声明@Bean之间的依赖:配置类内部不能通过方法调用来处理依赖,每次生成的都是一个新实例而并非IoC容器内的单例。(full 模式时,一个@Bean的方法调用了另一@Bean方法,被调用的bean并不会执行两次噢,会保证单例~~)
配置类就是一普通类,所以@Bean方法可以使用private/final/static等进行修饰
- @Bean(initMethod=”init”,destroyMethod=”destroy”)定义,在构造之后执行init,在销毁之前执行destroy。
注入Bean
AnnotationConfigApplicationContext
基于applicationContext-xxx.xml创建的bean是通过 ClassPathXmlApplicationContext(“xxx.xml”)来获取。
基于注解配置类创建的bean,要通过 new AnnotationConfigApplicationContext(MainConfig.class);来获取。
public class Application {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
UserDao userDao = context.getBean(UserDao.class);
}
}
- 前提:
@Configuration
public class MainConfig {
@Bean
public UserDao userDao() {
return new UserDao();
}
}
此种方式常用于将第三方jar中的某个类,注册为当前项目的一个bean。
- 或者:
@Import(UserDao.class)
@Configuration
public class MainConfig {
}
- 或者:
@ComponentScan
@Configuration
public class MainConfig {
@Bean
public UserDao userDao() {
return new UserDao();
}
}
@Component
public class userDao {
}
@Repository,@Service,@Controller,@Component
使用注解的类会被创建出一个bean
- @ComponentScan
使用 @Component等创建的bean,需要配置使用@ComponentScan扫描才能识别到这些bean。
@ComponentScan 其实就是:
<context:component-scan base-package="cn.itsource"/>.
@ComponentScan 可以指定多个路径,还可以指定路径下只包含部门类、排除部分类
@Lazy
指定bean懒加载,容器创建时不加载,第一次使用时才加载
@Scope
指定bean作用域
@Conditional-按照条件注册
类和方法上都可以
如:@Conditional(value = WindowsCondition.class) 表示根据当前os.name的环境来判断是否创建bean
@Import
@Import(Class<?>[] value)
可用于导入任意指定的如第三方jar中的类并自动创建bean到当前容器,也可以导入配置类,则配置类中所有@Bean的bean都能注册到当前容器。
对于第三方jar中的bean,并不能直接注册到当前容器,想要办到:
- 这个bean的包名是当前容器启动类的子包(哪怕在不同模块,只要编译后是子包就行)
- 不是子包的话,可以使用包扫描指定额外的包的范围,@ComponentScan(),如果有多个额外的包就要写很多。
- 不是子包的话,可以使用@Import指定具体的类,每一个想注册进容器的类都要单独写,也会很多。
- 由第三方jar提供一个@Enable开头的注解,这个注解就是对@Import(自己的类Class),的封装,对每一个想注册进当前容器的bean,都需要添加一个对应的@Enable开头的注解。
- @EnableAutoConfig
@Import(Class value)
导入自定义的ImportSelector选择器,重写selectImports()方法,方法返回需要被导入的组件的类全限定名数组 -springboot底层用的多
@Import(MyImportSelector.class)
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(Anno..) {
return new String[]{"com.example.User","com.example.Person"}
}
}
- Springboot 就是使用 ImportSelector 的方式,详细过程整理在 Spring Boot一篇中。
@Import(Class value)
通过bean定义注册器手动项目spring中容器中注册。 也需要覆写方法registerBeanDefinitions(),其中第二个参数,可以用于向我们的IOC容器注册bean。
@Import(MyImportBeanDefinitionRegistrar.class)
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public viod registerBeanDefinitions(Anno... anno, BeanDefinitionRegistry registry) {
BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(User.class).getBeanDefinition();
registry.registerBeanDefinition("user", beanDefinition) // (String beanName, BeanDefinition beanDefinition)
}
}
生命周期管理
- 方式一 : @Bean(initMethod = “init”,destroyMethod = “destory”)
- 方式二 : 实现InitializingBean和DisposableBean接口
InitializingBean:afterPropertiesSet(), 设置完属性后调用
DisposableBean:destroy(), 容器关闭时调用
- 方式三 : 注解@PostConstruct @PreDestroy
@Component
public class Test {
public Test(){
System.out.println("Test ....create....");
}
@PostConstruct
public void init(){
System.out.println("Test ....init....");
}
@PreDestroy
public void destory(){
System.out.println("Test ....destory....");
}
}
- 方式四 : BeanPostProcessor(接口),bean后置处理器
- Object postProcessBeforeInitialization:初始化之前调用
- Object postProcessAfterInitialization:初始化之后调用
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
//初始化之前调用
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessBeforeInitialization "+beanName);
return bean;
}
//初始化之后调用
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessAfterInitialization "+beanName);
return bean;
}
}
BeanFactory 和 FactoryBean
区别:BeanFactory是个Factory,也就是IOC容器或对象工厂,FactoryBean是个Bean。在Spring中,所有的Bean都是由BeanFactory(也就是IOC容器)来进行管理的。但对FactoryBean而言,这个Bean不是简单的Bean,而是一个能生产或者修饰对象生成的工厂Bean,它的实现与设计模式中的工厂模式和修饰器模式类似
最常用MVC注解
@PathVariable 放置在参数前,用来接受路径参数。
@RequestBody 允许request的参数在request体中,而不是在直接链接在地址的后面。此注解放置在参数前。
@ResponseBody 将返回值放在response体内。返回的是数据而不是页面
@RestController 组合注解,组合了@Controller和@ResponseBody,当我们只开发一个和页面交互数据的控制层的时候可以使用此注解。
@RequestMapping 用来映射web请求(访问路径和参数)。可以注解在类和方法上,注解在方法上的路径会继承注解在类上的路径。
同时支持Serlvet的request和response作为参数,也支持对request和response的媒体类型进行配置。其中有value(路径),produces(定义返回的媒体类型和字符集),method(指定请求方式)等属性。