Spring全注解开发
- @Configuration和 @Bean注解
- @ComponentScan
-
- 扫描时指定排除和包含哪些注解,一定要禁用掉默认的扫描规则
- 可以按照类型排除或者包含要扫描的组件:
- 自定义规则,指定要包含或者要排除的组件:
- @Scope
- @Lazy ---懒加载
- @Conditional ({condition})
-
- 给容器中注册组件方式:
- @Import
-
- 自定义选择器导入组件
- ImportBeanDefinitionRegistrar 手动注册Bean到容器
- 使用Spring提供的FactoryBean(工厂Bean)来注册容器
- Bean的生命周期(注解版本),通过@Bean注解指定Bean的初始化和销毁方法
-
- Bean的生命周期小结:
- 对应Bean继承InitializingBean和DisposableBean接口,来指定Bean的初始化和销毁方法
- 使用JSR250的@PostConstruct和@PreDestory注解指定bean的初始化和销毁方法
- Bean的后置处理器
-
- bean的后置处理器底层原理
- 属性赋值
-
- @Value
-
- @Value从配置文件取出来赋值给属性的中文如果出现乱码,是因为配置文件的编码问题,解决方式如下:
- @Value也可以加在方法的参数上,从配置文件中取出值,赋值给参数
- @PropertySource配置的用法
-
- @PropertySource读取外部配置文件中的k/v保存到运行的环境变量中,加载完外部配置文件中的值后使用${}取出配置文件中的值
- 获取环境变量中的值⇒ ioc.getEnvironment.getProperty
- 自动装配
-
- @Autowired和@Qualifier
- @Primary
-
- @Value和@bean执行顺序问题
- @AutoWired所在类必须处于容器之中,该注解才会生效,原因如下:
- @Resource和@Inject
-
- @Autowired和@Resource,@Inject的区别:
- 底层原理:
- @Autowired和@Resource,@Inject等注解放在构造方法,普通方法上:
- @Autowired放在属性,方法,参数,构造器上
- 1.标注在方法位置: @Bean+方法参数:参数从容器中获取,默认不写@Autowired效果一样,都能自动装配
-
- @Bean标注的方法创建对象的时候,方法参数的值从容器中获取
- 2.标注在构造器上,如果组件只有一个有参构造器,这个有参构造器的@Autowired可以省略,参数位置的组件还是从可以自动从容器中获取
-
- 如果有参构造上面不加@Autowired注解或者加在了有参构造的参数上,并且无参构造和有参构造同时存在,那么ioc容器创建bean时,默认调用无参构造,当只有有参构造的时候,才会调用有参构造
- 自定义组件中注入Spring底层组件
-
- 获取配置文件中的值目前有三种方法: 1.@Value注解 2. 继承EmbeddedValueResolverAware,使用StringValueResolver 值解析器 3.Environment对象
- 必须将配置文件中的值,放入到环境中后,才能取出来值,通过@PropertySource可以将配置文件中的值,放到环境中
- @Profile
-
- 加了环境标识的bean,只有在这个环境被激活的时候才能注册到容器中,默认是default环境
- 不指定环境的情况下:
- 1.使用命令行动态参数,在虚拟机参数位置加载: -Dspring.profiles.active=test,指定运行环境
- 2.通过代码来设置需要激活的环境
- @Profile写在配置类上,只有是指定的环境的时候,整个配置类里面的所有配置才能开始生效
- 没有标注环境标识的bean,在任何环境下都会加载,前提是所在配置类要生效
- 总结: 以上所有注解生效的前提是,对应所在的组件被加入到容器中
@Configuration和 @Bean注解
//配置类等于一起的spring.xml配置文件
@Configuration //标注当前是一个配置类
public class MyConfig
{
//给容器中注册一个Bean,类型为返回值类型
//id默认是用方法名作为id
@Bean(value = "dhy")//指定id
public people getPeople()
{
return new people("大忽悠",18);
}
}
测试:
public class Main
{
//传入的是配置类的位置,一开始是加载配置类,之前是加载配置文件的位置
private ApplicationContext ioc= new AnnotationConfigApplicationContext(MyConfig.class);
@Test
public void test()
{
people peo = ioc.getBean(people.class);
System.out.println(peo);
//获取对应组件在容器中的名字
String[] beans = ioc.getBeanNamesForType(people.class);
for (String bean : beans) {
System.out.println(bean);
}
}
}
@Configuration和 @Bean注解以及补充注解的详细说明,强推!!!
配合@Bean注解一起使用的@Scope注解,感兴趣可以看看
@ComponentScan
以前:
<!--包扫描,只要标注了@Controller,@Service,@Repository,@Component注解都会被扫描进来-->
<context:component-scan base-package="com.controller"></context:component-scan>
配置类:
@Configuration //标注当前是一个配置类
@ComponentScan("com.controller")
public class MyConfig
{ }
controller:
//@Controller("大忽悠")这里同样可以起一个别名
@Controller("大忽悠")//这里同样可以起一个别名
public class peoController
{}
测试:
public class Main
{
//传入的是配置类的位置,一开始是加载配置类,之前是加载配置文件的位置
private ApplicationContext ioc= new AnnotationConfigApplicationContext(MyConfig.class);
@Test
public void test()
{
//获取容器中所有定义的Bean的名字
String[] beans = ioc.getBeanDefinitionNames();
for (String bean : beans) {
System.out.println(bean);
}
}
}
扫描时指定排除和包含哪些注解,一定要禁用掉默认的扫描规则
@Configuration //标注当前是一个配置类
@ComponentScan(value = "com.controller",includeFilters = {
//按照注解排除,排除掉Controller注解
@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = Controller.class)
}, //这里禁用掉默认的扫描规则
useDefaultFilters = false)
public class MyConfig
{ }
@Configuration //标注当前是一个配置类
@ComponentScan(value = "com.controller",excludeFilters = {
//按照注解排除,排除掉Controller注解
@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = Controller.class)
}, //这里禁用掉默认的扫描规则
useDefaultFilters = false)
public class MyConfig
{ }
可以按照类型排除或者包含要扫描的组件:
@Configuration //标注当前是一个配置类
@ComponentScan(value = "com.controller",includeFilters= {
//按照注解包含,排除掉Controller注解
@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = Controller.class),
//按照类型包含要扫描的组件
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,classes = peoController.class)
}, //这里禁用掉默认的扫描规则
useDefaultFilters = false)
public class MyConfig
{ }
自定义规则,指定要包含或者要排除的组件:
继承TypeFilter接口,重写扫描匹配方法:
public class MyTypeFilter implements TypeFilter
{
//MetadataReader:读取到当前正在扫描的类的相关信息
//MetadataReaderFactory: 可以获取到其他任何类的信息
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();
//如果当前类名包含peo,则不扫描
if(className.contains("peo"))
return false;
//否则扫描
return true;
}
}
配置类:
@Configuration //标注当前是一个配置类
@ComponentScan(value = "com.controller",includeFilters= {
//按照自定义规则
@ComponentScan.Filter(type = FilterType.CUSTOM,classes = {MyTypeFilter.class})
}, //这里禁用掉默认的扫描规则
useDefaultFilters = false)
public class MyConfig
{ }
peo没被扫描进来,完美
@Scope
@Configuration //标注当前是一个配置类
@ComponentScan(value = "com.controller")
public class MyConfig
{
@Scope("prototype")
@Bean(value = "dhy")//指定id
public people getPeople()
{
return new people("大忽悠",18);
}
}
prototype:多实例的
ioc容器启动并不会去调用方法创建对象放在容器中,而是每次获取的时候才会调用方法创建对象
singleton:单实例的(默认值)
ioc容器启动时会调用方法创建对象放到ioc容器中,以后每一次获取就是直接从容器(map.get())中拿
request:同一次请求创建一个实例
session:同一个session创建一个实例
@Lazy —懒加载
@Configuration //标注当前是一个配置类
@ComponentScan(value = "com.controller")
public class MyConfig
{
@Lazy
@Bean(value = "dhy")//指定id
public people getPeople()
{
return new people("大忽悠",18);
}
}
懒加载:
单实例bean:默认在容器启动的时候创建对象
懒加载:容器启动不创建对象,第一次使用(获取)Bean创建对象,并初始化
@Conditional ({condition})
@Conditional: 按照一定的条件进行判断,满足条件给容器中注册Bean
LinuxCondition:
//判断是否linux系统
public class LinuxCondition implements Condition
{
/*
* ConditionContext:判断条件能使用的上下文环境
* AnnotatedTypeMetadata:注释信息
*
* */
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
//1.能获取到ioc容器使用的BeanFactory
ConfigurableListableBeanFactory beanFactory = conditionContext.getBeanFactory();
//2.获取类加载器
ClassLoader classLoader = conditionContext.getClassLoader();
//3.获取当前环境信息
Environment environment = conditionContext.getEnvironment();
//4.获取到bean定义的注册类:可以判断容器中的Bean的注册情况,也可以给容器中注册Bean
BeanDefinitionRegistry registry = conditionContext.getRegistry();
//获取当前操作系统名字
String property = environment.getProperty("os.name");
if(property.contains("linux"))
return true;
return false;
}
}
windowsCondition:
//判断是否windows系统
public class WindowsCondition implements Condition
{
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
//3.获取当前环境信息
Environment environment = conditionContext.getEnvironment();
//获取当前操作系统名字
String property = environment.getProperty("os.name");
if(property.contains("Windows"))
return true;
return false;
}
}
配置类:
//标注在类上,则只有符合条件时,当前配置类及其里面注册的Bean才会生效
//@Conditional({WindowsCondition.class})
@Configuration //标注当前是一个配置类
public class MyConfig
{
@Conditional({WindowsCondition.class})
@Bean
public people getWindows()
{
return new people("windows系统",18);
}
@Conditional(LinuxCondition.class)
@Bean
public people getLinux()
{
return new people("Linux系统",18);
}
}
给容器中注册组件方式:
1:包扫描+组件标注注解(@Controller/@Service/@Repository/@Component)
2:@Configuration注解标注类的会被放到容器中,因为AnnotationConfigApplicationContext(MyConfig.class),以前是通过配置文件的方式启动容器,现在是通过加载配置类的方式启动容器,并且@Configuration是个复合注解–里面由@Component等注解组成
3.@Bean导入第三方包里面的组件
4.@Import:快速的给容器中导入一个组件
@Import
@Import导入组件,id默认是全类名
配置类:
@Configuration //标注当前是一个配置类
//这里没有进行包扫描,所以在不进行导入的情况下peoController是不会被注册到容器中的
@Import({peoController.class,people.class})
public class MyConfig
{
@Conditional({WindowsCondition.class})
@Bean
public people getWindows()
{
return new people("windows系统",18);
}
@Conditional(LinuxCondition.class)
@Bean
public people getLinux()
{
return new people("Linux系统",18);
}
}
peoController:
@Controller("大忽悠")//这里同样可以起一个别名
public class peoController
{}
测试:
public class Main
{
//传入的是配置类的位置,一开始是加载配置类,之前是加载配置文件的位置
private ApplicationContext ioc= new AnnotationConfigApplicationContext(MyConfig.class);
@Test
public void test()
{
//获取容器中所有定义的Bean的名字
String[] beans = ioc.getBeanDefinitionNames();
for (String bean : beans) {
System.out.println(bean);
}
}
}
这里@Controller(“大忽悠”)已经起了一个别名,只是没被扫描到容器中,所以这里使用其别名作为id
自定义选择器导入组件
MyImportSelector:
//自定义逻辑返回需要导入的组件
public class MyImportSelector implements ImportSelector
{
//返回值就是要导入到容器中的组件的全类名
//AnnotationMetadata:当前标注@Import注解的类的所有信息
public String[] selectImports(AnnotationMetadata annotationMetadata) {
//方法不要返回null
return new String[]{"com.Pojo.rea", "com.Pojo.ha"};
}
}
MyConfig:
@Configuration //标注当前是一个配置类
//加入自定义导入选择器
@Import({peoController.class,people.class,MyImportSelector.class})
public class MyConfig
{
@Conditional({WindowsCondition.class})
@Bean
public people getWindows()
{
return new people("windows系统",18);
}
@Conditional(LinuxCondition.class)
@Bean
public people getLinux()
{
return new people("Linux系统",18);
}
}
ImportBeanDefinitionRegistrar 手动注册Bean到容器
MyImportBeanDefinitionRegister:
public class MyImportBeanDefinitionRegister implements ImportBeanDefinitionRegistrar
{
/*
* AnnotationMetadata:当前类的注解信息
* BeanDefinitionRegistry:BeanDefinition注册类
* 把所有需要添加到容器中的Bean,调用
* BeanDefinitionRegistry.registerBeanDefinition手工注册起来
* */
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
boolean ha = beanDefinitionRegistry.containsBeanDefinition("com.Pojo.rea");
boolean rea = beanDefinitionRegistry.containsBeanDefinition("com.Pojo.ha");
if(ha&&rea)
{
//指定Bean定义信息(Bean的类型,Bean的作用域...)
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(dhy.class);
//指定Bean名
beanDefinitionRegistry.registerBeanDefinition("手动组成的Bean",rootBeanDefinition);
}
}
}
MyConfig:
@Configuration //标注当前是一个配置类
//加入自定义导入选择器
@Import({people.class,MyImportSelector.class,MyImportBeanDefinitionRegister.class})
public class MyConfig
{
@Conditional({WindowsCondition.class})
@Bean
public people getWindows()
{
return new people("windows系统",18);
}
@Conditional(LinuxCondition.class)
@Bean
public people getLinux()
{
return new people("Linux系统",18);
}
}
使用Spring提供的FactoryBean(工厂Bean)来注册容器
自定义工厂类继承FactoryBean
//创建一个Spring定义的工厂Bean
public class MyFactoryBean implements FactoryBean<Mine> {
//返回一个Mine对象,这个对象会添加到容器中
public Mine getObject() throws Exception {
return new Mine();
}
public Class<?> getObjectType() {
return Mine.class;
}
//返回false:创建的Bean是多实例的
public boolean isSingleton() {
return false;
}
}
在配置类中,将自定义工厂类放到容器中:
@Configuration //标注当前是一个配置类
public class MyConfig
{
@Bean
public FactoryBean getFactoryBean()
{
return new MyFactoryBean();
}
}
测试:
public class Main
{
//传入的是配置类的位置,一开始是加载配置类,之前是加载配置文件的位置
private ApplicationContext ioc= new AnnotationConfigApplicationContext(MyConfig.class);
@Test
public void test()
{
//这里我们在自定义工厂Bean时,规定Bean为多实例
Object b1 = ioc.getBean("getFactoryBean");
Object b2 = ioc.getBean("getFactoryBean");
//工厂Bean获取的是调用getObject创建的对象
System.out.println(b1.getClass());
System.out.println(b2.getClass());
System.out.println(b1==b2);
//获取工厂Bean本身
Object b3 = ioc.getBean("&getFactoryBean");
System.out.println(b3.getClass());
}
}
1.默认获取到的是工厂Bean调用getObject创建的对象
2.要获取工厂Bean本身,我们需要给id前面加上一个&
Bean的生命周期(注解版本),通过@Bean注解指定Bean的初始化和销毁方法
car类:
public class Car
{
public Car()
{
System.out.println("car创建中...");
}
public void init()
{
System.out.println("car init...");
}
public void destory()
{
System.out.println("car destory...");
}
}
配置类:
@Configuration
public class MyConfig
{
//指定初始化和销毁方法
@Bean(initMethod = "init",destroyMethod = "destory")
public Car car()
{
return new Car();
}
}
Bean的生命周期小结:
bean的创建—初始化—销毁的过程
容器管理bean的生命周期
我们可以自定义初始化和销毁方法,容器在bean进行到当前生命周期的时候来调用我们自定义的初始化和销毁方法
1.指定初始化和销毁方法
构造(对象创建)
单实例:在容器启动的时候创建对象
多实例:在每次获取的时候创建对象
初始化方法调用时机:
对象创建完成并赋值好,调用初始化方法
销毁方法调用时机:
单实例: 容器关闭的时候
多实例:容器不会管理这个Bean,容器不会调用销毁方法
对应Bean继承InitializingBean和DisposableBean接口,来指定Bean的初始化和销毁方法
Dhy类:
@Component
public class Dhy implements InitializingBean, DisposableBean
{
public Dhy()
{
System.out.println("Dhy创建中...");
}
//销毁方法
public void destroy() throws Exception {
System.out.println("Dhy destory...");
}
//初始化方法
public void afterPropertiesSet() throws Exception {
System.out.println("Dhy init...");
}
}
配置类:
@ComponentScan("com.Pojo")
@Configuration
public class MyConfig
{}
测试类:
public class Main
{
//传入的是配置类的位置,一开始是加载配置类,之前是加载配置文件的位置
private ApplicationContext ioc= new AnnotationConfigApplicationContext(MyConfig.class);
@Test
public void test()
{
Dhy bean = ioc.getBean(Dhy.class);
System.out.println(bean);
}
}
使用JSR250的@PostConstruct和@PreDestory注解指定bean的初始化和销毁方法
@PostConstruct:在bean创建完成并属性赋值完成,来执行初始化方法
@PreDestory:在容器销毁bean之前通知我们进行清理工作
@Component
public class Dhy
{
public Dhy()
{
System.out.println("Dhy创建中...");
}
//销毁方法
@PreDestroy
public void destroy() {
System.out.println("Dhy destory...");
}
//初始化方法
@PostConstruct
public void afterPropertiesSet() {
System.out.println("Dhy init...");
}
}
测试:
public class Main
{
//传入的是配置类的位置,一开始是加载配置类,之前是加载配置文件的位置
private ApplicationContext ioc= new AnnotationConfigApplicationContext(MyConfig.class);
@Test
public void test()
{
Dhy bean = ioc.getBean(Dhy.class);
System.out.println(bean);
bean.destroy();
}
}
Bean的后置处理器
自定义后置处理器MyBeanPostprocess
//后置处理器:在bean初始化前后进行处理工作
@Component//放到容器中
public class MyBeanPostprocess implements BeanPostProcessor
{
//bean是容器创建的实例,还没初始化
//beanname是实例在容器中的名字
//返回值是后来将要用的bean实例,我们可以直接返回,也可以包装后再返回
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("后置处理器初始化前调用"+" "+beanName+"==》"+bean);
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("后置处理器初始化后调用"+" "+beanName+"==》"+bean);
return bean;
}
}
配置类
@ComponentScan({"com.Pojo","com.config"})
@Configuration
public class MyConfig
{}
测试:
public class Main
{
//传入的是配置类的位置,一开始是加载配置类,之前是加载配置文件的位置
private ApplicationContext ioc= new AnnotationConfigApplicationContext(MyConfig.class);
@Test
public void test()
{
Dhy bean = ioc.getBean(Dhy.class);
System.out.println(bean);
bean.destroy();
}
}
bean的后置处理器底层原理
Spring底层对BeanPostProcessor的使用
bean赋值,注入其他组件,@Autowired,生命周期注解功能等,底层都是使用相关后置处理器来完成的
属性赋值
@Value
@Value注解里面参数可填内容:
1.基本数值
2.可以写SPEL表达式==>#{}
3.可以写${},取出配置文件中的值(在运行环境变量里面的值)
@Data
@Component
public class Dhy
{
@Value("大忽悠")
String name;
@Value("#{3*6}")
String age;
public Dhy()
{
System.out.println("Dhy创建中...");
}
}
测试:
public class Main
{
//传入的是配置类的位置,一开始是加载配置类,之前是加载配置文件的位置
private ApplicationContext ioc= new AnnotationConfigApplicationContext(MyConfig.class);
@Test
public void test()
{
Dhy bean = ioc.getBean(Dhy.class);
System.out.println(bean);
}
}
@Value从配置文件取出来赋值给属性的中文如果出现乱码,是因为配置文件的编码问题,解决方式如下:
@Value也可以加在方法的参数上,从配置文件中取出值,赋值给参数
@PropertySource配置的用法
加载指定的属性文件(*.properties)到 Spring 的 Environment 中。可以配合 @Value 和
@ConfigurationProperties 使用。
@PropertySource 和 @Value
组合使用,可以将自定义属性文件中的属性变量值注入到当前类的使用@Value注解的成员变量中。
@PropertySource 和 @ConfigurationProperties
组合使用,可以将属性文件与一个Java类绑定,将属性文件中的变量值注入到该Java类的成员变量中。
@PropertySource读取外部配置文件中的k/v保存到运行的环境变量中,加载完外部配置文件中的值后使用${}取出配置文件中的值
@Data
@Component
public class Dhy
{
@Value("${name}")
String name;
@Value("#{3*6}")
String age;
public Dhy()
{
System.out.println("Dhy创建中...");
}
}
配置文件:
name=大忽悠
测试结果:
获取环境变量中的值⇒ ioc.getEnvironment.getProperty
public class Main
{
//传入的是配置类的位置,一开始是加载配置类,之前是加载配置文件的位置
private ApplicationContext ioc= new AnnotationConfigApplicationContext(MyConfig.class);
@Test
public void test()
{
Dhy bean = ioc.getBean(Dhy.class);
System.out.println(bean);
//获取环境变量中的保存的name
Environment env = ioc.getEnvironment();
String name = env.getProperty("name");
System.out.println(name);
}
}
自动装配
@Autowired和@Qualifier
@Autowired:自动注入:
1.默认优先按照类型去容器中寻找对应的组件
2.如果找到多个相同类型的组件,再将属性的名称作为组件的id去容器中查找
3.@Qualifier(“book”):使用@Qualifier指定需要装配的组件的id,而不是使用属性名
4.自动装配默认一定要将属性赋值好,没有就会报错
可以使用@Autowired(required=false);
@Primary
@Primary:让spring进行自动装配的时候,默认使用首选的bean
也可以继续使用@Qualifier指定需要装配的bean的名字
Dhy:
@AllArgsConstructor
@Data
@Component("d2")//默认类名作为id
public class Dhy
{
String name;
String age;
public Dhy()
{
System.out.println("Dhy创建中...");
}
}
MyConfig:
//读取外部配置文件中的k/v保存到运行的环境变量中
@PropertySource(value = {"classpath:/dhy.properties"})
@ComponentScan({"com.Pojo","com.controller"})
@Configuration
public class MyConfig
{
@Primary //在装配Dhy类型的bean时,默认使用下面id为d2的bean
@Bean("d1")//默认方法名作为id
public Dhy getDhy()
{
return new Dhy("首选Bean","19");
}
}
TestController:
@Controller
public class TestController
{
@Autowired
Dhy d;
public Dhy getDhy()
{
return d;
}
}
测试:
public class Main
{
//传入的是配置类的位置,一开始是加载配置类,之前是加载配置文件的位置
private ApplicationContext ioc= new AnnotationConfigApplicationContext(MyConfig.class);
@Test
public void test()
{
TestController b = ioc.getBean(TestController.class);
System.out.println(b.getDhy());
}
}
@Value和@bean执行顺序问题
如果2个标签在不同文件里,那么@Value比@Bean先执行
同一文件下,那么@Bean比@Value先执行,所以,有可能在使用@Value值为空
使用Environment取代@Value,它直接取配置文件,即getProperty()
@AutoWired所在类必须处于容器之中,该注解才会生效,原因如下:
如果一个类没有加注解如@component, @controller, @service等扫描这个类到容器中
在类中的变量加@Autowired注解无法生效。
因为如果一个类new对象生成的,那么这个类就不归spring容器管理,IOC等spring的功能也就无法使用了。
@Resource和@Inject
Spring还支持@Resource(JSR250)和@Inject(JSR330)[java规范的注解]
@Resource:
1.可以和@Autowired一样实现自动装配功能,默认是按照组件名称进行装配的,也可以指定要装配的组件id
2.没有能支持@Primary功能,没有能支持@Autowired(required=false)功能的
@Inject:
首先导入依赖:
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
功能和Autowired一样,但是没有required=false的功能,同样支持@Primary注解
@Autowired和@Resource,@Inject的区别:
@Autowired是spring定义的,@Resource和@Inject是java的规范,但是spring对其做了支持
因此@Resource和@Inject脱离了spring框架也可以使用,至于其他框架,只要对其做了对应的支持同样也可以使用
@Resource和@Autowired注解都是用来实现依赖注入的。只是@AutoWried默认先按by type自动注入,而@Resource默认按byName自动注入。
@Resource有两个重要属性,分别是name和type
spring将name属性解析为bean的名字,而type属性则被解析为bean的类型。所以如果使用name属性,则使用byName的自动注入策略,如果使用type属性则使用byType的自动注入策略。如果都没有指定,则通过反射机制使用byName自动注入策略。
@Resource依赖注入时查找bean的规则:(以用在field上为例)
1. 既不指定name属性,也不指定type属性,则自动按byName方式进行查找。如果没有找到符合的bean,则回退为一个原始类型进行查找,如果找到就注入。
2. 只是指定了@Resource注解的name,则按name后的名字去bean元素里查找有与之相等的name属性的bean,没有会报错
3. 只指定@Resource注解的type属性,则从上下文中找到类型匹配的唯一bean进行装配,找不到或者找到多个,都会抛出异常
4. 既指定了@Resource的name属性又指定了type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常
@Autowired注解有一个required属性,当指定required属性为false时,意味着在容器中找相应类型的bean,如果找不到则忽略,而不报错(这一条是两个注解所没有的功能)。由于@Inject注解没有属性,在加载所需bean失败时,会报错
底层原理:
AutowiredAnnotationBeanPostProcessor:解析完成自动装配功能(注解自动注入后置解析器)
@Autowired和@Resource,@Inject等注解放在构造方法,普通方法上:
在构造方法上使用 @Inject 时,其参数在运行时由配置好的IoC容器提供。比如,在下面的代码中,运行时调用MurmurMessage类的构造方法时,IoC 容器会注入其参数 Header 和Content 对象。
@Inject
public MurmurMessage(Header header, Content content)
{
this.headr = header;
this.content = content;
}
规范中规定向构造方法注入的参数数量是0个或多个,所以在不含参数的构造方法上使用 @Inject 注解也是合法的。
注意:因为JRE无法决定构造方法注入的优先级,所以规范中规定类中只能有一个构造方法带@Inject注解
也可以用@Inject注解方法,与构造方法一样,运行时可注入的参数数量为0个或多个。但使用参数注入的方法不能声明为抽象方法也不能声明其自身的类型参数。下面这段代码在set方法前使用@Inject,这是注入可选属性的常用技术。
@Inject
public void setContent(Content concent)
{
this.content = content;
}
向方法中注入参数的技术对于服务类方法来说非常有用,其所需的资源可以作为参数注入,比如向查询数据的服务方法中注入数据访问对象(Dao)。
注解放在方法上,在给方法参数注入值时,会执行该方法
@Autowired放在属性,方法,参数,构造器上
默认加在ioc容器中的组件,容器启动会调用无参构造器创建对象,再进行初始化赋值等操作
@Autowired: 标注在构造器,参数,方法和属性上,都是从容器中获取参数组件的值
1.标注在方法位置: @Bean+方法参数:参数从容器中获取,默认不写@Autowired效果一样,都能自动装配
@Bean标注的方法创建对象的时候,方法参数的值从容器中获取
//默认加在ioc容器中的组件,容器启动会调用无参构造器创建对象,再进行初始化赋值等操作
@Controller
public class TestController
{
Dhy d;
@Autowired
@Bean
public Dhy setD(Dhy d)
{
return d;
}
}
不加autowired同样也会自动注入:
@Controller
public class TestController
{
Dhy d;
@Bean
public Dhy setD(Dhy d)
{
return d;
}
}
2.标注在构造器上,如果组件只有一个有参构造器,这个有参构造器的@Autowired可以省略,参数位置的组件还是从可以自动从容器中获取
//默认加在ioc容器中的组件,容器启动会调用无参构造器创建对象,再进行初始化赋值等操作
@Controller
public class TestController
{
Dhy d;
TestController()
{
System.out.println("无参构造调用");
}
//构造器从需要的组件,都是从容器中获取
@Autowired
TestController(Dhy d)
{
System.out.println(d);
}
}
测试:
public class Main
{
//传入的是配置类的位置,一开始是加载配置类,之前是加载配置文件的位置
private ApplicationContext ioc= new AnnotationConfigApplicationContext(MyConfig.class);
@Test
public void test()
{
TestController b = ioc.getBean(TestController.class);
}
}
有参构造上不加@Autowired注解,也会自动注入:
@Controller
public class TestController
{
Dhy d;
TestController(Dhy d)
{
System.out.println(d);
}
}
如果有参构造上面不加@Autowired注解或者加在了有参构造的参数上,并且无参构造和有参构造同时存在,那么ioc容器创建bean时,默认调用无参构造,当只有有参构造的时候,才会调用有参构造
3.放在参数位置
@Controller
public class TestController
{
Dhy d;
TestController(@Autowired Dhy d)
{
System.out.println(d);
}
}
自定义组件中注入Spring底层组件
自定义组件想要使用Spring容器底层的一些组件(ApplicationContext,BeanFactory,xxx)
自定义组件实现xxxAware,在创建对象的时候,会调用接口规定的方法注入相关的组件
把Spring底层的一些组件注入到自定义的bean中
xxxAware:底层是使用xxxProcessor来调用接口规定的方法注入相关的组件
@Component
public class MyAware implements ApplicationContextAware, BeanNameAware, EmbeddedValueResolverAware
{
private ApplicationContext applicationContext;
//ApplicationContext: ioc容器
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
{
System.out.println("传入的ioc: "+applicationContext);
this.applicationContext=applicationContext;
}
public void setBeanName(String s) {
System.out.println("当前bean的名字: "+s);
}
//stringValueResolver:用来解析String的值,例如解析占位符${},#{}等
public void setEmbeddedValueResolver(StringValueResolver stringValueResolver) {
String s = stringValueResolver.resolveStringValue("你好${os.name},我是#{3*6}");
System.out.println(s);
}
}
测试:
public class Main
{
//传入的是配置类的位置,一开始是加载配置类,之前是加载配置文件的位置
private ApplicationContext ioc= new AnnotationConfigApplicationContext(MyConfig.class);
@Test
public void test()
{
//在创建myaware对象的时候,会注入相应组件,并进行方法回调
MyAware myAware = ioc.getBean(MyAware.class);
System.out.println(myAware);
System.out.println("当前ioc:"+ioc);
}
}
获取配置文件中的值目前有三种方法: 1.@Value注解 2. 继承EmbeddedValueResolverAware,使用StringValueResolver 值解析器 3.Environment对象
@Component
public class MyAware implements EmbeddedValueResolverAware,ApplicationContextAware
{
StringValueResolver resolver;
ApplicationContext applicationContext;
//MyAware对象创建的时候,就会回调该方法,注入组件
public void setEmbeddedValueResolver(StringValueResolver stringValueResolver) {
resolver=stringValueResolver;
}
//MyAware对象创建的时候,就会回调该方法,注入组件
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext=applicationContext;
}
//@Value从上下文环境中拿到name
@Bean
public ret show(@Value("${name}") String name)
{
//从上下文环境中拿到name
String res = resolver.resolveStringValue("${name}");
String app = applicationContext.getEnvironment().getProperty("name");
return new ret(name,res,app);
}
}
配置文件:
name=大忽悠
测试:
public class Main
{
//传入的是配置类的位置,一开始是加载配置类,之前是加载配置文件的位置
private ApplicationContext ioc= new AnnotationConfigApplicationContext(MyConfig.class);
@Test
public void test()
{
//在创建myaware对象的时候,会注入相应组件,并进行方法回调
ret bean = ioc.getBean(ret.class);
System.out.println(bean);
}
}
必须将配置文件中的值,放入到环境中后,才能取出来值,通过@PropertySource可以将配置文件中的值,放到环境中
//读取外部配置文件中的k/v保存到运行的环境变量中
@PropertySource(value = {"classpath:/dhy.properties"})
@ComponentScan({"com.Pojo","com.controller","com.aware"})
@Configuration
public class MyConfig
{
@Primary //在装配Dhy类型的bean时,默认使用下面id为d2的bean
@Bean("d1")//默认方法名作为id
public Dhy getDhy()
{
return new Dhy("首选Bean","19");
}
}
否则结果如下:
@Profile
Spring为我们提供的可以根据当前环境,动态的激活和切换一系列组件的功能
开发环境,测试环境,生产环境
指定组件在那个环境的情况下,才能被注册到容器中,默认任何环境都能注册该组件
加了环境标识的bean,只有在这个环境被激活的时候才能注册到容器中,默认是default环境
@Configuration
public class MyConfig
{
@Profile("test")
@Bean
public Dhy d1()
{
return new Dhy();
}
@Profile("default")
@Bean
public Dhy d2()
{
return new Dhy();
}
@Profile("prod")
@Bean
public Dhy d3()
{
return new Dhy();
}
}
不指定环境的情况下:
1.使用命令行动态参数,在虚拟机参数位置加载: -Dspring.profiles.active=test,指定运行环境
2.通过代码来设置需要激活的环境
public class Main
{
//传入的是配置类的位置,一开始是加载配置类,之前是加载配置文件的位置
private static AnnotationConfigApplicationContext ioc= new AnnotationConfigApplicationContext();
public static void main(String[] args)
{
//设置需要激活的环境,可以设置多个
ioc.getEnvironment().setActiveProfiles("test","prod");
//注册主配置类
ioc.register(MyConfig.class);
//刷新容器
ioc.refresh();
String[] beans = ioc.getBeanDefinitionNames();
for (String bean : beans) {
System.out.println(bean);
}
}
}