Web工具箱
Web工具箱mysql创建数据库增删改数据所有字段插入值批插修改删除查数据交叉连接内连接左外连接右外连接子查询复杂查询外键创建表之后单独添加创建表的时候,添加外键mybatis配置文件Mapper测试类xml写sqlforeachsql片段查询&修改驼峰映射Mapper接口映射resultMap前端js函数定义事件绑定事件绑定综合案例Servlet路径req & resppostman对象转json字符串 & json字符串转对象增删改查案例SpringSpring整合Mybatis依赖配置类Spring写测试类Spring注解动态代理JDK动态代理Cglib动态代理Spring事务事务配置SpringMVCSpringBoot启动类yml样例读取配置读取配置一读取配置二分页查询登录校验过滤器拦截器日志记录图片上传
mysql
创建数据库
-- 创建数据库: 创建一个名为db1的数据库 create database db1; -- 切换数据库: use db1;
增删改数据
所有字段插入值
insert into tb_emp values (null,'忘了爱','111','杨宁',1,'/img',1,'2002-12-21',now(),now());
批插
insert into tb_emp values (null,'忘了爱1','111','杨宁2',1,'/img',1,'2002-12-21',now(),now()) ,(null,'忘了爱2','111','杨宁3',1,'/img',1,'2002-12-21',now(),now());
修改
update tb_emp set name = '传智',entrydate='2022-01-01' where id =1;
删除
delete from tb_emp where id = 1;
查数据
交叉连接
select * from emp,dept;
内连接
select * from emp e inner join dept d on e.dept_id = d.id;
左外连接
select * from emp e left join dept d on e.dept_id = d.id;
右外连接
select * from emp e right join dept d on e.dept_id = d.id;
子查询
-- 1: 查询工资小于平均工资的员工有哪些?(子查询结果为一个值 标量子查询) select * from emp where salary < (select avg(salary) from emp); -- 2: 查询工资大于5000的员工,所在部门的名字 (子查询结果为多个值 列子查询) select name from dept where id in(select distinct dept_id from emp where salary > 5000); -- 3: 查询出2011年以后入职的员工信息,包括部门信息 (子查询结果为一张表 表子查询) select * from (select * from emp where join_date >= '2011-01-01') as l left join dept as d on l.dept_id = d.id;
复杂查询
# 菜品表:dish、分类表:category、套餐表:setmeal -- 1. 查询价格低于 10元 的菜品的名称 、价格 及其 菜品的分类名称 select d.name,d.price,c.name from dish d inner join category c on d.category_id = c.id where d.price <10; -- 2. 查询所有价格在 10元(含)到50元(含)之间 且 状态为'起售'的菜品名称、价格 及其 菜品的分类名称 # (即使菜品没有分类, 也需要将菜品查询出来). select d.name,d.price,c.name from dish d left join category c on d.category_id = c.id where d.price between 10 and 50 and d.status = 1; -- 3. 查询出 "商务套餐A" 中包含了哪些菜品 (展示出套餐名称、价格, 包含的菜品名称、价格、份数) # 三表关联,重复使用join select s.name,s.price,d.name,d.price,sd.copies from setmeal s join setmeal_dish sd on s.id = sd.setmeal_id join dish d on d.id = sd.dish_id where s.name = '商务套餐A'; -- 4. 查询出低于菜品平均价格的菜品信息 (展示出菜品名称、菜品价格) select name,price from dish where price < (select avg(price) from dish) order by price; -- 5. 查询每个分类下最贵的菜品, 展示出分类的名称、最贵的菜品的价格 select c.name,max(d.price) from dish d join category c on d.category_id = c.id group by c.name; -- 6. 查询各个分类下状态为'起售' , 并且该分类下 菜品总数量大于等于3的分类名称 select c.name,count(*) count from category c join dish d on c.id = d.category_id where c.status = 1 group by c.name having count >=3;
外键
创建表之后单独添加
# alter table 表名 add [constraint 约束名] foreign key (列名) references 主表(主键) alter table student add constraint class_id_fk foreign key (class_id) references class (id);
创建表的时候,添加外键
create table user_card( id int unsigned primary key auto_increment, nationality varchar(10) not null , birthday date not null , idcard char(18) not null , issued varchar(20) not null , expire_begin date not null , expire_end date, user_id int unsigned comment '用户id', # 创建表的时候,添加外键 (user_id 和 user表的id) constraint userid_id_fk foreign key (user_id) references user(id) );
mybatis
配置文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <settings> <!--在控制台输出发送的sql日志--> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <!--目前只关注这部分内容,它的作用就是声明要连接的数据信息--> <dataSource type="POOLED"> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis"/> <property name="username" value="root"/> <property name="password" value="1234"/> </dataSource> </environment> </environments> <mappers> <!--声明含有sql的接口所在包--> <package name="com.itheima.mapper"/> </mappers> </configuration>
Mapper
// 1. 这是一个接口 // 2. 接口名:操作的表名+Mapper public interface UserMapper { // #{}里面的内容 // 1. 如果参数是对象类型,#{}写的是对象的属性名 @Insert("insert into user values (null,#{name},#{age},#{gender},#{phone})") void save(User user); // @Options // useGeneratedKeys 拿到自增主键 // keyProperty 指定哪个字段是主键 @Options(useGeneratedKeys = true, keyProperty = "id") @Insert("insert into user values (null,#{name},#{age},#{gender},#{phone})") void save2(User user); @Update("update user set name = #{name},age=#{age},gender=#{gender},phone = #{phone} where id=#{id} ") void update(User user); @Delete("delete from user where id = #{id}") void delete(Integer id); @Delete("delete from user where name = #{name}") void deleteByName1(String name); @Delete("delete from user where name = ${name}") void deleteByName2(String name); }
测试类
public class UserMapperTest { @Test public void testSave() throws IOException { // 1. 创建对象 User user = new User(); user.setName("张三"); user.setAge(18); user.setGender(1); user.setPhone("15515847153"); // 工具类获取sqlSession SqlSession sqlSession = MyBatisUtil.getSqlSession(); // 获取UserMapper对象,调用方法 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); userMapper.save(user); // 工具类关闭sqlSession MyBatisUtil.close(sqlSession); } @Test public void testDeleteByName1() { SqlSession sqlSession = MyBatisUtil.getSqlSession(); UserMapper userMapper = sqlSession.getMapper(UserMapper.class); userMapper.deleteByName1("张三"); // delete from user where name = '张三' MyBatisUtil.close(sqlSession); } @Test public void testDeleteByName2() { SqlSession sqlSession = MyBatisUtil.getSqlSession(); // 用户名叫作:'李四' or id>1 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); userMapper.deleteByName2(" '李四' or id>1"); // ${}会产生sql注入问题 // delete from user where name = '李四' or id>1 MyBatisUtil.close(sqlSession); } }
xml写sql
foreach
<!-- * collection:集合名称 * item:集合遍历出来的元素 * separator:每一次遍历使用的分隔符 * open: 遍历开始前拼接的片段 * close:遍历结束后拼接的片段 delete from emp where id in (1,2,3) --> <delete id="deleteByIds"> delete from emp where id in <foreach collection="ids" item="id" separator="," open="(" close=")"> #{id} </foreach> </delete>
sql片段
<!--ctrl + alt + L:格式化代码--> <!-- * 定义SQL片段: <sql id="selectUser"></sql> * 引用SQL片段: <include refid="selectUser"></include> --> <sql id="selectUser"> select id, username, password, name, gender, image, job, entrydate as ed, dept_id, create_time, update_time from emp </sql> <!--id:哪个方法--> <!--resultType:方法返回值类型(sql结果数据类型)--> <select id="findById" resultType="com.itheima.domain.Emp"> <include refid="selectUser"></include> where id = #{id} </select>
查询&修改
<!-- 字符串需要跟null和空串比较 其他类型只要跟null比较 if:使用test进行条件判断,只有条件成立,条件中的sql才会生效 where:只会在<where>标签内部有内容的情况下才插入where子句,而且会自动去除子句的开头的AND或OR。 --> <select id="finByCondition" resultType="com.itheima.domain.Emp"> <include refid="selectUser"/> <where> <if test="name != null and name !='' "> and name like concat('%',#{name},'%') </if> <if test="gender != null"> and gender = #{gender} </if> <if test="begin != null and end != null"> and entrydate between #{begin} and #{end} </if> </where> </select> <!--set:动态地在set代码块之前加入SET关键字,并删掉set代码块中最后一个多余的逗号(用在update语句中)--> <!--ctrl + shift + / 生成快捷键 --> <update id="update"> update emp <set> <if test="username != null and username != '' "> username = #{username}, </if> <if test="name != null and name != '' "> name = #{name}, </if> <if test="gender != null"> gender = #{gender}, </if> <if test="job != null"> job = #{job}, </if> <if test="ed != null"> entrydate = #{ed}, </if> <if test="deptId != null"> dept_id = #{deptId}, </if> </set> where id = #{id} </update>
驼峰映射
<settings> <!--在控制台输出发送的sql日志--> <setting name="logImpl" value="STDOUT_LOGGING"/> <!--驼峰映射--> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings>
Mapper接口映射
<mappers> <!--声明含有sql的接口所在包--> <package name="com.itheima.mapper"/> </mappers>
resultMap
<resultMap id="empResultMap" type="com.itheima.domain.Emp"> <id property="id" column="id"></id> <!--设置主键--> <!--result:建立映射关系--> <!--property:实体类属性,column:数据库表字段--> <result property="username" column="username"></result> <result property="password" column="password"></result> <result property="name" column="name"></result> <result property="gender" column="gender"></result> <result property="image" column="image"></result> <result property="job" column="job"></result> <result property="ed" column="entrydate"></result> <result property="deptId" column="dept_id"></result> <result property="createTime" column="create_time"></result> <result property="updateTime" column="update_time"></result> </resultMap> <!--id:哪个方法--> <!--resultType:方法返回值类型(sql结果数据类型)--> <select id="findById" resultType="com.itheima.domain.Emp"> <include refid="selectUser"></include> where id = #{id} </select> <!--resultMap:sql结果通过resultMap映射到实体类字段中--> <select id="selectAll" resultMap="empResultMap"> select * from emp </select>
前端
js
函数定义
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>函数</title> </head> <body> <!--定义一个函数(方法)实现两个数相加--> <script> // 方式一:定义函数(方法) function add(a, b) { return a + b } let result = add(1, 2) console.log(result) // 方式二:定义函数 let add2 = function (a, b) { return a + b } let result2 = add2(1,2); console.log(result2) </script> </body> </html>
事件绑定
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>事件绑定</title> </head> <body> <input type="button" value="按钮1" οnclick="tan()"> <br> <input type="button" value="按钮2" id="btn"> <br> <!-- 事件绑定有两种方式: 1. 通过 HTML标签中的事件属性进行绑定 2. 通过 DOM 元素属性绑定 --> <script> // 1. 通过 HTML标签中的事件属性进行绑定 function tan() { alert('按钮一被点击了') } // 2. 通过 DOM 元素属性绑定 (document代表当前页面对象) document.getElementById('btn').onclick = function () { alert('按钮二被点击了') } </script> </body> </html>
事件绑定综合案例
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>案例:页面交互</title> <!-- 常用事件: 1. onfocus 获取焦点 2. onblur 失去焦点 3. onchange 表单控件的值改变时 4. onclick 鼠标单击 5. onload 页面加载完成 --> <script> //5. 将js代码调整到页面的head部分,使其依旧可以运行 window.onload = function (){ //1. 当姓名框获得焦点之后,设置其value值为传智 document.getElementById('username').onfocus = function () { // document.getElementById('username').value = '传智' this.value = '传智' // this代表当前对象 } //2. 当姓名框失去焦点之后,设置其value值为黑马 document.getElementById('username').onblur = function () { this.value = '黑马' } //3. 当学历发生变化的时候,在控制台打印出变化之后的value值 document.getElementById('edu').onchange = function () { console.log(this.value) } //4. 当鼠标点击按钮时,弹窗显示被点击了 document.getElementById('btn').onclick = function () { alert('被点击了') } } </script> </head> <body> 姓名 <input type="text" id="username"><br/> 学历 <select name="edu" id="edu"> <option value="0">请选择</option> <option value="1">本科</option> <option value="2">大专</option> </select> <br/> <button id="btn">按钮</button> </body> </html>
Servlet
路径
@WebServlet("/login")
req & resp
@WebServlet("/requestServlet") public class RequestServlet extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 请求参数编码,设置为UTF-8 req.setCharacterEncoding("UTF-8"); String name = req.getParameter("name"); String age = req.getParameter("age"); System.out.println("name=" + name + " age=" + age); String[] hobbies = req.getParameterValues("hobby"); System.out.println(Arrays.toString(hobbies)); } }
@WebServlet("/responseServlet") public class ResponseServlet extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 响应成功状态码 resp.setStatus(200); // 设置响应头 resp.setContentType("text/html;charset=utf-8"); // 返回数据 PrintWriter writer = resp.getWriter(); writer.write("hello servlet"); } }
postman
模拟浏览器发起请求
http://localhost:8080/requestServlet?name=张三&age=18
对象转json字符串 & json字符串转对象
<!--json转换工具--> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.9</version> </dependency>
Student student = new ObjectMapper().readValue(json, Student.class); System.out.println(student);
Student student = studentService.findById(id); String json = new ObjectMapper().writeValueAsString(student);
增删改查案例
@WebServlet("/studentServlet") public class StudentServlet extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String action = req.getParameter("action"); if ("findAll".equals(action)) { // 1. 操作service层 StudentService studentService = new StudentServiceImpl(); List<Student> students = studentService.findAll(); // 2. 把数据转为json字符串格式 String json = new ObjectMapper().writeValueAsString(students); System.out.println(json); // 3. 返回数据给前端 resp.getWriter().write(json); } else if ("save".equals(action)) { // 1. 以流的方式接收前端json数据 ServletInputStream inputStream = req.getInputStream(); String json = IoUtil.read(inputStream, "utf-8"); // 2. 把json字符串转为对象 Student student = new ObjectMapper().readValue(json, Student.class); System.out.println(student); // 3. 调用service层,添加学生 StudentService studentService = new StudentServiceImpl(); studentService.save(student); // 4. 返回ok resp.getWriter().write("OK"); } else if ("deleteById".equals(action)) { String id = req.getParameter("id"); StudentService studentService = new StudentServiceImpl(); studentService.deleteById(id); resp.getWriter().write("OK"); } else if ("findById".equals(action)) { String id = req.getParameter("id"); StudentService studentService = new StudentServiceImpl(); Student student = studentService.findById(id); String json = new ObjectMapper().writeValueAsString(student); resp.getWriter().write(json); } else if ("update".equals(action)) { ServletInputStream inputStream = req.getInputStream(); String json = IoUtil.read(inputStream, "utf-8"); Student student = new ObjectMapper().readValue(json, Student.class); System.out.println(student); StudentService studentService = new StudentServiceImpl(); studentService.update(student); resp.getWriter().write("OK"); } } }
Spring
Spring整合Mybatis
依赖
<dependencies> <!--lombok--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.26</version> </dependency> <!--mysql--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.28</version> </dependency> <!--druid--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.8</version> </dependency> <!--mybatis--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.9</version> </dependency> <!--spring--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.3.16</version> </dependency> <!--spring-jdbc--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.3.16</version> </dependency> <!--spring-mybatis--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>2.0.7</version> </dependency> <!--测试--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.3.16</version> </dependency> </dependencies>
配置类
@ComponentScan("com.itheima") // @ComponentScan 注解扫描范围 public class SpringConfig { // 1. 配置数据库连接池对象 @Bean public DruidDataSource dataSource(){ // 创建DruidDataSource对象 DruidDataSource dataSource = new DruidDataSource(); // 设置连接数据库的参数:driver(加载器)、路径、用户名、密码 dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://localhost:3306/spring"); dataSource.setUsername("root"); dataSource.setPassword("1234"); return dataSource; } // 2. 配置mapper映射对象 @Bean public MapperScannerConfigurer mapperScannerConfigurer(){ MapperScannerConfigurer configurer = new MapperScannerConfigurer(); configurer.setBasePackage("com.itheima.mapper"); // 设置接口所在包名 return configurer; } // 3. 配置SqlSessionFactoryBean // @Bean标注的方法如果需要参数,Spring会在自己的容器中查找,自动注入 @Bean public SqlSessionFactoryBean sqlSessionFactoryBean(DruidDataSource dataSource){ SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean(); factoryBean.setDataSource(dataSource); return factoryBean; } }
Spring写测试类
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = SpringConfig.class) public class DeptMapperTest { @Autowired private DeptService deptService; @Test public void testFindAll() { // 3. 验证deptService是否拿到;验证deptService是否拿到DeptMapper对象 List<Dept> deptList = deptService.findAll(); deptList.forEach(dept -> System.out.println(dept)); } @Test public void deleteByIdTest() { deptService.deleteById(1); } @Test public void insertDept() { Dept dept = new Dept(); dept.setId(6); dept.setName("网管部"); dept.setCreateTime(LocalDateTime.now()); dept.setUpdateTime(LocalDateTime.now()); deptService.insertDept(dept); } @Test public void updateDept(){ Dept dept = new Dept(); dept.setId(5); dept.setName("外交部"); deptService.updateDept(dept); } }
Spring注解
@Component @Controller @Service @Repository | 标注在自己开发的类上,用于将当前类对象放入Spring容器 |
---|---|
@Bean | 标注在配置类中的方法上,用于将方法的返回值对象放入Spring容器 |
@Autowired | 标注在类中的属性上,用于从Spring容器中获取属性的值 |
@Qualifier @Primary | 依赖注入时,根据一个接口查到了多个对象,使用这两个注解确定使用哪个对象 |
@Scope | 标注在类上,声明对象是单例还是多例 |
@Configuration | 标注在配置类上,Spring容器启动时会自动加载类中的配置 |
@ComponentScan | 标注在主配置类上,用于声明包扫描的范围 |
动态代理
JDK动态代理
被代理对象需要有接口
// jdk动态代理工厂 public class JDKProxyFactory { public static Object getProxy(Class clazz) { // 生成代理对象 Object proxy = Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Logger logger = new Logger(); Object obj = null; try { // 前置增强 logger.m1(); // method.invoke:被代理的对象执行方法 obj = method.invoke(clazz.newInstance(), args); // 后置增强 logger.m2(); } catch (Exception e) { e.printStackTrace(); logger.m3(); } return obj; } }); return proxy; } }
Cglib动态代理
被代理对象可以被继承
// Cglib动态代理 (基于继承) public class CglibProxyFactory { public static Object getProxy(Class clazz){ // 创建一个代理工具类 Enhancer enhancer = new Enhancer(); // 根据父类创建子类对象(认爹) enhancer.setSuperclass(clazz); // 调用代理对象的方法 (增强的代码写在这里) enhancer.setCallback(new InvocationHandler() { @Override // 参数一:代理对象本身(用不上) // 参数二:代理对象方法 // 参数三:方法的参数 public Object invoke(Object o, Method method, Object[] objects) throws Throwable { Logger logger = new Logger(); // 前置增强 logger.m1(); // 执行被代理对象的方法 Object invoke = method.invoke(clazz.newInstance(), objects); // 后置增强 logger.m2(); return invoke; // 返回方法执行的返回值 } }); // 生成代理对象并返回 Object proxy = enhancer.create(); return proxy; } }
Spring事务
事务配置
方法上加上@Transactional(rollbackFor = Exception.class)
配置类加上@EnableTransactionManagement 和 DataSourceTransactionManager bean
@Service public class AccountServiceImpl implements AccountService { @Autowired private AccountMapper accountMapper; @Override // @Transactional加在方法上,当前方法开启事务,默认只回滚运行时异常 // rollbackFor:指定回滚的异常 // @Transactional(rollbackFor = Exception.class) :标准写法 @Transactional(rollbackFor = Exception.class) public void transfer(String out, String in, Float money) throws Exception { // 1. 扣钱 accountMapper.subtract(out, money); // 模拟异常 // System.out.println(1/0); if (true){ throw new Exception(); } // 2. 加钱 accountMapper.add(in, money); } }
//配置类 @ComponentScan("com.itheima") @EnableTransactionManagement // 开启事务管理 public class SpringConfig { //配置mapper位置 @Bean public MapperScannerConfigurer scannerConfigurer(){ MapperScannerConfigurer scannerConfigurer = new MapperScannerConfigurer(); scannerConfigurer.setBasePackage("com.itheima.mapper");//设置接口包位置 return scannerConfigurer; } //配置数据源 @Bean public DruidDataSource dataSource(){ DruidDataSource dataSource = new DruidDataSource(); dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://localhost:3306/spring"); dataSource.setUsername("root"); dataSource.setPassword("1234"); return dataSource; } //配置SqlSessionFactory @Bean public SqlSessionFactoryBean sqlSessionFactoryBean(DruidDataSource dataSource){ SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); sqlSessionFactoryBean.setDataSource(dataSource);//设置数据源 return sqlSessionFactoryBean; } // 把数据库事务管理器加入IOC容器 @Bean public DataSourceTransactionManager transactionManager(DruidDataSource dataSource){ DataSourceTransactionManager manager = new DataSourceTransactionManager(); manager.setDataSource(dataSource); return manager; } }
SpringMVC
//@Controller //将当前类的对象放入容器 //@ResponseBody //将方法的返回值(对象和集合)转json返回 @RestController //@RequestMapping("/user") 如果@RequestMapping标注在类上, 代表提取公共请求路径 public class UserController { @RequestMapping("/user/demo1") // 给当前方法绑定一个请求地址 //@ResponseBody //将方法的返回值(对象和集合)转json返回 public User demo1(String name, Integer age) { User user = new User(name, age); return user; } // value: 等同于path, 用于给当前方法绑定请求路径 // 支持数组的写法, 代表可以为一个方法绑定多个请求路径 @RequestMapping(value = {"/user/demo2", "/user/demo3"}) //@ResponseBody public String demo2() { return "ok"; } // method: 限制请求类型,支持数组的写法, 代表可以同时允许多种请求方式 // 如果method属性省略, 代表的是全部方式都可以 @RequestMapping(value = "/user/demo4", method = {RequestMethod.GET, RequestMethod.POST}) //@ResponseBody public String demo4() { return "ok"; } // 使用简单类型接收 需要保证前端传递的参数名称跟方法的形参名称一致 // 对于一些简单类型的数据, mvc底层是可以自动完成转换的 @RequestMapping("/user/demo5") public String demo5(String name, Integer age) { System.out.println("name:" + name + ",age:" + age); return "ok"; } // 使用对象类型接收 需要保证前端传递的参数名称跟对象属性名称一致 @RequestMapping("/user/demo6") public String demo6(User user) { System.out.println(user); return "ok"; } // 使用数组类型接收 需要保证前端传递的参数名称跟方法的形参名称一致 @RequestMapping("/user/demo7") public String demo7(String[] hobby) { System.out.println(Arrays.toString(hobby)); return "ok"; } // 使用集合类型接收 需要保证前端传递的参数名称跟方法的形参名称一致 // 如果直接使用List来接收请求参数, 需要在参数类型之前加入@RequestParam @RequestMapping("/user/demo8") public String demo8(@RequestParam("hobby") List<String> hobby) { System.out.println(Arrays.toString(hobby.toArray())); return "ok"; } // 使用日期类型接收 // 在参数之前,使用@DateTimeFormat(pattern = "自定义日期格式") @RequestMapping("/user/demo9") public String demo9(@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date updateTime) { System.out.println(updateTime); return "ok"; } // 接收请求体中的json数据 // 在方法形参中去声明一个对象, 在对象之前使用@RequestBody注解 // 此注解就会从请求体中接收json字符串, 并且按照给的类型进行转换 // @RequestMapping("/user/demo10") // public String demo10(@RequestBody User user) { // System.out.println(user); // // return "ok"; // } @RequestMapping("/user/demo10") public String demo10(@RequestBody Map<String, Object> map) { System.out.println(map.get("name")); System.out.println(map.get("age")); return "ok"; } // 接收请求路径中的参数 //@PathVariable("name") 从请求路径上获取参数 注意: 路径上使用{}做占位符 @RequestMapping("/user/demo11/name/{name}/age/{age}") public String demo11( @PathVariable("name") String name, @PathVariable("age") Integer age ) { System.out.println("name:" + name + ",age:" + age); return "ok"; } //@RequestParam 有3个使用场景 // 1. 当请求中参数名称跟方法形参名称不对应的时候,可以使用此注解的name属性完成映射 // 2. 当请求中有可能不传递的参数时, 可以使用此注解的defaultValue属性设置默认值 // 3. 接收List集合 // 注意:@RequestParam标注的参数默认情况下不能为空, 如果想取消这个限制,使用required = false @RequestMapping("/user/demo12") public String demo12( @RequestParam(name = "name", required = false) String username, @RequestParam(defaultValue = "18") Integer age ) { System.out.println("username:" + username); System.out.println("age:" + age); return "ok"; } // 模拟异常 @RequestMapping("/user/demo21") public void demo21(Integer type) { if (type == 1) { // ArithmeticException int i = 1 / 0; } else if (type == 2) { // NullPointerException String s = null; System.out.println(s.length()); } else { // ArrayIndexOutOfBoundsException Integer[] arr = new Integer[2]; arr[5] = 1; } } // restful--添加 //@RequestMapping(value = "/users", method = RequestMethod.POST) @PostMapping("/users") // 仅仅接收post请求 public void save(@RequestBody User user) { System.out.println(user); } // restful--查询列表 //@RequestMapping(value = "/users", method = RequestMethod.GET) @GetMapping("/users") // 仅仅接收get请求 public List<User> findAll() { List<User> list = new ArrayList<>(); list.add(new User("张三", 18)); list.add(new User("李四", 19)); return list; } // 主键删除 @DeleteMapping("/users/{id}") public void deleteById(@PathVariable("id") Integer id) { System.out.println("删除id:" + id); } // 主键查询 @GetMapping("/users/{id}") public void findById(@PathVariable("id") Integer id) { System.out.println("查询id:" + id); } }
SpringBoot
启动类
// 启动类,位置必须高于其他类 @SpringBootApplication // 启动类注解 @MapperScan("com.itheima.mapper") // 指定mapper接口所在的包 @EnableAspectJAutoProxy // 开启aop @ServletComponentScan public class SpringBootDemoApplication { // springboot内置了tomcat,端口号默认8080 (约定大于配置) public static void main(String[] args) { // 固定写法 // 参数一:启动类.class // 参数二:args SpringApplication.run(SpringBootDemoApplication.class, args); } }
yml样例
# 大小写敏感 # 缩进表示层级关系 # 参数值和冒号一定要有空格 # #代表注释 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/springboot username: root password: 1234 mybatis: configuration: # 开启驼峰映射 map-underscore-to-camel-case: true # 日志级别( debug info warn error ) logging: level: com.itheima: info file: name: D:\\spring.log # 只允许本地IP访问 server: address: 127.0.0.1
读取配置
# 大小写敏感 # 缩进表示层级关系 # 参数值和冒号一定要有空格 # #代表注释 server: port: 8081 servlet: context-path: /itheima # 对象:键值对的集合 user: username: '张三' password: '123456' addressList: # 数组:固定写法 - '杭州' - '北京' - '上海'
读取配置一
@RestController public class IndexController { // @Value("${配置路径}"):读取配置 @Value("${user.username}") private String username; @Value("${user.password}") private String password; @GetMapping("/index") public String index() { return "hello spring boot"; } }
读取配置二
@Data @Configuration // 配置类 @ConfigurationProperties(prefix = "user") // 读取配置文件中指定配置 public class UserConfig { private String username; private String password; private List<String> addressList; }
@RestController public class IndexController { @Autowired private UserConfig userConfig; @GetMapping("/index") public String index() { System.out.println(userConfig); return "hello spring boot"; } }
分页查询
<!--mybatis分页插件--> <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper-spring-boot-starter</artifactId> <version>1.4.2</version> </dependency>
@GetMapping("/emps") public Result findByPage(@RequestParam("page") Integer page, @RequestParam("pageSize") Integer pageSize, @RequestParam(value = "name", required = false) String name, @RequestParam(value = "gender", required = false) Integer gender, @RequestParam(value = "begin", required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin, @RequestParam(value = "end", required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end) { PageBean<Emp> pageBean = empService.findByPage(page, pageSize, name, gender, begin, end); return Result.success(pageBean); }
@Service public class EmpServiceImpl implements EmpService { @Autowired private EmpMapper empMapper; @Override public PageBean<Emp> findByPage(Integer page, Integer pageSize, String name, Integer gender, LocalDate begin, LocalDate end) { // 1 设置分页参数 PageHelper.startPage(page,pageSize); // 2.执行查询 List<Emp> empList = empMapper.findList3(name, gender, begin, end); // 3.查询结果强转Page对象 Page<Emp> p = (Page<Emp>) empList; // 4. 封装对象返回 return new PageBean<>(p.getTotal(),p.getResult()); } }
@Mapper public interface EmpMapper { List<Emp> findList3(@Param("name") String name, @Param("gender") Integer gender, @Param("begin") LocalDate begin, @Param("end") LocalDate end); }
登录校验
<!--Token生成与解析--> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency>
过滤器
@Slf4j @WebFilter("/*") public class LoginCheckFilter implements Filter { @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { // 1. 请求和响应对象的强转 HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse; // 2. 获取请求uri // uri:/login url: http://localhost:90/login String uri = request.getRequestURI(); log.info("uri的值是: {}", uri); // 3. 判断请求uri是否是登录,如果是就放行 if ("/login".equals(uri)) { filterChain.doFilter(request, response); return; } // 4. 走到这行一定不是登录,获取请求头的token (令牌) String token = request.getHeader("token"); // 5. token不存在,返回前端未登录 if (token == null || token.equals("")) { String json = new ObjectMapper().writeValueAsString(Result.error("NOT_LOGIN")); response.setContentType("application/json;charset=utf-8"); response.getWriter().write(json); return; } // 6. 解析token,如果解析失败,返回前端未登录 try { JwtUtils.parseJWT(token); } catch (Exception e) { // 出现异常,token校验不通过,返回错误提示 String json = new ObjectMapper().writeValueAsString(Result.error("NOT_LOGIN")); response.setContentType("application/json;charset=utf-8"); response.getWriter().write(json); return; } // 7. 放行 filterChain.doFilter(request, response); } }
// 启动类,位置必须高于其他类 @SpringBootApplication // 启动类注解 @MapperScan("com.itheima.mapper") // 指定mapper接口所在的包 @EnableAspectJAutoProxy // 开启aop @ServletComponentScan // 开启过滤器 public class SpringBootDemoApplication { // springboot内置了tomcat,端口号默认8080 (约定大于配置) public static void main(String[] args) { // 固定写法 // 参数一:启动类.class // 参数二:args SpringApplication.run(SpringBootDemoApplication.class, args); } }
拦截器
@Component public class LoginCheckInterceptor implements HandlerInterceptor { @Autowired private Gson gson; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 请求头中拿令牌 String token = request.getHeader("token"); // 如果字符串没有长度(null 或 空串),返回登录错误 // org.springframework.util.StringUtils if (!StringUtils.hasLength(token)){ String json = gson.toJson(Result.error("NOT_LOGIN")); response.setContentType("application/json;charset=utf-8"); response.getWriter().write(json); // 返回浏览器错误提示 return false; } try { JwtUtils.parseJWT(token); } catch (Exception e) { String json = gson.toJson(Result.error("NOT_LOGIN")); response.setContentType("application/json;charset=utf-8"); response.getWriter().write(json); // 返回浏览器错误提示 return false; // 禁止通行 } // 放行 return true; } }
@Configuration public class MvcConfig implements WebMvcConfigurer { @Autowired private LoginCheckInterceptor loginCheckInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(loginCheckInterceptor) .addPathPatterns("/**") .excludePathPatterns("/login"); } }
日志记录
<!--添加依赖--> <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> </dependency> <!--aop--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
@Retention(RetentionPolicy.RUNTIME) // 保留到运行阶段 @Target({ElementType.METHOD}) // 只能加方法上 public @interface LogAnno { String methodDesc() default ""; // 方法用途 }
// 查询员工 @GetMapping("/emps/{id}") @LogAnno(methodDesc = "查询员工") public Result findById(@PathVariable("id") Integer id) { Emp emp = empService.findById(id); return Result.success(emp); } // 修改员工 @PutMapping("/emps") @LogAnno(methodDesc = "修改员工") public Result update(@RequestBody Emp emp) { empService.update(emp); return Result.success(); } // 新增员工 @PostMapping("/emps") @LogAnno(methodDesc = "新增员工") public Result save(@RequestBody Emp emp) { empService.save(emp); return Result.success(); }
// 记录日志的切面 @Aspect @Component public class LogAspect { @Autowired private HttpServletRequest request; @Autowired private OperateLogMapper operateLogMapper; // 切点 (标注LogAnno注解的方法都被切中) @Pointcut("@annotation(com.itheima.anno.LogAnno)") public void pt() { } // 环绕通知 @Around("pt()") public Object around(ProceedingJoinPoint pjp) throws Throwable { OperateLog operateLog = new OperateLog(); // 获取类名 operateLog.setClassName(pjp.getTarget().getClass().getName()); // 获取方法参数 operateLog.setMethodParams(Arrays.toString(pjp.getArgs())); // org.aspectj.lang.reflect.MethodSignature MethodSignature ms = (MethodSignature) pjp.getSignature(); // 获取方法名 operateLog.setMethodName(ms.getMethod().getName()); LogAnno logAnno = ms.getMethod().getAnnotation(LogAnno.class); // 获取方法描述 operateLog.setMethodDesc(logAnno.methodDesc()); String token = request.getHeader("token"); Claims claims = JwtUtils.parseJWT(token); // 拿到用户id Integer id = claims.get("id", Integer.class); operateLog.setOperateUser(id); // 操作时间 operateLog.setOperateTime(LocalDateTime.now()); long begin = System.currentTimeMillis(); Object object = pjp.proceed(); long end = System.currentTimeMillis(); // 方法执行时间 operateLog.setCostTime(end - begin); // 方法返回信息 operateLog.setReturnValue(object.toString()); // 落库 operateLogMapper.insert(operateLog); return object; } }
图片上传
<!--阿里云--> <dependency> <groupId>com.aliyun.oss</groupId> <artifactId>aliyun-sdk-oss</artifactId> <version>3.10.2</version> </dependency> <dependency> <groupId>com.aliyun</groupId> <artifactId>aliyun-java-sdk-core</artifactId> <version>4.0.6</version> </dependency>
spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/tlias username: root password: 1234 servlet: multipart: max-file-size: 10MB # 单个文件默认最大1MB max-request-size: 100MB # 一次多个文件默认最大10MB mybatis: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl map-underscore-to-camel-case: true aliyun: oss: endpoint: oss-cn-beijing.aliyuncs.com accessKeyId: LTAI5tQ823aWxiqK2BmKxyeN accessKeySecret: 0CYrcWVlpnNJdm7Zn1R53iyEcXbGbM bucketName: tlias-web-management-yn url: https://tlias-web-management-yn.oss-cn-beijing.aliyuncs.com
@Data @Component @ConfigurationProperties(prefix = "aliyun.oss") // 读取配置信息 public class OssProperties { // 服务器区域 private String endpoint; // 秘钥key private String accessKeyId; // 秘钥 private String accessKeySecret; // 存储空间 private String bucketName; // 访问域名 private String url; }
//阿里存储工具类 @Component public class OssTemplate { //注入配置参数实体类对象 @Autowired private OssProperties ossProperties; //文件上传 public String upload(String fileName, InputStream inputStream) { //创建客户端 OSS ossClient = new OSSClientBuilder().build(ossProperties.getEndpoint(), ossProperties.getAccessKeyId(), ossProperties.getAccessKeySecret()); //设置文件最终的路径和名称 String objectName = "images/" + new SimpleDateFormat("yyyy/MM/dd").format(new Date()) + "/" + System.currentTimeMillis() + fileName.substring(fileName.lastIndexOf(".")); //meta设置请求头,解决访问图片地址直接下载 ObjectMetadata meta = new ObjectMetadata(); meta.setContentType(getContentType(fileName.substring(fileName.lastIndexOf(".")))); //上传 ossClient.putObject(ossProperties.getBucketName(), objectName, inputStream, meta); //关闭客户端 ossClient.shutdown(); return ossProperties.getUrl() + "/" + objectName; } //文件后缀处理 private String getContentType(String FilenameExtension) { if (FilenameExtension.equalsIgnoreCase(".bmp")) { return "image/bmp"; } if (FilenameExtension.equalsIgnoreCase(".gif")) { return "image/gif"; } if (FilenameExtension.equalsIgnoreCase(".jpeg") || FilenameExtension.equalsIgnoreCase(".jpg") || FilenameExtension.equalsIgnoreCase(".png")) { return "image/jpg"; } return "image/jpg"; } }
@RestController public class FileController { @Autowired private OssTemplate ossTemplate; // 文件上传 @PostMapping("/upload") public Result uploadFile(MultipartFile image) throws IOException { // 调用阿里云工具类上传文件 String filePath = ossTemplate.upload(image.getOriginalFilename(), image.getInputStream()); // 返回文件路径 return Result.success(filePath); } }