{
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注解,感兴趣可以看看
=============================================================================
以前:
<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没被扫描进来,完美
=====================================================================
@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创建一个实例
=========================================================================
@Configuration //标注当前是一个配置类
@ComponentScan(value = “com.controller”)
public class MyConfig
{
@Lazy
@Bean(value = “dhy”)//指定id
public people getPeople()
{
return new people(“大忽悠”,18);
}
}
懒加载:
单实例bean:默认在容器启动的时候创建对象
懒加载:容器启动不创建对象,第一次使用(获取)Bean创建对象,并初始化
=========================================================================================
@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导入组件,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对象,这个对象会添加到容器中
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进行到当前生命周期的时候来调用我们自定义的初始化和销毁方法
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();
}
}
自定义后置处理器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注解里面参数可填内容:
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也可以加在方法的参数上,从配置文件中取出值,赋值给参数
**加载指定的属性文件(*.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:自动注入:
1.默认优先按照类型去容器中寻找对应的组件
2.如果找到多个相同类型的组件,再将属性的名称作为组件的id去容器中查找
3.@Qualifier(“book”):使用@Qualifier指定需要装配的组件的id,而不是使用属性名
4.自动装配默认一定要将属性赋值好,没有就会报错
可以使用@Autowired(required=false);
@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个标签在不同文件里,那么@Bean比@Value先执行,所以,有可能在使用@Value值为空
同一文件下,那么@Value比@Bean先执行
使用Environment取代@Value,它直接取配置文件,即getProperty()
@AutoWired所在类必须处于容器之中,该注解才会生效,原因如下:
如果一个类没有加注解如@component, @controller, @service等扫描这个类到容器中
在类中的变量加@Autowired注解无法生效。
因为如果一个类new对象生成的,那么这个类就不归spring容器管理,IOC等spring的功能也就无法使用了。
Spring还支持@Resource(JSR250)和@Inject(JSR330)[java规范的注解]
@Resource:
1.可以和@Autowired一样实现自动装配功能,默认是按照组件名称进行装配的,也可以指定要装配的组件id
2.没有能支持@Primary功能,没有能支持@Autowired(required=false)功能的
@Inject:
首先导入依赖:
javax.inject
javax.inject
1
功能和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 对象。