java---SSM

206 阅读13分钟

java开发的环境配置

  • JDK 1.8以上,安装指引链接 zhuanlan.zhihu.com/p/680741675 。 是Java开发工具包,它提供了开发和运行Java应用程序所需的工具、库和资源
  1. Java编译器(javac):JDK包含了Java编译器,可以将Java源代码编译为Java字节码
  2. Java虚拟机(JVM):JDK中包含了Java虚拟机,它是执行Java字节码的运行时环境。JVM负责将字节码转换为机器码并执行。
  3. 打包工具(Packaging Tools):JDK提供了用于打包和部署Java应用程序的工具

创建SSM项目

  • New Project Image.png

  • 选择必要的依赖

Image.png

------------------------------------------------------------------------

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类,用于封装数据。
  1. 可序列化:实现 Serializable 接口,虽然这不是强制性的,但通常是推荐的,以便于对象在网络间传输或存储。
  2. 无参数构造器:拥有一个公共的无参数构造函数,以允许外部工具或框架实例化该类。
  3. 属性私有化:类的属性(字段)应该是私有的,以确保封装性。
  4. 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。

  • @Resource vs @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

过滤器 + 拦截器

image.png

  • 过滤器
@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");  
}  
   
}
  • 拦截器
  1. 先定义个拦截器
@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 {  
    }  
}
  1. 添加拦截器
@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 目录

image.png

  • B模块 maven install 成jar包,再去maven目录验证jar包有无成功

image.png

  • 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(别名) 

2. parameterType一般都不用提供,resultType最好每次提供。且为集合的时候,resultType为集合中元素的类型。