SpringBoot自定义注解

版权声明:分享知识是一种快乐,愿你我都能共同成长! https://blog.csdn.net/qidasheng2012/article/details/84340742

我们经常使用自定义注解和AOP实现操作日志、权限、统计执行时间等功能,本文记录使用SpringBoot实现自定义注解

本文在一个ssm项目的基础上进行的
SpringBoot搭建SSM项目:https://blog.csdn.net/qidasheng2012/article/details/84233549

开始正文
1. 添加依赖

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-aop</artifactId>
</dependency>

普通的spring项目需要添加下面依赖

<!-- AspectJ -->
<dependency>
	<groupId>org.aspectj</groupId>
	<artifactId>aspectjrt</artifactId>
	<version>1.6.10</version>
</dependency>
<dependency>
	<groupId>org.aspectj</groupId>
	<artifactId>aspectjweaver</artifactId>
	<version>1.7.2</version>
</dependency>

下面给出整个pom.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.springbootssm</groupId>
	<artifactId>ssm</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>ssm</name>
	<description>Demo project for Spring Boot</description>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.5.18.BUILD-SNAPSHOT</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-aop</artifactId>
		</dependency>
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>1.3.2</version>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

	<repositories>
		<repository>
			<id>spring-snapshots</id>
			<name>Spring Snapshots</name>
			<url>https://repo.spring.io/snapshot</url>
			<snapshots>
				<enabled>true</enabled>
			</snapshots>
		</repository>
		<repository>
			<id>spring-milestones</id>
			<name>Spring Milestones</name>
			<url>https://repo.spring.io/milestone</url>
			<snapshots>
				<enabled>false</enabled>
			</snapshots>
		</repository>
	</repositories>

	<pluginRepositories>
		<pluginRepository>
			<id>spring-snapshots</id>
			<name>Spring Snapshots</name>
			<url>https://repo.spring.io/snapshot</url>
			<snapshots>
				<enabled>true</enabled>
			</snapshots>
		</pluginRepository>
		<pluginRepository>
			<id>spring-milestones</id>
			<name>Spring Milestones</name>
			<url>https://repo.spring.io/milestone</url>
			<snapshots>
				<enabled>false</enabled>
			</snapshots>
		</pluginRepository>
	</pluginRepositories>
	
</project>

2. 编写自定义注解类

package com.springbootssm.ssm.annotation;

import java.lang.annotation.*;

/**
 * 操作日志自定义注解
 */
@Target({ElementType.METHOD}) // 作用在方法上
@Retention(RetentionPolicy.RUNTIME) // 注解会在class字节码文件中存在,在运行时可以通过反射获取到
@Documented // 说明该注解将被包含在javadoc中
public @interface OperatingLogAnnotation {
    /**
     * 记录操作描述
     */
    String description() default "";

    /**
     * 增删改的数据的类型
     */
    Class<?> clazz();
}

  • @Target

@Target 说明了Annotation所修饰的对象范围

取值(ElementType)有:
    1.CONSTRUCTOR:用于描述构造器
    2.FIELD:用于描述域
    3.LOCAL_VARIABLE:用于描述局部变量
    4.METHOD:用于描述方法
    5.PACKAGE:用于描述包
    6.PARAMETER:用于描述参数
    7.TYPE:用于描述类、接口(包括注解类型) 或enum声明

  • @Retention

@Retention定义了该Annotation被保留的时间长短:某些Annotation仅出现在源代码中,而被编译器丢弃;而另一些却被编译在class文件中;编译在class文件中的Annotation可能会被虚拟机忽略,而另一些在class被装载时将被读取(请注意并不影响class的执行,因为Annotation与class在使用上是被分离的)。使用这个meta-Annotation可以对 Annotation的“生命周期”限制。
取值(RetentionPoicy)有:
    1.SOURCE:在源文件中有效(即源文件保留)
    2.CLASS:在class文件中有效(即class保留)
    3.RUNTIME:在运行时有效(即运行时保留)

  • @Documented

@Documented用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。Documented是一个标记注解,没有成员。

3. 编写注解类切面

扫描二维码关注公众号,回复: 4296280 查看本文章
package com.springbootssm.ssm.annotation;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

/**
 * 操作日志切面
 */
@Aspect
@Component
public class OperatingLogAspect {

    // 切入点签名
    @Pointcut("@annotation(com.springbootssm.ssm.annotation.OperatingLogAnnotation)")
    private void cut() {
    }

    // 前置通知
    @Before("cut()")
    public void BeforeCall() {
        System.out.println("====前置通知start");

        System.out.println("====前置通知end");
    }

    // 环绕通知
    @Around(value = "cut()")
    public Object AroundCall(ProceedingJoinPoint joinPoint)  throws Throwable {
        System.out.println("====环绕通知start");

        // 注解所切的方法所在类的全类名
        String typeName = joinPoint.getTarget().getClass().getName();
        System.out.println("目标对象:" + typeName);

        // 注解所切的方法名
        String methodName = joinPoint.getSignature().getName();
        System.out.println("所切方法名:" + methodName);

        StringBuilder sb = new StringBuilder();
        // 获取参数
        Object[] arguments = joinPoint.getArgs();
        for (Object argument : arguments) {
            sb.append(argument.toString());
        }
        System.out.println("所切方法入参:" + sb.toString());

        // 统计方法执行时间
        long start = System.currentTimeMillis();

        //执行目标方法,并获得对应方法的返回值
        Object result= joinPoint.proceed();
        System.out.println("返回结果:" + result);

        long end = System.currentTimeMillis();
        System.out.println("====执行方法共用时:" + (end - start));

        System.out.println("====环绕通知之结束");
        return result;
    }

    // 后置通知
    @After("cut()")
    public void AfterCall() {
        System.out.println("====后置通知start");

        System.out.println("====后置通知end");
    }

    // 最终通知
    @AfterReturning("cut()")
    public void AfterReturningCall() {
        System.out.println("====最终通知start");

        System.out.println("====最终通知end");
    }

    // 异常通知
    @AfterThrowing(value = "cut()", throwing="ex")
    public void afterThrowing(Throwable ex) {
        throw new RuntimeException(ex);
    }

}
  • @Aspect : 作用是把当前类标识为一个切面供容器读取

  • @component : 把普通pojo实例化到spring容器中,相当于配置文件中的

  • @Pointcut : 定义一个切点

  • @Before : 标识一个前置增强方法,相当于BeforeAdvice的功能

  • @Around : 环绕增强方法

  • @After :后置增强方法

4. 在controler层使用注解

package com.springbootssm.ssm.controller;

import com.springbootssm.ssm.annotation.OperatingLogAnnotation;
import com.springbootssm.ssm.domain.Student;
import com.springbootssm.ssm.service.StudentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
public class StudentController {
    @Autowired
    private StudentService studentService;

    @OperatingLogAnnotation(description = "获取所有用户信息", clazz = Student.class)
    @RequestMapping("/getAll")
    public List<Student> getAll(Student student){
        System.out.println("=====真正执行方法 ");
        return studentService.getAll();
    }
}

5. 打开浏览器,调用该接口,控制台输出

调用该接口:

http://localhost:8080/getAll?id=1&name=‘tom’

控制台输出:

====环绕通知start
目标对象:com.springbootssm.ssm.controller.StudentController
所切方法名:getAll
所切方法入参:Student{id=1, name=''tom'', age=null, sex='null'}
====前置通知start
====前置通知end
=====真正执行方法 
返回结果:[Student{id=1, name='Tom', age=13, sex='1'}, Student{id=2, name='jick', age=14, sex='1'}, Student{id=3, name='mery', age=15, sex='0'}]
====执行方法共用时:327
====环绕通知之结束
====后置通知start
====后置通知end
====最终通知start
====最终通知end

猜你喜欢

转载自blog.csdn.net/qidasheng2012/article/details/84340742