Spring所依赖的两个核心理念:控制反转(Ioc)、面向切面编程(AOP)
初学者可能不理解什么叫控制反转,那么我们来进一步描述一下Ioc。
Ioc是一种通过描述来生成或获取对象的技术,这里的对象当然是指java对象。
在Java中我们更多的是通过new关键字来创建对象,在Spring中,则是通过描述来创建对象。
所以我们知道了,Ioc就是用来获取java对象的东西,“控制反转”这个词先往后放放。
对象有了,我们就需要一个东西对这些对象进行存放、管理,用什么呢,没错,一个容器。
Spring把需要管理的对象叫做Spring Bean(简称Bean),管理这些Bean的容器叫做Ioc容器。
所以 Ioc容器的职责为:管理Bean的发布和获取以及Bean之间的依赖关系 。
Ioc容器需实现BeanFactory接口,这个接口里包含里许多getBean的方法以及其他的一些方法(比如isSingleton单例获取[只有一个],还是prototype原生获取[每次获取都生成一个新的])等。ApplicationContext接口通过继承上级接口进而继承了BeanFactory接口,它是我们熟知的上下文环境,没错,它的实现也是一个Ioc容器(注意spring中有好多容器呢)。
1. 通过@Bean注释我们再来说一个基于注解的Ioc容器:AnnotationConfigApplicationContext,之所以讲他是因为Springboot装配/获取Bean的方法与它如出一辙。
public class User{
private Long id;
...
}
//other file
@Configuration
public class AppConfig{
@Bean(name = "user")
public User initUser(){
return new User();
}
}
Spring容器会根据@Configuration这个注解来生成Ioc容器,然后以这个容器去装配Bean。而@Bean这个注解则会将其标注的方法返回的对象装配到Ioc容器中,name则是可以自定义装配的这个对象叫什么名字,我们可以通过以下代码来查看是否装配进去了
public static void main(String[] avgs){
//参数AppConfig.class就是我们自己写的那个容器类。
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
User user = ctx.getBean(User.class);
}
2.通过@Component等注解
通过以上的代码我们了解到了,Spring通过@Bean这个注解来装配Bean对象。
当然如果都用这个来装配,实在是很麻烦,所以Spring还以此为基础设置了Bean的扫描装配
@Component、@Controller、@Service、@Repository等,这些注解都是用来定义扫描类的,后面那几个其实都是基于@Component而建立的注解,所以他们也有@Component的作用。@ComponentScan则是用来告诉Spring要扫描的路径(包)的,它还有些过滤器参数,这里不做详谈。
@Component
public class User{
...
}
//Other file
@Configuration
@ComponentScan(backPackages={ "你要装配的Bean的包路径" })
public class AppConfig{
...
}
所以这个过程为:Spring容器根据@Configuration生成IoC容器,然后通过@ComponentScan的描述去对应的包路径下寻找被@Component注释的类装配相应的Bean对象。
3.通过DI依赖注入所谓依赖注入:当一个Bean(a)内部包含另一个Bean(b)时,Ioc容器就需要把a所依赖的b也装配进去。@Autowired注解就是做这个用的,它是按类型找到对应的Bean然后注入的,对应这BeanFactory接口中的按类型获取Bean的方法。
@Component
public class User{
@Autowired
private Animal animal;
....
}
这里有个小细节,既然他是按类型查找的,那如果Animal接口下有两个实现类Cat和Dog,并且这两个子类都已经装配到Ioc容器中了,那么Ioc容器会把哪个类注入到User中呢?对于这种注入歧义问题有两种方法可以消除:
i.@Primary 被这个注解修饰,当前类会获取优先权,也就是比如Cat被它修饰,而Dog没有,则注入时会自动选择Cat。
ii.@Quelifier 它会明确定义被注入的是哪个Bean。每个Bean都是有名字的,默认是类名且其首字母变成小写。
@Autowired
@Qualifier("dog")
private Animal animal;
这种DI注入也是可以放到构造方法中的
public User(@Autowired Animal animal){
...
}
4.引入xml的Bean
对于一些老旧项目,都是以xml配置的bean,那么我们怎么通过注释引入这些xml类型的bean呢?可以用注解@ImportResource,它可以引入xml文件来加载bean。
比如一个xml名为spring-other.xml
那么相应的装配代码为:
@Configuration
@ImportResource(value={"classpath:spring-other.xml"})
public class AppConfig{
...
}
5.条件装配
在装配一些bean时,有可能会引用properties文件中的属性,如果相应的属性没有,就有可能造成bean的装配错误,应对这样的场景,使用@Conditional限制装配的条件,如果不符合条件,则不装配,这样就不会报错了。(@Value注解是用来获取application.properties中的属性值的)
@Bean
@Conditional(MyConditional.class)
public DataSource getDataSource(
@Value("${database.driverName}") String driver,
....
){ ... }
//other file
public class MyConditional implements Condition{
@Override
public boolean matches(ConditionContext ctx,AnnotatedTypeMetadata metadata){
Environment env = ctx.getEnvironment();
return env.containsProperty("database.driverName");
}
}
Bean的生命周期
Ioc容器初始化和销毁Bean的过程,也可以说是Bean被Ioc容器管理,从生到死的过程。
大致分为四个阶段:Bean定义、Bean初始化、Bean生存期、Bean销毁。
Bean定义: Spring通过@Component等注解实现资源定位,然后解析资源,将类定义的信息保存起来(注意此时仅仅是Bean的定义,并没有实例化),然后把定义信息发布到Ioc容器中,仍旧没有实例化!更没有依赖注入。
Bean的初始化:
Spring针对Bean的初始化有两种情况:
以下顺次执行
过程 | 讲解 |
---|---|
初始化 | 也就是实例化 |
依赖注入 | DI |
setBeanName | 对应于BeanNameAware接口,设置对象名 |
setBeanFactory | 对应于BeanFactoryAware接口,让Bean拥有访问Spring容器的能力 |
setApplicationContext | 需要容器实现ApplicationContext接口才会调用,它实现的是ApplicationContextAware接口,目的是传入上下文 |
postProcessBeforeInitialization | BeanPostProcessor的预初始化方法,它是针对所有Bean的,是对所有的bean进行一个初始化之前和之后的代理 |
自定义的初始化之后方法 | 被@PostConstruct标注的方法 |
afterPropertiesSet | 实现接口InitializingBean,它是针对单个Bean的初始化完成之后的事件 |
postProcessAfterInitialization | BeanPostProcessor的后处事话方法,它是针对所有Bean的,是对所有的bean进行一个初始化之前和之后的代理 |
生存期 | |
自定义的销毁方法 | @PreDestroy标注的方法 |
destroy | 实现接口DisposableBean |
表中,只有BeanPostProcessor是针对所有Bean的,其余都是针对单个Bean的。
这样看来,Bean的声明周期就很好记了:
在bean初始化并依赖注入后,对bean进行命名并使其拥有对spring容器及上下文的访问能力。然后由于BeanPostProcessor对Bean的代理,执行了一个所有bean初始化前都要执行的方法,然后针对单个bean执行了一个被@PostConstruct修饰自定义的方法,这样初始化成功了,紧接着要执行一个在初始化成功之后的方法,最后再执行一个针对所有bean初始化之后都要执行的方法,ok,bean下生了,最后在bean死之前,可以执行一个被@PreDestroy修饰的自定义的方法,再执行一个destroy的方法,bean就死透了。
(有点类似于小孩从娘胎里出来后,给他冠名,给他能用的东西,然后在他长大前做一些一般小孩子经历的事情,比如打疫苗,然后再给他安排一些个性化只有他自己独有的东西,长大后,做一些个性化独有的事情,再做一些通用的事,比如上学,然后,小孩就正式成人了,好好生存呗,最后,变老了,要准备后事了,我们可以自定义个性化的做一些生前最后的事儿,最后,下葬destroy,死透透的了…)
我在文末摘抄了一个关于bean生命周期应用实例。
在容器实现的BeanFactory接口中,我们可以看到单例(Singleton)和原型(Prototype),Spring默认使用的就是单例,我们可以手动改为其他
@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class Test{...}
常用的作用域还包括:session 、 application、request、globalSession
例如request:同一个请求范围内获取这个Bean时,只会共用同一个Bean,第二次请求则会产生新的Bean。
包含xml形式中的init-method,此处代码引用自 https://www.cnblogs.com/grey-wolf/p/6627925.html
package com.ckl.springbeanlifecycle;
import com.ckl.springbeanlifecycle.service.IDemoService;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
/**
* desc:
*
* @author : caokunliang
* creat_date: 2019/7/20 0020
* creat_time: 18:45
**/
public class DemoController implements InitializingBean,DisposableBean {
@Autowired
private IDemoService iDemoService;
public DemoController() {
System.out.println();
System.out.println("constructor ");
System.out.println( "属性:" + iDemoService);
System.out.println();
}
@Override
public void destroy() throws Exception {
System.out.println();
System.out.println("implements DisposableBean interface");
System.out.println( "属性iDemoService已注入:" + (iDemoService != null));
System.out.println( "属性iDemoService已注入:" + iDemoService);
System.out.println();
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println();
System.out.println("afterPropertiesSet interface");
System.out.println( "属性iDemoService已注入:" + (iDemoService != null));
System.out.println( "属性iDemoService已注入:" + iDemoService);
System.out.println();
}
@PostConstruct
public void postConstruct(){
System.out.println();
System.out.println("@PostConstrut....");
System.out.println( "属性iDemoService已注入:" + (iDemoService != null));
System.out.println( "属性iDemoService已注入:" + iDemoService);
System.out.println();
}
@PreDestroy
public void preDestroy(){
System.out.println();
System.out.println("@PreDestroy.....");
System.out.println( "属性iDemoService已注入:" + iDemoService);
System.out.println();
}
public void init(){
System.out.println();
System.out.println("init-method by xml 配置文件");
System.out.println( "属性iDemoService已注入:" + (iDemoService != null));
System.out.println();
}
public void cleanUp(){
System.out.println();
System.out.println("destroy-method by xml 配置文件");
System.out.println( "属性iDemoService已注入:" + iDemoService);
System.out.println();
}
}
package com.ckl.springbeanlifecycle;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
import java.lang.reflect.Field;
/**
* desc:
*
* @author : caokunliang
* creat_date: 2019/7/20 0020
* creat_time: 18:52
**/
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof DemoController){
System.out.println();
System.out.println("BeanPostProcessor:" + "postProcessBeforeInitialization");
Field field = null;
try {
field = bean.getClass().getDeclaredField("iDemoService");
field.setAccessible(true);
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
try {
Object o = field.get(bean);
System.out.println( "属性iDemoService已注入:" + (o != null));
System.out.println( "属性iDemoService已注入:" + o);
System.out.println();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof DemoController){
System.out.println();
System.out.println("BeanPostProcessor:" + "postProcessAfterInitialization");
Field field = null;
try {
field = bean.getClass().getDeclaredField("iDemoService");
field.setAccessible(true);
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
try {
Object o = field.get(bean);
System.out.println( "属性iDemoService已注入:" + (o != null));
System.out.println( "属性iDemoService已注入:" + o);
System.out.println();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
return bean;
}
}
执行结果
原创文章 17获赞 2访问量 1232
关注
私信
展开阅读全文
作者:缔曦_deacy