网站建设开题报告ppt,h5网站架设,有谁做彩票网站吗,电子制作网站目录 前言阅读导航前置知识Q1#xff1a;你能描述一下JVM对象创建过程吗#xff1f;Q2#xff1a;Spring的特性是什么#xff1f;前置知识总结 课程内容一、Spring容器的启动二、一般流程推测2.1 扫描2.2 IOC2.3 AOP 2.4 小结三、【扫描】过程简单推测四、【IOC】过程简单推… 目录 前言阅读导航前置知识Q1你能描述一下JVM对象创建过程吗Q2Spring的特性是什么前置知识总结 课程内容一、Spring容器的启动二、一般流程推测2.1 扫描2.2 IOC2.3 AOP 2.4 小结三、【扫描】过程简单推测四、【IOC】过程简单推测4.1 推断构造方法过程细讲 五、【AOP】过程简单推测六、Spring事务 学习总结 前言
Spring啊可以说是我们大部分Java玩家【最熟悉的陌生人】了吧。八个字形容似懂非懂会也不会 你说简单应用我们大家都会那真要展开说两句的话那只能来这么两句这是第一句接着是第二句好了我说完了。 但是啊xdm据说Spring是一份非常非常非常优秀的源码不但有丰富的设计模式应用场景代码写的也很优美有条理所以非常推荐大家学习。除了能在日常装逼以外还能丰富一下见识提升自己写代码的能力。
阅读导航 阅读对象有过Spring开发经验的人 前置知识
Q1你能描述一下JVM对象创建过程吗
答看图说话
类加载在使用一个类之前Java虚拟机需要先将类的字节码加载到内存中。类加载是Java虚拟机的核心过程它负责查找类的字节码文件并加载到内存的方法区。类加载包括加载、验证、准备、解析和初始化这五个阶段。分配内存在类加载完成后Java虚拟机会为对象分配内存空间。内存分配通常在堆Heap上进行但也有一些特殊情况下的对象可以在栈Stack上分配内存例如线程栈上的局部对象。实例化初始化零值在分配内存后Java虚拟机会将对象的内存空间初始化为零值。这包括基本类型的默认值例如0、false等和引用类型的默认值null。设置对象头Java对象在内存中的布局包括对象头和实例数据两部分。对象头存储了一些元数据如对象的哈希码、锁状态等。在对象创建过程中Java虚拟机会设置对象头的值。执行构造方法对象创建的最后一步是执行构造方法。构造方法用于初始化对象的实例数据并执行其他必要的初始化操作。构造方法可以是类的默认构造方法也可以是自定义的构造方法。返回对象引用对象创建完成后Java虚拟机会返回一个指向该对象的引用。通过引用程序可以操作对象的属性和方法。
PS为什么要问这个问题因为Spring是IOC技术就算再怎么玩出花来他也要按照这个基本流程来创建对象。只不过可以提前告知大家的是SpringIOC在这个流程之中新增了很多槽点通过热插拔的方式丰富了IOC的功能
Q2Spring的特性是什么
答Spring的特性就是IOC跟AOP两大概念甚至可以这么说Spring就是实现了AOP技术的IOC容器。容器容器容器
Q3什么是IOC什么是AOP 答下面答案来源于百度【文心一言】
IOC控制反转是一种设计模式思想它允许将对象的创建和管理交给Spring容器来处理而不是在代码中直接创建对象。通过使用IOC可以将对象的依赖关系从代码中解耦使得代码更加灵活、可维护和可测试。AOP面向切面编程也是一种设计模式思想它通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态添加功能。AOP解决了面向对象编程中无法解决的问题例如事务管理、安全性、日志记录等。
Spring框架通过实现IOC和AOP使得程序更加模块化、灵活和易于维护。同时Spring还提供了许多其他模块和功能如DAO、ORM、WebMVC等使得它成为一个功能强大的Java开发框架。
前置知识总结
从上面的问题里面我们提到了一个很重要的东西即Spring就是实现了AOP技术的IOC容器。并且也概括地描述了IOC跟AOP的概念。既然我们也知道了IOC其实也管理了对象的创建那么说到对象创建肯定也离不开我们在Q1说的对象创建的过程。而且无论对象怎么创建谁创建都没办法离开上面的流程的。 事实上可以提前告诉大家的是IOC在创建对象的过程无非就是在上面的对象创建流程中丰富了一些细节新增了一些拓展点为Spring功能实现提供支持。
课程内容
为了展开对Spring源码的研究我们这里线大致地串讲一下Spring的一些核心知识点让大家对Spring的底层一些基础逻辑有个清晰的认知。
一、Spring容器的启动
我想经历过SSM/SSH时代的朋友对下面的代码都不会陌生
ClassPathXmlApplicationContext context new ClassPathXmlApplicationContext(spring.xml);
UserService userService (UserService) context.getBean(userService);
userService.test();
System.out.println(userService);?xml version1.0 encodingUTF-8?
beans xmlnshttp://www.springframework.org/schema/beansxmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-4.3.xsd!-- import resource引入其他bean xml配置文件 /--bean iduserService classorg.example.spring.bean.UserService/
/beans如果真的很陌生也没关系下面这个可能就相对熟悉一点了后面也会围绕这个启动方式的Spring讲解。除了是下面的比较主流也因为下面这种方式使用更广、更新内容相对丰富点
AnnotationConfigApplicationContext context new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService (UserService) context.getBean(userService);
userService.test();
System.out.println(userService);Component
public class UserService {public void test() {System.out.println(这是一个测试方法);}
}哈哈我估计很多直接进入了Java【SpringBoot】时代的朋友可能连上面这个都没看到过。 那上面段代码是干啥的呢很简单就是启动一个Spring容器而已。上面两个不同的启动方式也仅仅是Bean注册方式不一样。比如前者是通过读取xml里面的bean标签定义后者是读取的注解式Bean。 到这里想问大家一个问题那就是通过上面第二种方式的代码你发现了什么我的发现是我仅仅只是调用了一行代码就可以开始使用Spring定义的Bean了什么依赖注入AOP啥的我都没管直接就可以了。这证明了啥其实很粗浅也有点废话那就是证明通过这一行代码里面就帮我完成了所有我们平时使用过的Spring的基础能力。
二、一般流程推测
根据我们之前学习过Spring相关的操作简单推测一下这一行代码里面干了什么。
AnnotationConfigApplicationContext context new AnnotationConfigApplicationContext(AppConfig.class);2.1 扫描
首先是第一点【扫描】。我们在项目中写了这么多Bean或者说我项目里面这么多类哪些是Bean哪些是普通类Spring是怎么识别到的其实道理很简单的Spring它也没那么智能想要获取这个类的信息Spring肯定要【亲自】去看一眼才能知道这个类的具体信息。你有这么多少个文件他就要扫描多少个类。关键代码如下
// 定义需要扫描的基础包名
ComponentScan(org.tuling.spring)
public class AppConfig {
}2.2 IOC
扫描完了所有的文件那基本上Spring已经能确定哪些是Bean哪些是普通类了。接下来就可以开始创建Bean了这里就是所谓的IOC过程
2.3 AOP
AOP肯定是发生在IOC之后的如果你们了解设计模式里面的【代理模式】的话理解这一点并不难。毕竟如果目标对象功能不完整代理对象的功能也会收到影响。
2.4 小结 三、【扫描】过程简单推测
之前我们也说了扫描就是需要Spring亲自去看一看哪些是需要被创建Bean的哪些是不需要的。就拿我们举例用的new AnnotationConfigApplicationContext(AppConfig.class)来说大概步骤如下简单推测不详
它需要先看AppConfig.class读取扫描包的基础路径根据上一步读到的基础路径遍历包下所有的文件如果类上存在Component、Service等注解则确认为是一个Bean筛选完后将读取的Bean信息记录下来比如说存到一个Map里面方便后续遍历 四、【IOC】过程简单推测
IOC过程其实在Spring中有个比较专业的术语叫做Bean的生命周期。简单的几个字包含了很多内容。在此之前大家先看一看【前置知识】里面【JVM对象创建过程】加深一下印象。 其中有
新建一个Bean对象。说到新建Bean对象无论如何都绕不开构造方法的调用。我有多个构造函数那我应该使用哪个呢这在Spring内部叫做【推断构造方法】Bean对象创建好了之后当然不能忘了我们的【依赖注入】有一点大家可能用的会比较少但如果有过经验的同学可能会比较熟悉比如ApplicationContextAware我们可以直接直译叫做ApplicationContext感知感知ApplicationContext所以通过设置我们就可以在Bean里面获取到这个组件了init方法。如果大家有过PostConstruct以及InitializingBean使用经验的话或许知道创建完Bean之后在返回Bean之前还有这一步动作。事实上根据我们在前言里面说的【IOC在创建对象的过程无非就是在上面的对象创建流程中丰富了一些细节新增了一些拓展点】所以在IOC里面还有很多切入点比如实例化前、实例化后初始化前、初始化后等等最后就是进入AOP的流程了。如果需要AOP则进行AOP并且返回AOP对象否则直接返回已经创建好的对象
另外需要注意的是Bean对象创建出来后
如果当前Bean是单例Bean那么会把该Bean对象存入一个MapString, ObjectMap的key为beanNamevalue为Bean对象。这样下次getBean时就可以直接从Map中拿到对应的Bean对象了实际上在Spring源码中这个Map就是单例池如果当前Bean是原型Bean那么后续没有其他动作不会存入一个Map下次getBean时会再次执行上述创建过程得到一个新的Bean对象。 4.1 推断构造方法过程细讲
Spring在基于某个类生成Bean的过程中需要利用该类的构造方法来实例化得到一个对象但是如果一个类存在多个构造方法Spring会使用哪个呢
Spring的判断逻辑如下
如果一个类只有一个构造函数不管构造函数是有参还是无参Spring都会使用这个构造函数创建对象因为没得选了如果这个类存在多个构造函数 如果存在无参构造函数则使用无参构造函数。因为在Java里面无参构造函数本身就具有默认的意思在里面如果没有无参构造函数则看多个无参构造函数哪个有Autowired修饰有就选择没有就只能报错了
还有一个问题。如果Spring选择了一个有参的构造方法Spring在调用这个有参构造方法时需要传入参数那这个参数是怎么来的呢答案是Spring会根据入参的类型和入参的名字去Spring中找Bean对象。 3. 先根据入参类型找如果只找到一个那就直接用来作为入参 4. 如果根据类型找到多个则再根据入参名字来确定唯一一个 5. 最终如果没有找到则会报错无法创建当前Bean对象。
五、【AOP】过程简单推测
AOP就是进行动态代理在创建一个Bean的过程中Spring在最后一步放入单例池之前会去判断当前正在创建的这个Bean是不是需要进行AOP如果需要则会进行动态代理。 那么如何判断一个Bean是否需要被AOP代理呢步骤如下
找出所有的切面Bean切面也是Bean来的或者叫做特殊的Bean遍历切面中的每个方法看是否写了Before、After等注解通知如果写了则判断所对应的Pointcut是否和当前Bean对象的类是否匹配如果匹配则表示当前Bean对象有匹配的的Pointcut表示需要进行AOP
利用cglib进行AOP的大致流程看上面的代理范式大概就知道了
新增一个代理类XxxProxy继承自被代理对象XxxTarget并且持有一个XxxTarget成员变量这个成员变量需要经过一个Bean的声明周期即完成了IOC等在代理类中重写父类的方法执行代理类的方法时调用的代理类的方法但同时也需要执行切面的逻辑 然后这里给大家一个【代理模式】的范式
// 被代理对象
public class ProxyTarget {public void run() {System.out.println(这是普通对象的run);}
}// 代理对象
public class ProxyModel extends ProxyTarget {private ProxyTarget proxyTarget;public void setProxyTarget(ProxyTarget proxyTarget) {this.proxyTarget proxyTarget;}Overridepublic void run() {System.out.println(我代理对象可以在这里做加强---1);super.run();System.out.println(我代理对象也可以在这里做加强---2);}
}六、Spring事务
当我们在某个方法上加了Transactional注解后就表示该方法在调用时会开启Spring事务而这个方法所在的类所对应的Bean对象会是该类的代理对象。 Spring事务的代理对象执行某个方法时的步骤
判断当前执行的方法是否存在Transactional注解如果存在则利用事务管理器TransactionMananger新建一个数据库连接修改数据库连接的autocommit为false执行target.test()执行程序员所写的业务逻辑代码也就是执行sql执行完了之后如果没有出现异常则提交否则回滚
Spring事务是否会失效的判断标准某个加了Transactional注解的方法被调用时要判断到底是不是直接被代理对象调用的如果是则事务会生效如果不是则失效。PS这一点很容易被疏忽 另外还有个经典例子那就是Bean在有跟没有Configuration的时候结果是不一样的如下
声明Bean的方法
ComponentScan(org.tuling.spring)
Configuration
public class AppConfig {Beanpublic UserService userService() {return new UserService(walletService());}Beanpublic UserService userService1() {return new UserService(walletService());}Beanpublic WalletService walletService() {return new WalletService();}
}// UserService声明
public class UserService {private WalletService walletService;public UserService() {}public UserService(WalletService walletService) {this.walletService walletService;}public WalletService getWalletService() {return walletService;}/*** 自我介绍*/public void selfIntroduction() {System.out.println(你好我是阿通我有好多钱);walletService.showMyBalance();}
}大家看上面的声明Bean的方法按照设想WalletService肯定也是一个单例嘛所以userService跟userService1持有的walletService对象肯定是一样的。
调用方法 AnnotationConfigApplicationContext context new AnnotationConfigApplicationContext(AppConfig.class);UserService userService (UserService)context.getBean(userService);System.out.println(userService);System.out.println(userService.getWalletService());System.out.println(--------------------------------);UserService userService1 (UserService)context.getBean(userService1);System.out.println(userService1);System.out.println(userService1.getWalletService());结果输出如下
org.tuling.spring.bean.UserService2c34f934
org.tuling.spring.bean.WalletService12d3a4e9
--------------------------------
org.tuling.spring.bean.UserService240237d2
org.tuling.spring.bean.WalletService12d3a4e9看结果没什么问题如期输出。但如果我们把声明Bean的方法里面的Configuration去掉结果会变成这样
ComponentScan(org.tuling.spring)
//Configuration
public class AppConfig {Beanpublic UserService userService() {return new UserService(walletService());}Beanpublic UserService userService1() {return new UserService(walletService());}Beanpublic WalletService walletService() {return new WalletService();}
}org.tuling.spring.bean.UserService710726a3
org.tuling.spring.bean.WalletService646007f4
--------------------------------
org.tuling.spring.bean.UserService481a15ff
org.tuling.spring.bean.WalletService78186a70为什么只是简单注释了一个Configuration结果就不一样了呢分析如下
Bean注解可以将方法返回的对象注册为一个 Bean并且该 Bean 会被 Spring 容器管理。仅此而已所以在userService()方法重调用walletService()方法实际上就是一个普通Java调用而已肯定会重新new WalletService()而被Configuration注解之后所有方法都将被代理暂时还没找到源码证据等后面我看懂了再附上
学习总结
简单学习了Spring启动的流程通过一些常见Spring操作的串讲大概了解了一下IOC和AOP的大致流程