//@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对象,这个对象会添加到容器中
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和Disposa
《一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》
【docs.qq.com/doc/DSmxTbFJ1cmN1R2dB】 完整内容开源分享
bleBean接口,来指定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();
}
}
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);
}
}
@PropertySource配置的用法
**加载指定的属性文件(*.properties)到 Spring 的 Environment 中。可以配合 @Value 和
@ConfigurationProperties 使用。**
**@PropertySource 和 @Value
组合使用,可以将自定义属性文件中的属性变量值注入到当前类的使用@Value注解的成员变量中。**
**@PropertySource 和 @ConfigurationProperties
组合使用,可以将属性文件与一个Java类绑定,将属性文件中的变量值注入到该Java类的成员变量中。**
@Data
@Component
public class Dhy
{
@Value("${name}")
String name;
@Value("#{3*6}")
String age;
public Dhy()
{
System.out.println(“Dhy创建中…”);
}
}
配置文件:
name=大忽悠
测试结果:
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());
}
}
如果2个标签在不同文件里,那么@Bean比@Value先执行,所以,有可能在使用@Value值为空
同一文件下,那么@Value比@Bean先执行
使用Environment取代@Value,它直接取配置文件,即getProperty()
如果一个类没有加注解如@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:
首先导入依赖:
javax.inject
javax.inject
1
功能和Autowired一样,但是没有required=false的功能,同样支持@Primary注解
@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:解析完成自动装配功能(注解自动注入后置解析器)
在构造方法上使用 @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)。
注解放在方法上,在给方法参数注入值时,会执行该方法
默认加在ioc容器中的组件,容器启动会调用无参构造器创建对象,再进行初始化赋值等操作
@Autowired: 标注在构造器,参数,方法和属性上,都是从容器中获取参数组件的值