深入了解Spring的IOC(DI)、AOP和SpringBoot自动配置原理

1、Spring的IOC和DI
(1)IOC
IOC全称是Inverse of Control,控制反转的意思,就是把对象的创建和生命周期的管理交给spring,这里提到对象的创建,那spring创建对象的方法有几种?
四种:
第一种是针对只有无参构造的类而言,直接利用反射原理创建对象,这种方式创建的对象如何放入spring容器中?在xml中写一个bean标签对,里面写id和class属性;
第二种是针对有参构造的类或者抽象类而言(当然也可以用于只有无参构造的类),此时需要自己创建一个静态工厂类,里面定义一个getInstance()静态方法,那这种方式创建的对象又如何放入spring容器中?在xml中写一个bean标签对,里面除了id和class属性,还要写一个factory属性,这个属性写的是静态工厂方法(注意xml里写两个bean,因为除了工厂类,还需要原材料类)(静态工厂方法在xml中的确只写一个!):
第三种和第二种使用场景一样,也需要创建一个工厂类和里面的工厂方法,但只是这里不用静态方法而是实例方法,从形式上就是少一个static,其他一样,但这种方式创建的对象放到spring容器中的xml里写法不一样,需要注意的是静态工厂方法只有一个bean,实例工厂却是两个,第一个bean是写工厂类的id和class,第二个bean是写原材料类的id、factory-bean(其实就是第一个bean的id值,这个取代class)和factory-method。
第四种是采用Spring内置的工厂接口完成。
随着需求的不断增加,bean标签对里的属性只有上面的id、class、factory-method、factory-bean肯定不够,所以出现了scope、lazy-init、init-method和destroy-method:
1)scope:定义单例还是多例
如果不写,默认是单例,即对象只创建一次后就放在spring容器中,然后每次getbean都是从容器中拿这个对象,不会取一次创建一次,而如果加了这个属性并且值设置为prototype,则说明开启多例模式,那就是每取一次创建一次。
2)lazy-init:定义是否开启懒加载机制
如果项目需要在xml中要写很多的bean而且是在单例模式下,这样解析xml后就会对里面所有bean进行创建对象,这样会耗费很多时间和内存,如果都创建了很多还用不到,那就更是大大浪费了,所以引申出一个属性:懒加载,即一开始解析xml的时候有这个属性的bean就先不创建,只有主程序中第一次用到了才创建,当然这个属性也可以设置为全局,即所有bean都满足,如果全局和局部设置不同而且都有哪个优先?局部优先。
注:为什么这里强调“而且是在单例模式下”?多例模式下不需要懒加载机制,相同点都是用的时候再创建对象,但不同点是单例懒加载机制还是只有第一次才创建对象。
3)init-method和destroy-method
这两个属性体现了spring对对象的生命周期管理,这个类比前面学过的servlet的生命周期,即spring根据id+class或id+class+factory-method或id+class+id+factory-bean+factory-method这三种方法创建对象后(可以设置多例模式、懒加载机制),先调用init方法进行初始化操作,再调用对象里的普通方法,最后在对象销毁之前才调用destroy方法进行销毁操作。
:使用spring不一定是web工程还可以是普通工程;还有就是一定要导spring的包,这样才能完成spring自动解析xml文件等功能;最后要注意写一个xml模板,配置文件和注解的模板形式不一样。
上面提到如果xml中bean太多,那Spring解析这个xml就要创建这么多bean对象很浪费时间和空间,所以为单例模式弄了一个懒加载机制,这是为Spring考虑,那我们能不能为自己考虑下,毕竟写这么多bean太累,而且看起来太麻烦了,为了解决这个需求,出现了IOC的注解@Component:
这个注解只要写在我们要创建对象的类的上面即可,然后再在xml文件中写一个包扫描的标签对就可以自动找到这个类,并对其创建对象然后放到Spring的map容器中以id为键、class为值保存。
问题是这个包扫描的标签对怎么写?
包扫描标签对的名字是context:component-scan,怎么记这个名字?context是上下文、环境的意思,component就是IOC的注解名,scan是扫描,即扫描有Component这个注解名的环境,其实就是扫描包,这个标签对里的属性也是包名即base-package,主程序启动以后解析xml文件,xml文件就会去指定的包名里扫描所有带有Component注解的类进行创建对象并放入内置map容器中。
:虽然注解方便,但因为是封装的,所以效率其实没有配置文件高,类比java效率没有c高一样,因为c更偏底层。
IOC的注解名其实和bean标签对中的属性名息息相关,只是Component这个注解和属性名无关,但多例模式的注解名还是Scope,即@Scope(value=’prototype’);如果是开启懒加载机制,则为@Lazy;初始化方法和销毁方法分别是@PostConstruct和@PreDestroy,除了这两个注解写在方法上,其他都在类上。
还需要注意的是@Component这个注解是在所有类通用的,但为了更好地在Web层、Service层和Dao层区分,所以在Web层(包)的类用@Controller表示,Service层(包)的类用@Service表示,Dao层(包)的类用@Repository表示。
使用@Repository的时候要注意一种情况:如果Dao层写了一个接口userDao,并有两个个实现类实现这个接口,并且这些实现类上面都有@Repository注解,然后此时在Service层里的userService的实现类调用了userDao接口,而且用@Autowire自动注入userDao接口的实现类,但此时注入的是userDao的哪个实现类?所以必须在@Repository的后面写(“userDao”),这样@Autowire的就会自动找到这里,如下所示:
在这里插入图片描述
在这里插入图片描述
除了上面这种方法还可以在@Autowired下面再加一个注解为@Qualifier(“MySqlUserDao”)。
综上所述,Spring的IOC实现原理:
当我们在启动类里初始化一个Spring容器的时候,Spring会自动去解析xml文件,当解析到xml里的bean标签对的时候,会根据标签中的class属性即类的全路径名,然后根据反射方法创建给类的实例对象(具体是Class.forName(全路径名)这种反射方法,还有两种是对象名.getClass()和类名.class,然后是再调用newInstance()方法即可创建对象,这是默认的无参构造创建对象的方式),创建好的对象会放到Spring容器内置的Map中,键是Bean的id,值是该对象,然后我们需要的时候用getBean()方法就可以拿到了。

(2)DI
DI全称是Dependency Injection,依赖注入的意思,其实就是在Spring创建的对象过程中给对象的属性传入属性值,首先回顾下前面说的因为java面向对象的封装特性,所以javaBean里的属性一般私有化,那怎么给私有化属性传入属性值?两种方法,一种是写set方法,对应也有get方法;另一种是写有参构造方法。
那Spring怎么实现这两种方法?
1)set方法:对应properties标签对
这个标签对写在bean标签对里,如果把bean标签对类比为html中的form标签对,那properties标签对就是input标签对(表、行和列标签先不管),但因为属性值有不同的数据类型,所以需要具体问题具体分析,这里大致可以分为Spring内置的和非内置的:
A、Spring内置的
Spring内置的数据类型如果是int、String则比较简单,直接写name和vale属性即可,但如果是List 、Set、Map、Properties等,则会比较麻烦,首先还是写properties标签对,虽然properties标签对里的属性还是name和value,但value已经独立,可能因为它太多管不住了,以List为例,需要写多个value标签对,属性值写在value标签对里面,还需要注意的是这么多value标签对前后必须写上list标签对包裹起来,如下所示:

         <property name="jobs">
                        <list>
                                <value>上单</value>
                                <value>打野</value>
                                <value>辅助</value>
                                <value>中单</value>
                        </list>
        </property>

如果是Set类型,只需要把上面的List改成Set即可,Map和Properties在value标签对这个部分会有点不同,这里不聊,遇到可以查看资料。
B、Spring非内置的
Spring非内置的数据类型其实就是这个类中用到的其他类的对象(这些都是自定义的),比如Web层的Controller类的方法调用Service层的对象,那就要把Service层的对象作为属性值注入到Controller类中,怎么在xml中写?还是写一个properties标签对,里面的属性是name和ref bean(bean可以省略),ref的值对应下面新写的bean的id属性,这个新写的bean的id和class对应上面提到的其他类。
Spring非内置的数据类型除了写properties标签对和name、ref属性之外还可以直接在bean中用autowire属性,这个属性是自动装配的意思,属性值可以写成byName表示根据其他bean的id注入,和懒加载一样,这个也可以全局装配,而且全局和局部都存在的情况下优先局部,但我测试了下如果除了非内置数据类型外还有内置数据类型,则非内置数据类型不能用自动装配这种方法。
2)有参构造方法:对应constructor-arg标签对。
注意这个标签对是和properties标签对同级别(DI级别,name属性是DI的),而不是bean标签对(IOC级别,id属性才是IOC的),bean标签对是共有了,除非改成注解形式,所以和properties标签对相比,如果是Spring非内置的数据类型中的int和String则还是name+value属性;如果是Spring内置的数据类型则还是用name+ref属性,ref和后面bean的id对应,但需要注意的是这里还要加上index,这个值从0开始算,分别对应有参构造方法里参数的位置顺序。
上面聊了IOC的注解形式,这里也聊下DI的注解形式:@Value和@Autowire(注解的首字母大写),前面提到Spring内置的数据类型是在properties标签对里写name+value,如果改成注解形式properties标签对肯定不要了,因为前面IOC的注解里也把bean标签对去掉了,但也没保留id或者class作为注解名称,而是用Component作为注解名,而DI不一样,Spring内置的数据类型用原来的value属性作为注解名即@Value;如果是Spring非内置的则用自动装配里的Autowire属性名作为注解名。
这里需要特别指出的是Spring内置的数据类型有简单和复杂的,简单的是非集合类型,比如int和String,这两种数据类型的赋值是直接在@Value后面跟上()并填上相应数据即可,,但都需要加上双引号;如果是复杂的集合类型则比较麻烦,还是需要在配置文件中写很多。
:如果在很多类中的属性上面一个个赋予具体的值,则比较麻烦,所以考虑用properties配置文件,如果用这个配置文件,则首先写一个这样的文件,再在xml文件中写context:property-placeholder标签对来引入这个文件,最后再在不同类的一个个属性上写上@Value(”${属性名}”)即可,不需要具体的值,注意要加上双引号。

(1)Spring Bean的生命周期
首先是Spring容器实例化Bean对象(4种方法:默认反射、静态工厂、实例工厂和Spring工厂),再设置Bean属性(两种方法:set方法和有参构造),如果接下来想注入Bean对容器基础设施层面的依赖,可以通过各种Aware接口声明其依赖关系,然后调用init方法执行初始化操作,接着就可以调用Bean中的所有方法,最后Bean销毁之前,Spring容器会调用destory方法来执行销毁操作。
(2)Spring Bean的作用域
Spring Bean的作用域一共有五种:
A、Singleton:这个是Spring默认的作用域,指的是Spring Bean是单例的;
B、prototype:指的是Spring Bean是多例的,即每次getBean()方法,就会在Spring容器中创建一次Bean的实例对象;
C、request:指的是同一个request范围内使用同一个Bean实例对象;
D、session:指的是同一个Session范围内使用同一个Bean实例对象;
E、GlobalSession:指的是所有Session使用同一个Bean实例对象。

2、Spring的AOP
AOP全称是Aspect Oriented Programming,意思是方面 面向 编程,组合起来就是面向切面编程。
在讲面向切面编程之前先学习它的基础代理设计模式,首先要知道为什么引出代理设计模式?这就不得不说前面讲的IOC和DI的作用了,Spring的IOC和DI让我们不再自己new对象了,而是从Spring的内置map容器中拿,而且类与类之间对象的调用也不需要new,而是自动装配,此时再结合接口,就可以实现软件设计中低耦合的目标,低耦合就是让类与类之间的关联性尽量低,或者理解为是否非它莫属,如果不是就是低耦合。
软件设计的另一个目标:高内聚,这个是IOC和DI无法实现的,所谓高内聚,应该是减少方法中无关的功能,比如每个方法开始前开启事务,结束后关闭事务,显然开启事务和关闭事务这两个和当前类无关,所以可以把开启事务和关闭事务这两个功能交给代理者帮忙完成,代理有静态代理和动态代理这两种,应该选哪个?选择动态代理,因为静态代理解决了相关性但没有解决重复性,这也就是AOP里用动态代理的原因。
那Spring如何实现动态代理?
两种方式实现,一种是JDK自带的Proxy类实现;另一种是采用第三方包cglib。
Proxy类如何实现动态代理?调用Proxy类的newProxyInstance()方法就可以创建一个代理者对象,这个方法的方法参数有三个:被代理者的类加载器、被代理者实现的所有接口和InvocationHandler接口的一个实现类并在类中重写invoke()方法实现动态调用被代理者的方法,可以在被代理者的方法调用前后进行一些其他操作,比如事务、日志等。
Cglib如何实现动态代理?首先创建一个增强器,再把被代理者当做父类继承,至于父类实现的接口可以选择性实现,然后设置回调方法对被代理者的方法进行增强。
这两种动态代理方法的区别在哪?Proxy类实现动态代理的前提是实现被代理者实现的所有接口,而Cglib不一定要实现接口,只需要继承被代理者即可,所有Cglib的应用场景更广泛,Spring的AOP默认先用Proxy类实现,如果不能实现再去用Cglib,当然我们可以自己设置为Cglib使其强制都用Cglib。
SpringAOP还有很多专业术语
(1)切面:就是处理类;
(2)通知:就是处理类的方法,具体分为前置通知、环绕通知、后置通知、异常通知和最
终通知这五种。
(3)连接点:目标层的方法;
(4)切入点:根据within(具体到类名)或者Executor(具体到方法名)这些切入点规则选
出来的连接点;
(5)织入:把切面里的通知应用到目标对象从而创建新的代理对象过程;
所以SpringAOP的具体原理是是什么?(盲点:让动态代理和专业术语联系起来)
Spring会在用户获取对象时,生成目标对象的代理对象,然后根据切入点规则,匹配用户连接点,得到切入点,当切入点被调用的时候,不会直接去找目标对象,而是通过代理对象拦截之后由切面类中指定的通知执行来对目标对象中的方法进行增强。
注:Spring事务:
A、隔离级别:
1)ISOLATION_DEFAULT:使用数据库默认的事务隔离级别
2)ISOLATION_READ_UNCOMMITTED:最低级别,允许读取到其他事务未提交的数据,会产生脏读、 不可重复读和幻读;
3)ISOLATION_READ_COMMITTED:只能读取到其他事务已提交的数据,可以防止脏读,但不能防止可重复读和幻读;
4)ISOLATION_REPEATABLE_READ:可以防止脏读和不可重复读,但不能防止幻读;
5)ISOLATION_SERIALIZABLE:最高级别,事务处理为串行且阻塞的,所以能避免所有情况。
对于不同数据库,默认事务隔离级别是不一样的,MySQL默认是ISOLATION_REPEATABLE_READ这种,而Oracle因为没有ISOLATION_REPEATABLE_READ这种,所以采用ISOLATION_READ_COMMITTED。
为什么不采用最高级别和最低级别?因为二者分别是安全和效率的极端,所以最好是用平衡了效率和安全的ISOLATION_REPEATABLE_READ或者ISOLATION_READ_COMMITTED。
B、传播机制:
1)支持当前事务,如果没有当前事务,就新建一个事务,这是最常见的选择;
2)支持当前事务,如果当前没有事务,就以非事务方式执行;
3)支持当前事务,如果当前没有事务,就抛出异常;
4)支持当前事务,如果当前事务存在,则执行一个嵌套事务,如果当前没有事务,则新建一个事务;
5)新建事务,如果当前存在事务,则把当前事务挂起;
6)以非事务方式执行,如果当前存在事务,则把当前事务挂起;
7)以非事务方式执行,如果当前存在事务,则抛出异常。

3、SpringBoot自动配置原理
SpringBoot的优点有自动配置、内嵌Tomcat、简化Maven依赖等,以前SSM框架需要写很多的xml文件,而SpringBoot只需要写application.properties或者yml即可,甚至不写用默认0配置也可以实现SSM框架。那SpringBoot自动配置原理是什么?
SpringBoot核心注解@SpringBootApplication是由
@SpringBootConfiguration+@ComponentScan+@EnableAutoConfiguration组合而成,第一个注解启动Spring的xml文件;第二个注解是以前xml文件中的包扫描;第三个注解是关键,如果想深入理解这个注解,可以去看maven dependencies里的源码,点击里面的spring-boot-autoconfigure-版本号.jar这个jar包,然后找到里面的EnableAutoConfiguration.class,里面有个@import,导入select结尾的类,这个类里面有个方法是去扫描该jar包里META-INF中的spring.factories,这个文件里声明了哪些自动配置,自动配置是有条件的,一般是指定的类或者bean或者属性是否存在才会自动配置,所以这也是为什么pom.xml中添加依赖Spring就会自动配置的原因,关键就是条件注解的出现。

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
回答: SpringDI(依赖注入)和IOC(控制反转)是Spring框架中的两个核心概念。DI是指通过外部容器来注入对象的依赖关系,而不是在对象内部创建或查找依赖对象。IOC是指将对象的创建和依赖关系的管理交给容器来完成,而不是由对象自己来管理。\[1\] 在Spring中,IOC容器的初始化过程包括加载配置文件、解析配置文件、实例化Bean对象并将其存储在IOC容器中。可以通过注解来实现基于注解的容器初始化,通过在类上添加注解来标识其作为Bean,并通过注解来指定依赖关系。\[2\] AOP(面向切面编程)是Spring框架中的另一个重要概念,它允许在程序运行期间动态地将额外的行为织入到代码中,而不需要修改原始代码。AOP的实现方式包括基于代理的方式和基于字节码增强的方式。AOP原理是通过在目标方法的前后插入切面逻辑来实现。\[2\] 在Spring中,可以通过注解来标识切面,并通过注解来指定切入点和通知类型。常用的AOP注解包括@Aspect、@Pointcut、@Before、@After等。\[3\] 总结来说,DIIOCSpring框架中用于管理对象依赖关系的机制,而AOP是用于实现横切关注点的机制。它们都是Spring框架中重要的特性,可以帮助开发者更好地组织和管理代码。 #### 引用[.reference_title] - *1* [Spring IoCAOP的通俗理解](https://blog.csdn.net/qq_39144436/article/details/123394242)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [关于SpringIoCAOP的面试题,快看看你都能答上来哪些](https://blog.csdn.net/Gaowumao/article/details/124919483)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [面试汇总-Spring-IOCAOP](https://blog.csdn.net/weixin_37672801/article/details/126415598)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值