Spring系列(二):Spring IoC应用
本文内容纲要:
- 一、Spring IoC的核心概念- 二、Spring IoC的应用
- 1、定义Bean的信息
- 2、Spring IoC常用注解使用
一、Spring IoC的核心概念
IoC(Inversion of Control 控制反转),详细的概念见**Spring系列(一):Spring核心概念**
二、Spring IoC的应用
1、定义Bean的信息
1.1 基于xml的形式定义Bean的信息
① 新建一个Bean:
package com.toby.ioc.component;/**
* @desc:
* @author: toby
* @date: 2019/7/13 1:49
*/
public class TobyBean{
public TobyBean(){
System.out.println("TobyBean Constructor");
}
}
② 在resources下面新建一个spring.xml
xml配置如下:
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="tobyBean" class="com.toby.ioc.component.TobyBean"/>
</beans>
③ 写一个测试类进行测试
package com.toby.ioc.xml;import com.toby.ioc.component.TobyBean;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @desc: 基于xml
* @author: toby
* @date: 2019/8/6 17:36
*/
public class XmlTest {
private ClassPathXmlApplicationContext context;
@Before
public void before(){
context = new ClassPathXmlApplicationContext("spring.xml");
}
@Test
public void test(){
TobyBean tobyBean = context.getBean(TobyBean.class);
System.out.println(tobyBean);
}
}
总结:由于现在基本基于spring boot 约定大于配置,而且大量的xml配置也不易于维护,所以这里就简单介绍下基于xml的原理:首先读取资源配置文件,然后解析成BeanDefinition,最后利用反射进行相应的实例化操作。我们接下来重点讲解基于注解的方式
1.2 基于读取配置类的形式定义Bean信息
① 同上面基于xml一样,需要一个Bean
② 新建一个配置类定义相应的Bean信息
package com.toby.ioc.config;import com.toby.ioc.component.TobyBean;
import org.springframework.context.annotation.*;
/**
* @desc: ioc config 类
* @author: toby
* @date: 2019/7/13 1:10
*/
@Configuration
public class IocConfig {
@Bean
public TobyBean tobyBean(){
return new TobyBean();
}
}
③ 写一个测试类进行测试
package com.toby.ioc.configuration;import com.toby.ioc.config.IocConfig;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
/**
* @desc: 基于配置类
* @author: toby
* @date: 2019/8/6 17:59
*/
public class ConfigurationTest {
private AnnotationConfigApplicationContext context;
@Before
public void before(){
context = new AnnotationConfigApplicationContext(IocConfig.class);
}
@Test
public void test(){
System.out.println(context.getBean("tobyBean"));
}
}
2、Spring IoC常用注解使用
2.1 @Configuration 相当于 xml配置的
2.2 @Bean 相当于 xml配置的
默认(单实例 延迟加载)
package com.toby.ioc.config;import com.toby.ioc.component.TobyBean;
import org.springframework.context.annotation.*;
/**
* @desc: ioc config 类
* @author: toby
* @date: 2019/7/13 1:10
*/
@Configuration
public class IocConfig {
@Bean
public TobyBean tobyBean(){
return new TobyBean();
}
}
配置Bean的作用域
① 在不指定@Scope的情况下,所有的bean都是单实例的bean,而且是饿汉加载(容器启动实例就创建好了)
② @Scope为prototype表示为多实例的,而且还是懒汉模式加载(IOC容器启动的时候,并不会创建对象,而是在每次使用的时候才会创建)注意:当指定多例的时候是无法解决循环依赖的后续源码会分析
@Configurationpublic class IocConfig {
@Bean
@Scope("prototype")
public TobyBean tobyBean(){
return new TobyBean();
}
}
如何测试是否多实例:
public class IocMain { public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(IocConfig.class);
TobyBean tobyBean1 = context.getBean(TobyBean.class);
TobyBean tobyBean2 = context.getBean(TobyBean.class);
//单例返回true 多例返回false
System.out.println(tobyBean1 == tobyBean2);
}
}
③ @Scope指定的作用域取值:singleton 单实例的(默认),prototype 多实例的,request 同一次请求,session 同一个会话级别
Bean的懒加载@Lazy
Bean的懒加载@Lazy(主要针对单实例的bean在容器启动的时候,不创建对象,而在第一次使用的时候才会创建该对象,多实例bean没有懒加载一说)
@Configurationpublic class IocConfig {
@Bean
@Lazy
public TobyBean tobyBean(){
return new TobyBean();
}
}
2.3 @CompentScan 包扫描(重点)
** **在配置类上写@CompentScan注解来进行包扫描
① 常规用法:这样在basePackages包下面具有@Controller @Service @Repository @Component注解的组件都会被加载到spring容器中
@Configuration@ComponentScan(basePackages = {"com.toby.ioc"})
public class IocConfig {
}
② 排除用法:excludeFilters(排除@Controller注解和TobyService)
@Configuration@ComponentScan(basePackages = {"com.toby.ioc"},excludeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION,value = {Controller.class}),
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,value = {TobyService.class})
})
public class IocConfig {
}
③ 包含用法:includeFilters,注意:若使用包含,需要把useDefaultFilters属性设置为false(true表示扫描全部的),后续源码解析会说到这个原因
@Configuration@ComponentScan(basePackages = {"com.toby.ioc"},includeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION,value = {Controller.class, Service.class})
},useDefaultFilters = false)
public class IocConfig {
}
④ 自定义Filter用法:
自定义一个TobyTypeFilter实现TypeFilter
public class TobyTypeFilter implements TypeFilter { @Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
//获取当前类的class的源信息
ClassMetadata classMetadata = metadataReader.getClassMetadata();
//类名称中包含Dao就可以被扫描到
if(classMetadata.getClassName().contains("Dao")) {
return true;
}
return false;
}
}
配置类:
@Configuration@ComponentScan(basePackages = {"com.toby.ioc"},includeFilters = {
@ComponentScan.Filter(type = FilterType.CUSTOM,value = TobyTypeFilter.class)
},useDefaultFilters = false)
public class IocConfig {
}
2.4 @Conditional 条件注解(spring boot中大量用到)
① 新建2个Bean TobyA和TobyB 如下:
public class TobyA { public TobyA() {
System.out.println("TobyA Constructor");
}
}
public class TobyB {
public TobyB() {
System.out.println("TobyB Constructor");
}
}
② 新建一个TobyCondition实现Condition接口
public class TobyCondition implements Condition { private static final String TOBY_A_BEAN_NAME = "tobyA";
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
//判断容器中是否有TobyA组件
if(context.getBeanFactory().containsBean(TOBY_A_BEAN_NAME)){
return true;
}
return false;
}
}
③ 配置类 只有当容器中有TobyA的时候才实例化TobyB
@Configurationpublic class IocConfig {
@Bean
public TobyA tobyA(){
return new TobyA();
}
@Bean
@Conditional(TobyCondition.class)
public TobyB tobyB(){
return new TobyB();
}
}
2.5 往IOC容器中添加组件的方式
① 通过@ComponentScan包扫描 + @Controller、@Service、@Repository、@Component 针对我们自己写的组件可以通过该方式来加载到容器中
② 通过@Bean的方式来导入组件(适用于导入第三方组件)
③ 通过@Import
Ⅰ 通过@Import直接导入组件(导入组件的id为全限定类名)
配置类:
@Configuration@Import({TobyBean.class})
public class IocConfig {
}
Ⅱ 通过@Import的ImportSelector类实现组件的导入(导入组件的id为全限定类名),自定义的TobyImportSelector需要实现ImportSelector接口。
public class TobyImportSelector implements ImportSelector { @Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
//返回全限定类名的数组
return new String[]{"com.toby.ioc.component.TobyBean"};
}
}
配置类:
@Configuration@Import({TobyImportSelector.class})
public class IocConfig {
}
Ⅲ 通过@Import的ImportBeanDefinitionRegistrar导入组件 (可以指定bean的名称),自定义TobyImportBeanDefinitionRegistrar实现ImportBeanDefinitionRegistrar。
public class TobyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { @Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//创建一个bean定义对象
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(TobyBean.class);
//把bean定义对象导入到容器中
registry.registerBeanDefinition("tobyBean",rootBeanDefinition);
}
}
配置类:
@Configuration@Import({TobyImportBeanDefinitionRegistrar.class})
public class IocConfig {
}
④ 通过实现FactoryBean接口来实现注册组件
创建一个FactoryBean,注意要获取FactoryBean本身需要在beanName前面加上&
@Componentpublic class TobyBeanFactoryBean implements FactoryBean<TobyBean> {
@Override
public TobyBean getObject() throws Exception {
return new TobyBean();
}
@Override
public Class<?> getObjectType() {
return TobyBean.class;
}
@Override
public boolean isSingleton() {
return false;
}
}
单元测试:
public class FactoryBeanTest { private AnnotationConfigApplicationContext context;
@Before
public void before(){
context = new AnnotationConfigApplicationContext(IocConfig.class);
}
@Test
public void test(){
//获取TobyBean
System.out.println(context.getBean("tobyBeanFactoryBean"));
//如何获取TobyBeanFactoryBean
System.out.println(context.getBean("&tobyBeanFactoryBean"));
}
}
2.6 Bean的生命周期
由容器管理Bean的生命周期,我们可以指定bean的初始化方法和bean的销毁方法
① 通过@Bean的initMethod和destroyMethod属性
新建一个LifeCycleBean1 Bean:
package com.toby.ioc.beanlifecycle;/**
* @desc: bean生命周期1
* @author: toby
* @date: 2019/7/13 1:26
*/
public class LifeCycleBean1 {
public LifeCycleBean1(){
System.out.println("LifeCycleBean1 Constructor");
}
public void init(){
System.out.println("LifeCycleBean1 Init");
}
public void destroy(){
System.out.println("LifeCycleBean1 Destroy");
}
}
配置类:
@Configurationpublic class IocConfig {
@Bean(initMethod = "init",destroyMethod = "destroy")
public LifeCycleBean1 lifeCycleBean1(){
return new LifeCycleBean1();
}
}
②通过实现InitializingBean, DisposableBean2个接口
新建一个LifeCycleBean2
package com.toby.ioc.beanlifecycle;import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;
/**
* @desc: bean生命周期2 通过实现2个接口
* @author: toby
* @date: 2019/7/13 1:30
*/
@Component
public class LifeCycleBean2 implements InitializingBean, DisposableBean {
public LifeCycleBean2(){
System.out.println("LifeCycleBean2 Constructor");
}
@Override
public void destroy() throws Exception {
System.out.println("LifeCycleBean2 destroy");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("LifeCycleBean2 afterPropertiesSet");
}
}
③ 通过JSR250规范提供的注解@PostConstruct和@PreDestroy标注的方法
新建一个LifeCycleBean3
package com.toby.ioc.beanlifecycle;import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
/**
* @desc: bean生命周期3 通过2个注解
* @author: toby
* @date: 2019/7/13 1:30
*/
@Component
public class LifeCycleBean3{
public LifeCycleBean3(){
System.out.println("LifeCycleBean3 Constructor");
}
@PostConstruct
public void init(){
System.out.println("LifeCycleBean3 init");
}
@PreDestroy
public void destroy(){
System.out.println("LifeCycleBean3 destroy");
}
}
2.7 后置处理器(很重要,后面源码解析会讲)
① BeanPostProcessor:也称为Bean后置处理器,它是Spring中定义的接口,在Spring容器的创建过程中(具体为Bean初始化前后)会回调BeanPostProcessor中定义的两个方法。分别是postProcessBeforeInitialization(初始化之前)和postProcessAfterInitialization(初始化之后)
自定义TobyBeanPostProcessor后置处理器:
package com.toby.ioc.processor;import com.toby.ioc.component.TobyBean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
/**
* @desc: bean的后置处理器
* @author: toby
* @date: 2019/7/13 2:08
*/
@Component
public class TobyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if(bean instanceof TobyBean){
System.out.println("马上开始初始化TobyBean了,注意下");
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if(bean instanceof TobyBean){
System.out.println("初始化完成TobyBean了,注意下");
}
return bean;
}
}
② BeanFactoryPostProcessor:Bean工厂的后置处理器,触发时机bean定义注册之后bean实例化之前
自定义TobyBeanFactoryPostProcessor Bean工厂的后置处理器:
package com.toby.ioc.processor;import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.stereotype.Component;
/**
* @desc: bean工厂的后置处理器 触发时机 bean定义注册之后 bean实例化之前
* @author: toby
* @date: 2019/7/21 23:04
*/
@Component
public class TobyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("调用了TobyBeanFactoryPostProcessor的postProcessBeanFactory方法");
for(String beanName : beanFactory.getBeanDefinitionNames()){
if("tobyBean".equals(beanName)){
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
beanDefinition.setLazyInit(true);
}
}
}
}
③ BeanDefinitionRegistryPostProcessor:Bean定义的后置处理器,它继承了BeanFactoryPostProcessor,触发时机,在bean的定义注册之前
自定义TobyBeanDefinitionRegistryPostProcessor Bean定义的后置处理器
package com.toby.ioc.processor;import com.toby.ioc.component.TobyBean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.stereotype.Component;
/**
* @desc: bean定义的后置处理器
* @author: toby
* @date: 2019/7/21 23:11
*/
@Component
public class TobyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
System.out.println("调用TobyBeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法");
System.out.println("bean定义的数据量:"+registry.getBeanDefinitionCount());
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(TobyBean.class);
registry.registerBeanDefinition("tobyBean",rootBeanDefinition);
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("调用TobyBeanDefinitionRegistryPostProcessor的postProcessBeanFactory方法");
System.out.println(beanFactory.getBeanDefinitionCount());
}
}
2.8 Aware接口
Spring提供了大量的Aware接口,使得我们可以使用Spring的一些底层提供的容器,资源比如获取ApplicationContext就可以实现ApplicationContextAware接口,获取BeanFactory就可以实现BeanFactoryAware,这些Aware接口的回调是在Bean初始化 initializeBean() 方法中进行回调的
比如我们要使用Spring底层的ApplicationContext,则需要实现ApplicationContextAware如下:
package com.toby.ioc.aware;import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
/**
* @desc: 应用中需要获取spring的上下文
* @author: toby
* @date: 2019/7/13 1:15
*/
@Component
public class TobyApplicationContextAware implements ApplicationContextAware {
/**
* spring上下文
*/
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("应用程序获取到了spring 容器");
this.applicationContext = applicationContext;
}
}
2.9 Lifecycle接口
每个对象都有自己生命周期的需求,主要方法:isAutoStartup()返回true时,Spring容器启动时会去执行start()方法。isRunning()返回true的时候,容器销毁时会调用stop()方法。比如eruaka启动的入口就是通过实现SmartLifecycle接口来实现
自定义TobyLifecycle实现SmartLifecycle接口:
package com.toby.ioc.lifecycle;import org.springframework.context.SmartLifecycle;
import org.springframework.stereotype.Component;
/**
* @desc: 每个对象都有自己生命周期的需求,比如eruaka启动的入口就是用这个实现的
* @author: toby
* @date: 2019/7/13 2:00
*/
@Component
public class TobyLifecycle implements SmartLifecycle {
@Override
public boolean isAutoStartup() {
return true;
}
@Override
public void stop(Runnable callback) {
}
@Override
public void start() {
System.out.println("TobyLifecycle start");
}
@Override
public void stop() {
}
@Override
public boolean isRunning() {
return false;
}
@Override
public int getPhase() {
return 0;
}
}
2.10 自动装配
① @Autowired 默认情况下:首先是按照类型进行装配,若在IOC容器中发现了多个相同类型的组件,那么就按照属性名称来进行装配。
② @Autowired 假设我们需要指定特定的组件来进行装配,我们可以通过使用@Qualifier("tobyDao")来指定装配的组件或者在配置类上的@Bean加上@Primary注解
@Autowired + @Qualifier:
@Servicepublic class TobyService {
@Autowired
@Qualifier("tobyDao")
private TobyDao tobyDao;
public TobyDao getTobyDao(){
return this.tobyDao;
}
}
@Bean + @Primary:
@Configurationpublic class IocConfig {
@Bean
@Primary
public TobyDao tobyDao(){
return new TobyDao();
}
@Bean
public TobyDao tobyDao2(){
return new TobyDao();
}
}
③ 假设我们指定Autowire.BY_TYPE,这时候容器出现2个及以上,那么在装配的时候就会抛出异常
@Configurationpublic class PrincipleConfig {
@Bean
public PrincipleBean principleBean(){
return new PrincipleBean();
}
@Bean(autowire = Autowire.BY_TYPE)
public PrincipleAspect principleAspect(){
return new PrincipleAspect();
}
@Bean
public PrincipleLog principleLog(){
return new PrincipleLog();
}
@Bean
public PrincipleLog principleLog2(){
return new PrincipleLog();
}
}
④ @Resource(JSR250规范)功能和@AutoWired的功能差不多一样,但是不支持@Primary和@Qualifier的支持
⑤ @Inject(JSR330规范)需要导入jar包依赖功能和支持@Primary功能,但是没有Require=false的功能
总结:通过上面的示例,对Spring IoC常用注解以及接口有一定了解,Spring系列完整代码在码云:spring系列,接下来将进入:Spring系列(三):Spring IoC源码解析(干货多多)
本文内容总结:一、Spring IoC的核心概念,二、Spring IoC的应用, 1、定义Bean的信息, 2、Spring IoC常用注解使用,
原文链接:https://www.cnblogs.com/toby-xu/p/11310127.html
以上是 Spring系列(二):Spring IoC应用 的全部内容, 来源链接: utcz.com/z/362552.html