容器
AnnotationConfigApplicationContext
组件添加(注册)
组件复制
组件注入
以上是IOC部分
AOP
声明式事务
一、组件注册
spring重要的是IOC控制反转和DI依赖注入,spring认为所有的组件都应该放在IOC容器中,然后组件之间的关系通过容器进行自动装配,也就是依赖注入。
总结概括:
1.包扫描+组件标注注解(@Conntroller/@Service/@Repository/@Component)[自己写的类]
2.@Bean[导入的第三方包里面的组件,也就是第一种方式以外的(自己创建一个组件,再@Bean]
3.@Import[快速给容器中导入一个组件]:
①@Import(要导入到容器中的组件):容器会自动注册这个组件,id默认是全类名;
②ImportSelector:返回需要导入的组件的全类名数组;
③ImportBeanDefinitionRegistrar:手动注册bean到容器中
4.使用spring提供的FactoryBean(工厂Bean):
①默认获取到的是工厂bean调用getObject创建的对象(而不是注册的工厂Bean)(这里的理解用工厂模式来理解);
②要获取工厂Bean本身,我们需要给id前面加一个&
1. @Configuration和@Bean
@Configuration:告诉spring这是一个配置类,加入IOC容器中,配置类==配置文件
@Bean:注册组件。默认的beanId是函数名,可以用value属性进行指定。
@Configuration
public class MyConfig {
@Bean
public Person person() {
return new Person("ZhangSan", 10);
}
}
加载配置类
public class MainTest {
public static void main(String[] args) {
//注解式开发 传入配置类的位置
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
Person bean = applicationContext.getBean(Person.class);
System.out.println(bean);
//获取bean的名称
String[] namesForType = applicationContext.getBeanNamesForType(Person.class);
for (String name : namesForType) {
System.out.println(name);
}
}
}
2. @ComponentScan和@ComponentScans
在原来的spring的配置文件中都会 <context:component-scan base-package=" "></context:component-scan>来进行包扫描
但是如果用注解就是@ComponentScan
例如@ComponentScan(value=“指定要扫描的包名”,excludeFilters={@Filter(type=FilterType.排除方式(例如按注解排除),class={注解名.class})
value:指定要扫描的包
excludeFilters=@Filter[],指定扫描的时候按什么规则排除哪些组件
includeFilters=@Filter[],指定扫描的时候只需要包含哪些组件
要让includeFilters生效,需要让useDefaultFilters=false
扫描com.student包下的组件,排除@Controller、@Service注解的类
@Configuration
@ComponentScan(value = "com.student", excludeFilters = {@Filter(type = FilterType.ANNOTATION, classes = {Controller.class, Service.class})})
public class MyConfig {
@Bean
public Person person() {
return new Person("ZhangSan", 10);
}
}
只包含@Controller、@Service注解的类。(需要先禁用默认的过滤规则useDefaultFilters=false,因为默认的过滤规则是全部扫描)
@Configuration
@ComponentScan(value = "com.student",
useDefaultFilters = false,
includeFilters = {@Filter(type = FilterType.ANNOTATION, classes = {Controller.class, Service.class})
})
public class MyConfig {
@Bean
public Person person() {
return new Person("ZhangSan", 10);
}
}
配置包扫描有哪些过滤规则
FilterType.ANNOTATION:按照注解(也就是标注了某一注解的类)
FilterType.ASSIGNABLE_TYPE:按照给定的类型(也就是具体的类)
FilterType.ASPECTJ:使用ASPECTJ表达式
FilterType.REGEX:使用正则指定
FilterType.CUSTOM:使用自定义的规则(也就是自己可以自定义一个Filter实现TypeFilter,然后返回true则容器中包含这些类,返回false则不包含这些类)
自定义TypeFilter规则:
MetadataReader:读取到的当前正在扫描的类的信息
MetadataReaderFactory:可以获取到其他任何类的信息
public class MyTypeFilter implements TypeFilter {
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
// 获取当前正在扫描类的注解信息
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
// 获取当前正在扫描的类的类信息
ClassMetadata classMetadata = metadataReader.getClassMetadata();
// 获取当前的资源信息(类的路径)
metadataReader.getResource();
String className = classMetadata.getClassName();
if(className.contains("er"))
return true;
return false;
}
}
@ComponentScans可以包含多个@ComponentScan
格式为
@ComponentScans(
value = {
@ComponentScan(value = "com.atguigu",includeFilters = {
@Filter(type = FilterType.CUSTOM,classes = {MyTypeFilter.class})
},useDefaultFilters = false)
})
3. @Scope
设置组件作用域
可以取哪些范围
prototype(ConfigurableBeanFactory.SCOPE_PROTOTYPE ):多实例的:ioc容器启动并不会去3调用方法创建对象放在容器中,每次获取的时候才会调用方法创建对象
singleton(ConfigurableBeanFactory.SCOPE_SINGLETON):单实例的(默认值):ioc容器启动会调用方法创建对象放到ioc容器中,以后每次获取都是直接从容器(map.get())中拿
以上两个的区别是是:只创建容器的时候,单实例会调用方法而多实例就不会调用方法(多实例是在获取bean的时候才会调用方法)
request(WebApplicationContext.SCOPE_REQUEST):同一次请求创建一个实例
session(WebApplicationContext.SCOPE_SESSION):同一个session创建一个实例
4. @Lazy 懒加载
已知,单实例bean,默认在容器启动时创建对象。但懒加载,容器启动不创建对象,第一次使用(获取)Bean创建对象并初始化(也就是说懒加载只针对单实例)
spring认为所有的组件都应该放在容器中,然后组件之间的关系通过容器来进行自动装配
5. @Conditional
按照一定的条件进行判断,满足条件给容器中注册bean
位置:可以在方法或类上
若在类上配置表示只有满足当前条件,这个类中配置的所有bean注册才能生效
条件可以是一个条件也可以是条件数组
如何使用?
使用时需要自己创建一个条件类继承Condition然后重写matches方法
matches方法的参数中:
ConditionContext:判断条件能使用的上下文(环境)
AnnotatedTypeMetadata:注释信息
根据返回true和false来判断要不要注册
例子:
自定义判断条件类:
//判断是否Linux系统
public class LinuxCondition implements Condition{
/**
* ConditionContext:判断条件能使用的上下文(环境)
* AnnotatedTypeMetadata:注释信息
*/
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// TODO是否Linux系统
//1、获取到ioc使用的beanfactory
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
//2、获取类加载器
ClassLoader classLoader = context.getClassLoader();
//3、获取当前环境信息
Environment environment = context.getEnvironment();
//4、获取到bean定义的注册类
BeanDefinitionRegistry registry = context.getRegistry();
String property = environment.getProperty("os.name");
//可以判断容器中的bean注册情况,也可以给容器中注册bean(但实际上根据根据boolean值来进行注册)
boolean definition = registry.containsBeanDefinition("person");
if(property.contains("linux")){
return true;
}
return false;
}
}
//判断是否Windows系统
public class WindowsCondition implements Condition {
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment environment = context.getEnvironment();
String property = environment.getProperty("os.name");
if(property.contains("Windows")){
return true;
}
return false;
}
}
在bean上加上判断条件:
@Bean("person01")
@Conditional(LinuxCondition.class)
public Person person01() {
return new Person("lisi", 18);
}
测试类:
public class IOCTest {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
@Test
public void test() {
String[] namesForType = applicationContext.getBeanNamesForType(Person.class);
ConfigurableEnvironment environment = applicationContext.getEnvironment();
//动态获取环境变量的值:window 8
String property = environment.getProperty("os.name");
System.out.println(property);
for (String name : namesForType) {
System.out.println(name);
}
Map<String, Person> persons = applicationContext.getBeansOfType(Person.class);//获取所有组装好的组件
System.out.println(persons);
}
}
注意:os.name是获取当前系统,默认取的当前系统的系统名称,但是当我们想要测试别的环境时可以在 JVM 参数上强制给一个值。
操作:右键Run As选择Run Configuration设置VM arguments:-Dos.name=linux
6. @Import
@Import[快速给容器中导入一个组件]:
①@Import(要导入到容器中的组件):容器会自动注册这个组件,id默认是全类名;
②ImportSelector:返回需要导入的组件的全类名数组;
③ImportBeanDefinitionRegistrar:手动注册bean到容器中
这三种使用方法可以混合使用
第一种用法:
@Import(要导入到容器中的组件):容器会自动注册这个组件,id默认是全类名
导入多个的时候要用大括号
第二种用法:
ImportSelector:自定义逻辑返回需要导入的组件
如何使用?
MyImportSelector要继承ImportSelector接口,重写方法selectImports(可以返回一个空数组不能返回null)
selectImports中的参数:
AnnotationMetadata:当前注解@Import注解的类的所有注解信息(返回值不能是null,否则会报空指针异常。如果需要返回的内容,则需使用:return new String[0]。)
自定义MyImportSelector:
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{"com.study.bean.Yellow", "com.study.bean.Blue"};
}
}
第三种用法
ImportBeanDefinitionRegistrar:手动注册bean到容器中
如何使用?
MyImportBeanDefinitionRegistrar要实现ImportBeanDefinitionRegistrar接口,重写方法registerBeanDefinition
registerBeanDefinition的参数含义:
AnnotationMetadata:当前类的注解信息
BeanDefinitionRegistry:BeanDefinition注册类,把所有需要添加到容器中的bean,调用BeanDefinitionRegistry.registerBeanBeanDefinition手工注册进来,可以指定bean名
自定义MyImportBeanDefinitionRegistrar:
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
String[] beanDefinitionNames = registry.getBeanDefinitionNames();
Arrays.asList(beanDefinitionNames).forEach(System.out::println);
// 判断ioc容器中是否注册了指定id的Bean
boolean redDefinition = registry.containsBeanDefinition("com.study.bean.Red");
boolean blueDefinition = registry.containsBeanDefinition("com.study.bean.Blue");
//重点在这里(自己写也可以不用判断条件,自己想注册什么就注册什么)
if(redDefinition && blueDefinition) {
// 调用registry的registerBeanDefinition方法手工注册Bean
// 使用RootBeanDefinition指定要注册的Bean的类型
RootBeanDefinition beanDefinition = new RootBeanDefinition(RainBow.class);
// 指定注册进容器的bean的id
registry.registerBeanDefinition("rainBow", beanDefinition);
}
}
}
7. 用FactoryBean注册组件
Spring提供的 工厂Bean接口
创建一个Spring定义的工厂bean
如何使用?
要求实现FactoryBean并重写方法(这个跟工厂模式很像)
创建一个ColorFactoryBean实现FactoryBean:
具体步骤:
准备类Color.class(里边可以什么都不写)
点进去FactoryBean我们可以看到他是一个接口,包含三个方法
getObject创建bean,getObjectType获取类型,isSingleton设置每次定义bean的方式是单例还是多例
配置文件MainConfig.java中用@Bean注入ColorFactoryBean
public class ColorFactoryBean implements FactoryBean<Color> {
// 返回一个color对象,这个对象会添加进容器中
@Override
public Color getObject() throws Exception {
return new Color();
}
// 返回的对象的类型
@Override
public Class<?> getObjectType() {
return Color.class;
}
// 是否为单例:true代表这个bean是单实例的,在容器中保存一份;false代表这个bean是多实例,代表每次获取都会创建一个新的bean
@Override
public boolean isSingleton() {
return true;
}
}
public class MainConfig {
@Bean
public ColorFactoryBean colorFactoryBean(){
return new ColorFactoryBean();
}
}
注意:默认获取到的是工厂bean调用getObject创建的对象,要获取工厂Bean本身,我们需要给id前面加一个&(这个前缀是在BeanFactory.FACTORY_BEAN_PREFIX中定义的。)
测试类:
public class IOCTest {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
@Test
public void testImport(){
printBeans(applicationContext);
//工厂Bean获取的是调用getObject创建的对象
Object bean2 = applicationContext.getBean("colorFactoryBean");
Object bean3 = applicationContext.getBean("colorFactoryBean");
System.out.println("bean的类型:"+bean2.getClass());
//bean判断相等输出true,表明工厂bean只会创建一个Bean(单实例)
System.out.println(bean2 == bean3);
Object bean4 = applicationContext.getBean("&colorFactoryBean");
System.out.println(bean4.getClass());
}
private void printBeans(AnnotationConfigApplicationContext applicationContext) {
String[] definitonNames = applicationContext.getBeanDefinitionNames();
for(String name: definitonNames) {
System.out.println(name);
}
}
}
总结理解
前面学到的注解可以理解成是物料库的进货方式,主要目的就是采购到所需要的物料来支撑工厂正常运转,鉴于不同物料的进货渠道不同,所以有多种进货方式,比如有一种稀缺物料,你只能靠进口才能获得,所以要有多种获取物料的方法,spring也一样,提供多种注册组件的方式,没有优劣,只有合适不合适,工厂库管员上班肯定要先了解每种物料的进货方式,spring使用者也得知道各种类的注册方式,没有什么确切的使用场景,合适就可以用。
二、bean的生命周期
生命周期:bean的创建-----初始化-----销毁的过程
容器管理bean的生命周期:
我们可以自定义初始化和销毁方法,容器在bean进行到当前生命周期的时候来调用我们自定义的初始化和销毁方法
1. @Bean中配置生命周期(
使用@Bean注解的initMethod、destroyMethod两个属性指定初始化和销毁方法
如何使用?
创建一个实体类,在里面除了Car的构造器,还要有初始化和销毁方法
在配置文件中用bean注入,并配置注解的initMethod、destroyMethod两个属性
public class Car {
public Car() { System.out.println("Car constructor..."); }
public void init() { System.out.println("car .... init ..."); }
public void destroy() { System.out.println("car...destroy"); }
}
@Configuration
public class MainConfigOfLifeCycle {
@Bean(initMethod = "init", destroyMethod = "destroy")
public Car car() {
return new Car();
}
}
测试类IOCTest_LifeCycle.java中创建容器,查看bean的创建销毁:
public class IOCTest_LifeCycle {
@Test
public void test01(){
//1、创建ioc容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfLifeCycle.class);
System.out.println("容器创建完成...");
//applicationContext.getBean("car");
//关闭容器
System.out.println("容器进行关闭...");
applicationContext.close();
}
}
运行结果:容器创建完成之前就注册了组件并进行了初始化,关闭容器的时候销毁bean
注意:如果在注册多实例Bean,则只有在bean调用的时候(也就是运行applicationContext.getBean(“car”);)才会创建Bean,而且并不会在容器关闭的时候销毁Bean,需要我们自行调用销毁方法进行销毁
总结:
构造(对象创建)
单实例:在容器启动的时候创建对象
多实例:在每次获取的时候创建对象
初始化:
对象创建完成,并赋值好,调用初始化方法。。。
销毁:
单实例:容器关闭的时候
多实例:容器不会管理这个bean;容器不会调用销毁方法;
2.实现InitializingBean和DisposableBean接口
创建Bean的时候我们还可以通过让Bean实现InitializingBean(定义初始化逻辑), DisposableBean(定义销毁逻辑)自定义方法做初始化销毁
InitializingBean接口的afterPropertiesSet()实现初始化逻辑;DispoableBean接口的destroy()实现销毁逻辑;
如何使用?
具体步骤:
创建一个Cat.java,用@Component方式注册组件
配置类MainConfigOfLifeCycle.java中,我们用扫描包的方式将组件加入到容器中,Cat类路径为com.atguigu.bean
@Component
public class Cat implements InitializingBean,DisposableBean{
public Cat() {
System.out.println("cat constructor...");
}
public void destroy() throws Exception {
// TODO Auto-generated method stub
System.out.println("cat ... destory...");
}
public void afterPropertiesSet() throws Exception {
// TODO Auto-generated method stub
System.out.println("cat ... afterPropertiesSet...");
}
}
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@ComponentScan("com.atguigu.bean")
@Configuration
public class MainConfigOfLifeCycle {
}
3.@PostConstruct & @PreDestroy
创建Bean的时候我们还可以使用JSR250做初始化/销毁:@PostConstruct:在bean创建完成并且属性赋值完成;来执行初始化;@PreDestroy:在容器销毁bean之前通知我们进行清理工作
如何使用?
具体步骤:
创建一个Dog.java,还是用@Component方式注册组件
创建初始化方法,将@PostConstruct注解加到方法上面,对象执行构造函数,创建并赋值之后执行。将@PreDestroy注解加到方法上面,在容器移除对象之前执行
配置类MainConfigOfLifeCycle.java中,我们还是用扫描包的方式将组件加入到容器中,Dog类路径为com.atguigu.bean
@Component
public class Dog {
public Dog(){
System.out.println("dog constructor...");
}
//对象创建并赋值之后调用
@PostConstruct
public void init(){
System.out.println("Dog....@PostConstruct...");
}
//容器移除对象之前
@PreDestroy
public void detory(){
System.out.println("Dog....@PreDestroy...");
}
}
@ComponentScan("com.atguigu.bean")
@Configuration
public class MainConfigOfLifeCycle {
}
4. BeanPostProcessor接口
Bean的后置处理器,在bean的初始化前后进行处理。
在bean初始化前后进行一些处理工作:
postProcessBeforeInitialization:Bean构造方法执行之后、初始化方法执行之前处理
postProcessAfterInitialization:在初始化之后工作
如何使用?
BeanPostProcessor是一个接口,我们需要定义一个类实现接口中的两个方法
具体步骤:
创建一个后置处理器,将后置处理器加入到容器中
配置类MainConfigOfLifeCycle.java中,我们还是用扫描包的方式将组件加入到容器中,MyBeanPostProcessor类路径为com.atguigu.bean
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
/**
* bean 容器创建的实例
* beanName 实例在容器中的名字
*/
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
// TODO Auto-generated method stub
System.out.println("postProcessBeforeInitialization..."+beanName+"=>"+bean);
return bean;// 返回传入的原始bean,或者进行了包装之后的bean
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
// TODO Auto-generated method stub
System.out.println("postProcessAfterInitialization..."+beanName+"=>"+bean);
return bean;// 返回传入的原始bean,或者进行了包装之后的bean
}
}
@ComponentScan("com.atguigu.bean")
@Configuration
public class MainConfigOfLifeCycle {
}
测试类:
public class IOCTest_LifeCycle {
@Test
public void test01(){
//1、创建ioc容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfLifeCycle.class);
System.out.println("容器创建完成...");
//关闭容器
System.out.println("容器进行关闭...");
applicationContext.close();
}
}
运行结果为:
car constructor
postProcessBeforeInitialization…car=>com.atguigu.bean.Car@17e8de
car…init…
postProcessAfterInitialization…car=>com.atguigu.bean.Car@17e8de
容器创建完成
容器进行关闭
car…destory…
具体的执行过程(BeanPostProcessor原理)
AbstractAutowireCapableBeanFactory.doCreateBean中调用popolulateBean为bean填充属性,
然后调用initializeBean开始进行bean的初始化以及bean初始化的前后处理。
该方法(initializeBean)会先调用applyBeanPostProcessorsBeforeInitialization(warppedBean, beanName);进行初始化前前置处理(applyBeanPostProcessorsBeforeInitialization会遍历所有的BeanPostProcessor,执行每个BeanPostProcessor的postProcessBeforeInitialization方法,如果某个BeanPostProcessor::postProcessBeforeInitialization返回null,则直接退出循环,不再执行后面的BeanPostProcessor。)
然后调用invokeInitMethods对bean进行初始化
最后调用applyBeanPostProcessorsAfterInitialization,进行初始化后置处理。
总结
就相当于我们之前总结顺序初始化前后多了操作
构造(对象创建)
单实例:在容器启动的时候创建对象
多实例:在每次获取的时候创建对象
BeanPostProcessor.postProcessBeforeInitialization
初始化:
对象创建完成,并赋值好,调用初始化方法。。。
BeanPostProcessor.postProcessAfterInitialization
销毁:
单实例:容器关闭的时候
多实例:容器不会管理这个bean;容器不会调用销毁方法;
三、属性赋值
补充知识:什么是SpEL
Spring Expression Language(简称 SpEL)是一种功能强大的表达式语言,支持运行时查询和操作对象图 。表达式语言一般是用最简单的形式完成最主要的工作,以此减少工作量。
SpEL 并不与 Spring 直接相关,可以被独立使用。SpEL 表达式的创建是为了向 Spring 社区提供一种受良好支持的表达式语言,该语言适用于 Spring 家族中的所有产品。也就是说,SpEL 是一种与技术无关的 API,可以集成其它表达式语言。
SpEL对Bean定义的支持
SpEL 表达式可以与 XML 或基于注解的配置元数据一起使用,SpEL 表达式以#{开头,以}结尾,如#{‘Hello’}。
1.@Value
赋值可以有三种方式
① 基本数值:@Value(“张三”)
②SpEL :@Value(#{1 + 2})
③配置文件、环境变量中的值:@Value(${os.name})
2.@PropertySource
读取外部配置文件中的k/v保存到运行环境中,结合@value使用,或使用@ConfigurableEnvironment获取(但是@ConfigurableEnvironment获取的是默认配置文件的属性值,所以如果想获得别的配置文件的属性值就得用@PropertySource )
@PropertySource的value属性可以传多个路径。
四、自动装配
什么是自动装配?
自动装配就是让应用程序上下文为你找出依赖项的过程。说的通俗一点,就是Spring会在上下文中自动查找,并自动给bean装配与其关联的属性
Spring自动装配有两类:
基于xml文件的自动装配:byType(类型),byName(名称), constructor(根据构造函数)
基于注解的自动装配:@Autowired,@Resource…
1.@Autowired
在使用@Autowired时,首先在容器中查询对应类型的bean
如果查询结果刚好为一个,就将该bean装配给@Autowired指定的数据;
如果查询的结果不止一个,那么@Autowired会根据名称来查找;
如果查询的结果为空,那么会抛出异常。解决方法时,使用required=false。
@Autowired有一个属性required,默认为true。如果设置为false,则该对象可以在获取不到bean时默认为null。
出现位置:可以在属性上,也可以在方法、参数上。
①标注在属性(即变量)上
②标注在方法上
Spring容器创建当前对象,就会调用方法,完成赋值。方法使用的参数,自定义类型的值从ioc容器中获取。
@Autowired
public void setCar(Car car) {
this.car = car;
}
③标注在有参构造器上
默认加在ioc容器中的组件,容器启动会调用无参构造器创建对象,再进行初始化赋值等操作。
所以使用@Autowired要给bean移除无参构造器,添加一个有参构造器,为该有参构造器添加@Autowired注解。这样Spring容器启动时会调用该有参构造器,并从ioc容器中获取参数对应类型的bean对象进行注入。
@Autowired
public Boss(Car car) {
this.car = car;
}
④ 标注在构造器参数位置
注意:在使用注解进行注入时,变量的setter方法就不是必须的了
2. @Qualifier
在按照类型匹配的基础上,再按照名称匹配注入。它在给类的成员变量注入时,不能单独使用,要和@Autowired配合使用。它在给方法参数进行注入时,可以单独使用。
属性:value:用于指定要注入的bean的id。
3. @Primary
让Spring自动装配时默认使用首选的Bean。配合@Bean使用
当某个bean在容器中存在多个同类型bean,且使用@Autowired时没有明确指定@Qualifier,则默认使用@Primary首选转配。也可以继续使用@Qualifier指定装配的Bean的名字。
4.@Resource
直接按照bean的id进行注入。它可以独立使用。
属性:name:用于指定bean的id。
不能支持@Primary 以及required=false的功能
5.@Inject
使用前要导入依赖
<dependency>
<gourpId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
和@Autowired功能类似,但是没有required属性。
6.XXXAware接口
Aware接口从字面上翻译过来是感知捕获的含义。
单纯的bean(未实现Aware系列接口)是没有知觉的;
实现了Aware系列接口的bean可以访问Spring容器。
也就是说Aware系列接口主要用于辅助Spring bean访问Spring容器
这些Aware系列接口增强了Spring bean的功能,但是也会造成对Spring框架的绑定,增大了与Spring框架的耦合度。
spring在创建对象的时候,会帮我们自动注入。spring通过BeanPostProcessor机制来实现XXXXAware的自动注入。
Aware系列接口的共性
都以“Aware”结尾
都是Aware接口的子接口,即都继承了Aware接口
接口内均定义了一个setXXX方法,而方法中的形参是接口Aware前面的内容,也就是当前Bean需要感知的内容。所以我们需要在Bean中声 明相关的成员变量来接收。
例如:在Bean中注入ioc容器ApplicationContext,实现ApplicationContextAware接口
public class Car implements ApplicationContextAware {
private ApplicationContext applicationContext;// 保存感知的信息
public Car() {
System.out.println("Car constructor...");
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("为car注入applicationContext");
this.applicationContext = applicationContext;
}
}
7.@Profile
为什么要使用该注解:
@profile注解是spring提供的一个用来标明当前运行环境的注解。我们正常开发的过程中经常遇到的问题是,开发环境是一套环境,测试是一套环境,线上部署又是一套环境。这样从开发到测试再到部署,会对程序中的配置修改多次。
为了解决上面多环境开发的问题,我们一般会使用一种方法,就是配置不同的文件,然后通过读取不同的配置文件,从而实现在不同的场景中跑我们的程序。(即实现不同的开发环境使用不同的数据源)
加了@Profile环境标识的bean,只有这个环境被激活的时候才能被注册到容器中。
结合@Bean使用,默认为default环境,可以通过命令行参数来切换环境
//spring3.2之前 @Profile注解用在类上
//spring3.2 之后 @Profile注解用在 方法上
@Profile("test") // value可以随便自定义
@Bean("color")
public Color color() {
return new Color();
}
注意:
若@Profile在类上,假设此时激活的环境为dev:虽然devDataSource这个bean上配置的环境是dev,但是因为整个配置类的环境是test,所以整个配置类都不进行加载,devDataSource也不会进行加载。
扩展原理
BeanFactoryPostProcessor
BeanDefinitionRegistryPostProcessor
ApplicationListener
Spring容器创建过程
Web
servlet3.0
异步请求