Maven
Maven 可以用于管理 Java 项目的依赖
在 pom.xml
文件中指定了 Maven 坐标,其构成如下:
groupId
:通常是域名反写
artifactId
:通常是模块名称
version
:版本号
引入依赖:在 <dependencies>
标签下,使用 <dependency>
引入标签,并定义 Maven 坐标
依赖有传递性,可以使用 <exclusion>
手动排除依赖的资源
使用 <scope>
设置其作用范围,通常使用默认的和 <test>
Maven 有 3 套独立的生命周期:
- clean
- default:编译、测试、打包、部署等
- site:生成报告、发布站点
可以手动执行其中的若干个生命周期
请求与响应
Tomcat 是一个轻量级的 web 服务器,支持 Servlet 规范
几种请求参数:
简单参数:名字一致,也可以使用一个对象接收多个参数
GET http: @RequestMapping("/simpleParam") public String simpleParam(String name) { return "OK"; }
|
数组集合:可以直接用数组封装或通过 @RequestParam
绑定关系
日期参数:使用 @DateTimeFormat
转换格式,如:
@RequestMapping("/dateParam") public String dateParam(@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocaoDateTime updateTime) { return "OK"; }
|
json 参数,使用 @RequestBody
封装到参数中
路径参数:
@RequestMapping("/path/{id}") public String pathParam(@PathVariable Integer id) { return "OK"; }
|
控制器类外面加上 @RestController
注解,实现一个 RESTful 服务器,可以自动返回一个 json 对象
分成三层架构:
- controller:接受前端的请求并响应数据
- service:处理具体的业务逻辑
- dao:负责数据访问操作,即增删改查
为了实现高内聚低耦合,spring 使用到了
- 控制反转 inversion of control(IOC):对象的创建控制权由程序自身转移到容器
- 依赖注入 dependency injection(DI):容器为程序提供运行时所依赖的资源
- bean:IOC 容器中创建、管理的对象
要把某个对象交给 IOC 容器管理,需要加上以下注解之一:
@Controller
:控制器
@Service
:业务类
@Repository
:数据访问类
以上三者和 @Component
没有什么区别,只是为了区分而取了不同的名字
可以通过 value
属性知道 bean 的名字,如果没有,则默认为类首字母小写
想要被组件扫描到需要 @ComponentScan
,而 @SpringBootApplication
中默认扫描的是所在类的包及子包:
@SpringBootApplication public class SpringBootHelloWorld { public static void main(String[] args) { SpringApplication.run(SpringBootHelloWorld.class, args); } }
|
使用 bean 对象时使用 @Resource
注解指定要使用注入哪一个实现:
@Resource(name = "empServiceB") private EmpService empService;
|
MySQL
登录:
mysql -u用户名 -p密码 [-h服务器IP地址 -P端口号]
|
查询所有数据库:show databases;
查询当前数据库:select database();
使用数据库:use 数据库名;
创建数据库:create database 数据库名;
删除数据库:drop database 数据库名;
其中的 database
也可以替换为 schema
一些数据类型:char
、varchar
、text
、int
、double
、date
、datetime
等
comment 注释
修改表:alter table 表名
- 添加:
add
- 修改类型:
modify 字段名 新数据类型
- 修改字段名和类型:
change 旧字段名 新字段名 类型
- 删除:
drop column 字段名
- 修改表明:
rename table 表名 to 新表名
添加数据:insert into 表名(字段1,字段2) values (值1,值2);
更新数据:update 表名 set 字段名1 = 值1 [where 条件]
删除数据:delete from 表名
MyBatis
需要引入的依赖有 MyBatis
和响应的数据库驱动(如 MySQL Driver
)
然后配置用户信息,如:
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/mybatis spring.datasource.username=root spring.datasource.password=1234
|
定义对象模型:
public class User { private Integer id; private String name; }
|
并定义 Mapper
:
@Mapper public interface UserMapper { @Select("select * from user") public List<User> list(); }
|
JDBC 是使用 Java 语言操作关系型数据库的一套 API,比较原始,所以才要使用 MyBatis 简化
数据库连接池是一个容器,负责分配、管理数据库连接,产品有 Druid、Hikari 等
Lombok 可以通过注解的形式自动生成构造器,如
注解 |
作用 |
@Data |
@Getter + @Setter + @ToString + @EqualsAndHashCode |
NoArgsConstructor |
无参构造函数 |
AllArgsConstructor |
有各参数的构造函数 |
一个使用参数删除的例子:
@Delete("delete from emp where id = #{id}") public void delete(Integer id);
|
注意如果使用的是 ${...}
,则是直接将参数拼接在 SQL 语句中
如果想要新增数据并获取返回的逐渐,则加上注解:
@Options(keyProperty = "id", useGeneratedKeys = true)
|
因为数据库中用下划线分割,代码中用驼峰命名法,所以需要打开自动转换的开关:mybatis.configuration.map-underscore-to-camel-case=true
XML 映射文件与 Mapper 接口同包同名,支持动态查询,
如 if
(where
标签可以自动处理掉多余的 and
等):
<select id="list" resultType="com.itheima.pojo.Emp"> select id, username from emp <where> <if test="name != null"> name like concat('%', #{name}, '%') </if> <if test="gender != null"> and gender = #{gender} </if> </where> </select>
|
同理,还有 <set>
标签,用于 update
语句中
一个使用 <foreach>
循环生成的例子:
<delete id="deleteByIds"> delete from emp where id in <foreach collection="ids" item="id" seperator="," open="(" close=")" > #{id} </foreach> </delete>
|
不少 sql 片段有重用,可以使用 <sql id="名字">
并使用 <include refid="名字">
来引用
登录
这里使用 JWT 令牌校验,依赖为 jjwt
public void genjwt(){ Map<String,Object> claims =new HashMap>(); claims.put("id" ,1); claims.put("username" , "Tom" ); String jwt = Jwts.builder() .setClaims(claims) .signWith(SignatureAlgorithm.HS256, "itheima") .setExpiration(new Date(System.currentTimeMillis() + 12*3600*1000)) .compact(); System.out.println(jwt); }
|
解析令牌:
public void parsejwt()( Claims claims =Jwts.parser() .setSigningKey("itheima") .parseClaimsjws("eyJhbGciOijIUzI1Nij9.eyJpZCI6MSwiZXhwljoxNjU50Tk1NTE3LCJ1c2VybmFtZSI6IlRvbSJ9.EUTfeqPkGslekdKBezcWCe7a7xbcllwB1MXllccTMwo") .getBody(); System.out.printin(claims); }
|
拦截器 interceptor 用于拦截错误请求
首先定义拦截器:
@Component @Override public boolean preHandle(HttpServletRequest req, HttpServletResponse resp, Object handler) throws Exception { System.out.println("preHandle ..."); return true; }
@Override public void postHandle(HttpServletRequest req, HttpServletResponse resp, Object handler, ModelAndView modelAndView) { System.out.println("postHandle ..."); }
@Override public void afterCompletion(HttpServletRequest req, HttpServletResponse resp, Object handler, Exception ex) { System.out.println("afterCompletion..."); }
|
然后注册拦截器:
@Configuration public class WebConfig implements WebMvcConfigurer { @Autowired private LoginCheckInterceptor loginCheckInterceptor;
@Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(loginCheckInterceptor).addPathPatterns("/**"); } }
|
对于异常处理,定义一个全局异常处理器:
@RestControllerAdvice public class GlobalExceptionHandle { @ExceptionHandler(Exception.class); public Result ex(Exception ex) { ex.printStackTrace(); return Result.error("操作无效"); } }
|
杂项
分页查询:
引入依赖:pagehelper-spring-boot-starter
PageHelper.startPage(pageNum, pageSize); List<Emp> list = empMapper.list(); Page<Emp> page = (Page<Emp>)list();
|
也可以直接在 SQL 语句中使用 limit
实现
参数配置化:
外部服务的配置信息可以定义在 application.yml
中,然后在代码中引用,如:
aliyun: oss: endpoint: https://oss-cn-hangzhou.aliyuncs.com
|
然后获取该配置:
@Value("${aliyun.oss.endpoint}") private String endpoint;
|
当然,因为有共同的前缀 aliyun.oss
,可以使用 @ConfigurationProperties
合并:
引入依赖:spring-boot-configuration-processor
,然后在类前面加上注解 @ConfigurationProperties(prefix = "aliyun.oss")
保持变量和配置名称相同即可自动配置
文件上传
获取的是一个 MultipartFile
,同时因为可能出现文件名重复的问题,要使用一个随机的 UUID
给文件命名:
@RestController public class UploadController { @PostMapping("/upload") public Result upload(MultipartFile image) throws IOException { String originalFilename = image.getOriginalFilename(); String newFilename = UUID.randomUUID().toString() + originalFilename.substring(originalFilename.lastindexOf(".")); image.transferTo(new File("E:/images/" + newFilename)); return Result.success(); } }
|
可以在 application.yml
中修改上传文件大小的限制:
spring: servlet: multipart: max-file-size: 10MB max-request-size: 100MB
|
事务管理
事务是一组操作的集合,这些操作要么同时成功,要么同时失败
开启事务:在 service
层的方法上、类上、接口上添加注解 @Transactional
默认情况下,只有 RuntimeException
才回滚异常,可以手动配置出现何种异常类型时回滚事务:@Transactional(rollbackFor = Exception.class)
事务传播行为:当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行事务控制
REQUIRED
:默认值,有则加入,无则创建新事物
REQUIRES_NEW
:无论有无,总是创建新事物
还有很多其他的属性,设置方法:@Transactional(propagation = Propagation.REQUIRES_NEW)
AOP
AOP (Aspect Oriented Programming) 面向切面编程
导入依赖:spring-boot-starter-aop
一个简单的例子:
@Component @Aspect public class TimeAspect { @Around("execution(* com.itheima.service.*.*(..))") public Object recordTime(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { long begin = System.currentTimeMillis(); Object object = proceedingJoinPoint.proceed(); long end = System.currentTimeMillis(); log.info(proceedingJoinPoint.getSignature() + "执行耗时:{}ms", end - begin); return object; } }
|
核心概念:
- 连接点 JoinPoint:可以被 AOP 控制的方法
- 通知 Advice:共性功能
- 切入点 PointCut:匹配连接点的条件
- 切面 Aspect:描述通知与切入点的对应关系
- 目标对象 Target:通知所应用的对象
通知类型:
@Around
:在前、后都执行
@Before
:前
@After
:后
@AfterReturning
:返回后通知,有异常不会执行
@AfterThrowing
:异常后通知
注意 @Around
需要自己调用 proceedingJoinPoint.proceed()
来让原始方法运行
通知顺序:用 @Order(数字)
来控制顺序
切入点表达式:
execution
:根据方法的签名匹配
- 格式为
execution(访问修饰符? 返回值 包名.类名.?方法名(方法参数) throws 异常?)
*
单个独立的任意符号
..
多个连续的任意符号
@annotation
:根据注解匹配
先自定义一个注解:
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Mylog { }
|
然后在需要目标对象前加上 @Mylog
即可
设置通知:
@Pointcut("@annotation(com.itheima.aop.Mylog)") private void pt() { }
@Before("pt()") public void before() { log.info("MyAspect"); }
|
连接点可以获取方法执行时的目标类名、方法名等:
@Before("execution(* com.*(..))") public void before(JoinPoint joinPoint) { String className = joinPoint.getTarget().getClass().getName(); Signature signature = joinPoint.getSignature(); String methodName = joinPoint.getSignature().getName(); Object[] args = joinPoint.getArgs(); }
|
bean 的管理
主动获取 bean:
- 根据名称获取:
Object getBean(String name)
- 根据类型获取:
<T> T getbean(Class<T> requiredType)
- 也可以两者结合
bean 有多种作用域
singleton
单例,默认
prototype
每次使用 bean 都会创建新的实例
默认容器启动时创建,可以使用 @Lazy
延迟初始化到第一次使用时
第三方 bean 的引入:
@Configuration public class CommonConfig { @Bean public SAXReader saxReader() { return new SAXReader(); } }
|