目录
一、注册Servlet三大组件 Servlet/Filter/Listener
四、SpringBoot整合Mybatis与Mybatis-Plus
一、注册Servlet三大组件 Servlet/Filter/Listener
首先使用传统 @WebFilter...实现注册
条件:
- 1.一定是自定义组件
- 2.启动类添加@ServletComponentScan
MyServlet.class
@WebServlet("/myServlet") public class MyServlet extends HttpServlet { @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("进入servlet"); resp.getWriter().println("<h1>hello</h1>"); } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this.doPost(req,resp); } }
MyFilter.class
@WebFilter(urlPatterns = {"/*"}) public class MyFilter implements Filter { @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("============请求过滤"); servletRequest.setCharacterEncoding("utf-8"); //分水岭 filterChain.doFilter(servletRequest, servletResponse); servletResponse.setCharacterEncoding("utf-8"); System.out.println("============响应过滤"); } }
MyListener.class
@WebListener public class MyListener implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent sce) { ServletContextListener.super.contextInitialized(sce); System.out.println("-------------MyListener inited !"); } @Override public void contextDestroyed(ServletContextEvent sce) { ServletContextListener.super.contextDestroyed(sce); System.out.println("----------------MyListener Destroy !"); } }
访问该页面
而由于 Spring Boot 默认是以 jar 包的方式运行嵌入式Servlet容器来启动应用,没有web.xml文件, Spring提供以下Bean来注册三大组件
- ServletRegistrationBean 注册自定义Servlet
- FilterRegistrationBean 注册自定义Filter
- ServletListenerRegistrationBean 注册自定义Listener
在上面的基础上 自定义类的注解就不需要了 统一写在配置类里
MyMvcConfig.class
@Configuration public class MyMvcConfig { //注册Servlet //替换:@WebServlet(urlPatterns = "/myServlet") @Bean public ServletRegistrationBean doServlet() { ServletRegistrationBean<MyServlet> bean = new ServletRegistrationBean<MyServlet>(); bean.setServlet(new MyServlet()); bean.setUrlMappings(Arrays.asList("/myServlet")); bean.setLoadOnStartup(1); return bean; } //注册Filter @Bean public FilterRegistrationBean doFilter(){ FilterRegistrationBean filter = new FilterRegistrationBean(); filter.setFilter(new MyFilter()); filter.addUrlPatterns("/*"); return filter; } //注册Listener @Bean public ServletListenerRegistrationBean doListener(){ //关闭监听器切记不要直接点击红色按钮,太暴力,点击控制台左侧exist ServletListenerRegistrationBean listener = new ServletListenerRegistrationBean(); listener.setListener(new MyListener()); return listener; } }
二、切换为其他嵌入式Servlet容器
SpringBoot 默认针对Servlet容器提供以下支持:
- Tomcat(默认使用)
- Jetty :支持长连接项目(如:聊天页面)[ˈdʒeti]
- Undertow : 不支持 JSP , 但是并发性能高,是高性能非阻塞的容器[ˈʌndətəʊ]
默认Tomcat容器
在spring-boot-starter-web启动器中默认引入了tomcat容器 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <version>2.1.0.RELEASE</version> <scope>compile</scope> </dependency
切换 Jetty 容器
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <!-- 排除坐标 --> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </exclusion> </exclusions> </dependency> <!-- 引入其他的Servlet容器 --> <dependency> <artifactId>spring-boot-starter-jetty</artifactId> <groupId>org.springframework.boot</groupId> </dependency>
可以在运行日志里面看到更改成功
使用外置Servlet容器Tomcat9.x
嵌入式Servlet容器:运行启动类就可启动,或将项目打成可执行的 jar 包
- 优点:简单、快捷;
- 缺点:默认不支持JSP、优化定制比较复杂使用定制器, 还需要知道 每个功能的底层原理
外置Servlet容器:配置 Tomcat, 将项目部署到Tomcat中运行
三、SpringBoot 数据访问操作
四、SpringBoot整合Mybatis与Mybatis-Plus
1. SpringBoot整合Mybatis
步骤①:创建模块时勾选要使用的技术,MyBatis,由于要操作数据库,还要勾选对应数据库。
导入坐标
<dependencies> <!--1.导入对应的starter--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.2.2</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> </dependencies>
步骤②:配置数据源相关信息,没有这个信息你连接哪个数据库都不知道
配置yml文件
#数据源 spring: datasource: url: jdbc:mysql://localhost:3306/javaee2404?serverTimezone=GMT username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver mybatis: configuration: map-underscore-to-camel-case: true #自动驼峰映射 type-aliases-package: com.zkt.pojo #起别名 #javaBean db #stuId stu_id #stuName stu_name
SpringBoot把配置中所有可能出现的通用配置都简化了。下面就可以写一下MyBatis程序运行需要的Dao(或者Mapper)就可以运行了
这里使用一个插件 lombok
点击settings -》plugins 搜索lombok
安装即可 也可以在官网自己下载 Lombok - IntelliJ IDEs Plugin | Marketplace
然后在pom.xml 中导入坐标即可
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency>
然后就是使用他的注解了
实体类
@Data @NoArgsConstructor @AllArgsConstructor @TableName("student") public class Student implements Serializable { @TableId(value = "stu_id",type = IdType.AUTO) private int studId; @TableField("stu_name") private String stuName; @TableField("stu_nick") private String stuNick; @TableField("stu_hobby") private String stuHobby; @TableLogic(value = "0",delval = "1") private int isdeleted; @TableField(exist = false) private MultipartFile pic; public Student(String stuName, String stuNick, String stuHobby) { this.stuName = stuName; this.stuNick = stuNick; this.stuHobby = stuHobby; } }
@Data 代表的是get set 方法
@NoArgsConstructor @AllArgsConstructor是无参构造 和有参构造
@TableId(value = "stu_id",type = IdType.AUTO) 这个是主键 同时自增
TableField("stu_name") 属性映射
数据库对应字段
映射接口(Dao)
@Mapper//逐个注入 public interface StudentMapper extends BaseMapper<Student> { @Select("select * from student ") public List<Student> findAll(); }
测试类
@SpringBootTest public class Test01 { @Autowired(required = false) StudentMapper mapper; //mybatis @Test void contextLoads() { List<Student> list = mapper.findAll(); for (int i = 0; i < list.size(); i++) { Student student = list.get(i); System.out.println(student); } } }
注意:当前使用的SpringBoot版本是2.5.4,对应的坐标设置中Mysql驱动使用的是8x版本。当SpringBoot2.4.3(不含)版本之前会出现一个小BUG,就是MySQL驱动升级到8以后要求强制配置时区,如果不设置会出问题。解决方案很简单,驱动url上面添加上对应设置就行了
此外在运行程序时还会给出一个提示,说数据库驱动过时的警告,根据提示修改配置即可,弃用com.mysql.jdbc.Driver,换用com.mysql.cj.jdbc.Driver。前面的例子中已经更换了驱动了,在此说明一下。
Loading class `com.mysql.jdbc.Driver'. This is deprecated. The new driver class is `com.mysql.cj.jdbc.Driver'. The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary.
总结
- 整合操作需要勾选MyBatis技术,也就是导入MyBatis对应的starter
- 数据库连接相关信息转换成配置
- 数据库SQL映射需要添加@Mapper被容器识别到
- MySQL 8.X驱动强制要求设置时区
- 修改url,添加serverTimezone设定
- 修改MySQL数据库配置
- 驱动类过时,提醒更换为com.mysql.cj.jdbc.Driver
2. SpringBoot整合Mybatis-Plus
整合究竟哪些是核心?总结下来就两句话
- 导入对应技术的starter坐标
- 根据对应技术的要求做配置
接下来在MyBatis的基础上再升级一下,整合MyBaitsPlus(简称MP)
步骤①:导入对应的starter 导入坐标 同时将mybatis注掉
<!-- mybatis+mp --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.3</version> </dependency> <!--mybatis--> <!-- <dependency>--> <!-- <groupId>org.mybatis.spring.boot</groupId>--> <!-- <artifactId>mybatis-spring-boot-starter</artifactId>--> <!-- <version>2.2.2</version>--> <!-- </dependency>-->
步骤②:配置数据源相关信息 配置yml
mybatis-plus: configuration: map-underscore-to-camel-case: true #自动驼峰映射(默认开启) log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #日志
剩下的就是写MyBaitsPlus的程序了
映射接口(Dao)
@Mapper//逐个注入 public interface StudentMapper extends BaseMapper<Student> { @Select("select * from student ") public List<Student> findAll(); }
核心在于Dao接口继承了一个BaseMapper的接口,这个接口中帮助开发者预定了若干个常用的API接口,简化了通用API接口的开发工作。
测试类
新增
/** * @author zkt * @Version 1.0 * @since 2024/7/20 */ @SpringBootTest public class Test02 { @Autowired(required = false) StudentMapper mapper; //新增 @Test public void show1() { Student student = new Student("zkt2", "god", "sleep"); int row = mapper.insert(student); System.out.println("主键回填id" + student.getStudId()); System.out.println("影响行数" + row); } }
修改id
//修改ID @Test public void test02() { Student stu = new Student(); stu.setStudId(3); stu.setStuHobby("网抑云"); int row = mapper.updateById(stu); System.out.println("影响行数" + row); }
修改name
//修改Name @Test public void test03() { //修改数据 Student stu = new Student(); stu.setStuHobby("撸铁"); //创建条件 QueryWrapper<Student> wrapper = new QueryWrapper<>(); wrapper.eq("stu_name", "zkt"); int row = mapper.update(stu, wrapper); System.out.println("影响行数" + row); }
查询ID
//查询ID @Test public void test04() { Student student = mapper.selectById(2); System.out.println(student); }
查询IDS
//查询IDS @Test public void test05() throws Exception { List<Student> list = mapper.selectBatchIds(Arrays.asList(1, 3)); for (int i = 0; i < list.size(); i++) { Student student = list.get(i); System.out.println(student); } }
查询count
//查询count @Test public void test06(){ Integer count = mapper.selectCount(null); System.out.println(count); }
查询list
//查询list @Test public void test07(){ QueryWrapper<Student> queryWrapper = new QueryWrapper<>(); queryWrapper.eq("stu_name","zkt").or().eq("stu_hobby","撸铁"); List<Student> list = mapper.selectList(queryWrapper); for (int i = 0; i <list.size() ; i++) { Student student = list.get(i); System.out.println(student); } }
分页
/** * mp分页使用 * 注意: * 1.page.setCurrent(2);当前页码从1开始 * 2.分页需要配置插件 * */ @Test public void test08(){ //1.定义分页规则 Page<Student> page = new Page<>(); page.setSize(2);//每页记录数 page.setCurrent(1);//当前页码 //2.查询条件(可选) QueryWrapper queryWrapper = new QueryWrapper(); queryWrapper.eq("stu_sex","man"); //这里没有选条件 因为表中数据太少了 只为了测试 Page<Student> iPage = mapper.selectPage(page, null); List<Student> list = iPage.getRecords();//分页结果 System.out.println("总记录数"+iPage.getTotal()); System.out.println("总记页数"+iPage.getPages()); for (int i = 0; i <list.size() ; i++) { Student student = list.get(i); System.out.println(student); } }
逻辑删除
/** * 物理删除:业务数据从数据库中丢弃,执行的是delete操作 * 逻辑删除:为数据设置是否可用状态字段,删除时设置状态字段为不可用状态, * 数据保留在数据库中,执行的是update操作 * 实现步骤: * 步骤1:修改数据库表添加`deleted`列,比如`0`代表正常,`1`代表删除,可以在添加列的同时设置其默认值为`0`正常。 * 步骤2:实体类添加属性以及注解 * @TableLogic(value="0",delval="1") * private Integer deleted; * value为正常数据的值,delval为删除数据的值 * */ //逻辑删除 @Test public void test09(){ int row = mapper.deleteById(4); System.out.println("影响行数" + row); }
这个时候如果使用mybatis 查询 还在
如果使用mp查询的话 会自带条件查询 不在
@Test public void test10(){ List<Student> list = mapper.selectList(null); // System.out.println(list); for (int i = 0; i <list.size() ; i++) { Student student = list.get(i); System.out.println(student); } }
总结
==================mybatis-plus===================
步骤:
1.坐标<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus</artifactId> <version>3.1.1</version> </dependency>
注意:mp坐标添加后,mybatis坐标移除
2.编写注解配置实体类与关系表映射关系(truncate清空表以及主键)
@TableName(value = "关联表名称")=========================》修饰在类
@TableField(value = "关联字段名称")======================》修饰在属性
exist = "忽略字段"
@TableId(type="指定主键生成策略,默认雪花算法")=============》修饰在属性
AUTO(0),
NONE(1),
INPUT(2),
ASSIGN_ID(3),
ASSIGN_UUID(4);
3.使用
BaseMapper===========》公共的数据访问层
IService/ServiceImp==》公共的业务层
五、SpringBoot切换druid数据源
前面整合MyBatis和MP的时候,使用的数据源对象都是SpringBoot默认的数据源对象,下面我们手工控制一下,自己指定了一个数据源对象,Druid。
在没有指定数据源时,我们的配置如下:
#数据源 spring: datasource: url: jdbc:mysql://localhost:3306/javaee2404?serverTimezone=GMT username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver
此时虽然没有指定数据源,但是根据SpringBoot默认使用了HiKari
更换数据源,只需要两步
- 导入对应的技术坐标
- 配置使用指定的数据源类型
下面就切换一下数据源对象
步骤①:导入对应的坐标(注意,是坐标,此处不是starter)
<!-- druid坐标 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.2.8</version> </dependency>
步骤②:修改配置,在数据源配置中有一个type属性,专用于指定数据源类型
#数据源 spring: datasource: url: jdbc:mysql://localhost:3306/javaee2404?serverTimezone=GMT username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver type: com.alibaba.druid.pool.DruidDataSource filters: stat,wall
测试切换成功
1. DRUID配置参数
配置 缺省值 说明 name 无 配置这个属性的意义在于,在存在多个数据源、监控的时候可以通过名字来区分开。如果没有配置,将会生成一个名字,格式是:“DataSource-”+ System.identityHashCode(this) jdbcUrl 无 连接数据库的url,不同数据库不一样,例如:mysql:jdbc:mysql://10.20.153.43:306/druid2 oracle:jdbc:oracle:thin:@10.20.149.85:152:ocnauto username 无 连接数据库的用户名 password 无 连接数据库的密码。如果你不希望密码直接写在配置文件中,可以使用ConfigFilter,详细看这里:http://git.hub.com/alibaba/druid/wiki/%E4%BD%BF%E7%94%A8ConfigFilter driverClassName 根据url自动识别 这一项可配可不配。如果不配置druid会根据url自动识别dbType,然后选择相应的driverClassNam(建议配置下) initialSize 0 初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时 maxActive 8 最大连接池数量 maxIdle 8 已经不再使用。配置了也没效果 minIdle 无 最小连接池数量 maxWait 无 获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降。如果需要可以通过配置useUnfairLock属性为true使用非公平锁。 poolPreparedStatements false 是否缓存PreparedStatement,也就是PSCache。PSCache对支持持游标的数据库性能能提升巨大,比如说Oracle,在mysql下建议关闭。 maxOpenPreparedStatements -1 要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100 validationQuery 无 用来检测连接是否有效的sql,要求是一个查询语句,如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会其作用。 testOnBorrow true 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 testOnReturn false 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能 testWhileIdle false 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测是否有效。 timeBetweenEvictionRunsMillis 有二个含义:1) Destroy线程会检测连接的间隔时间2) testWhileIdle的判断依据,详细看testWhileIdle属性的说明 numTestsPerEvictionRun 不再使用,一个DruidDataSource只支持一个EvictionRun minEvictableIdleTimeMillis connectionInitSqls 物理连接初始化的时候执行的sql exceptionSorter 根据dbType自动识别 当数据库抛出一些不可恢复的异常时,抛弃连接 filters 监控统计用的filter:stat日志用的filter:log4j防护注入的filter:wall proxyFilters 类型是List<com.alibaba.druid.filter.Filter>,如果同时配置了filters和proxyFilters,是组合关系,并非替换关系
2. Druid监控平台
@Configuration public class DruidConfig { //1.注入数据源 @Bean @ConfigurationProperties(prefix = "spring.datasource") public DataSource dataSource(){ return new DruidDataSource(); } //2.配置servlet @Bean public ServletRegistrationBean registrationBean(){ //1.创建servlet注册类 ServletRegistrationBean<StatViewServlet> servletRegistrationBean = new ServletRegistrationBean<StatViewServlet>(); //2.创建制作页面的servlet StatViewServlet statViewServlet = new StatViewServlet(); //3.绑定servlet servletRegistrationBean.setServlet(statViewServlet); servletRegistrationBean.setUrlMappings(Arrays.asList("/druid/*")); //4.参数绑定 Map<String,String> maps = new HashMap<String,String>(); maps.put(StatViewServlet.PARAM_NAME_USERNAME,"admin"); maps.put(StatViewServlet.PARAM_NAME_PASSWORD,"123"); maps.put(StatViewServlet.PARAM_NAME_ALLOW,"");//白名单 maps.put(StatViewServlet.PARAM_NAME_DENY,"192.168.0.12");//黑名单 servletRegistrationBean.setInitParameters(maps); return servletRegistrationBean; } //3.配置filter @Bean public FilterRegistrationBean filterRegistrationBean(){ FilterRegistrationBean<WebStatFilter> bean = new FilterRegistrationBean<WebStatFilter>(); bean.setFilter(new WebStatFilter()); //所有请求进行监控处理 bean.setUrlPatterns(Arrays.asList("/*")); Map<String, String> initPrams = new HashMap<>(); //添加不需要忽略的格式信息 initPrams.put(WebStatFilter.PARAM_NAME_EXCLUSIONS, "*.js,*.css,/druid/*"); bean.setInitParameters(initPrams); return bean; }
总结
======》数据源切换
1.加坐标
2.做配置