Spring 第二 随笔记
【spring除了XML配置外,还有注解类配置,这个就主要讲注解类配置】
第一部分 : 往容器中添加组件的几种方式。。
package com.abc.config; import com.abc.condition.LinuxCondition; import com.abc.condition.WindowsCondition; import com.abc.domain.Color; import com.abc.domain.Person; import com.abc.domain.Student; import com.abc.myTypeFilter; import org.springframework.context.annotation.*; import org.springframework.context.annotation.ComponentScan.Filter; /** * excludeFilters 排除过滤容器的对象 * includeFilters 包含容器对象 */ /** * 一个 ComponentScans 【这个带 s 哦】 里面有个属性为 value,value是个集合,可以有多个ComponentScan属性,然后再...... * * 【value = "com.abc" ;它的作用是扫描这个包下的,但是是否加入包含还是排斥,需要后面的代码了】 * * excludeFilters:不包含过滤-->即 Filter注解上的都不加载进容器 * includeFilters: 包含过滤---->即 Filter注解上的才会加载进容器 * FilterType.ANNOTATION 表示注解类型的 即例如 Controller.class,Component.class ,Service.class 等等类型 * FilterType.ASSIGNABLE_TYPE 表示指定类型的 即如我自己写的Student类型【Student.class】 StudentController类型【StudentController.class】等等 * FilterType.ASPECTJ 表示Aspect类型的 ,几乎不用 * FilterType.REGEX 表示满足正则表达式的, * FilterType.CUSTOM 表示使用自定义规则 创建实现TypeFilter实现类,若返回true,加入容器,返回false,不让加入容器 */ /*@ComponentScans(value = { @ComponentScan(value = "com.abc",excludeFilters = { @Filter(type = FilterType.ANNOTATION,classes = {Controller.class}) }) })*/ /*@ComponentScans(value = { @ComponentScan(value = "com.abc",includeFilters = { @Filter(type = FilterType.ASSIGNABLE_TYPE,classes = {Student.class}) }) })*/ @Configuration @ComponentScans(value = { @ComponentScan(value = "com.abc",excludeFilters = { @Filter(type = FilterType.CUSTOM,classes = {myTypeFilter.class}) },useDefaultFilters = false) }) /** * 【使用第三种方式给容器导入组件使用---->>Import】 */ @Import(Color.class) public class myConfig { /** * @Scope作用是否是单例 * * singleton: 单例的(默认值)Ioc容器启动会调用方法创建对象放到Ioc容器中,以后调用直接从容器中拿 * prototype: 多实例的,Ioc容器并不会去调用方法创建对象放在容器中 * request: 同意一次请求创建一个实例 * session: 同一个Session会话 */ @Scope("singleton") @Bean public Student student(){ Student student = new Student("张三", "男", 23); return student; } /** * @Conditional * @return */ @Conditional(WindowsCondition.class) @Bean("bill") public Person person01(){ return new Person("李四",24); } @Conditional(LinuxCondition.class) @Bean("linux") public Person person02(){ return new Person("王五",25); } /** * 使用第三种方式给容器导入组件使用---->>Import * 1. 在配置类上加 Import(类的class对象)这是个数组,可以多个 * 2. 还可以实现 【ImportSelector】 接口,然后在实现类添加需要加入容器的组件,注意加入的是 类的全限定名 * 3. 还可以实现ImportBeanDefinitionRegistrar 接口:手动注册 */ }
【使用自定义规则】
import org.springframework.core.io.Resource; import org.springframework.core.type.AnnotationMetadata; import org.springframework.core.type.ClassMetadata; import org.springframework.core.type.classreading.MetadataReader; import org.springframework.core.type.classreading.MetadataReaderFactory; import org.springframework.core.type.filter.TypeFilter; import java.io.IOException; public class myTypeFilter implements TypeFilter { /** * metadataReader : 读取到的当前类扫描到的信息 * metadataReaderFactory: 可以获取到其他任何类型的 */ @Override 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(); /* if (className.contains("er")){ System.out.println(" *********************"+className); return true; }*/ return false; } }
【小知识 0.1】
@Scope()// 1 .默认是 singleton 但是可以改变使用 prototype变成多实例的
singleton: 单例的(默认值)Ioc容器启动会调用方法创建对象放到Ioc容器中,以后调用直接从容器中拿
【懒加载】:容器启动不创建对象,第一次使用(获取)Bean创建对象,并初始化
prototype: 多实例的情况,Ioc容器启动并不会去调用方法创建对象放在容器中,而是每次获取的时候才会调用方法新创建对象
每次调用都会新的 new一个
@Scope()// 1 .默认是 singleton 但是可以改变使用 prototype变成多实例的
@Bean public Person person(){ return new Person("张三",23); }
一:往容器中加组件的七种方式
0:包扫描+组件标注注解(@Controller、@Service、@Repository、@Component)
1.注解@Bean
2.注解@Condition ------加条件
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.env.Environment; import org.springframework.core.type.AnnotatedTypeMetadata; public class LinuxCondition implements Condition { /** * ConditionContext :上下文信息 * AnnotatedTypeMetadata:获取当前类注解信息 */ @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { //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"); if (property.contains("linux")){ return true; } return false; } }
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.env.Environment; import org.springframework.core.type.AnnotatedTypeMetadata; public class WindowsCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { //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"); if (property.contains("Windows")){ return true; } return false; } }
3.注解@Import 【集合:可以多个】
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @Configuration @Import(Color.class,Person.class) //导入组件,id默认是组件的全类名 public class MainConfigration { }
4.实现 ImportSelector 接口
//自定义组件 public class MyImportSelector implements ImportSelector { //返回值就是导入组件的全类名 //AnnotationMetadata:当前标注@Import注解类的所有信息,还能获取其他注解 // 比如: //@Configuration //@Import({Color.class, Person.class,MyImportSelector.class}) @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { return new String[]{"aynu.test3.Color","aynu.test2.Person"};//方法不能返回null } }
5.实现 ImportBeanDefinitionRegistrar 接口
import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; import org.springframework.core.type.AnnotationMetadata; public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { /** * AnnotationMetadata: 当前类大注解信息 * BeanDefinitionRegistry: BeanDefinition注册类 * 把所有需要添加到容器的中的Bean: 调用 * BeanDefinitionRegistry.registryBeanDefinition手工注册进来 */ @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { boolean red = registry.containsBeanDefinition("red"); boolean blud = registry.containsBeanDefinition("blud"); //如果容器中有 【red】 和【blud】两个组件,就把下面的组件加入容器 if (red && blud){ RootBeanDefinition beanDefinition = new RootBeanDefinition(RainBow.class); //注册一个Bean,并且制定bean名称 registry.registerBeanDefinition("raidBow",beanDefinition); } } }
6.实现 FactoryBean<> 接口
import com.abc.domain.Color; import org.springframework.beans.factory.FactoryBean; public class myFactoryBean implements FactoryBean<Color> { //返回一个对象,这个对象会被添加到容器中 @Override public Color getObject() throws Exception { return new Color(); } //从容器拿出组件时的类型 @Override public Class<?> getObjectType() { return Color.class; } //报存是否为单例 ,如果是true 就是单例,返回false就是非单例 @Override public boolean isSingleton() { return false; } }
第二部分 : bean的生命周期
第一种方式:1)。指定初始化和销毁的方法 , 指定init-method 和 destroy-method
import org.junit.Test; import org.springframework.context.annotation.AnnotationConfigApplicationContext; /** * 构造 ; * 单实例:容器启动的时候创建对象 * 多实例:在每次获取的时候创建对象
* 初始化:
* 对象创建完成,并赋值好,调用初始化方法
* 销毁:
* 单实例:容器关闭的时候
* 多实例:容器不会管理这个bean:容器不会调用销毁 * 1)。指定初始化和销毁的方法 * 指定init-method 和 destroy-method * */ public class IocTest { @Test public void fun1(){ AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(MianConfigLeftCycle.class); System.out.println("容器创建完成"); //关闭容器 ac.close(); } }
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class MianConfigLeftCycle { //如果此处加上注解 @prototype 这就是多实例化,就会在调用的时候才创建 @Bean(initMethod = "init",destroyMethod = "destory") public Car car(){ return new Car(); } } public class Car { public Car(){ System.out.println("生成 Car 了......."); } public void init(){ System.out.println("初始化完成......"); } public void destory(){ System.out.println("销毁完成....."); } }
第二种方式:通过让Bean实现 InitializingBean(定义初始化逻辑),
DisposableBean(定义销毁逻辑)
@Component public class Car implements InitializingBean, DisposableBean { public Car(){ System.out.println("生成 Car 了......."); } @Override public void destroy() throws Exception { System.out.println("销毁完成......."); } @Override public void afterPropertiesSet() throws Exception { System.out.println("创建完成.........."); } } import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration @ComponentScan("com.bcd") public class MianConfigLeftCycle { @Bean public Car car(){ return new Car(); } }
第三种:JSR250装饰,
@PostConstruct:在Bean 创建完成并且属性赋值完成:来执行初始化方法
@PreDestory: 在容器销毁Bean之前通知我们进行清理工作
public class IocTest { @Test public void fun1(){ AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(MianConfigLeftCycle.class); System.out.println("容器创建完成"); //关闭容器 ac.close(); } } //打印结果 生成 Car 了....... 销毁完成.......... dog 初始化完成 Dog @PostConstruct 初始化完成 容器创建完成 Dog @PreDestroy 销毁完成 初始化完成.......】】】】】】】 import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; @Component public class Dog { public Dog(){ System.out.println("dog 初始化完成"); } //对象创建并赋值之后调用 @PostConstruct public void init(){ System.out.println("Dog @PostConstruct 初始化完成"); } //销毁之前调用 @PreDestroy public void destory(){ System.out.println("Dog @PreDestroy 销毁完成"); } } @Configuration @ComponentScan("com.bcd") public class MianConfigLeftCycle { @Bean public Car car(){ return new Car(); } }
第四种:BeanPostProcessor :bean的后置处理器
在Bean初始化前后进行一些处理工作
postProcessBeforeInitialization :在初始化调用之前调用 init
postProcessAfterInitialization : 在初始化调用之后调用 init
import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.stereotype.Component; @Component//加入容器 public class MyBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("postProcessAfterInitialization"+beanName+"=="+bean); return bean; } @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("postProcessBeforeInitialization"+beanName+"=="+bean); return bean; } } 【读读源码】 /* * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.beans.factory.config; import org.springframework.beans.BeansException; import org.springframework.lang.Nullable; /** * Factory hook that allows for custom modification of new bean instances, * e.g. checking for marker interfaces or wrapping them with proxies. * * <p>ApplicationContexts can autodetect BeanPostProcessor beans in their * bean definitions and apply them to any beans subsequently created. * Plain bean factories allow for programmatic registration of post-processors, * applying to all beans created through this factory. * * <p>Typically, post-processors that populate beans via marker interfaces * or the like will implement {@link #postProcessBeforeInitialization}, * while post-processors that wrap beans with proxies will normally * implement {@link #postProcessAfterInitialization}. * * @author Juergen Hoeller * @since 10.10.2003 * @see InstantiationAwareBeanPostProcessor * @see DestructionAwareBeanPostProcessor * @see ConfigurableBeanFactory#addBeanPostProcessor * @see BeanFactoryPostProcessor */ public interface BeanPostProcessor { /** * Apply this BeanPostProcessor to the given new bean instance <i>before</i> any bean * initialization callbacks (like InitializingBean's {@code afterPropertiesSet} * or a custom init-method). The bean will already be populated with property values. * The returned bean instance may be a wrapper around the original. * <p>The default implementation returns the given {@code bean} as-is. * @param bean the new bean instance * @param beanName the name of the bean * @return the bean instance to use, either the original or a wrapped one; * if {@code null}, no subsequent BeanPostProcessors will be invoked * @throws org.springframework.beans.BeansException in case of errors * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet */ @Nullable default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } /** * Apply this BeanPostProcessor to the given new bean instance <i>after</i> any bean * initialization callbacks (like InitializingBean's {@code afterPropertiesSet} * or a custom init-method). The bean will already be populated with property values. * The returned bean instance may be a wrapper around the original. * <p>In case of a FactoryBean, this callback will be invoked for both the FactoryBean * instance and the objects created by the FactoryBean (as of Spring 2.0). The * post-processor can decide whether to apply to either the FactoryBean or created * objects or both through corresponding {@code bean instanceof FactoryBean} checks. * <p>This callback will also be invoked after a short-circuiting triggered by a * {@link InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation} method, * in contrast to all other BeanPostProcessor callbacks. * <p>The default implementation returns the given {@code bean} as-is. * @param bean the new bean instance * @param beanName the name of the bean * @return the bean instance to use, either the original or a wrapped one; * if {@code null}, no subsequent BeanPostProcessors will be invoked * @throws org.springframework.beans.BeansException in case of errors * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet * @see org.springframework.beans.factory.FactoryBean */ @Nullable default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { return bean; } } 打印结果 postProcessBeforeInitializationmianConfigLeftCycle==com.bcd.MianConfigLeftCycle$$EnhancerBySpringCGLIB$$f7ef0e67@75f32542 postProcessAfterInitializationmianConfigLeftCycle==com.bcd.MianConfigLeftCycle$$EnhancerBySpringCGLIB$$f7ef0e67@75f32542 生成 Car 了....... postProcessBeforeInitializationcar==com.bcd.Car@29176cc1 初始化完成.......... postProcessAfterInitializationcar==com.bcd.Car@29176cc1 dog 初始化完成 postProcessBeforeInitializationdog==com.bcd.Dog@77167fb7 Dog @PostConstruct 初始化完成 postProcessAfterInitializationdog==com.bcd.Dog@77167fb7 容器创建完成 Dog @PreDestroy 销毁完成 销毁完成.......
第三部分:属性赋值
第一种:使用@Value赋值
import org.junit.Test; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class TestpropertyValues { AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(MianConfigpropertyValues.class); @Test public void fun1(){ System.out.println("容器创建完成"); String[] beanDefinitionNames = ac.getBeanDefinitionNames(); for (String beanDefinitionName : beanDefinitionNames) { System.out.println(beanDefinitionName); } Object person = ac.getBean("person"); System.out.println(person); //关闭容器 // ac.close(); } } package com.cdf; import org.springframework.beans.factory.annotation.Value; public class Person { //使用@Value赋值 /** * 1 .基本类型 * 2 。可以写SPEL;#{} * 3 .可以用${} , 从配置文件获取 */ @Value("张三") private String name; //@Value("23") private Integer age; @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Person() { } public Person(String name, Integer age) { this.name = name; this.age = age; } } import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; //使用@PropertySource读取外部配置文件中的K/V保存到运行的环境变量中: //加载完外部的配置使用${}获取 /** * ConfigurableEnvironment environment = ac.getEnvironment(); * String age = environment.getProperty("age"); * 用这个方法也是可以的,默认都加载到环境变量 */ @PropertySource(value = {"class:/person.properties"}) @Configuration public class MianConfigpropertyValues { @Bean public Person person(){ return new Person(); } }
第二种:用@PropertySource读取外部配置文件中的K/V保存到运行的环境中;加载完外部的配置文件以后
使用${}去除配置文件的值
第四部分:自动装配
Spring利用依赖注入(DI)完成对IOC容器各个组件的赋值
第一种:使用Autoware
/**
* 1)自动装配Autowared
* 1. 默认优先按照类型去容器中找对应的组件:applicationContext.getBean(Person.class);
* 2.如果找到多个类型相同的组件,再将属性的名称作为组件的Id去容器查找
* applicationContext.getBean("personDao");
* 3.使用 @Qualifier("persondao");,可以明确制定去组件ID
* 4.只要使用自动装配,就一定要将属性赋值好,如果容器没有就会报错
* 5. @Primary ,让Spring进行自动装配的时候,默认首选的是Bean
* 也可以继续使用 @Qualifier指定需要装配的Bean的名字
* 【注意】
* Autowared(required=false) 指定为false 就不必须了会默认赋值为null
*/
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; /** * 1)自动装配Autowared * 1. 默认优先按照类型去容器中找对应的组件:applicationContext.getBean(Person.class); * 2.如果找到多个类型相同的组件,再将属性的名称作为组件的Id去容器查找 * applicationContext.getBean("personDao"); * 3.使用 @Qualifier("persondao");,可以明确制定去组件ID * 4.只要使用自动装配,就一定要将属性赋值好,如果容器没有就会报错 * 5. @Primary ,让Spring进行自动装配的时候,默认首选的是Bean, 就是在类上加上@primary * 也可以继续使用 @Qualifier指定需要装配的Bean的名字 * 【注意】 * Autowared(required=false) 指定为false 就不必须了会默认赋值为null */ @Configuration @ComponentScan("com.def") public class MainConfigAutoware { @Bean("persondao2") public PersonDao personDao(){ return new PersonDao(); } } @Controller public class PersonCtroller { @Autowired private PersonService personService; } import org.springframework.stereotype.Repository; @Repository public class PersonDao { } import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class PersonService { @Autowired private PersonDao personDao; public void print(){ System.out.println(personDao); } @Override public String toString() { return "PersonService{" + "personDao=" + personDao + '}'; } } import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class Test { @org.junit.Test public void test1(){ AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(MainConfigAutoware.class); PersonService bean = ac.getBean(PersonService.class); System.out.println(bean); PersonDao bean1 = ac.getBean(PersonDao.class); System.out.println(bean1); } }
【特殊 一 】 @Autowared 不仅可以标注 属性位置,还可以构造器、参数、属性、方法
@Autowire //标注方法上,Spring容器创建当前对象,就会调用方法,完成赋值
//标注方法上,Spring容器创建当前对象,就会调用方法,完成赋值
//方法使用的参数,自定义类型的值从IOC容器中获取
@Autowired
public void setCar(Car car) {//这个Car就是从容器中获取
this.car = car;
}
//放在有参构造器上
@Autowired public Boos(Car car) { this.car = car; System.out.println("00有参构造器00"); }
//放在参数上
public Boos(@Autowired Car car) { this.car = car; System.out.println("00有参构造器00"); }
//@Bean 标注的方法创建对象的时候,方法参数从容器中获得 @Bean public Person person(Person person){ return new Person(); }
【特殊 二 】 自定义组件想要使用Spring容器底层的一些组件(ApplicationContext、BeanFactory、等等 ....);
只需要自定义实现 xxxxAware ,在创建对象的时候,会调用接口规定的方法注入相关组件
xxxAware,功能使用xxxProcessor,
ApplicationContextAware ==> ApplicationContextAwareProcessor,
第二种 使用@Resource(JSR250) 和 @Inject(JSR330) [java 规范注解]
@Resource
可以和Autoware 一样,默认按照属性名称装配
没有能支持@Primary 功能没有,支持@Autoware(reqiured = false)
@Resource(name="bookDao") private BookDao bookDao;
@Inject 使用需要导入依赖
<artifactid>javax.inject</artifactid>
@Inject //和 Autoware 的功能一样,但是不支持required=false这个功能
private BookDao bookDao;
AutowiredAnnotationBeanPostProcessor 后置处理器,解析完成自动装配过程
第五部分 : Profile
Spring为我们提供了可以根据当前环境,动态激活和切换一系列组件n的功能
开发环境、测试环境、生产环境
数据源(/A)(/B)(/C)