spring ioc应用和部分问题

spring ioc应用和部分问题

前言,本片文章为本人学习总结,不保证内容一定正确,请辩证的看。

一、一些简单的问题

1.什么是ioc?

ioc就是控制反转(Inverse of Control),是一种思想,就是将创建对象的权力从使用者转移到容器;
spring中的DI(依赖注入)就是ioc思想的一种实现,体现就是在spring中开发者不用再去手动去new对象,而是只需要声明对象,加对应注解,对象是spring的ico容器去创建的。

下面得栗子,讲的就是spring中得ico的实现思路和起到的作用

举个例子,比如数据库Dao层,如果项目一直使用同一种数据库那么这些实现的DaoImpl都可以正常运行,但是如果某次上线
以前,老板非要换成其他的数据库,这个时候就要考虑兼容比如sql语句在不同项目的兼容,如果下次还要要再次换回来等;
如果都是使用new去创建的,那么需要改地方随着业务越多改动越大,这个时候聪明的你想到了我可以建一个简单工厂,业务中使用的DaoImpl都从工厂里获取,
这样不管数据库怎么切换,我都只需要改一个类; 一般到这里我们就觉得设计得不错了,但是哈如果有人误删了某个数据库得DaoImpl,
那我得项目还是跑不起来,编译也过不去,这个时候聪明你想到我可以使用反射+类全面名字得方式去从工厂里创建对象,这样就算有人
误删得某个DaoImpl,也不会有影响项目的编译,这里在扩展一下硬编码得问题,因为你的类全名都是卸载代码里的,是写死的
如果隔三岔五就要去切换不同得数据库就得不停的改,这个时候可以将类全名放在配置文件里面,这样每次切换都只需要改配置文件,而不用动代码,
到这里的时候就差最后一步,可以想一下,每次从工厂去获取对象得时候是不是每次都会创建一个新的,如果获取100次
就会创建100次,这其实是没有必要得,因为这个数据库得对象其实只创建一次就好了,重复创建不仅浪费时间还浪费内存,
所以可以加一个缓存,第一次创建好以后,就放入缓存里,后面每次后去就直接从缓存里获取。

2.spring 为什么要用ioc?

其实用ioc主要是为了解耦,使对象在使用时不在强依赖的new关键字去创建。其次是由容器去统一的创建和调度管理,也更能节省资源。 举个栗子:如果继续使用new关键字去创建,如果出现代码意外丢失,或者编码规范问题,就可能会出现项目无法启动报错的问题,或者重复的对象创建等,使用ioc后如果某部分编码有问题,只会影响相关功能,不会影响其他的功能,项目也能正常运行。

3.可不可以不用ioc?

需要分场景,如果是功能很少的很小的项目,其实根本不用引入ico就使用new去创建对象就可以了,引入了可能对导致只项目过于臃肿。话说回来,现在企业使用的很少会有这种小项目了,所以使用ioc又成了较好的解决方案,使用spring开发的后端项目一般也都是比较大的项目,所以ioc又成了标配。

4.spring里ioc创建bean的流程,或者说创建对象的流程?

创建的bean的流程是(bean的生命周期): 实例化–> DI(依赖注入)–> 初始化–> 使用–> 销毁 ;

5.什么是循环依赖?

首先要名明白依赖是什么,依赖其实就是要创建的的对象内的成员属性,循环依赖发生在ioc对象创建的依赖注入中,发现两个对象互相依赖,(举个栗子:两个对象A,B,A的成员属性有B,B的成员属性有A,在ioc创建A以后在依赖注入入成员属性b的时,发现b还没创建,然后去创建B,给B属性注入的时候发现又需要a,就这样形成了循环),因为依赖注入方式有两种,一种是构造器方式,另一种是set的方式,spring的ioc只能解决set方式的循环依赖,因为构造器方式的循环依赖发生在实例化的过程中,没办法通过三级缓存去创建。

6.spring中的ioc是怎么实现的?

通过单例模式+反射实现的。容器是用过map实现的。

7.为什么说spring 中的DI是ico思想的一种实现?

IoC是一个设计思想,而DI是实现这一思想的“具体操作”,通过DI,开发者不再需要手动管理依赖的生命周期和绑定,这正是控制权反转的体现。
我自己也反思了这个问题,能提出这个疑问还是对ico的思想不太理解,我提出这个问题以前是这样想的为什么不是整个bean的创建流程
实例化->依赖注入Di->初始化销毁,ioc的控制反转,就是不用自己去创建对象而是容器去创建,而在spring的bean的创建周期中,Di才是帮助我们去创建和注入对象不用我们
自己创建的核心操作,(Di的时候如果没有创建对象会去创建对象,启动实例化流程。)

二、应用

常用的用法

1.spring 的ioc在程序中怎么用

两个方向spring boot 和spring mvc的区别。

  • spring boot中,首先spring会默认扫描项目根包目录下面的所有类,也可以自己指定扫描路径,在springboot的启动类上
    使用@CompentScan去扫描多个或者单个包,其次就是使用spring提供的注解@Compent、@Service、@Restory、@Controller这些
    注解加在类上面,同时要保证实体类这种的内部属性都有Get和Set方法以及构造方法,这样就能保证spring boot的注入了。
  • spring boot中的第二个方法,使用@Configuration注解和@Bean注解的get方法,也可以将bean注入到容器。
  • spring mvc中,就是使用xml了,使用标签去指定名字和类的全类名,以及标签去注入bean,因为spring mvc中
    没有启动类所以他的扫描范围也是使用标签指定的,在springMvcContext.XML里去指定通过<drive:sCan/>去指定包的扫描范围。
// 扩展一种特殊的注入方式,看代码的时候偶然看到没用@Autowared和@Resouce;
@Slf4j
@Service
@RequiredArgsConstructor
public class xxxxService {

    private final XXXXService xxxxtService;

    private final XXXXXXQueryService xxxxQueryService;

    public void test(){
        // 直接拿来就可以使用不需要别的注解
        xxxxtService.setAge(1);
    }

}

// 这样也能注入成功,在方法可以直接使用,主要依赖@RequiredArgsConstructor 而且对象声明的时候要是private final 的,这样就可以注入成功,@RequiredArgsConstructor来自lombok依赖

2.怎么在spring中获取bean?

两个方向,一种是自动获取注入,一种是自己手动获取去做操作

  • 第一种就是使用spring的@Autowared、@Resource,这种就不需要手动获取在代码里使用到的时候spring
    会自动的去创建或者一开开始就创建好注入进去,这取决于有没有设置懒加载(lazy);这也就是我们开发中常用的,声明加注解直接使用。
  • 第二种,手动获取,比如我不提前声明,而是从容器中去获取,然后做一些操作,这个时候就需要知到
    两种获取方式(注入方式)byName和byType,也就是根据名字和根据类型去从从容器获取bean,字面意思根绝类的类型或者类的名字、
    去获取bean,(名字也如果没指定别名,一般是类名的首字母小写,类型不用想了,肯定就只有一种),手动获取bean主要通过Context去获取
    获取到以后,就可以拿到对象去进行一些操作,比如设置值,解析值这种。 (一般用于读取配置属性做一些初始化的操作)

// 获取bean的方式,很多种 
// 这是封装自己工具类用来获取bean
@Component
public class SpringUtils implements ApplicationContextAware {

    private static ApplicationContext context;

    public static <T> T getBean(Class<T> clz) {
        return context.getBean(clz);
    }

    public static Object getBean(String beanName) {
        return context.getBean(beanName);
    }

    @Override
    public void setApplicationContext(ApplicationContext ctx) throws BeansException {
        context = ctx;
    }
}
    // 本质就是获取到工厂,让后从工厂里获取bean

扩展,当然大家都知道: @Resouce默认byName,也可以通过属性变成byType; @Autowared byType注入,搭配@Quleter注解可以变为byName;

不常用的

1.扩展,aware接口

用法:用于获取一些ico创建好的对象,注入到类的属性里,aware接口有很多,下面只是展示一部分用法

public class Monkey implements BeanNameAware,BeanFactoryAware, BeanClassLoaderAware {
    
    private String name;
    private String beanName;
    private BeanFactory beanFactory;
    private ClassLoader classLoader;
    
    @Override // 将bean存在的工厂放入到属性中
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }
    
    @Override // 写方法将bean的名字放入到属性中
    public void setBeanName(@NotNull String beanName) {
        this.beanName = beanName;
    }
    
    @Override // 重写方法将类加载器放入到属性中
    public void setBeanClassLoader(ClassLoader classLoader) {
        this.classLoader = classLoader;
    }
    
    public void setName(String name) {
    this.name = name;
    }
    
    public String getName() {
    return name;
    }
}

2.扩展,实现接口或者继承类

用法,先这样的前置后置处理器还有很多

// 1.继承context方法实现重写
public class XXXXClass extends ClassPathXmlApplicationContext {
    
    // 构造方法
    public XXXXClass(String location) {
        super(location);
    }

    @Override // 实现方法
    protected void  postProcessBeanFactory(ConfigurableListableBean Factory beanFactory) {
        BeanDefinition beanDefinition = beanFactory.getBeanDefinition("monkey");
        System.out.println("beanDefinition before:" + beanDefinition.getDescription());
        // 给属性设置值
        beanDefinition.setDescription("test");
        System.out.println("beanDefinition after:" + beanDefinition.getDescription());
    }
}

// 2.实现接口的方式实现调用 (记得注册到容器里面)
public class xxxxProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws  BeansException {
        BeanDefinition monkey =
        beanFactory.getBeanDefinition("monkey");
        monkey.setDescription("this is monkey");
        System.out.println("BeanFactoryPostProcessor前开始执行:" +
        beanFactory.getBeanDefinitionNames());
    }
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值