企业网站建设的重要性和必要性,如何上国外购物网站,手机做免费个人网站,中国石油工程建设有限公司网站首先#xff0c;SpringBoot是基于的Spring#xff0c;所以我们要依赖Spring#xff0c;然后我希望我们模拟出来的SpringBoot也支持Spring MVC的那一套功能#xff0c;所以也要依赖Spring MVC#xff0c;包括Tomcat等#xff0c;所以在SpringBoot模块中要添加以下依赖SpringBoot是基于的Spring所以我们要依赖Spring然后我希望我们模拟出来的SpringBoot也支持Spring MVC的那一套功能所以也要依赖Spring MVC包括Tomcat等所以在SpringBoot模块中要添加以下依赖
dependencygroupIdorg.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在User模块下我们进行正常的开发就行了比如先添加SpringBoot依赖 在业务层定义常用的controller、Service之类的包 当我们真正使用SpringBoot时核心主要是SpringBootApplication和SpringApplication前面的是用来让Spring扫描后面是Spring容器相关。我们接下来自行研究一下这两个核心内容。
一般开始的时候SpringBoot项目是下面的样子 我们模拟一下注解SpringBootApplication以及SpringApplication.run(SpringbootDemoApplication.class, args);
项目整体结构
项目结构如下 需要添加SpringBoot项目依赖才可以在启动类中添加注解TigerSpringBootApplication 由于SpringBoot用的容器也是Spring的所以我们首先得在run方法创建一个Spring容器。 一般情况下run方法执行完了我们就能在浏览器中访问到controller里面的方法那么在run方法中肯定会启动tomcat然后才能接收web请求。
tomcat里需要配置一个DispatcherServlet然后这个DispatcherServlet需要和Spring容器绑定这样DispatcherServlet在接收到请求后才能根据请求路径去匹配Spring容器中的controller的mapping路径。
所以在run方法中我们要实现如下逻辑 创建一个Spring容器 创建Tomcat对象 生成DispatcherServlet对象并且和前面创建出来的Spring容器进行绑定 将DispatcherServlet添加到Tomcat中 启动Tomcat 接下来对每一步进行详细说明。 创建Spring容器
容器创建语句
AnnotationConfigApplicationContext applicationContext new AnnotationConfigApplicationContext();applicationContext.register(clazz);把配置类注册进去。 如图所示在调用run方法时候传入了MyApplication.class这个类由于他上面有TigerSpringBootApplication注解而这个又是一个组合注解里面包括了Configuration所以最终我们上面的applicationContext.register(clazz);就拿到了配置类然后调用applicationContext.register();这样容器就启动了。容器启动了之后就去解析传进来的配置类。由于配置类上面有TigerSpringBootApplication他是一个组合注解包含了ComponentScan这样Spring就知道了扫描路径。
总结 经过上面的步骤我们就把Spring容器创建好了。
我们创建的是一个AnnotationConfigWebApplicationContext容器并且把run方法传入进来的class作为容器的配置类比如在MyApplication的run方法中我们就是把MyApplication.class传入到了run方法中最终MyApplication就是所创建出来的Spring容器的配置类并且由于MyApplication类上有TigerSpringBootApplication注解而TigerSpringBootApplication注解上又存在ComponentScan注解所以AnnotationConfigWebApplicationContext容器在执行refresh时就会解析MyApplication这个配置类从而发现定义了ComponentScan注解也就知道了要进行扫描只不过扫描路径为空而AnnotationConfigWebApplicationContext容器会处理这种情况如果扫描路径会空则会将MyApplication所在的包路径做为扫描路径从而就会扫描到UserService和UserController。
所以Spring容器创建完之后容器内部就拥有了UserService和UserController这两个Bean。
启动Tomcat服务器
run方法执行完之后我们就可以通过浏览器访问controller接口了。为了能够接收web请求我们需要启动一个web容器。
接下来启动tomcat容器。启动Tomcat时需要配置DispatcherServlet。之前单独用Spring MVC的时候我们要在web.xml单独配置一个DispatcherServlet就相当于往tomcat里面配置了DispatcherServlet。 tomcat.addServlet(contextPath, dispatcher, new DispatcherServlet(applicationContext));context.addServletMappingDecoded(/*, dispatcher);通过上面的形式就可以把DispatcherServlet配置到我们Tomcat当中。context.addServletMappingDecoded(“/*”, “dispatcher”);表示当Tomcat启动时所有的请求都会交给DispatcherServlet处理。 当我们在浏览器中请求http://localhost:8081/test的时候端口8081定位 到了Tomcat容器然后请求交给了DispatcherServlet它会根据/test去我们的controller进行匹配。然后controller是我们后端Spring 的一个bean为了能关联起来我们需要在初始化DispatcherServlet的时候在构造函数中接收一个Spring容器。 在构造DispatcherServlet对象时传入了一个ApplicationContext对象也就是一个Spring容器就是我们前文说的DispatcherServlet对象和一个Spring容器进行绑定。 修改后使用的是web的Spring容器AnnotationConfigWebApplicationContext public static void run(Class clazz) {AnnotationConfigWebApplicationContext applicationContext new AnnotationConfigWebApplicationContext();applicationContext.register(clazz);applicationContext.register();startTomcat(applicationContext);}启动成功 至此一个简单的Springboot demo就写好了。
总结一下一个完整的请求流程在浏览器发起http请求http://localhost:8081/test由于Tomcat监听的是8081所以被Tomcat接收到然后Tomcat再把请求交给DispatcherServletDispatcherServlet拿到请求后就用路径/test去Spring容器中匹配controller的bean然后再遍历bean里面的方法发现刚好匹配下面的方法 然后执行方法test().
最终把结果返回前端
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gxEU885s-1690601777922)(https://typora-chenhui-2021-08-03.oss-cn-shenzhen.aliyuncs.com/img/启动Tomcat.gif)] 自定义的注解TigerSpringBootApplication有两个重要的组合注解Configuration、ComponentScan。
Configuration表示当前的类是配置类ComponentScan表示需要spring扫描默认当前类的子包路径都要扫描。执行run方法的时候创建好了spring容器web型容器)然后还通过编程的方式启动了一个tomcat服务器。
当我们想把tomcat换成jetty或者undertown的时候只需要再pom依赖中修改一下即可如下 如果我想继续按下面的要求修改
如果项目中有Tomcat的依赖那就启动Tomcat如果项目中有Jetty的依赖就启动Jetty如果两者都没有则报错如果两者都有也报错
我们希望SpringBoot自动帮我们把上面的逻辑实现对于我们程序员而言只要在Pom文件中添加相关依赖就可以了想用Tomcat就加Tomcat依赖想用Jetty就加Jetty依赖。
那这个底层原理是怎么实现的
实现tomcat和Jetty切换
我们需要判断依赖里面有什么如果有Tomcat就启动Tomcat有Jetty就启动Jetty有Undertow就启动Undertow。
如果是在run方法里面写if else不仅代码太难看而且也不好扩展。
像tomcat、jetty、undertown都是属于servlet容器有共性我们就要用抽象的思维把这三个都容器抽象为WebServer然后提供一个启动的方法
public interface WebServer {void start();
}然后写一个Tomcat类实现WebServer接口
public class TomcatWebServer implements WebServer{Overridepublic void start() {System.out.println(tomcat启动了);}
}public class JettyWebServer implements WebServer{Overridepublic void start() {System.out.println(jetty启动了);}
}回到run方法那里我们要改变一下思路不能直接启动tomcat容器而是先获取webserver然后再使用webserver的start方法 public static void run(Class clazz) {AnnotationConfigWebApplicationContext applicationContext new AnnotationConfigWebApplicationContext();applicationContext.register(clazz);applicationContext.refresh();// startTomcat(applicationContext);WebServer webServer getWebServer(applicationContext);webServer.start();}public static WebServer getWebServer(WebApplicationContext applicationContext) {//在这里判断tomcat、jetty、undertown到底返回那个webserverreturn null;}注意getWebServer方法的参数WebApplicationContext这是一个Spring容器我们可以直接从容器里面获取web类型容器的beanapplicationContext.getBean(WebServer.class)
然后直接返回 public static WebServer getWebServer(WebApplicationContext applicationContext) {//在这里判断tomcat、jetty、undertown到底返回那个webserverreturn applicationContext.getBean(WebServer.class);}我们得判断项目中的依赖是什么是Tomcat还是Jetty这个时候就得引入条件注解。
引入条件注解
我们新建一个自动配置类WebServerAutoConfiguration然后我们定义 Tomcat、Jetty的bean
Configuration
public class WebServerAutoConfiguration implements AutoConfiguration{Beanpublic TomcatWebServer tomcatWebServer(){return new TomcatWebServer();}Beanpublic JettyWebServer jettyWebServer(){return new JettyWebServer();}
}虽然定义了两个bean但是我们可以让bean在某种条件下生效这就是条件注解。
新增一个条件注解类TigerCondition实现Spring的Condition条件注解返回false表示不符合逻辑返回true表示符合逻辑。
public class TigerCondition implements Condition {Overridepublic boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {return false;}
}我们在上面两个bean上加条件注解 Conditional(TigerCondition.class)
Configuration
public class WebServerAutoConfiguration implements AutoConfiguration{BeanConditional(TigerCondition.class)public TomcatWebServer tomcatWebServer(){return new TomcatWebServer();}BeanConditional(TigerCondition.class)public JettyWebServer jettyWebServer(){return new JettyWebServer();}
}虽然定义了两个bean但是只有条件注解返回true的时候对应的bean才能生效,容器中才会存在对应bean。
但是在一个条件注解里面同时判断Tomcat和Jetty不方便我们看下spring boot原始的判断方法。SpringBoot会写一个注解ConditionalOnClass
Target({ ElementType.TYPE, ElementType.METHOD })
Retention(RetentionPolicy.RUNTIME)
Conditional(TigerCondition.class)
public interface ConditionalOnClass {String value();
}然后我们把指定条件的代码Conditional 换成ConditionalOnClass。ConditionalOnClass是SpringBoot的注解它封装了Spring的Conditional注解。
Configuration
public class WebServerAutoConfiguration implements AutoConfiguration{BeanConditional(TigerCondition.class)ConditionalOnClass(org.apache.catalina.startup.Tomcat)public TomcatWebServer tomcatWebServer(){return new TomcatWebServer();}BeanConditional(TigerCondition.class)ConditionalOnClass(org.eclipse.jetty.server.Server)public JettyWebServer jettyWebServer(){return new JettyWebServer();}
}如果当前项目中有org.apache.catalina.startup.Tomcat这个类那么这个bean就生效同理如果有org.eclipse.jetty.server.Server这个类那么Jetty的bean就生效。
那这个判断怎么实现呢得回到TigerCondition这个注解的方法这里。
我们需要拿到org.eclipse.jetty.server.Server、org.apache.catalina.startup.Tomcat这两个字符串。Spring去解析注解的时候可以获取到value的值这样就拿到了这两个字符串由因为注解ConditionalOnClass包含了Conditional(TigerCondition.class)那么Spring就去执行指定类TigerCondition下的判断逻辑即match方法里的逻辑。 Overridepublic boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {MapString, Object annotationAttributes metadata.getAnnotationAttributes(ConditionalOnClass.class.getName());String className (String) annotationAttributes.get(value);// org.eclipse.jetty.server.Server 或 Tomcattry {context.getClassLoader().loadClass(className);return true;} catch (ClassNotFoundException e) {return false;}}通过className从容器中加载这个类如果能加载到返回true表示项目中有这个类否则抛异常ClassNotFoundException类加载失败返回false。
启动项目 找不到类。
因为我们写的配置类WebServerAutoConfiguration是定义在SpringBoot中的那我们得让Spring知道这个配置类的存在让他扫描到。所以加一个注解Import(WebServerAutoConfiguration.class)。当我们执行run方法时传入了一个类MyApplication.classspring接收到这个类之后就会去解析这个类找到这个类之后就会去解析上面的注解TigerSpringBootApplication,发现需要去扫描因为组合注解包含了ComponentScan然后Spring就把Service层Controller层都扫描一遍并把bean放入容器中。
TigerSpringBootApplication
Import(WebServerAutoConfiguration.class)
public class MyApplication
{public static void main( String[] args ){TigerSpringApplication.run(MyApplication.class);}
}当TigerSpringBootApplication解析完之后再解析Import(WebServerAutoConfiguration.class)Spring发现要导入一个类WebServerAutoConfiguration然后发现上面有Configuration再解析这个注解发现里面与bean的定义。
重新启动项目还是报错再仔细看代码发现问题 删除Conditional(TigerCondition.class)后重新启动代码还是报错最终发现报错原因如下 把代码移动到tomcat模块中 重新启动项目启动成功这样就实现了根据pom依赖自动选择web服务器的功能 如果依赖中移除依赖会报错找不到对应bean。如果两个bean都有那么也会报错因为Spring不知道要去使用哪个bean。 类似web服务器还有消息队列看你是用rabbitmq还是用rocketmq还是kafka或者spring事务或者datasource等。这些如果在Spring项目中都是要程序员自己手动配置这些bean的。
但是到了SprinBboot就不用我们手动去配置SpringBoot默认帮我们配置好如下 我们刚开始是在启动类中import了我们需要的自动配置Import(WebServerAutoConfiguration.class)
TigerSpringBootApplication
Import(WebServerAutoConfiguration.class)
public class MyApplication
{public static void main( String[] args ){TigerSpringApplication.run(MyApplication.class);}
}那如我想导入更多的自动配置全部都直接在上面添加Import(xxxx)注解那这个类就得膨胀了 所以引入一个自定义类TigerImportSelector.class
public class TigerImportSelector implements DeferredImportSelector {Overridepublic String[] selectImports(AnnotationMetadata importingClassMetadata) {ServiceLoaderAutoConfiguration loader ServiceLoader.load(AutoConfiguration.class);ListString list new ArrayList();for (AutoConfiguration configuration : loader) {list.add(configuration.getClass().getName());}return list.toArray(new String[0]);}
}selectImports方法返回的是一个数组 String[]我们可以把上面多个自动配置类的类名直接通过方法selectImports返回这样启动类就不用import很多的配置类了。
发现自动配置类
虽然启动类是少了注解,这种做法也是不太优雅的我们看看springboot是怎么实现的。这个时候就用到了spi机制。
创建一个接口AutoConfiguration里面什么 都不用实现
public interface AutoConfiguration {
}然后我们的自动配置类都要实现这个接口
public class WebServerAutoConfiguration implements AutoConfiguration然后我们在resource目录下新建文件夹META-INF、services以及文件org.example.AutoConfiguration 文件是接口的名字org.example.AutoConfiguration文件的内容是需要自动生效的配置类全限定类名。
配置好之后我们就可以从代码中去到上面的这些配置信息通过SPI机制。在下面的类中
public class TigerImportSelector implements DeferredImportSelector {Overridepublic String[] selectImports(AnnotationMetadata importingClassMetadata) {ServiceLoaderAutoConfiguration loader ServiceLoader.load(AutoConfiguration.class);ListString list new ArrayList();for (AutoConfiguration configuration : loader) {list.add(configuration.getClass().getName());}return list.toArray(new String[0]);}
}通过Java的SPI可以load 我们自定义的接口AutoConfiguration底层会去目录META-INF/services/读org.example.AutoConfiguration文件把文件内容读取出来然后通过反射的方式把这些全限定类名获取到的类返回给loader这个变量接收。如果后面还想实现如aop的自动配置那我们新建一个类实现接口AutoConfiguration然后 在META-INF下面的文件增加对应的全限定类名即可。
这种spi的机制主要是为了方便扩展。
比如像mybatisspring官方就没提供自动配置的starter就需要通过这种spi机制进行扩展。
配置类就是为了定义bean然后把bean纳入容器中管理。配置类就像之前的xml配置文件一样用来定义外部bean的。
SpringBoot 自动配置AOP
下面再写一个aop的自动配置例子。
案例目的要在user模块中使用aop功能只要在pom中添加依赖即可无需配置。如果不想使用aop则把依赖删除即可。
首先新增一个配置类AopAutoConfiguration我们主要是在这个配置类中开启aop配置
Configuration
public class AopAutoConfiguration implements AutoConfiguration{ConfigurationEnableAspectJAutoProxyConditionalOnClass(org.aspectj.weaver.Advice)class AspectJConfiguration{}
}ConditionalOnClass(“org.aspectj.weaver.Advice”):表示pom中有这个依赖项目中有这个bean当前的类才生效
EnableAspectJAutoProxy 表示开启aop的功能
在User模块中使用AspectJ的时候需要把它的jar包引入进来这是程序员必须要做的 dependencygroupIdorg.aspectj/groupIdartifactIdaspectjrt/artifactIdversion1.9.7/version/dependencydependencygroupIdorg.aspectj/groupIdartifactIdaspectjweaver/artifactIdversion1.9.7/version/dependency引入上面的依赖之后才能使用注解Aspect
然后在User模块配置一个切面
Component
Aspect
public class TigerAspect {Before(execution((public * org.example.service.UserService.test())))public void tigerBefore(JoinPoint joinPoint){System.out.println(进入aop拦截的方法了);}
}因为我们是为了验证http请求进来的时候切面是否生效而tomcat容器中我们只简单的打印所以需要修改一下 把方法挪上去之后发现缺少spring容器。
如何获取Spring容器
只要让TomcatWebServer实现接口ApplicationContextAware这样就能拿到spring容器 定义变量
private WebApplicationContext webApplicationContext;然后实现接口ApplicationContextAware并重写方法setApplicationContext这样在方法里面就可以给成员变量WebApplicationContext赋值了。
实现效果如下
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-I8iPnhPZ-1690601777928)(https://typora-chenhui-2021-08-03.oss-cn-shenzhen.aliyuncs.com/img/aop拦截.gif)]
在userService中我们只添加了pom依赖并没有在启动类增加开启aop的注解就能在项目中使用aop功能。这是我们写的配置类AopAutoConfiguration自动生效了。