简单理解
spring就是一个容器,在这个容器中存放着一些公共的实例或者模板,当我们想使用是,直接从里面取就好了
介绍Spring
首先熟悉两个概念(这里只给出自己的简单理解)
1.IOC控制反转(控制反转指的事对象生成管理权)应用程序将对象的生成管理权交给了spring容器
2.DI依赖注入(从容器中获得我们想要的对象)
站在应用程序依赖的角度上:
应用程序依赖于IOC容器,并从中获取到所需要的资源(也就是对象)
站在注入的角度上:
容器注入了某个应用程序所依赖的资源,注入的是对象
通过示例理解
使用xml
-
首先定义接口和实现类
-
创建xml文件,在bean标签中有三个属性,1.id 2.name 3.全限定类名
-
使用test示例进行测试。
案例一:不存在对象依赖
1.首先创建ClassPathXmlApplicationContext容器
2.利用applicationContext.getBean()从容器中取出组件,有三种方式,1>.通过id/name(这里的id和name都是自己起的),2>.通过全限定类名,3>.通过全限定类名和name
案例二:存在对象依赖
1.如在service类中增加dao类成员变量维护依赖关系,必须要在service中设置set方法
2.在bean标签中增加标签,name值为自己取的,ref值为某个bean标签中的name
使用纯注解
首先使用注解的方式,要创建配置类等价替代xml文件
1.类上添加@Configuration注解,该类就成为一个配置类
方式一:在该类中增加@Bean注解
即@Bean注解所修的的方法即为一个Bean,Bean的name为方法名,也可以增加@Bean的value属性值作为name,全限定类名为返回值类型
3.解决了最基础的问题,现在来解决使用纯注解方式的依赖问题,一个方法中返回的是对象1,需要调用对象2作为该方法的参数,这就是@Bean注解中的依赖问题(有一个细节:我们需要自己创建对象)
4.解决了最基础的依赖问题,现在来解决如果Spring容器中有多个相同类型的对象怎么办?使用@Qualifier
注解,通过在方法的参数前添加@Qualifier(“dataSource1”),dataSource1为该类中的其中一个方法
小结:通过使用纯注解+@Bean方式,我们使用了两个注解1.@Configuration创建配置类2.使用@Bean在容器中插入组件
现在又有新的问题,如果我们要将几十个类的对象作为Spring Bean住的到Spring容器中,那样配置类就会变得很繁琐,为了方便解耦和简化代码,我们可以
使用包扫描功能:
1.在配置类上增加ComponentScan注解,并直接指定包扫描路径@ComponentScan(“com.cskaoyan.demo1”)
2.@Component注解,将包中的标记类对象注册为Spring Bean的注解。其中id默认为类名的首字母小写,也可以使用@Component中的value竖向来制定Spring Bean中的id
3.除了@Component注解外还有@Service注解,@Repository注解,@Controller注解(@Component通用,@Service用于Service层,@Repository用于dao层,@Controller用于springMVC阶段)
使用包扫描功能,如何解决依赖注入问题?有三种解决方式:
构造器注入
1.优先使用无参构造方法
2.没有无参构造,则使用有参构造,如果这个类型的组件在容器中不止一个,则使用@Qualifier,常量值可以使用@Value
3.如果有多个有参构造方法,则优先使用@Autowired注解的构造方法
方法注入
默认这个方法不会自动执行:
1.增加@Autowired,@Qualifier按需使用
成员变量注入(最常用的)
写在配置类中的属性和容器中
注入功能的注解使用这三组:
-
@Autowired
-
@Autowired + @Qualifier
-
@Resource
(Autowire默认按类型,resource默认先按名字,再按类型)
三:Spring单元测试
1.引入spring-test依赖
2.@Runwith和@ContextConfiguration注解
@RunWith(SpringJUnit4ClassRunner.class) // 让测试类可以自动使用Spring容器
@ContextConfiguration(classes = ScanConfig.class) // 指定容器所使用的配置类(可以指定多个)
小结:
1.自动依赖注入的前提是spring管理/创建对象,
2.从创建对象的角度来讲,@bean注解和包扫描的区别,包扫描是spring创建,@bean是自己创建,如果一个已经存在的对象,则使用@bean
补充
1.单元测试
引入spring-test依赖
引入sp@Runwith和@ContextConfiguration注解,在单元测试类中可以使用注入功能的注解
2.FactoryBean
将目标对象注册为Spring Bean,我们得将返回目标对象(getObject方法)的FactoryBean子类对象注册为Spring Bean
Spring Bean
在spring Bean上添加注解,默认是Singleton单例,也可以添加Prototype(每次创建的都是新的对象)
生命周期
7个阶段
- Bean的实例化(有参构造方法、无参构造方法)
- 设置参数方法(方法注入、成员变量注入)@Autowired@Resource注解注入
- BeanNameAware、BeanFactoryAware、ApplicationContextAware
- BeanPostProcessor(Bean的后处理器,这个后指的是实例化之后,其实还是在放入到容器中之前)的postProcessBeforeInitialization(后面有初始化方法)(针对一个还不完整的bean处理)
- 自定义的init方法(我们自己做开发的时候通常使用的方式)(除了使用@Autowired,@Resource之外的,一种实现依赖注入的方式)
@PostConstruct
是一个用于标注初始化方法的注解 - InitializingBean的afterPropertiesSet方法(通常是一些框架提供的类初始化的方式)(所有的依赖填充完了,让我们能够确保整改SpringBean的所有注入了依赖的属性)
- BeanPostProcessor的postProcessAfterInitialization(BeanPostProgress也能买到一个完整的Bean对象)(AOP核心)
/*
对于Bean(容器中的组件),在容器中也会经历这样的一些阶段
1.容器初始化的时候,组件做准备性工作 → 放入到容器中之前,会执行哪一些方法来准备实例**
2.组件可以从容器中取出,提供服务,比如从容器中取出userService实例,调用其sayHello方法
3.容器关闭,组件做销毁工作
初始化阶段:
1. Bean的实例化(有参构造方法、无参构造方法)
2. 设置参数方法(方法注入、成员变量注入)
3. BeanNameAware、BeanFactoryAware、ApplicationContextAware (Aware)
4. BeanPostProcessor(Bean的后处理器,这个后指的是实例化之后,其实还是在放入到容器中之前)
的postProcessBeforeInitialization(后面有初始化方法)针对一个还不完整的bean做处理
5. 自定义的init方法(我们自己做开发的时候通常使用2的方式): 除了使用@Autowired,@Resource之外的,一种实现依赖注入的方式
6. InitializingBean的afterPropertiesSet方法(通常是一些框架提供的类初始化的方式)所有的依赖都填充完了,
让我们能够确保整改SpringBean的所有注入了依赖的属性
7. BeanPostProcessor的postProcessAfterInitialization 让beanPostProcessor也能拿到一个完整的Bean对象
销毁阶段:
1. 自定义的destroy方法(通常是自定义的)
2. DisposableBean的destroy方法(通常是框架提供的类采用这种方式)
注意事项:
1. @Bean 还是 包扫描方式,生命周期一致
2. singleton scope的SpringBean 包含销毁阶段的声明周期,prototype scope的Spring Bean不包含
3. BeanPostProcessor它不会处理其他的BeanPostProcessor
*/
// @Component
public class LifeCycelBean implements BeanNameAware, BeanFactoryAware, ApplicationContextAware, InitializingBean, DisposableBean {
// 定义构造方法
public LifeCycelBean() {
System.out.println(" 1. LifeCycelBean");
}
String parameter;
@Autowired
public void setParam(@Value("zs") String parameter) {
System.out.println(" 2. Autowired setParam");
this.parameter = parameter;
}
BeanFactory beanFactory;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
System.out.println("3. setBeanFactory");
}
String beanName;
@Override
public void setBeanName(String name) {
this.beanName = name;
System.out.println("3. set setBeanName");
}
ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
System.out.println("3. setApplicationContext");
}
// 自定义初始化方法
@PostConstruct
public void init() {
System.out.println("5. init");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("6. afterPropertiesSet");
}
@PreDestroy
public void preDestroy() {
System.out.println("8. preDestroy");
}
@Override
public void destroy() throws Exception {
System.out.println("9. destroy");
}
}
.在springbean中实现Aware…,然后定义对应的变量
2.定义getter、setter方法
3.在测试类中调用getter方法时,因为容器会初始化的时候自动调用setter方法,注入我们所需要的对象,如果结果能显示,则验证了这种猜想
PostProcessor会执行这个包下所有springBean,配置类和测试类都为springbean
@Component
public class CustomerBeanPostProcessor implements BeanPostProcessor {
/*
Sping容器会先调用这个方法
1. 方法参数:
1)bean就表示当前正在处理的对象(正在初始化的Spring Bean对象)
2)beanName表示当前正在处理的对象名称(正在初始化的Spring Bean对象)
2. 执行时机: 在每一个Spring Bean初始化阶段,都会调用该方法一次
*/
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("4. postProcessBeforeInitialization: " + beanName);
return bean;
}
/*
Sping容器会后调用这个方法
1. 方法参数:
1)bean就表示当前正在处理的对象(正在初始化的Spring Bean对象)
2)beanName表示当前正在处理的对象名称(正在初始化的Spring Bean对象)
2. 执行时机: 在每一个Spring Bean初始化阶段,都会调用该方法一次
*/
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("7. postProcessAfterInitialization: " + beanName);
return bean;
}
}
两个区别
使用@Bean方法的生命周期和使用包扫描的生命周期方式基本相同
1.区别:在使用@Bean方法,注册依赖阶段是自己创建的
2.scope区别:singleton scope的springBean包含销毁阶段的生命周期,prototype scope的springBean不包含销毁阶段的生命周期
区别2的原因:singleton
scope 的 Spring Bean 由于由容器统一管理整个生命周期,所以有销毁阶段以进行资源清理;而 prototype
scope 的 Spring Bean 更多依赖客户端代码使用情况,容器不主动管理其销毁阶段,因而默认没有销毁阶段相关的生命周期过程。