java开发的环境配置
JDK 1.8以上,安装指引链接 zhuanlan.zhihu.com/p/680741675 。 是Java开发工具包,它提供了开发和运行Java应用程序所需的工具、库和资源
- Java编译器(javac):JDK包含了Java编译器,可以将Java源代码编译为Java字节码
- Java虚拟机(JVM):JDK中包含了Java虚拟机,它是执行Java字节码的运行时环境。JVM负责将字节码转换为机器码并执行。
- 打包工具(Packaging Tools):JDK提供了用于打包和部署Java应用程序的工具
数据库安装,安装指引链接 blog.csdn.net/weixin_4589…redis安装,安装指引链接 www.cnblogs.com/object360/p…idea安装,安装指引链接 www.exception.site/
创建SSM项目
-
New Project
-
选择必要的依赖
------------------------------------------------------------------------
Spring
- IoC(Inversion of Control,控制反转):IoC是一种设计模式,是指将对象的创建、管理和控制权交给IoC容器,由IoC容器来负责对象的创建、管理和生命周期,而不是由应用程序自己来实现。在Spring中,IoC容器是BeanFactory或ApplicationContext,并且Spring IoC容器提供了丰富的配置方式,如XML、Java配置等。
- DI(Dependency Injection,依赖注入):DI是IoC的一种实现方式,是指通过IoC容器自动将需要的依赖注入到对象中。即一个对象声明了一个或多个依赖关系,而不需要自己去实现依赖的获取或创建。依赖注入可以通过构造函数、setter方法或字段注入方式实现。
- AOP(Aspect-Oriented Programming,面向切面编程):AOP是一种编程思想,是将应用程序中的横切关注点(如日志、事务等)从业务逻辑中分离出来,并将其封装成可重用的模块。AOP通过在程序运行时动态地将这些模块织入到业务逻辑中实现。Spring框架提供了强大的AOP支持,使得开发人员可以方便地实现AOP编程。
- 事务:将⼀组操作封装成⼀个执⾏单元,要么全部成功要么全部失败。
为什么要⽤事务?
⽐如转账分为两个操作:
第⼀步操作:A 账户 -100 元,
第⼆步操作:B 账户 +100 元。
如果没有事务,第⼀步执⾏成功了,第⼆步执⾏失败了,那么 A 账户平⽩⽆故的 100 元就“⼈间蒸发”了。如果使⽤事务就可以解决这个问题,让这⼀组操作要么⼀起成功,要么⼀起失败。
Java Bean 和 Spring Bean
Java Bean: Java类,用于封装数据。
- 可序列化:实现
Serializable接口,虽然这不是强制性的,但通常是推荐的,以便于对象在网络间传输或存储。 - 无参数构造器:拥有一个公共的无参数构造函数,以允许外部工具或框架实例化该类。
- 属性私有化:类的属性(字段)应该是私有的,以确保封装性。
- Getter和Setter方法:为每个私有属性提供公共的getter(获取器)和setter(设置器)方法,用于访问和修改这些属性的值。
Spring Bean: 是由Spring IoC(Inverse of Control,控制反转)容器管理的对象实例。这意味着开发者不需要手动创建对象,而是将对象的创建、装配以及管理交给了Spring容器
xml配置文件方式创建bean 【IoC】
package t;
// 创建接口
public interface HelloWorld {
public void sayHello();
}
package t;
// 创建实现类
public class HelloWorldImpl implements HelloWorld {
@Override
public void sayHello() {
System.out.println("helloworld");
}
}
// 编写配置文件 t/IOC.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
<!-- 用bean标签声明一个HelloWorldImpl类的实例, 并且把实例的名字定义为helloWorld-->
<!-- 其中需要注意,class属性里一定是实现类;id属性在整个配置文件里必须唯一-->
<bean id="helloWorld" class="t.HelloWorldImpl"/>
</beans>
// 编写测试类
package t;
import org.springframework.context.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class test{
public static void main(String[] args){
//通过src目录(即classpath)下的t目录下的IOC.xml配置文件,创建spring容器
AbstractApplicationContext applicationContext = new ClassPathXmlApplicationContext("t/IOC.xml");
//去容器里取到id=helloWorld 的实例,并且把这个实例声明为接口的类型
HelloWorldImpl bean = (HelloWorldImpl)applicationContext.getBean("helloWorld");
bean.sayHello();
applicationContext.close();//关闭容器
}
}
xml配置文件方式依赖注入 【DI】
- 即当一个类(A)需要依赖另外一个对象(B)的时候,把B的赋值给A的过程叫做依赖注入
package t;
// 创建实现类
public class HelloWorldImpl implements HelloWorld {
private HelloWorldDao helloWorldDao;
// 提供依赖对象对应的set方法
public void setHelloWorldDao(HelloWorldDao helloWorldDao){
this.helloWorldDao = helloWorldDao
}
@Override
public void sayHello() {
helloWorldDao.save()
System.out.println("helloworld");
}
}
<bean id="helloWorld" class="t.HelloWorldImpl">
// name的值为 HelloWorldImpl 中的属性名称 ref的值为id="helloWorldDao"中的helloWorldDao
<property name="helloWorldDao" ref="helloWorldDao"/>
</bean>
<bean id="helloWorldDao" class="t.HelloWorldDaoImpl"/>
通过注解创建bean【IoC】
某个类通过@Controller、@Service、 @Repository、@Component注解快速将类添加到ioc容器中,并为我们创建bean
@Controller 给控制器层加上该注解
@Service 给业务逻辑层加上该注解
@Repository 给操作数据库层加上该注解
@Component 给不属于以上的组件加该注解
beanId默认为类名首字母小写,默认是单例
改写bean名字:@Controller("personpp")
改成多实例: @Scope(value = "prototype")
通过注解@Autowired自动装配【DI】
@Autowired
private BookService bookService;
spring会自动为这个属性赋值:去ioc容器中找到这个属性对应的组件
// 1、先按照类型BookService去容器中找到对应的组件
bookService = ioc.getBean("BookService.class")
//1.1找到1个就给属性赋值
//1.2没找到就报错
//1.3根据类名找到多个,就按照后面的bookService变量名作为id进行匹配(譬如BookService在容器中有多个不同组件,这个时候会按照bookService变量进行二次匹配);若根据变量名没有找到就会报错
@Qualifier("bookService") 指定一个新的id(改变1.3策略),出现1.3中情况时,将不根据变量名查找,而是根据Qualifier指定的新的beanId查找对应的bean。
@Resourcevs@Autowired自动装配
@Autowired : spring提供的、 强大(离开spring包就不行)
@Resource: j2ee提供的,扩展性更强(换成另外一个容器框架也运行ok)
AOP
- 定义切入类
package com.example.demo.aop;
import com.example.demo.common.base.Result;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Component
@Aspect
@Slf4j
public class LogAop {
// 定义切入点
@Pointcut("execution(* com.example.demo.rest.UserRest.list(*))")
private void pt() {}
// 确定具体的执行时机
@Before("pt()")
public void before(JoinPoint joinPoint) {
Object[] args = joinPoint.getArgs();
String targetName = joinPoint.getTarget().getClass().getName();
// 执行被切入的方法
Signature signature = joinPoint.getSignature();
log.info("LogAop---start: args:{},signature:{},targetName:{}", args, signature, targetName);
}
@Around("pt()")
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
Result proceedRes = (Result) proceedingJoinPoint.proceed();
long endTime = System.currentTimeMillis();
log.info("LogAop---around:time:{},proceedRes:{}", endTime - startTime, proceedRes);
return proceedRes;
}
}
- 切入点表达式 execution(返回值 全限定类名.方法名(参数))
* 单个独立的任意符号,也可以作为前缀、后缀匹配
.. 任意个的缩写
- 切入时机
@Before 切入点方法之前执行 JoinPoint 可以获取切入点方法的参数
@After 切入点方法之后执行 JoinPoint 可以获取切入点方法的参数 + 切入点方法的返回值
@Around 对切入点进行环绕 ProceedingJoinPoint 可以获取切入点方法的参数 + 切入点方法的返回值 + 异常信息
事务@Transactional
- 事务生效的前提是被管理的方法或者类使用的是1个数据源
- timeout-int(单位是秒),事务若超出指定执行时长后自动终止并回滚
- readOnly-boolean: readOnly:true表示该方法内的事务都是查询,能加快查询速度。但若该方法内有修改或者插入操作,则会报异常
- noRollbackFor: 可以指定那些运行时异常不回滚
- rollbackFor: 可以指定那些编译时异常让回滚(默认的 Error + 运行时异常才会回滚,其他的异常均需指定rollbackFor才会回滚)
- propagation:
// 如下代码因为运行超时,所以代码会报错,并且数据库所有操作恢复到事务执行之前的状态
@Transactional(timeout = 3)
public void check(String num){
testDao.update(num)
Thread.sleep(3000)
}
// 如下代码演示了数据库混滚
@Transactional(rollbackFor = BusinessException.class)
public void test3() {
Student s = new Student(12, "22", new BigDecimal(12.0), "男");
studentMapper.insertStudent(s);
try {
FileInputStream f = new FileInputStream(".test");
} catch (FileNotFoundException e) {
throw new BusinessException(ResultEnum.FAIL);
}
List<Student> list = new ArrayList<>();
list.add(new Student(44, "55", new BigDecimal(12.0), "男"));
studentMapper.batchInsertStudent(list);
}
- 异常 和 回滚
// 如下代码没有加@Transactional时,因为报错在操作数据库之后,所以能插入到数据库成功
public void test3(){
Student s = new Student(12,"22",new BigDecimal(12.0),"男");
studentMapper.insertStudent(s);
Integer i = 12/0;
}
// 编译时异常,默认不回滚;运行时异常默认回滚
@Transactional
public void test3() throws FileNotFoundException {
Student s = new Student(12, "22", new BigDecimal(12.0), "男");
studentMapper.insertStudent(s);
// test文件不存在编译时异常,但是studentMapper.insertStudent(s)操作数据库会成功。不会进行回滚
FileInputStream fileInputStream = new FileInputStream(".test");
List<Student> list = new ArrayList<>();
list.add(new Student(44, "55", new BigDecimal(12.0), "男"));
studentMapper.batchInsertStudent(list);
}
// 如下代码是演示运行时异常,
@Transactional
public void test3(){
Student s = new Student(12, "22", new BigDecimal(12.0), "男");
studentMapper.insertStudent(s);
// 运行时异常,studentMapper.insertStudent(s)不会操作数据库成功,会进行回滚。
Integer i = 10/0;
List<Student> list = new ArrayList<>();
list.add(new Student(44, "55", new BigDecimal(12.0), "男"));
studentMapper.batchInsertStudent(list);
}
- 隔离级别 (p108)
// 读未提交会存在脏读问题(脏读问题产生背景:在并发情况下,对数据库同一条数据进行写和读取操作,且读的是并未提交时的写的操作,一旦写的操作进行回滚,那么之前读到的数据就是脏数据)
// 读已提交:在并发情况下,对数据库同一条数据进行写和读取操作,读会等待写(修改)提交后的数据
// 可重复读:只要在同一事物期间,第一次读取是多少,以后就是什么,哪怕在这个期间外界修改或删除这条数据,不会影响读取结果。(同一事物期间,以后每次读的都是第一次的结果,而不是拿取数据库的结果)
------------------------------------------------------------------------
SpringMvc
入参返参处理
1. 映射请求 @RequestMapping
2. 获取请求路径中的参数 @PathVariable
// @PathVariable 获取路径中占位符中变量值
@RequestMapping("/test/{pid}/{id}")
public void test(@PathVariable("pid") String pid, @PathVariable("id") String id){
log.info("pid:{}---id:{}", pid, id);
}
3. 获取(包含 get + post[form-data / x-www-form-urlencoded])请求的入参
//以下这几种方式都可以 自动映射装配 或者 @RequestParam
//springmvc 可以自动为pojo映射
@RequestMapping("/testA")
public void testA(User user){
log.info("user:{}", JSON.toJSONString(user));
}
//映射多个参数
@RequestMapping("/testB")
public void testB(String user, String age){
log.info("user:{}---age:{}", user, age);
}
//@RequestParam 方便取别名
@RequestMapping("/testC")
public void testC(@RequestParam("user") String userName){
log.info("userName:{}", userName);
}
4. 获取请求体的入参(json数据)
@PostMapping("/testE")
public void testE(@RequestBody User user){
log.info("user:{}", JSON.toJSONString(user));
}
5.@ResponseBody/@RequestBody
@ResponseBody : 把后端的对象pojo转成json字符串传输给前端
@RequestBody :把前端的json字符串转成后端的pojo对象
6. 文件(图片/excel)下载(前端get/post请求均可)
/**
* 下载图片等文件
* @param httpServletResponse
*/
public void downLoadFile(HttpServletResponse httpServletResponse) {
//模拟读取 resources目录下的资源
String filePath = this.getClass().getResource("/static/pic/1.jpg").getPath();
File file = new File(filePath);
String fileName = filePath.substring(filePath.lastIndexOf("/")+1);
try {
InputStream inputStream = Files.newInputStream(file.toPath());
//响应头需要设置内容的处理方式:下载文件需要指定为文件
httpServletResponse.setHeader("Content-Disposition", "attachment;filename=" + fileName);
httpServletResponse.setHeader("Content-type","blob");
cn.hutool.core.io.IoUtil.copy(inputStream, httpServletResponse.getOutputStream());
} catch (IOException e) {
e.printStackTrace();
}
}
//excel下载
public void exportExcel(HttpServletResponse httpServletResponse){
List<User> list = "下载的集合"
httpServletResponse.setContentType("application/vnd.ms-excel");
httpServletResponse.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".xlsx");
httpServletResponse.setCharacterEncoding("UTF-8");
com.alibaba.excel.EasyExcel.write(httpServletResponse.getOutputStream, User.class).sheet("sheet1").doWrite(list)
}
@Data
@AllArgsConstructor
public class User{
/**
* value={"一级表头","二级表头","三级表头"}
* index=0 排序从左到右
*/
@ExcelProperty(value={"用户信息","姓名"},index=0)
private String userName;
@ExcelProperty(value={"用户信息","性别"},index=1)
private String sex;
/**
* 不要在excel中展示
*/
@ExcelIgnore
private String score
}
7. 图片上传
/**
* 模拟前端上传图片
* h5请求方式
* post
* h5 入参
* let formData = new FormData();
* formData.append("files", file.raw);
* formData.append("files", file.raw);
* formData.append(其他字段名, 字段对应的值)
* h5 请求头
* {headers:{"Content-Type":"multipart/form-data"}}
* h5发送
* {headers:{"Content-Type":"multipart/form-data"}}
* data:formData
*/
@PostMapping("/upload/pic")
public void uploadPic(@RequestParam("fileList") MultipartFile[] files) throws Exception {
for (MultipartFile file : files) {
file.transferTo(new File("/Users/baoweifang/Desktop/code/wmy/myJava/src/main/resources/static/pic"+file.getOriginalFilename()));
}
}
入参校验 + 全局异常拦截器@RestControllerAdvice
//1. 校验非post json方式的参数并把message传输给前端
//@Validated ---->>>@NotBlank(message = "name 不能为空")...--->>>异常拦截BindException.class
@RequestMapping("/testA")
public void testA( @Validated User user){
log.info("user:{}", JSON.toJSONString(user));
}
@Data
@AllArgsConstructor
public class User extends BaseDo{
@NotBlank(message = "name 不能为空")
private String name;
}
/**
* 2020-12-31
* 全局异常拦截处理器
*/
@RestController
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
//处理非@RequestBody的validated
@ExceptionHandler(value = BindException.class)
public Result handle(BindException e) {
log.info("进入错误处理中心---BusinessException.class---e",e);
String message = e.getBindingResult().getFieldError().getDefaultMessage();
return new Result(-1 ,message,null);
}
}
//2. 校验post json方式的参数
//@Validated ---->>>@NotBlank(message = "name 不能为空")...--->>>MethodArgumentNotValidException.class
过滤器 + 拦截器
- 过滤器
@Slf4j
@Component
public class GlobalFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
String serverName = servletRequest.getServerName();
log.info("GlobalFilter---doFilter---start:{}", serverName);
// 放行【如果没有下面的doFilter过程,即为拦截了】,放行后可以到达servlet https://www.cnblogs.com/skyice/p/17575040.html
filterChain.doFilter(servletRequest, servletResponse);
// 相当于是模拟善后工作【servlet工作完后,再回到这里】
log.info("GlobalFilter---doFilter---end");
}
}
- 拦截器
- 先定义个拦截器
@Slf4j
@Component
public class GlobalInterceptor implements HandlerInterceptor {
// 在每个请求处理之前被调用,用来对请求做一些初始化操作,或者是预处理,判断当前请求是否放行。当返回false则立即返回,当返回true时,请求到下一个HandlerInterceptor的preHandle方法,如果是最后一个,则会调用到Controller的具体请求。
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
log.info("GlobalInterceptor---preHandle:{}", method);
}
return true;
}
// 在处理完每个请求后调用
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
}
}
- 添加拦截器
@Configuration
public class ResourcesConfig implements WebMvcConfigurer {
@Autowired
private GlobalInterceptor globalInterceptor;
/**
* 拦截请求
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
InterceptorRegistration interceptor = registry.addInterceptor(globalInterceptor);
interceptor.addPathPatterns("/**").excludePathPatterns("/user/list","/css/**","/js/**");
}
}
------------------------------------------------------------------------
Maven
概念 www.cnblogs.com/ydyxcode/p/…
Maven是专门用于管理和构建Java项目的工具
- 项目构建管理:maven提供一套对项目生命周期管理的标准,开发人员、和测试人员统一使用maven进行项目构建。项目生命周期管理:编译、测试、打包、部署、运行。
- 管理依赖(jar包):maven能够帮我们统一管理项目开发中需要的jar包。
- 管理插件:maven能够帮我们统一管理项目开发过程中需要的插件。
仓库
Maven可以通过pom.xml中的配置,就能从仓库中够获取到想要的jar包,仓库分为:本地仓库、第三方仓库(私服)和中央仓库。
- 本地仓库:计算机中一个文件夹,自己定义是哪个文件夹。Maven会将工程中依赖的构件(Jar包)从远程下载到本机的该目录下进行管理。
- 中央仓库:网上地址repo1.maven.org/maven2/ 这个公共仓库是由Maven自己维护
- 第三方仓库(私服):一般是由公司自己设立的,只为本公司内部共享使用
坐标
- groupId:定义当前Maven项目隶属组织名称(通常是域名反写,例如:com.baidu)
- artifactId:定义当前Maven项目名称(通常是模块名称,例如 order-service、goods-service)
- version:定义当前项目版本号
生命周期相关指令
Reload 安装所有依赖到本地
- mvn clean:调用clean生命周期的clean阶段,清理上一次构建项目生成的文件。
- mvn compile:编译src/main/java中的java代码。
- mvn deploy:部署到私服。
- mvn install: 发布项目到本地仓库
多模块
- 创建 A、B两个Module【假设A模块依赖B模块】
- 设置 Maven 目录
- B模块 maven install 成jar包,再去maven目录验证jar包有无成功
- A模块依赖B模块,在A模块pom.xml中添加依赖(B的坐标),然后重新reload
<!-- 依赖pojo -->
<dependency>
<groupId>com.example</groupId>
<artifactId>pojo</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
------------------------------------------------------------------------
mybatis
1. 参数
1.1 单个参数
// 1. 非pojo 随便取出
// 2. pojo或者map 则按照属性 key名取出即可
1.2 多个参数
//只要传入多个参数,mybatis会自动将这些参数封装在一个map里面,封装的key默认是索引1或者param1; 所以为了方便起见,可以给key取名,使用@Param(key名)。
method(@Param(id) Integer userId, String name, User user)
取出:
id---->>>#{id}
name--->>>#{param2}
取User里面的sex属性--->>>#{param3.sex}
1.3 List形式参数(比较特殊)
// 默认取出 #{list} 名称为list
// 或者@Param(别名)