一、spring核心概念 IOC和DI
IOC即控制反转,这是一种设计思想,有反转既有正转,那么什么是反转什么是正转呢?
- 控制反转控制反转,首先是控制,再者是反转。
- 所谓控制,在传统JAVA SE设计标准中,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象;而IoC是有专门一个容器来创建这些对象,即由Ioc容器来控制对象的创建;谁控制谁?当然是IoC 容器控制了对象;控制什么?那就是主要控制了外部资源获取(不只是对象包括比如文件等)。
- 所谓反转,有反转就有正转,传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象;为何是反转?因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转;哪些方面反转了?依赖对象的获取被反转了。
-
所谓IoC,对于spring框架来说,就是由spring来负责控制对象的生命周期和对象间的关系。这是什么意思呢,举个简单的例子,我们是如何找女朋友的?常见的情况是,我们到处去看哪里有长得漂亮身材又好的mm,然后打听她们的兴趣爱好、qq号、电话号、ip号、iq号………,想办法认识她们,投其所好送其所要,然后嘿嘿……这个过程是复杂深奥的,我们必须自己设计和面对每个环节。传统的程序开发也是如此,在一个对象中,如果要使用另外的对象,就必须得到它(自己new一个,或者从JNDI中查询一个),使用完之后还要将对象销毁(比如Connection等),对象始终会和其他的接口或类藕合起来。
那么IoC是如何做的呢?有点像通过婚介找女朋友,在我和女朋友之间引入了一个第三者:婚姻介绍所。婚介管理了很多男男女女的资料,我可以向婚介提出一个列表,告诉它我想找个什么样的女朋友,比如长得像李嘉欣,身材像林熙雷,唱歌像周杰伦,速度像卡洛斯,技术像齐达内之类的,然后婚介就会按照我们的要求,提供一个mm,我们只需要去和她谈恋爱、结婚就行了。简单明了,如果婚介给我们的人选不符合要求,我们就会抛出异常。整个过程不再由我自己控制,而是有婚介这样一个类似容器的机构来控制。Spring所倡导的开发方式就是如此,所有的类都会在spring容器中登记,告诉spring你是个什么东西,你需要什么东西,然后spring会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其他需要你的东西。所有的类的创建、销毁都由 spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被spring控制,所以这叫控制反转。
-
IoC的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过DI(Dependency Injection,依赖注入)来实现的。比如对象A需要操作数据库,以前我们总是要在A中自己编写代码来获得一个Connection对象,有了 spring我们就只需要告诉spring,A中需要一个Connection,至于这个Connection怎么构造,何时构造,A不需要知道。在系统运行时,spring会在适当的时候制造一个Connection,然后像打针一样,注射到A当中,这样就完成了对各个对象之间关系的控制。A需要依赖 Connection才能正常运行,而这个Connection是由spring注入到A中的,依赖注入的名字就这么来的。那么DI是如何实现的呢? Java 1.3之后一个重要特征是反射(reflection),它允许程序在运行的时候动态的生成对象、执行对象的方法、改变对象的属性,spring就是通过反射来实现注入的。
理解了IoC和DI的概念后,一切都将变得简单明了,剩下的工作只是在spring的框架中堆积木而已。
二、Spring IOC 容器底层注解使用
配置spring 容器有多种形式,起初是以xml配置文件为主流,这一方式复杂且繁琐,后期代码量大的时候难以维护。后来注解形式的配置变成了为主流,springboot便是其集大成者。
-
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的信息 <bean id="car" class="com.tuling.compent.Car"></bean>
</beans>
public static void main( String[] args ) {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
System.out.println(ctx.getBean("person"));
}
2. 基于读取配置类的形式定义Bean信息
- 定义配置类,配置类可以直接定义一些列的bean,也可以通过扫描的形式加入bean
@Configuration public class MainConfig { @Bean public Person person(){ return new Person(); } }
-
在配置类上写@CompentScan注解来进行包扫描,这种形式可以排除或者指定某些bean。
@Configuration @ComponentScan(basePackages = {"com.tuling.testcompentscan"}) public class MainConfig { }
-
- 排除用法 excludeFilters(排除@Controller注解的,和TulingService的),
@Configuration @ComponentScan(basePackages = {"com.tuling.testcompentscan"},excludeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION,value = {Controller.class}), @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,value = {TulingService.class}) }) public class MainConfig { }
-
- 包含用法 includeFilters ,注意,若使用包含的用法,需要把useDefaultFilters属性设置为false(true表示扫描全部的)
@Configuration @ComponentScan(basePackages = {"com.tuling.testcompentscan"},includeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION,value = {Controller.class, Service.class}) },useDefaultFilters = false) public class MainConfig { }
-
- @ComponentScan.Filter type的类型
a)注解形式的FilterType.ANNOTATION @Controller @Service @Repository @Compent
-
-
- FilterType.CUSTOM 自定义类型如何使用
-
public class TulingFilterType implements TypeFilter { @Override public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata(); ClassMetadata classMetadata = metadataReader.getClassMetadata(); Resource resource = metadataReader.getResource(); if (classMetadata.getClassName().contains("dao")) { return true; } return false; } }
@ComponentScan(basePackages = { "com.tuling.testcompentscan"} , includeFilters = { @ComponentScan.Filter(type = FilterType.CUSTOM, value = TulingFilterType.class) } , useDefaultFilters = false) public class MainConfig { }
-
去容器中读取Bean的信息(传入配置类)
public static void main( String[] args ) { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MainConfig.class); System.out.println(ctx.getBean("person")); }
3.配置Bean的作用域对象
- @Scope指定的作用域方法取值,其中单例和多例多常用,最后两个几乎用不到
a) singleton 单实例的(默认)
b) prototype 多实例的
c) request 同一次请求
d) session 同一个会话级别
- 在不指定@Scope的情况下,所有的bean都是单实例的bean,而且是饿汉加载(容器启动实例就创建好了)
@Bean public Person person() { return new Person(); }
-
指定@Scope为 prototype表示为多实例的,而且还是懒汉模式加载(IOC容器启动的时候,并不会创建对象,而是在第一次使用的时候才会创建)
@Bean @Scope(value = "prototype") public Person person() {
return new Person();
}
-
Bean的懒加载@Lazy(主要针对单实例的bean 容器启动的时候,不创建对象,在第一次使用的时候才会创建该对象)
@Bean @Lazy public Person person() { return new Person(); }
-
@Conditional进行条件判断等.
public class TulingCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { if (context.getBeanFactory().containsBean("tulingAspect")) { return true; } return false; } }
public class MainConfig { @Bean public TulingAspect tulingAspect() { return new TulingAspect(); }
@Bean @Conditional(value = TulingCondition.class) public TulingLog tulingLog() { return new TulingLog(); } }
4.往IOC 容器中添加组件的方式
- ①:通过@CompentScan +@Controller @Service @Respository @componet
适用场景: 针对我们自己写的组件可以通过该方式来进行加载到容器中。
- ②:通过@Bean的方式来导入组件(适用于导入第三方组件的类)
- ③:通过@Import来导入组件 (导入组件的id为全类名路径)
@Configuration @Import(value = { Person.class, Car.class} ) public class MainConfig { }
-
- 通过@Import 的ImportSeletor类实现组件的导入 (导入组件的id为全类名路径)
public class TulingImportSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { return new String[] { "com.tuling.testimport.compent.Dog" }; } }
@Configuration @Import(value = { Person.class, Car.class, TulingImportSelector.class} ) public class MainConfig { }
-
- 通过@Import的 ImportBeanDefinitionRegister导入组件 (可以指定bean的名称)
public class TulingBeanDefinitionRegister implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,BeanDefinitionRegistry registry) {
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Cat.class);
registry.registerBeanDefinition("cat", rootBeanDefinition); } }
@Configuration @Import(value = { Person.class, Car.class, TulingImportSelector.class, TulingBeanDefinitionRegister.class} ) public class MainConfig { }
-
④:通过实现FacotryBean接口来实现注册组件
public class CarFactoryBean implements FactoryBean<Car> { @Override public Car getObject() throws Exception { return new Car(); }
@Override public Class<?> getObjectType() { return Car.class; }
@Override public boolean isSingleton() { return true; } }
5.BeanFactory和ApplicationContext(bean工厂和应用上下文)
- Bean 工厂(com.springframework.beans.factory.BeanFactory)是Spring 框架最核心的接口,它提供了高级IoC 的配置机制。
- 应用上下文(com.springframework.context.ApplicationContext)建立在BeanFactory 基础之上。
由上图可以知道,BeanFactory是最顶层接口,而ApplicationContext则集成BeanFactory,增加了许多高级特性。
- ApplicationContext 的初始化和BeanFactory 有一个重大的区别:BeanFactory在初始化容器时,并未实例化Bean,直到第一次访问某个Bean 时才实例目标Bean;而ApplicationContext 则在初始化应用上下文时就实例化所有单实例的Bean
6.bean的生命周期
- 指定bean的初始化和销毁方法
-
- 针对单实例bean的话,容器启动的时候,bean的对象就创建了,而且容器销毁的时候,也会调用Bean的销毁方法
- 针对多实例bean的话,容器启动的时候,bean是不会被创建的而是在获取bean的时候被创建,而且bean的销毁不受IOC容器的管理.
@Configuration public class MainConfig { //指定了bean的生命周期的初始化方法和销毁方法. @Bean(initMethod = "init", destroyMethod = "destroy") public Car car() { return new Car(); } }
-
通过 InitializingBean和DisposableBean 的二个接口实现bean的初始化以及销毁方法
@Component public class Person implements InitializingBean, DisposableBean { public Person() { System.out.println("Person的构造方法"); } @Override public void destroy() throws Exception { System.out.println("DisposableBean的destroy()方法 "); } @Override public void afterPropertiesSet() throws Exception { System.out.println("InitializingBean的 afterPropertiesSet方法"); } }
- :通过JSR250规范 提供的注解@PostConstruct 和@ProDestory标注的方法
@Component public class Book { public Book() { System.out.println("book 的构造方法"); } @PostConstruct public void init() { System.out.println("book 的PostConstruct标志的方法"); } @PreDestroy public void destory() { System.out.println("book 的PreDestory标注的方法"); } }
-
通过Spring的BeanPostProcessor的 bean的后置处理器会拦截所有bean创建过程
-
- postProcessBeforeInitialization 在init方法之前调用
-
- postProcessAfterInitialization 在init方法之后调用
@Component public class TulingBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println( "TulingBeanPostProcessor...postProcessBeforeInitialization:" + beanName); return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println( "TulingBeanPostProcessor...postProcessAfterInitialization:" + beanName); return bean; } }
populateBean(beanName, mbd, instanceWrapper)
initializeBean{
applyBeanPostProcessorsBeforeInitialization() invokeInitMethods{ isInitializingBean.afterPropertiesSet
自定义的init方法
}
applyBeanPostProcessorsAfterInitialization()方法
}
-
自动装配
-
- @AutoWired的使用
//一个Dao @Repository public class TulingDao {
} @Service public class TulingService { @Autowired private TulingDao tulingDao; }
@Autowired private TulingDao tulingDao;
@Autowired @Qualifier("tulingDao") private TulingDao tulingDao2;
-
- @Resource(JSR250规范)
-
- @InJect(JSR330规范)
-
- 使用autowired 可以标注在方法上
@Autowired public void setTulingLog(TulingLog tulingLog) { this.tulingLog = tulingLog; }
@Autowired public TulingAspect(TulingLog tulingLog) { this.tulingLog = tulingLog; }
-
- 我们自己的组件 需要使用spring ioc的底层组件的时候,比如 ApplicationContext等
@Component public class TulingCompent implements ApplicationContextAware, BeanNameAware { private ApplicationContext applicationContext; @Override public void setBeanName(String name) { System.out.println("current bean name is :【" + name + "】"); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } }