spring(一):spring的基础以及组件
spring简介
spring是一种开源轻量级框架,是为了解决企业应用程序复杂性而创建的
spring是企业应用开发的“一站式”框架,致力于为javaEE应用的各层(表现层、业务层、持久层)开发提供解决方案,而不仅仅是某一层的解决方案
spring并不会代替原有的那些框架,而是以高度的开放性,与已存在的框架进行整合
通过spring技术,不需要重复制造轮子,在已有较好解决方案的技术领域绝不重复实现。例如,对象持久化和OR映射,spring只对现有的JDBC、Hibernate等技术提供支持,使之更加便于使用,不需要做重复的实现
spring组成结构
spring core:spring核心,是框架最基础的部分,提供spring IOC和依赖注入的功能
spring context:spring上下文容器,是对BeanFactory功能增强的一个子接口
spring web:spring的web模块,提供了对web应用开发的支持
spring mvc:针对web应用mvc思想的实现
spring orm:支持对流行ORM框架的整合,mybatis、hibernate
spring dao:提供对JDBC的抽象,简化JDBC编码
spring aop:面向切面编程,提供了与AOP联盟兼容的编程实现
spring常用组件
@Configuration
在类上使用,指定该类为配置类,相当于配置文件
@Bean
在方法上使用,向容器注册一个bean,返回值是bean的类型,bean的id默认是方法名,可以设置bean的id @Bean(name)
/** * 配置类,相当于配置文件的作用 * @author qf * @create 2019-05-20 9:55 */ @Configuration public class MainConfig { /** * 向容器注册一个bean,返回类型为bean的类型 * 默认方法名是bean的id名,可以通过配置@Bean(yoursName)设置id * @return */ @Bean("wxf") public Person person(){ return new Person("wxf",19); } }
AnnotationConfigApplicationContext
根据注解配置类获取spring的IOC容器
@Test public void testConfig(){ /** * 注解配置来获取spring IOC容器 */ ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class); Person person = (Person) context.getBean("wxf"); System.out.println(person); }
@ComponentScan
设置扫描规则,指定要扫描的包,扫描带有@Controller、@Service、@Repository以及@Component注解的类
value:指定要扫描的包
excludeFilters:Filters[],指定扫描的时候按照什么规则排除哪些组件
includeFilters:Filters[],指定扫描时按照什么规则只包含哪些组件
扫描规则
FilterType.ANNOTATION:按照注解
FilterType.ASSIGNABLE_TYPE:按照给定的类型;比如按BookService类型
FilterType.ASPECTJ:使用ASPECTJ表达式
FilterType.REGEX:使用正则指定
FilterType.CUSTOM:使用自定义规则,自已写类,实现TypeFilter接口
classes:
Controller.class:表示扫描的是使用了@Controller注解的类
Service.class、Repository.class、Component.class
useDefaultFilters:默认true,扫描所有组件;false:使用自定义扫描范围
@Configuration
@ComponentScan(value = "com.enjoy.study.cap2",excludeFilters={
@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class})
},includeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Service.class})
},useDefaultFilters = false)
public class MainConfig {
@Bean()
public Person person(){
return new Person("wxf",19);
}
}
案例:测试自定义规则使用
1. 自定义过滤规则类
public class MyTypeFilter implements TypeFilter { /** * * @param metadataReader:读取到的当前正在扫描的类的信息 * @param metadataReaderFactory:可以获取其它任何类的信息的工厂 * @return * @throws IOException */ @Override public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { //获取当前类的注解信息 AnnotationMetadata metadata = metadataReader.getAnnotationMetadata(); //获取当前正在扫描的类的信息 ClassMetadata classMetadata = metadataReader.getClassMetadata(); //获取当前类资源(类路径) Resource resource = metadataReader.getResource(); //获取当前类的类名 String className = classMetadata.getClassName(); if(className != null && className.contains("Dao")){ return true; } return false; } }
2. 在配置类中使用
com.enjoy.study.cap2包下
@Configuration
@ComponentScan(value = "com.enjoy.study.cap2",includeFilters = {
@ComponentScan.Filter(type = FilterType.CUSTOM,classes = {MyTypeFilter.class})
},useDefaultFilters = false)
public class MainConfig {
@Bean()
public Person person(){
return new Person("wxf",19);
}
}
3. 测试方法,打印spring的IOC容器中的所有对象
@Test public void testConfig2(){ /** * 注解配置来获取spring IOC容器 */ ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class); //获取类名 String[] names = context.getBeanDefinitionNames(); for (String name : names) { System.out.println(name); } }
4. 打印结果
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
mainConfig
userDao
person
@Scope
spring中默认的bean是单例的,@Scope(value)可以设置bean实例的不同创建方式
singleton:默认方式,单实例,IOC容器启动时调用方法创建对象并放入IOC容器中,以后每次获取的就是从容器中拿到的同一个实例对象
prototype:多实例,IOC容器启动时并不会调用方法创建对象,以后每次获取时都调用方法创建一个对象
request:主要针对web应用,一个请求创建一个对象
session:主要针对web应用,一次session会话创建一个对象
@Configuration public class MainConfig { @Bean @Scope(value = "prototype") public Person person(){ return new Person(); } } @Test public void getBean(){ ApplicationContext context = new AnnotationConfigApplicationContext(com.enjoy.study.cap3.MainConfig.class); Object person1 = context.getBean("person"); Object person2 = context.getBean("person"); System.out.println(person1 == person2); }
没使用@Scope注解之前返回true,因为默认使用singleton模式;添加@Scope("prototype")后返回false
@Lazy
懒加载
默认情况下,启动容器是创建对象;配置懒加载后,启动容器时不创建对象,在第一次使用bean时创建对象
@Configuration public class MainConfig { @Bean @Lazy public Person person(){ System.out.println("IOC容器创建对象"); return new Person("qf",21); } } public class LazyTest { /** * 测试IOC容器创建bean实例对象的时机 */ @Test public void lazyT(){ ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class); System.out.println("IOC容器启动完成"); context.getBean("person"); } }
/* 注释@Lazy */ IOC容器创建对象 IOC容器启动完成 /* 不注释@Lazy */ IOC容器启动完成 IOC容器创建对象
使用懒加载时,第一次context.getBean时才会创建实例对象
@Conditional
条件注册bean
IOC容器注册bean时,使用@Conditional(MyCondition.class),使得满足自定义条件类MyCondition的bean才会被注册到IOC容器中
MyCondition类必须实现Condition接口,实现其中的match方法
测试案例:根据不同操作系统,Windows系统时wxf注入IOC,linux系统下qf注入IOC容器
条件类
public class MyWinCondition implements Condition {
/**
*
* @param conditionContext :判断条件能使用的上下文
* @param annotatedTypeMetadata :注释信息
* @return
*/
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
//获取IOC容器使用的BeanFactory
ConfigurableListableBeanFactory beanFactory = conditionContext.getBeanFactory();
//获取类加载器
ClassLoader classLoader = conditionContext.getClassLoader();
//获取当前环境信息
Environment environment = conditionContext.getEnvironment();
//获取bean定义的注册类
BeanDefinitionRegistry registry = conditionContext.getRegistry();
//获取当前环境的操作系统名
String osName = environment.getProperty("os.name");
if(osName != null && osName.contains("Windows")){
return true;
}
return false;
}
}
public class MyLinuxCondition implements Condition {
/** * * @param conditionContext :判断条件能使用的上下文 * @param annotatedTypeMetadata :注释信息 * @return */ @Override public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) { //获取当前环境信息 Environment environment = conditionContext.getEnvironment(); //获取当前环境的操作系统名 String osName = environment.getProperty("os.name"); if(osName != null && osName.contains("Linux")){ return true; } return false; } }
配置类
@Configuration public class Cap5MainConfig { @Bean("person") public Person person(){ System.out.println("person 被注册到IOC容器"); return new Person(); } @Conditional(MyWinCondition.class) @Bean("wxf") public Person wxf(){ System.out.println("wxf 被注册到IOC容器"); return new Person(); } @Conditional(MyLinuxCondition.class) @Bean("qf") public Person qf(){ System.out.println("qf 被注册到IOC容器"); return new Person(); } }
测试类
public class Cap5Test { @Test public void testGetBean(){ ApplicationContext context = new AnnotationConfigApplicationContext(Cap5MainConfig.class); String[] names = context.getBeanDefinitionNames(); for (String name : names) { System.out.println(name); } } }
打印结果
person 被注册到IOC容器 wxf 被注册到IOC容器 org.springframework.context.annotation.internalConfigurationAnnotationProcessor org.springframework.context.annotation.internalAutowiredAnnotationProcessor org.springframework.context.annotation.internalRequiredAnnotationProcessor org.springframework.context.annotation.internalCommonAnnotationProcessor org.springframework.context.event.internalEventListenerProcessor org.springframework.context.event.internalEventListenerFactory cap5MainConfig person wxf
当前系统是Windows,所以qf没被注册到IOC容器中
@Import
注册bean
在类上使用的注解
使用:
- @Import({Person.class})或者@Import({Person.class,User.class}):容器中会自动注册这个bean,id是这个bean的全路径名
- @Import+ImportSelector接口:ImportSelector接口返回需要导入的组件的全类名数组
- @Import+ImportBeanDefinitionRegistrar接口:自定义注册bean到容器中
测试
测试类
public class Cap6Test { @Test public void importTest(){ ApplicationContext context = new AnnotationConfigApplicationContext(Cap6MainConfig.class); String[] names = context.getBeanDefinitionNames(); for (String name : names) { System.out.println("name = " + name); } }
- @Import({Person.class})或者@Import({Person.class,User.class})
配置类 @Configuration @Import({User.class}) public class Cap6MainConfig { }
结果 name = org.springframework.context.annotation.internalConfigurationAnnotationProcessor name = org.springframework.context.annotation.internalAutowiredAnnotationProcessor name = org.springframework.context.annotation.internalRequiredAnnotationProcessor name = org.springframework.context.annotation.internalCommonAnnotationProcessor name = org.springframework.context.event.internalEventListenerProcessor name = org.springframework.context.event.internalEventListenerFactory name = cap6MainConfig name = com.enjoy.study.cap6.pojo.User
- @Import+ImportSelector接口
配置类 @Configuration @Import({User.class, MyImportSelector.class}) public class Cap6MainConfig { }
自定义导入选择器 /** * 自定义逻辑返回要导入容器的组件 * @author qf * @create 2019-05-21 13:57 */ public class MyImportSelector implements ImportSelector { /** * * @param annotationMetadata 当前标注@Import注解的类的所有注解信息,不仅仅能获取到@Import注解,可以获取该类的所有注解 * @return */ @Override public String[] selectImports(AnnotationMetadata annotationMetadata) { /** * 注意:不要返回null,返回null,会报空指针 */ return new String[]{"com.enjoy.study.cap6.pojo.Student","com.enjoy.study.cap6.pojo.Teacher"}; } }
结果 name = org.springframework.context.annotation.internalConfigurationAnnotationProcessor name = org.springframework.context.annotation.internalAutowiredAnnotationProcessor name = org.springframework.context.annotation.internalRequiredAnnotationProcessor name = org.springframework.context.annotation.internalCommonAnnotationProcessor name = org.springframework.context.event.internalEventListenerProcessor name = org.springframework.context.event.internalEventListenerFactory name = cap6MainConfig name = com.enjoy.study.cap6.pojo.User name = com.enjoy.study.cap6.pojo.Student name = com.enjoy.study.cap6.pojo.Teacher
- @Import+ImportBeanDefinitionRegistrar接口
/** * 自定义bean注册类 * @author qf * @create 2019-05-21 14:09 */ public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { /** * * @param annotationMetadata:当前类的注解信息 * @param beanDefinitionRegistry:BeanDefinition注册类,把所有需要添加到容器的bean, * 调用BeanDefinitionRegistry的registerBeanDefinitions方法自定义手工注册进来 */ @Override public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) { //容器中是否存在Student boolean definitionStu = beanDefinitionRegistry.containsBeanDefinition("com.enjoy.study.cap6.pojo.Student"); //容器中是否存在Teacher boolean definitionTea = beanDefinitionRegistry.containsBeanDefinition("com.enjoy.study.cap6.pojo.Teacher"); //如果容器中存在Student和Teacher if(definitionStu && definitionTea){ RootBeanDefinition beanDefinition = new RootBeanDefinition(User.class); /** * 第一个参数:自定义bean的名,不一定是全路径 * 第二个参数:beanDefinition,bean的定义信息(类型,作用域等) */ beanDefinitionRegistry.registerBeanDefinition("user",beanDefinition); } } }
配置类 @Configuration @Import({MyImportSelector.class,MyImportBeanDefinitionRegistrar.class}) public class Cap6MainConfig { }
结果 name = org.springframework.context.annotation.internalConfigurationAnnotationProcessor name = org.springframework.context.annotation.internalAutowiredAnnotationProcessor name = org.springframework.context.annotation.internalRequiredAnnotationProcessor name = org.springframework.context.annotation.internalCommonAnnotationProcessor name = org.springframework.context.event.internalEventListenerProcessor name = org.springframework.context.event.internalEventListenerFactory name = cap6MainConfig name = com.enjoy.study.cap6.pojo.Student name = com.enjoy.study.cap6.pojo.Teacher name = user
FactoryBean
主要功能是将bean注册到容器中
public interface FactoryBean<T> { T getObject() throws Exception; Class<?> getObjectType(); boolean isSingleton(); }
- getObject():容器调用getObject方法返回对象,并将该对象放入容器中
- getObjectType():返回对象类型
- isSingleton():是否是单例进行控制
测试一
自定义FactoryBean public class MyFactoryBean implements FactoryBean<User> { @Override public User getObject() throws Exception { return new User(); } @Override public Class<?> getObjectType() { return User.class; } @Override public boolean isSingleton() { return false; } }
配置类 @Configuration public class Cap6MainConfig { @Bean public MyFactoryBean user(){ return new MyFactoryBean(); } }
结果
name = org.springframework.context.annotation.internalConfigurationAnnotationProcessor
name = org.springframework.context.annotation.internalAutowiredAnnotationProcessor
name = org.springframework.context.annotation.internalRequiredAnnotationProcessor
name = org.springframework.context.annotation.internalCommonAnnotationProcessor
name = org.springframework.context.event.internalEventListenerProcessor
name = org.springframework.context.event.internalEventListenerFactory
name = cap6MainConfig
name = user
测试二
@Test public void factoryBean() throws Exception { ApplicationContext context = new AnnotationConfigApplicationContext(Cap6MainConfig.class); //获取自定义FactoryBean本身 Object bean = context.getBean("&user"); System.out.println(bean.getClass()); System.out.println("--------------"); //获取通过自定义FactoryBean注册到容器中的bean对象 bean = context.getBean("user"); System.out.println(bean.getClass()); }
结果 class com.enjoy.study.cap6.MyFactoryBean -------------- class com.enjoy.study.cap6.pojo.User
spring中注册bean的方式总结:
- 包扫描(@ComponentScan)+组件标注注解(@Controller、@Service、@Repository、@Component)
- @Bean【导入第三方类或者包的组件,比如Person是第三方提供的类,使用@Bean注册到IOC容器】
- @Import【快速给容器导入一个组件】
- 实现FactoryBean接口