一.aop

概念:AOP(Aspect Oriented Programming),即面向切面编程,可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善。

二.代码

  一.日志实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Log {
    private Integer id;
    private String username;
    private String className;
    private String methodName;
    private String args;
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
    private Date beginTime;
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
    private Date endTime;
    private Integer exeTime;
}

  二.Aop五种增强(代码中为环绕增强)

1.前置增强(Before Advice) 在目标方法执行之前执行的横切逻辑。可以用于执行一些预处理操作,如参数验证、权限检查等。
2.后置增强(After Advice) 在目标方法执行之后执行的横切逻辑。可以用于执行一些清理操作,如资源释放、日志记录等。
3.环绕增强(Around Advice) 在目标方法执行前后都可以执行的横切逻辑。环绕增强可以完全控制目标方法的执行过程,可以选择是否调用目标方法以及何时调用。
4.异常增强(After Throwing Advice) 在目标方法抛出异常时执行的横切逻辑。可以用于处理异常、发送通知等
5.最终增强(After finally) 它在目标方法执行结束后(无论是否发生异常)都会执行。最终增强通常用于资源清理或日志记录等必须执行的操作。
@Component
@Aspect
@Slf4j
public class LogAdvice {
    @Qualifier("logServiceImpl")
    @Autowired
    private ILogService iLogService;

    @Autowired
    HttpSession session;

//    @Around("execution(* com.woniu.guoqing.controller.*.*(..))")
    @Around("@annotation(com.woniu.guoqing.ann.anno)")

//使用自定义注解的方式控制进行生效
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {

        Date beginDate=new Date();
        String methodName = joinPoint.getSignature().getName();
        String className = joinPoint.getTarget().getClass().getName();
        Object[] target = joinPoint.getArgs();

        log.info(className,methodName, Arrays.asList(target));
        Object proceed = joinPoint.proceed();
        Date endDate = new Date();
        long exeTime = endDate.getTime() - beginDate.getTime();
        String username=null;
        Author author=(Author) session.getAttribute("author");
        if (author!=null){
            username=author.getUsername();
        }

        Log log = new Log();
        log.setUsername(username);
        log.setClassName(className);
        log.setMethodName(methodName);
        log.setArgs(Arrays.toString(target));
        log.setBeginTime(beginDate);
        log.setEndTime(endDate);
        log.setExeTime((int) exeTime);
        iLogService.insert(log);
        return proceed;
    }
}

  三.自定义注解

@Target(ElementType.<em>METHOD</em>)
@Retention(RetentionPolicy.<em>RUNTIME</em>)
public @interface anno {
}

二.生成验证码

@Controller
public class ImageCodeController {
    @RequestMapping("/imageCode")
    public void imageCode(HttpSession httpSession, HttpServletResponse httpServletResponse) throws IOException {
        String s = RandomUtil.randomNumbers(4);//生成随机的四位数
        httpSession.setAttribute("yzm",s);
        BufferedImage imageCode = ImageCodeUtils.createImageCode(200, 43, s);
        ImageIO.write(imageCode,"png", httpServletResponse.getOutputStream());

    }
}
/**
 * 生成图片验证码
 */
public class ImageCodeUtils {

    //根据传入的位数,生成随机字符
    public static String getRandomStr(int length)
    {
        String s = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ";

        Random random = new Random(System.currentTimeMillis());

        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < length; i++) {
            int i1 = random.nextInt(s.length());
            sb.append(s.charAt(i1));
        }
        return sb.toString();
    }

    public static void main(String[] args) throws IOException, InterruptedException {

        for (int i = 0; i < 100; i++) {
            Thread.sleep(10);
            String randomStr = getRandomStr(4);
            System.out.println(randomStr);
        }
//        //BuffereImage 图片缓冲流
//        BufferedImage bufferedImage = createImageCode(100, 34, "ABCD");//生成图片的缓冲流
//        //把缓冲流,写到某个输出流
//        ImageIO.write(bufferedImage,"jpg",new FileOutputStream("/Users/edz/Desktop/11.jpg"));
    }

    public static BufferedImage createImageCode(int width, int height, String code) {
        int codeSize = code.length();
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Random rand = new Random();
        Graphics2D g2 = image.createGraphics();
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        Color[] colors = new Color[5];
        Color[] colorSpaces = new Color[] { Color.WHITE, Color.CYAN, Color.GRAY, Color.LIGHT_GRAY, Color.MAGENTA,
                Color.ORANGE, Color.PINK, Color.YELLOW };
        float[] fractions = new float[colors.length];
        for (int i = 0; i < colors.length; i++) {
            colors[i] = colorSpaces[rand.nextInt(colorSpaces.length)];
            fractions[i] = rand.nextFloat();
        }
        Arrays.sort(fractions);

        g2.setColor(Color.GRAY);// 设置边框色
        g2.fillRect(0, 0, width, height);

        Color c = getRandColor(200, 250);
        g2.setColor(c);// 设置背景色
        g2.fillRect(0, 2, width, height - 4);

        // 绘制干扰线
        Random random = new Random();
        g2.setColor(getRandColor(160, 200));// 设置线条的颜色
        for (int i = 0; i < 20; i++) {
            int x = random.nextInt(width - 1);
            int y = random.nextInt(height - 1);
            int xl = random.nextInt(6) + 1;
            int yl = random.nextInt(12) + 1;
            g2.drawLine(x, y, x + xl + 40, y + yl + 20);
        }

        // 添加噪点
        float yawpRate = 0.05f;// 噪声率
        int area = (int) (yawpRate * width * height);
        for (int i = 0; i < area; i++) {
            int x = random.nextInt(width);
            int y = random.nextInt(height);
            int rgb = getRandomIntColor();
            image.setRGB(x, y, rgb);
        }

        shear(g2, width, height, c);// 使图片扭曲

        g2.setColor(getRandColor(100, 160));
        int fontSize = height - 4;
        Font font = new Font("Algerian", Font.ITALIC, fontSize);
        g2.setFont(font);
        char[] chars = code.toCharArray();
        for (int i = 0; i < codeSize; i++) {
            AffineTransform affine = new AffineTransform();
            affine.setToRotation(Math.PI / 4 * rand.nextDouble() * (rand.nextBoolean() ? 1 : -1),
                    (width / codeSize) * i + fontSize / 2, height / 2);
            g2.setTransform(affine);
            g2.drawChars(chars, i, 1, ((width - 10) / codeSize) * i + 5, height / 2 + fontSize / 2 - 10);
        }

        g2.dispose();
        return image;
    }

    private static Color getRandColor(int fc, int bc) {
        Random random = ThreadLocalRandom.current();

        if (fc > 255)
            fc = 255;
        if (bc > 255)
            bc = 255;
        int r = fc + random.nextInt(bc - fc);
        int g = fc + random.nextInt(bc - fc);
        int b = fc + random.nextInt(bc - fc);
        return new Color(r, g, b);
    }

    private static int getRandomIntColor() {
        int[] rgb = getRandomRgb();
        int color = 0;
        for (int c : rgb) {
            color = color << 8;
            color = color | c;
        }
        return color;
    }

    private static int[] getRandomRgb() {
        Random random = ThreadLocalRandom.current();
        int[] rgb = new int[3];
        for (int i = 0; i < 3; i++) {
            rgb[i] = random.nextInt(255);
        }
        return rgb;
    }

    private static void shear(Graphics g, int w1, int h1, Color color) {
        shearX(g, w1, h1, color);
        shearY(g, w1, h1, color);
    }

    private static void shearX(Graphics g, int w1, int h1, Color color) {
        Random random = ThreadLocalRandom.current();
        int period = random.nextInt(2);

        boolean borderGap = true;
        int frames = 1;
        int phase = random.nextInt(2);

        for (int i = 0; i < h1; i++) {
            double d = (period >> 1) * Math.sin((double) i / (double) period + (6.2831853071795862D * phase) / frames);
            g.copyArea(0, i, w1, 1, (int) d, 0);
            if (borderGap) {
                g.setColor(color);
                g.drawLine((int) d, i, 0, i);
                g.drawLine((int) d + w1, i, w1, i);
            }
        }

    }
    private static void shearY(Graphics g, int w1, int h1, Color color) {
        Random random = ThreadLocalRandom.current();
        int period = random.nextInt(40) + 10; // 50;

        boolean borderGap = true;
        int frames = 20;
        int phase = 7;
        for (int i = 0; i < w1; i++) {
            double d = (period >> 1) * Math.sin((double) i / (double) period + (6.2831853071795862D * phase) / frames);
            g.copyArea(i, 0, 1, h1, 0, (int) d);
            if (borderGap) {
                g.setColor(color);
                g.drawLine(i, (int) d, i, 0);
                g.drawLine(i, (int) d + h1, i, h1);
            }
        }
    }
}

三、过滤器

@Configuration
public class SpringConfig implements WebMvcConfigurer {
    @Value("${imglocation}")
    String imglocation;
    @Autowired
    LoginAuthIntecepter loginAuthIntecepter;


    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/test/**")
                .addResourceLocations("file:"+imglocation);
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginAuthIntecepter)
                .addPathPatterns("/**")//多级目录全部拦截
                .excludePathPatterns("/author/login","/author/register","/author/loginPage",
                        "/author/registerPage","/imageCode","/bootstrap/**","/js/**","/static/favicon.ico");
    }
}
//登录过滤器
@Component
public class LoginAuthIntecepter implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HttpSession session = request.getSession();
        Author author = (Author) session.getAttribute("author");
        if (author==null){
            response.sendRedirect("/author/loginPage");
            return false;
        }else {
            return true;
        }
    }
}
//自定义过滤器
@Component
public class MyIntecepter implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle");

        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion");
    }
}
//权限过滤器
@Component
public class Authrizalntecepter implements HandlerInterceptor {
    @Qualifier("IUserRoleServiceImpl")
    @Autowired
    private IUserRoleService iUserRoleService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        User user = (User) request.getSession().getAttribute("user");
        Role roleByUserId = iUserRoleService.getRoleByUserId(user.getId());
        if (handler instanceof HandlerMethod){
            HandlerMethod handlerMethod=(HandlerMethod) handler;
            PreAuthorize annotation = handlerMethod.getMethod().getAnnotation(PreAuthorize.class);
            if (annotation!=null){
                String[] values = annotation.value();

                boolean b= Arrays.stream(values).anyMatch(value->value.equals(roleByUserId.getName()));
                if (!b){
                    response.setContentType("application/json;charset=utf-8");
                    response.setCharacterEncoding("utf-8");
                    response.getWriter().write(JSON.toJSONString(new ResData(40001,"权限不足",null)));

                    return false;
                }
            }
        }
    return true;
    }
}

四.上传图片

配置文件
imglocation: D:/test/
@Controller
public class UploadController {
    @Value("${imglocation}")
    String imglocation;

    @RequestMapping("/uploadPage")
    public String uploadPage(){
        return "upload";
    }


    @RequestMapping("/upload")
    @ResponseBody
    public ResData upload(MultipartFile file) throws IOException {
        String originalFilename = file.getOriginalFilename();
        String extension = FilenameUtils.getExtension(originalFilename);
        String s = UUID.randomUUID().toString().replace("-", "") + "." + extension;
        file.getSize();
        file.transferTo(new File( imglocation+ s));
        return new ResData(200, "ok",s);
    }
}
前端页面
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="/js/jquery-3.5.1.js"></script>
    <link rel="stylesheet" href="/bootstrap/css/bootstrap.css">
    <script src="/bootstrap/js/bootstrap.js"></script>
    <script src="/js/axios.min.js"></script>
    <script src="/js/vue.min-v2.5.16.js"></script>
</head>
<body>
<div id="app">
  <input type="file" @change="upload">
    <img :src="'/test/'+icon">
</div>
<script>
    let app=new Vue({
        el:"#app",
        data:{
            icon:""
        },
        methods:{
            upload(env){
                let file = env.target.files[0];
                let formData = new FormData();
                formData.append("file",file);

                axios.post("/upload",formData,{
                    'Content-type': 'multipart/form-data'//要发送给后端的,有文件
                }).then(response=>{
                    this.icon = response.data.data;
                })}}

    })
</script>
</body>
</html>

五.时间转换工具类

//(String和Date转换)
public class DateUtils {
    public static Date stringToDate(String str,String pattern){
        DateFormat df = new SimpleDateFormat(pattern);
        Date birthdayDate = null;
        try {
            birthdayDate = df.parse(str);
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }
        return birthdayDate;
    }

    public static Date stringToDate(String str){
        return stringToDate(str,"yyyy-MM-dd");
    }
    public static Date addOneDay(Date date){
        date.setTime(date.getTime() + 24*60*60*1000-1);
        return date;
    }
}

六、SqlSession

//SqlSession 的主要作用包括:
//1. 提供对数据库的操作方法:SqlSession提供了一系列方法,用于执行数据库操作,如插入数据、更新数据、删除数据和查询数据等。
//2. 管理数据库连接:SqlSession 负责获取数据库连接,并在操作完成后释放连接,确保数据库连接的有效性和安全性。
//3. 实现事务管理:SqlSession 可以管理事务的提交、回滚和关闭,确保数据库操作的一致性和可靠性。
//4. 缓存管理:SqlSession中包含了一个缓存,用于缓存查询的结果,提高查询性能。在同一个 SqlSession 实例中执行相同的查询,可以直接从缓存中获取结果,而无需再次访问数据库。
public class SqlSessionUtil {
    public static SqlSession sqlSession;

    static{
        InputStream inputStream = null;
        try {
            inputStream = Resources.getResourceAsStream("mybatis-config.xml");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        //工厂建造者对象
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        //根据mybatis配置文档建造一个工厂
        SqlSessionFactory sqlSessionFactory = builder.build(inputStream);
        //工厂,生成一个SqlSession
        sqlSession = sqlSessionFactory.openSession(true);
    }

    public static <T> T getMapper(Class<T> mapperClass){
        return sqlSession.getMapper(mapperClass);
    }
}
//mapper.xml文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.woniu.guoqing.mapper.LogMapper">
</mapper>

七、配置文件

//application.yml
server:
  port: 80
spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: root
    url: jdbc:mysql://localhost:3306/javaweb?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
mybatis:
  type-aliases-package: com.woniu.guoqing.entity
  mapper-locations:  classpath:mapper/*Mapper.xml
  configuration:
    map-underscore-to-camel-case: true
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    
imglocation: D:/test/

八、一对多查询

<!--    newsMap映射,把下面sql查询出来的结果,映射成一条news信息-->
    <resultMap id="newsMap" type="news">
        <id column="ttt" property="id"></id>
        <result column="title" property="title"></result>
        <result column="content" property="content"></result>
        <collection property="commentList" ofType="comment">
            <id column="comment_id" property="id"></id>
            <result column="id" property="newsId"></result>
            <result column="comment_content" property="content"></result>
<!--            放在collection里面,select="com.woniu.mapper.CommentMapper.query"colun="id>-->
        </collection>
    </resultMap>
   <select id="selectById1" parameterType="integer" resultMap="newsMap">
       select news.id,news.title,news.content,comment.id comment_id,comment.content comment_content from news
       left join comment
       on news.id=comment.news_id
       where news.id=#{id}
   </select>

九、全局异常类

@ControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(NullPointerException.class)
    @ResponseBody
    public ResData nullPointExceptionHandler() {
        return new ResData(10001, "空指针异常", null);
    }
    @ExceptionHandler(ArithmeticException.class)
    @ResponseBody
    public ResData ArithmeticException() {
        return new ResData(10002, "数字异常", null);
    }
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public ResData Exception() {
        return new ResData(10003, "系统繁忙", null);
    }
}

十、手机验证码登录

/**
 * new SmsThread(ShortMes)
 * 因为这个线程是发短信的线程,所以需要用户给一个手机号,验证码才能发...,所以通过构造函数传一个
 */
public class SmsThread implements Runnable{

    private ShortMes shortMes;

    public SmsThread() {
    }

    public SmsThread(ShortMes shortMes) {
        this.shortMes = shortMes;
    }

    /**
     * 子类不能抛出父类没有的异常
     *
     */
    @Override
    public void run() {
        try {
            SMSUtils.send(shortMes);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
//工具类(通过阿里云获取)
public class SMSUtils {

    public static void send(ShortMes shortMes) throws IOException {

        DefaultProfile profile = DefaultProfile.getProfile("cn-hangzhou", "LTAI5tKSK6vEeTHY5Mzvd9yN", "TPGxhbOWtmeQxwAlrMP97s8PoROreR");
        IAcsClient client = new DefaultAcsClient(profile);

        CommonRequest request = new CommonRequest();
        request.setSysMethod(MethodType.POST);
        request.setSysDomain("dysmsapi.aliyuncs.com");
        request.setSysVersion("2017-05-25");
        request.setSysAction("SendSms");
        request.putQueryParameter("RegionId", "cn-hangzhou");
        request.putQueryParameter("PhoneNumbers", shortMes.getAccount());//往什么手机号发
        request.putQueryParameter("SignName", "MT4账号");
        request.putQueryParameter("TemplateCode", "SMS_157449478");
        request.putQueryParameter("TemplateParam", JSON.toJSONString(shortMes));//发送的内容
        try {
            CommonResponse response = client.getCommonResponse(request);
            System.out.println(response.getData());
        } catch (ServerException e) {
            e.printStackTrace();
        } catch (ClientException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws IOException {
        ShortMes shortMes = new ShortMes("手机号码","手机号码","1111");
        send(shortMes);
    }
}
@RequestMapping("/loginSms")
    @ResponseBody
    @anno
    public ResData loginSms(@RequestBody User user, HttpSession httpSession) {
        if (StrUtil.isEmpty(user.getTel()) || StrUtil.isEmpty(user.getYzm())) {
            return new ResData(10001, "必填项为空", null);
        }
        String s = redisTemplate.opsForValue().get(user.getTel());

        if (user.getYzm().equals(s)){
            return new ResData(10002, "验证码错误", null);
        }
        redisTemplate.delete(user.getTel());
        User user1 = iUserService.queryByTel(user.getTel());
        httpSession.setAttribute("user", user1);
        return new ResData(200, "ok", user1.getUsername());
    }
//前端页面     
          loginsms(){
                let owner = {};
                owner.tel = this.tel;
                owner.yzm = this.yzm;

                axios.post("/user/loginSms",owner)
                    .then(response=>{
                        let rs = response.data;
                        if (rs.code == 200) {
                            localStorage
                                .setItem("username",this.username);
                            window.location = "/index.html";
                        } else {
                            this.msg = rs.msg;
                        }
                    },error=>{

                    })
            }