Spring作为应用很广泛的开源框架,在面试中肯定少不了
Bean作为我们Spring的一大核心(容器)是非常重要的,下面是自己看周瑜老师学的还有其他一些视频的学习笔记
说一下Bean的加载过程,生命周期 (肯定少不了重要的IOC)
class———>推断构造方法———–>实例化——>对象——> 属性填充(IOC)——>初始化——->AOP------>Bean(这里没总结完)
bean的加载过程
我是这样理解的,从配置文件加载或者组件注解加载我们的Bean的时候会先进入一个叫BeanDefinition中,它是一个Map类型,主要是读取我们的Bean定义信息,注意这个时候我们的Bean中属性并没有进行赋值操作,然后图中的几个接口很重要,一个是Bean工厂,还有两个以Processor结尾的接口,我是这样理解的,他就是来修饰扩展我们的Bean的也可以说是增强我们的Bean,
Spring作为一个框架,必须要有一定的可扩展性,这个Processor就是让我们自定义我们的扩展组件的
总结:作为一个框架,首先要考虑我们的扩展性
Spring提供了什么扩展性
1.在对象创建之前我们可以欠佳某些功能
2.在容器初始化之前添加某些功能
3.在不同的阶段发出不同的时间,完成一些功能
4.抽象出一堆的接口来帮助扩展
FactoryBean中3个方法,是用来根据我们自己的想法步骤去创建出我们想要的Bean的;也就是说这一步肯定要先把Bean定义读取到BeanDefinitionReader的Map结构中,然后Spring提供的增强方法去修饰它,
构造工厂图中也表示出来了,可以看出是根据构造函数来实例化对象的,那么是怎么个原理呢?
推断构造方法,Spring如何判断构造函数来实例化对象
UserService是一个Bean,里面既有默认的无参构造函数,又有一个有参的构造函数(随便就行),那么它是怎么进行实例化的
答案是我们的Spring会调用我们的无参构造函数,我想让Spring用带参构造方法怎么办呢?
我们可以在有参构造函数方法上加@AutoWired注解
如果有两个带参的构造方法,就会报错,因为Spring不知道要用那个构造方法,会爆出No default constructor found
我们Spring对带参 的构造函数的参数是通过byType,byName的顺序一起匹配来找的,这里要区分单例Bean(单例池中)和单例模式的区别(有兴趣的可以看看)
只通过名字:那么我们找到的这个对象有可能跟我们的入参类型不匹配
只通过类型:要是我们的单例池中有好几个OrderService呢?不知道选哪一个
IOC DI 依赖注入
属性填充
**@AutoWired值怎么来的呢?**还是通过buType 和byName来的
AutowiredAnnotationBeanPostProcessor
1.先找注入点(这个类哪里加@AutoWired注解了)
根据我们的byType来找,在根据byName
@Resource注解
CommonAnnotationBeanPostProcessor
首先也会去寻找被@Resource注解了的方法和属性
1.如果@Resource注解指定了name属性,就直接从Spring容器拿对应的Bean,如果不存在则表示没有找到注入对象(报错)
2.如果@Resource注解没有指定了name属性,但是根据属性名字或setXXX中的**xxx在Spring容器中有对应的Bean,(不是根据参数名字找的)**那也直接根据名字拿
3.否则根据注入点Type类型去找Bean
那么怎么把值赋值进去呢?
反射
这里我只写了一个简单的例子,实际上肯定会比较复杂,但是意思就是这么个意思,只是少了很多逻辑,动态改变这些,看明白意思就行
代码准备
UserServiceImpl代码
public class UserServiceImpl {
}
UserController代码
public class UserController {
//这只是声明了引用,并没有new对象
//@AutoWired是怎么实现的呢?
//spring的就是反射来实现的
//我自己写一个
@AutoWired//这是我自己写的
private UserServiceImpl userServiceImpl;
public UserServiceImpl getUserServiceImpl(){
return userServiceImpl;
}
}
实现@Autowired功能
/**
* @author 王xx
* @version 1.0
* @date 20xxx 22:45
* @Description TODO
* @pojectname 自己实现我们的@AutoWired功能
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)//作用在属性字段上
@Inherited
@Documented
public @interface AutoWired {
}
下面就是我们怎么样通过反射的方式,实现我们的Autowired功能,将我们的UserServiceImpl对象,赋值给我们的UserController
//测试我们自己写的注解,也是我们IOC怎么实现的核心
@Test
public void testAutoWired(){
UserController userController = new UserController();
//获取Class对象
Class<? extends UserController> aClass = userController.getClass();
//获取所有属性值
Stream.of(aClass.getDeclaredFields()).forEach(field -> {
//获取属性名
String name = field.getName();
//获取属性名上的注解信息
AutoWired annotation = field.getAnnotation(AutoWired.class);
//判断我们的属性名是否备注解
if (annotation != null){
//设置访问属性
field.setAccessible(true);
//获取我们属性的类型,得到要注入的目标值
Class<?> type = field.getType();
//创建我们的注入对象的实例
try {
Object o = type.newInstance();
//为我们的UserController注入对象
field.set(userController,o);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
});
System.out.println(userController.getUserServiceImpl());
}
初始化
这里的初始化指的是我们的类中可能还有没有被@Autowired注解的属性,普通属性,怎么初始化呢?
eg:
public class UserService{
@AutoWired
private OrderService orderService;
//我们没用注解标识,不会通过反射去动态的为其注入依赖
private User defaultuser;
}
让我们的这个类实现我们的InitializingBean接口,并重写他的方法,这个方法就是在我们的属性注入之后来进行这个类的初始化的
public class UserService implements InitializingBean{
@AutoWired
private OrderService orderService;
//我们没用注解标识,不会通过反射去动态的为其注入依赖
private User defaultuser;
@Override
public void afterPropertiesSet() throws Exception {
//我们可以设置我们的逻辑
//比如从数据库查数据在给我们的默认user赋值
defauteUser = new User(xxxx);
}
}
或者使用他的注解**@PostConstruct**,表明我们这个方式是个初始化的方法
初始化完成后,就看看我们是否有AOP的支持
比如我们的UserService的其中一个方法需要AOP支持,那么我们的Spring就会用CGLB代理生成一个UserServiceProxy的代理对象,但是需要注意的是,我们的这个UserServiceProxy中的有些属性是没有值的!!!(小坑)
public class UserService {
@AutoWired
private OrderService orderService;
public void test(){
//xxxxxxxxx
}
}
实际上我们产生的代理类是这样的(模拟的)
class UserServiceProxy extend UserService{
//目标对象
private UserService target;
public void test(){
//先执行我们的代理逻辑
//我这里想表达的就是只有一个前置方法
//也就是我们的切面Bean的那个增强方法(我是这样理解的)
UserServiceAspect.testBefore();
//然后执行我们的目标方法
target.test();
//有可能还有后置方法,我这就不写了
}
}
我们的代理类生成了一个目标对象,而且这个目标对象里也有一个OrderService对象,为啥为空?
我们的这个AOP逻辑处理的时候,我们是不会在进行属性注入的,就不会回去了,但是,我们的这个UserServiceProxy里面其实是有一个UserService对象的,而且我们点开这个对象发现,它里面的OrderService对象是有值的。
其余的还没总结完,下次聊