Spring带给了我们什么?#
假如我们是第一次学习Spirng,我们大可不必关心spring带给我了我们什么便利,因为spring大概是web阶段筑基需要学习的第一个主流框架,初学难免会遇见各种各样的错误,所以尽管放心大胆的去学习如何使用就行了.先会用,其它的不用多想
过几天,用着熟悉了如何使用,再考虑spring究竟带给了我们什么便利也不迟, 那么,spring究竟带给了我们什么便利呢?
- IOC(Inverse of Control),把对象的创建权反转给Spring容器,实现了解耦
我们使用Spring提供给我们的注解,把bean注册进IOC容器,进而把bean之间的依赖关系全部交给Spring管理,现在想想这绝对是一件超级赞的事情,可能原来的我会想,我自己可以new对象,干嘛非让Spring插一脚,是,假如我的项目就是个小demo全文一共三对象,完全顾的上来,那么spring对我来说就是累赘,但是!慢慢的接触的工程大了就会发现,离开了Spring,自己去管理那些对象之间的依赖会累死人的,而且SpringIOC的诞生也应正了bean的管理,完全不需要我们关系,我们关心的就是,我们如何向Spring索取依赖的Bean就行了,所以说Spring真的是web爱好者的小春天.
- AOP(Aspect Oriented Programming),spring的aop
面向切面编程,aop说白了就是代码复用,把大量的重复代码从我们的业务逻辑中抽取出去,等程序运行的时候再动态的植入抽取出去的代码,这是件要多优雅就多优雅的事情!
应用:
- 日志记录
- 权限校验
- 效率检查
- 事务管理
- 声明式事务
后端天天和数据库打交道,事务安全这样的不可能遇不见,我们这一代学习的人还真的是幸运,因为spring提供的声明式事务,我们不用再去苦苦的去写编程式事务,而且,现在spring4全面支持注解开发,我们甚至连配置文件都不用写,加一个注解就引入了spring的事务模块,激动不?
- 方便集成其他开源框架
spring最擅长的事情就是整合这使它的生态变的超级庞大
例外给大家推荐一篇很棒的博客,里面详细图文论述了,spring究竟带给了我们什么?
注解版Spring-IOC怎么玩?#
组件注册#
只使用IOC,导入Spring-Context就可以
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.3.7.RELEASE</version> </dependency>
这个过程,我们要注意IOC以下几点
- 组件注册的过程中有哪些过滤规则?
- 如何控制组件的作用域(单例多例)?
- 六种注册组件的方式?
组件注册及其过滤规则我串联在下面相邻的两部分里,这里先提一下Spring提供的所有过滤规则类型
在spring启动,开始扫描包,注册组件时,如果我们想按照我们的需求往IOC里面添加组件,就需要指定过滤规则,下面这五种类型的过滤规则,都源于我们在主配置类(相当于配置文件)上添加的@ComponentScan()
包扫描注解
- 按照注解过滤
ANNOTATION
@ComponentScan(// 这个注解替换了原来配置文件中的包扫描 value="com.changwu.tryspring", useDefaultFilters=false, includeFilters = { @Filter(type = FilterType.ANNOTATION, classes = Service.class)} )
- 按照给定的类型过滤
@Configuration @ComponentScan(// 这个注解替换了原来配置文件中的包扫描 value="com.changwu.tryspring", useDefaultFilters=false, includeFilters = { //只要是给定的类型的类,就可以加载进IOC,包括它的子类 @Filter(type = FilterType.ASSIGNABLE_TYPE,classes = BookDao.class) } )
- 按照切面 (ASPECTJ) 基本不用
- 按照正则 REGEX
- 自定义规则 CUSTOM
@Configuration @ComponentScan(// 这个注解替换了原来配置文件中的包扫描 value="com.changwu.tryspring", useDefaultFilters=false, includeFilters = { // 只要是给定的类型的类,就可以加载进IOC,包括它的子类 @Filter(type = FilterType.CUSTOM,classes = MyTypeFilter.class) } )
注解版本中,配置类替换了原来的配置文件#
@Configuration
注解标记本类为配置类
那么我不加
@Configuration
注解,这个类就不能成为配置类吗? 里面通过@Bean
注解就添加不进bean来了? 不是的,@Configuration
标记会被Spring使用Cglib技术拦截下来,换言之我们得到的bean,不再是我们自己new 的那种小白bean,而是被代理过的Bean那么什么才是百分百的配置类呢? 配置类是我们调用如下代码所需要的那个类
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); applicationContext.register(配置类.class);
如下代码! 原来的包扫描被@ComponentScan
取代!!! 里面可以配置多个信息
- 基础包信息
value="com.XXX"
- 排除被哪个注解标记的类
excludeFilters = @Filter(type... classes...)
- 只要被哪个注解标记的类,(配置文件版本的分两步!第一步禁用掉默认的过滤器),注解版同样分两步
useDefaultFilters=false, includeFilters = @Filter(classes= Service.class)
excludeFilters 把bean排除出IOC容器!!! 一般都是按照注解, 后面是 !!注解!!的Class数组,
另外,过滤器Filter的过滤类型,默认为按照注解过滤!!!
还可以在配置类中往IOC容器中组成对象!!!@Bean
,用的时候 @Autowired
默认情况下,无论获取多少次,都是单实例的!!!
/** * 配置类,等同于原来的配置文件 * * @Author: Changwu * @Date: 2019/3/31 16:57 */ @Configuration @ComponentScan(// 这个注解替换了原来配置文件中的包扫描 value="com.changwu.tryspring", excludeFilters = @Filter( type =FilterType.ANNOTATION, classes = Repository.class) ) // excludeFilters 把bean排除出IOC容器!!! 一般都是按照注解, 后面是 !!注解!!的Class数组, public class MainConfig { /** * 给容器注册bean * 类型: 为返回值的类型 * id 默认使用方法名 * @return */ @Bean(value = "stu") // value可以自定义名字 public Student getStu(){ return new Student("张三",23); } }
此外在java8中,有如下代码,@ComponentSacn
的倒数第二个注解是@Repeatble
意味着我们可以在主配置类上写多个@ComponentScan
*/ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Repeatable(ComponentScans.class) public @interface ComponentScan {...}
自定义TypeFilter
的过滤规则#
自定义注解,要求实现FilterType
接口,实现他的match()
,该方法里面的两个参数都是接口类型的,我们可以从它里面取出关于IOC所扫描到的所有类的源信息,满足我们的预期,返回true,表示允许注册进IOC
public class MyTypeFilter implements TypeFilter { /** * * @param metadataReader // 获取到当前的正在扫描的类的信息 * @param metadataReaderFactory // 可以获取到,其他任何类的信息 * @return * @throws IOException */ @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(); System.out.println("---->"+className); // 自定义,如果有@Repository注解,注入进去 Set<String> annotationTypes = annotationMetadata.getAnnotationTypes(); for (String annotationType : annotationTypes) { System.out.println("++++++"+annotationType); } if (annotationMetadata.hasAnnotation("org.springframework.stereotype.Service")){ return true; } /* if (className.contains("er")){ return true; }*/ return false; } }
@Scope, 设置组件的作用域#
容器中的对象默认都是单实例的!!!,我们使用@Scope
注解改变这种状态
/* String SCOPE_SINGLETON = "singleton"; 单例 String SCOPE_PROTOTYPE = "prototype"; 多例 */ @Scope(scopeName = "prototype") @Bean(value = "stu2") public Student getStu2(){ return new Student("李四",24); } }
观察在单实例和多实例情况下,bean创建的时机
- 默认在单实例的情况下,ioc容器创建完,直接加载bean
但是以后每次获取都会都是从IOC容器中获取
懒加载
@Lazy
! 针对单实例Bean,我们知道,容器启动的时候,就会创建一个唯一的bean,我们使用懒加载可以做到,在第一次使用的时候加载bean
- 多实例情况下,只有在获取类时,IOC容器才会创建bean,!!!
以后每次获取Bean,ioc容器都会调用那个方法去new Bean
六种给容器注册组件的方法#
着重看一种,按照条件注册bean @Conditional
springboot底层大量的使用!!!#
它的实现方式和自定义过滤条件相似
@Conditional : 按照条件注册bean
// 既可以标在类上,也可以标在方法上 @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD}) public @interface Conditional { /** * 所有的条件,都必须是注册过的!!!. */ Class<? extends Condition>[] value(); }
解释一下怎么用:
- 它是个接口,需要的属性名为
value
, 值是Class数组
,可以进一步看到它的泛型是Condition接口
我们自己实现接口,重写里面的方法match()
,根据方法提供的参数可以获取到我们想要的任何信息,返回true表示满足条件,注册bean,否则不注册
其他注册组件的方法
- 方式1: 包扫描+注解 (@Cotroller @Service @Repository等等注解的类可以被IOC扫描到,添加进容器)
这种方法有局限性!!!, 只能是我们自己写的类, id为首字母小写的类名
- 方式2: 在配置类内部 使用:
@Bean
注解,
更多的使用它注册第三方bean
- 方式3: 在配置类头上使用
@Import(XXX.class,YYY.class)
解放了第二种通过@Bean还的自己new的缺陷!!! 组册进IOC中的组件的默认id,是组件的全类名
- 方式4:
@Import({Color.class, MyImportSelector.class})
实现ImportSelector接口: 返回需要导入的组件的全类名数组,完成批量导入
- 方式5: 手动注册
@Import({Color.class,MyImportSelector.class, MyImportBeanDefinitionRegisrar.class})
实现ImportBeanDefinitionRegistrar接口: 它里面的
registerBeanDefinitions()
方法的第二个参数就是BeanDefinitionRegistry
, 所有bean注册进IOC容器都经由他完成,因此我们可以手动注册bean, 还可以通过第一个参数获取当前标注@Import
注解的类的全部注解信息,加上第二个参数可以获取当前IOC容器的全部信息,动态判断是否要注入类到IOC同时,第五种在SpringBoot中得到了大量的使用,实现SpringBoot的自动配置
- 方式6 : 使用Spring提供的
FactoryBean
(工厂bean)
- 自己实现FactoryBean接口,重写三个方法
- @Bean,把自己实现的工厂bean添加到IOC
- 测试自己实现的BeanFactory的类型,是我们指定的泛型的类型的!!!
- 想获取到工厂的话, 需要添加前缀&
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig02.class); Object getFB1 = applicationContext.getBean("&getFB"); Object getFB2 = applicationContext.getBean("getFB");