北京网站建设哪家便宜做链接的网站

当前位置: 首页 > news >正文

北京网站建设哪家便宜,做链接的网站,自考本科,淘宝网店营销策划方案通过手写模拟实现一个Spring Boot#xff0c;让大家能以非常简单的方式就能知道Spring Boot大概是如何工作的。 依赖 建一个工程#xff0c;两个Module: 1.springboot模块#xff0c;表示springboot框架的源码实现 2.user包#xff0c;表示用户业务系统#xff0c;用来写…通过手写模拟实现一个Spring Boot让大家能以非常简单的方式就能知道Spring Boot大概是如何工作的。 依赖 建一个工程两个Module: 1.springboot模块表示springboot框架的源码实现 2.user包表示用户业务系统用来写业务代码来测试我们所模拟出来的SpringBoot 首先SpringBoot是基于的Spring所以我们要依赖Spring然后我希望我们模拟出来的SpringBoot也支持Spring MVC的那一套功能所以也要依赖Spring MVC包括Tomcat等所以在SpringBoot模块中要添加以下依赖 dependenciesdependencygroupIdorg.springframework/groupIdartifactIdspring-context/artifactIdversion5.3.18/version/dependencydependencygroupIdorg.springframework/groupIdartifactIdspring-web/artifactIdversion5.3.18/version/dependencydependencygroupIdorg.springframework/groupIdartifactIdspring-webmvc/artifactIdversion5.3.18/version/dependencydependencygroupIdjavax.servlet/groupIdartifactIdjavax.servlet-api/artifactIdversion4.0.1/version/dependencydependencygroupIdorg.apache.tomcat.embed/groupIdartifactIdtomcat-embed-core/artifactIdversion9.0.60/version/dependency /dependencies在User模块下我们进行正常的开发就行了比如先添加SpringBoot依赖 dependenciesdependencygroupIdorg.example/groupIdartifactIdspringboot/artifactIdversion1.0-SNAPSHOT/version/dependency /dependencies然后定义相关的Controller和Service: RestController public class UserController {Autowiredprivate UserService userService;GetMapping(test)public String test(){return userService.test();} }因为我们模拟实现的是SpringBoot而不是SpringMVC所以我直接在user包下定义了UserController和UserService最终我希望能运行MyApplication中的main方法就直接启动了项目并能在浏览器中正常的访问到UserController中的某个方法。 核心注解和核心类 我们在真正使用SpringBoot时核心会用到SpringBoot一个类和注解 1.SpringBootApplication这个注解是加在应用启动类上的也就是main方法所在的类 2.SpringApplication这个类中有个run()方法用来启动SpringBoot应用的 所以我们也来模拟实现他们。 一个ZhouyuSpringBootApplication注解 Target(ElementType.TYPE) Retention(RetentionPolicy.RUNTIME) Configuration ComponentScan public interface ZhouyuSpringBootApplication { }一个用来实现启动逻辑的ZhouyuSpringApplication类。 public class ZhouyuSpringApplication {public static void run(Class clazz){}}注意run方法需要接收一个Class类型的参数这个class是用来干嘛的等会就知道了。 有了以上两者我们就可以在MyApplication中来使用了比如 ZhouyuSpringBootApplication public class MyApplication {public static void main(String[] args) {ZhouyuSpringApplication.run(MyApplication.class);} }现在用来是有模有样了但中看不中用所以我们要来好好实现以下run方法中的逻辑了。 run方法 run方法中需要实现什么具体的逻辑呢 首先我们希望run方法一旦执行完我们就能在浏览器中访问到UserController那势必在run方法中要启动Tomcat通过Tomcat就能接收到请求了。 大家如果学过Spring MVC的底层原理就会知道在SpringMVC中有一个Servlet非常核心那就是DispatcherServlet这个DispatcherServlet需要绑定一个Spring容器因为DispatcherServlet接收到请求后就会从所绑定的Spring容器中找到所匹配的Controller并执行所匹配的方法。 所以在run方法中我们要实现的逻辑如下 1.创建一个Spring容器 2.创建Tomcat对象 3.生成DispatcherServlet对象并且和前面创建出来的Spring容器进行绑定 4.将DispatcherServlet添加到Tomcat中 5.启动Tomcat 创建Spring容器 这个步骤比较简单代码如下 public class ZhouyuSpringApplication {public static void run(Class clazz){AnnotationConfigWebApplicationContext applicationContext new AnnotationConfigWebApplicationContext();applicationContext.register(clazz);applicationContext.refresh();} }我们创建的是一个AnnotationConfigWebApplicationContext容器并且把run方法传入进来的class作为容器的配置类比如在MyApplication的run方法中我们就是把MyApplication.class传入到了run方法中最终MyApplication就是所创建出来的Spring容器的配置类并且由于MyApplication类上有ZhouyuSpringBootApplication注解而ZhouyuSpringBootApplication注解的定义上又存在ComponentScan注解所以AnnotationConfigWebApplicationContext容器在执行refresh时就会解析MyApplication这个配置类从而发现定义了ComponentScan注解也就知道了要进行扫描只不过扫描路径为空而AnnotationConfigWebApplicationContext容器会处理这种情况如果扫描路径会空则会将MyApplication所在的包路径做为扫描路径从而就会扫描到UserService和UserController。 所以Spring容器创建完之后容器内部就拥有了UserService和UserController这两个Bean。 启动Tomcat 我们用的是Embed-Tomcat也就是内嵌的Tomcat真正的SpringBoot中也用的是内嵌的Tomcat而对于启动内嵌的Tomcat也并不麻烦代码如下 public static void startTomcat(WebApplicationContext applicationContext){Tomcat tomcat new Tomcat();Server server tomcat.getServer();Service service server.findService(Tomcat);Connector connector new Connector();connector.setPort(8081);Engine engine new StandardEngine();engine.setDefaultHost(localhost);Host host new StandardHost();host.setName(localhost);String contextPath ;Context context new StandardContext();context.setPath(contextPath);context.addLifecycleListener(new Tomcat.FixContextListener());host.addChild(context);engine.addChild(host);service.setContainer(engine);service.addConnector(connector);tomcat.addServlet(contextPath, dispatcher, new DispatcherServlet(applicationContext));context.addServletMappingDecoded(/*, dispatcher);try {tomcat.start();} catch (LifecycleException e) {e.printStackTrace();}}代码虽然看上去比较多但是逻辑并不复杂比如配置了Tomcat绑定的端口为8081后面向当前Tomcat中添加了DispatcherServlet并设置了一个Mapping关系最后启动其他代码则不用太过关心。 而且在构造DispatcherServlet对象时传入了一个ApplicationContext对象也就是一个Spring容器就是我们前文说的DispatcherServlet对象和一个Spring容器进行绑定。 接下来我们只需要在run方法中调用startTomcat即可 public static void run(Class clazz){AnnotationConfigWebApplicationContext applicationContext new AnnotationConfigWebApplicationContext();applicationContext.register(clazz);applicationContext.refresh();startTomcat(applicationContext);}实际上代码写到这一个极度精简版的SpringBoot就写出来了比如现在运行MyApplication就能正常的启动项目并能接收请求。 启动能看到Tomcat的启动日志 然后在浏览器上访问http://localhost:8081/test 也能正常的看到结果
此时你可以继续去写其他的Controller和Service了照样能正常访问到而我们的业务代码中仍然只用到了ZhouyuSpringApplication类和ZhouyuSpringBootApplication注解。 实现Tomcat和Jetty的切换 虽然我们前面已经实现了一个比较简单的SpringBoot不过我们可以继续来扩充它的功能比如现在我有这么一个需求这个需求就是我现在不想使用Tomcat了而是想要用Jetty那该怎么办 我们前面代码中默认启动的是Tomcat那我现在想改成这样子 1.如果项目中有Tomcat的依赖那就启动Tomcat 2.如果项目中有Jetty的依赖就启动Jetty 3.如果两者都没有则报错 4.如果两者都有也报错 这个逻辑希望SpringBoot自动帮我实现对于程序员用户而言只要在Pom文件中添加相关依赖就可以了想用Tomcat就加Tomcat依赖想用Jetty就加Jetty依赖。 那SpringBoot该如何实现呢 我们知道不管是Tomcat还是Jetty它们都是应用服务器或者是Servlet容器所以我们可以定义接口来表示它们这个接口叫做WebServer别问我为什么叫这个因为真正的SpringBoot源码中也叫这个。 并且在这个接口中定义一个start方法 public interface WebServer {public void start();}有了WebServer接口之后就针对Tomcat和Jetty提供两个实现类 public class TomcatWebServer implements WebServer{Overridepublic void start() {System.out.println(启动Jetty);} }public class JettyWebServer implements WebServer{Overridepublic void start() {System.out.println(启动Tomcat);} }而在ZhouyuSpringApplication中的run方法中我们就要去获取对应的WebServer然后启动对应的webServer代码为 public static void run(Class clazz){AnnotationConfigWebApplicationContext applicationContext new AnnotationConfigWebApplicationContext();applicationContext.register(clazz);applicationContext.refresh();WebServer webServer getWebServer(applicationContext);webServer.start();}public static WebServer getWebServer(ApplicationContext applicationContext){return null; }这样我们就只需要在getWebServer方法中去判断到底该返回TomcatWebServer还是JettyWebServer。 前面提到过我们希望根据项目中的依赖情况来决定到底用哪个WebServer我就直接用SpringBoot中的源码实现方式来模拟了。 模拟实现条件注解 首先我们得实现一个条件注解ZhouyuConditionalOnClass对应代码如下 Target({ ElementType.TYPE, ElementType.METHOD }) Retention(RetentionPolicy.RUNTIME) Conditional(ZhouyuOnClassCondition.class) public interface ZhouyuConditionalOnClass {String value() default ; }注意核心为Conditional(ZhouyuOnClassCondition.class)中的ZhouyuOnClassCondition因为它才是真正得条件逻辑: public class ZhouyuOnClassCondition implements Condition {Overridepublic boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {MapString, Object annotationAttributes metadata.getAnnotationAttributes(ZhouyuConditionalOnClass.class.getName());String className (String) annotationAttributes.get(value);try {context.getClassLoader().loadClass(className);return true;} catch (ClassNotFoundException e) {return false;}} }具体逻辑为拿到ZhouyuConditionalOnClass中的value属性然后用类加载器进行加载如果加载到了所指定的这个类那就表示符合条件如果加载不到则表示不符合条件。 模拟实现自动配置类 有了条件注解我们就可以来使用它了那如何实现呢 这里就要用到自动配置类的概念我们先看代码 Configuration public class WebServiceAutoConfiguration {BeanZhouyuConditionalOnClass(org.apache.catalina.startup.Tomcat)public TomcatWebServer tomcatWebServer(){return new TomcatWebServer();}BeanZhouyuConditionalOnClass(org.eclipse.jetty.server.Server)public JettyWebServer jettyWebServer(){return new JettyWebServer();} } 这个代码还是比较简单的通过一个WebServiceAutoConfiguration的Spring配置类在里面定义了两个Bean一个TomcatWebServer一个JettyWebServer不过这两个要生效的前提是符合当前所指定的条件比如 1.只有存在org.apache.catalina.startup.Tomcat类那么才有TomcatWebServer这个Bean 2.只有存在org.eclipse.jetty.server.Server类那么才有TomcatWebServer这个Bean 并且我们只需要在ZhouyuSpringApplication中getWebServer方法如此实现 public static WebServer getWebServer(ApplicationContext applicationContext){// key为beanName, value为Bean对象MapString, WebServer webServers applicationContext.getBeansOfType(WebServer.class);if (webServers.isEmpty()) {throw new NullPointerException();}if (webServers.size() 1) {throw new IllegalStateException();}// 返回唯一的一个return webServers.values().stream().findFirst().get(); } 这样整体SpringBoot启动逻辑就是这样的 1.创建一个AnnotationConfigWebApplicationContext容器 2.解析MyApplication类然后进行扫描 3.通过getWebServer方法从Spring容器中获取WebServer类型的Bean 4.调用WebServer对象的start方法 有了以上步骤我们还差了一个关键步骤就是Spring要能解析到WebServiceAutoConfiguration这个自动配置类因为不管这个类里写了什么代码Spring不去解析它那都是没用的此时我们需要SpringBoot在run方法中能找到WebServiceAutoConfiguration这个配置类并添加到Spring容器中。 MyApplication是Spring的一个配置类但是MyApplication是我们传递给SpringBoot从而添加到Spring容器中去的而WebServiceAutoConfiguration就需要SpringBoot去自动发现而不需要程序员做任何配置才能把它添加到Spring容器中去而且要注意的是Spring容器扫描也是扫描不到WebServiceAutoConfiguration这个类的因为我们的扫描路径是com.zhouyu.user而WebServiceAutoConfiguration所在的包路径为com.zhouyu.springboot。 那SpringBoot中是如何实现的呢通过SPI当然SpringBoot中自己实现了一套SPI机制也就是我们熟知的spring.factories文件那么我们模拟就不搞复杂了就直接用JDK自带的SPI机制。 发现自动配置类 为了实现这个功能以及为了最后的效果演示我们需要把springboot源码和业务代码源码拆分两个maven模块也就相当于两个项目最后的源码结构为
现在我们只需要在springboot项目中的resources目录下添加如下目录META-INF/services和文件
SPI的配置就完成了相当于通过com.zhouyu.springboot.AutoConfiguration文件配置了springboot中所提供的配置类。 并且提供一个接口 public interface AutoConfiguration { }并且WebServiceAutoConfiguration实现该接口 Configuration public class WebServiceAutoConfiguration implements AutoConfiguration {BeanZhouyuConditionalOnClass(org.apache.catalina.startup.Tomcat)public TomcatWebServer tomcatWebServer(){return new TomcatWebServer();}BeanZhouyuConditionalOnClass(org.eclipse.jetty.server.jetty)public JettyWebServer jettyWebServer(){return new JettyWebServer();} }然后我们再利用spring中的Import技术来导入这些配置类我们在ZhouyuSpringBootApplication的定义上增加如下代码: Target(ElementType.TYPE) Retention(RetentionPolicy.RUNTIME) Configuration ComponentScan Import(ZhouyuImportSelect.class) public interface ZhouyuSpringBootApplication { } ZhouyuImportSelect类为 public class ZhouyuImportSelect implements DeferredImportSelector {Overridepublic String[] selectImports(AnnotationMetadata importingClassMetadata) {ServiceLoaderAutoConfiguration serviceLoader ServiceLoader.load(AutoConfiguration.class);ListString list new ArrayList();for (AutoConfiguration autoConfiguration : serviceLoader) {list.add(autoConfiguration.getClass().getName());}return list.toArray(new String[0]);} }这就完成了从com.zhouyu.springboot.AutoConfiguration文件中获取自动配置类的名字并导入到Spring容器中从而Spring容器就知道了这些配置类的存在而对于user项目而言是不需要修改代码的。 此时运行MyApplication就能看到启动了Tomcat 因为SpringBoot默认在依赖中添加了Tomcat依赖而如果在User模块中再添加jetty的依赖 dependenciesdependencygroupIdorg.example/groupIdartifactIdspringboot/artifactIdversion1.0-SNAPSHOT/version/dependencydependencygroupIdorg.eclipse.jetty/groupIdartifactIdjetty-server/artifactIdversion9.4.43.v20210629/version/dependency /dependencies那么启动MyApplication就会报错
只有先排除到Tomcat的依赖再添加Jetty的依赖才能启动Jetty
注意由于没有了Tomcat的依赖记得把最开始写的startTomcat方法给注释掉并删除掉相关依赖。 总结 到此我们实现了一个简单版本的SpringBoot因为SpringBoot首先是基于Spring的而且提供的功能也更加强大随着后续内容的展开相信大家会对本文中的各个功能会有更加深刻的理解也希望大家都自己去实现一边