注解的理解、使用、原理,Java小白入门(三)

news2024/12/26 11:00:09

背景

  • 随便点开一些Java代码,发现在代码的注释下,有这样的符号 @Component , @Autowired ,@Override , 等等,这些符号从字面看也能估计出一点来含义,比如 @Override 方法是否为重写方法,但是这个@符号具体的语境是什么呢?虽然对于很多内容,可以不求甚解,但是要学会推测,如果我来实现这个应该怎么做,它的好处是什么?

啰嗦一句,可以先猜测,这个注解的含义,实现原理,然后查看相关文献,相关用法,以及自己如何做个性化的内容,大部分的技术学习都应该走类似的路线。

例子

  • 因为学java首先使用的框架是若依,所以打开其公共类库,如下图,一堆鲜活的例子出现在眼前:

  • 接着看代码,Excel比较亲切,打开文件,类名上就是@符号,可见这个注解符号,令人发指的程度,@Retention 百度为,注解的注解,元注解,直接蒙圈的感觉,继续分析一下代码
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Excel
{
    /**
     * 导出时在excel中排序
     */
    public int sort() default Integer.MAX_VALUE;

    /**
     * 导出到Excel中的名字.
     */
    public String name() default "";

    /**
     * 日期格式, 如: yyyy-MM-dd
     */
    public String dateFormat() default "";
  1. 貌似注解是一个接口,没有实现,只有声明
  2. 发现dateFormat()这个明显的特征,直接看在哪里用到,SysUser.java里面 的使用如下
/** 最后登录时间 */
@Excel(name = "最后登录时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss", type = Type.EXPORT)
private Date loginDate;

很容易猜想,导出Excel,做了些处理,比如这个字段的name是“最后登录时间” width是30,dateFormat格式是“yyyy-MM-dd HH:mm:ss”

  1. 哪个地方处理的呢? 在 public class ExcelUtil<T>文件中, 看到如下代码
String dateFormat = field.getAnnotation(Excel.class).dateFormat();
if (StringUtils.isNotEmpty(dateFormat))
{
    val = parseDateToStr(dateFormat, val);
}

字段找到注解为dateFormat,也就是某个类属性上面是否有这个注解,如果有这样的注解,就做个性化的动作,基本验证应该没有问题,接下来看官方的解释。

注解基础

注解作用

  • 作为特定标记,用于告诉编译器一些信息
  • 编译时动态处理,如动态生成代码
  • 运行时动态处理,作为额外信息的载体,如获取注解信息

基础接口

在Annotation的源码注释中有说明:所有的注解类型都需要继承该公共接口,本质上看注解是接口,但是代码并没有显式声明继承关系,可以直接查看字节码文件;

-- 1、声明注解
public@interface Excel {}

-- 2、查看指令
javap -v Excel.class

-- 3、打印结果
Compiled from "Excel.java"
public interface com.base.test.Excel extends java.lang.annotation.Annotation

元注解

声明注解时使用,用来定义注解的作用目标,保留策略等;

元注解

元注解就是用于定义注解的注解,通常用于注解的定义上,标明该注解的使用范围、生效范围等。元XX 都代表最基本最原始的东西,因此,元注解就是最基本不可分解的注解,我们不能去改变它只能使用它来定义自定义的注解

元注解包含以下五种:

  1. @Retention:注解的生命周期
  2. @Target:注解的作用目标
  3. @Inherited:是否允许子类继承该注解
  4. @Repeatabl:是否可以重复标注。
  5. @Documented:注解是否应当被包含在 JavaDoc 文档中
     

其中最常用的是@Retention和@Target下面分别介绍一下这五种元注解。

@Retention

中文翻译为保留的意思,标明自定义注解的生命周期

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
    RetentionPolicy value();
}

从编写Java代码到运行主要周期为源文件→ Class文件 → 运行时数据,@Retention则标注了自定义注解的信息要保留到哪个阶段,分别对应的value取值为SOURCE →CLASS→RUNTIME。

  • SOURCE 源代码java文件,注解编译期可见,生成的class文件中时丢弃
    一个最简单的用法,就是自定义一个注解例如@ThreadSafe,用来标识一个类时线程安全的,就和注释的作用一样,不过更引人注目罢了。
     
  • CLASS class文件中会保留注解,但是jvm加载运行时就没有了(类加载阶段丢弃)
    个人觉得主要是起到标记作用,还没有做实验,例如标记一个@Proxy,JVM加载时就会生成对应的代理类。
     
  • RUNTIME 运行时,如果想使用反射获取注解信息,则需要使用RUNTIME,反射是在运行阶段进行反射的。永久保存
    反射实在运行阶段执行的,那么只有Runtime的生命周期才会保留到运行阶段,才能被反射读取,也是我们最常用的。
     

@Target

中文翻译为目标,描述自定义注解的使用范围——作用的目标是谁。也就是指明,你的注解到底是用来修饰方法的?修饰类的?还是用来修饰字段属性的。

允许自定义注解标注在哪些Java元素上(类、方法、属性、局部属性、参数…)

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface MyAnnotation {
    String description() default "";
}

@MyAnnotation
public class AnnotationTest {
    // @MyAnnotation   用在属性上则会报错
    public String name;

    @MyAnnotation
    public void test(){}

}

value是一个数组,可以有多个取值,说明同一个注解可以同时用于标注在不同的元素上。value的取值如下

说明

TYPE

类、接口、注解、枚举

FIELD

属性

MEHOD

方法

PARAMETER

方法参数

CONSTRUCTOR

构造函数

LOCAL_VARIABLE

局部变量(如循环变量、catch参数)

ANNOTATION_TYPE

注解

PACKAGE

TYPE_PARAMETER

泛型参数 jdk1.8

TYPE_USE

任何元素 jdk1.8

@Inherited

是否可以被标注类的子类继承。被@Inherited修饰的注解是具有继承性的,在自定义的注解标注到某个类时,该类的子类会继承这个自定义注解。这里需要注意的是只有当子类继承父类的时候,注解才会被继承,类实现接口,或者接口继承接口,都是无法获得父接口上的注解声明的。正确的示例如下(通过反射获取注解)

@Repeatabl

是否可以重复标注。这个注解其实是一个语法糖,jdk1.8之前也是有办法进行重复标注的,就是使用数组属性(自定义注解会讲到)。

@Documented

是否在生成的JavaDoc文档中体现,被标注该注解后,生成的javadoc中,会包含该注解,这里就不做演示了。

注解原理

这里涉及到两个核心概念:反射机制、动态代理;反射机制可以在程序运行时获取类的完整结构信息,代理模式给目标对象提供一个代理对象,由代理对象持有目标对象的引用;网上搜到的例子:

先来看一个简单的注解使用案例,再细致的分析其中原理,案例并不复杂,就是常见的标注与解析两个关键动作;

publicclass LogInfo {
@SystemLog(model = "日志模块")
public static void main(String[] args) {
// 生成代理文件
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
// 反射机制
        Method[] methods = LogInfo.class.getMethods();
for (Method method:methods){
            SystemLog systemLog = method.getAnnotation(SystemLog.class) ;
if (systemLog != null){
// 动态代理:com.sun.proxy.$Proxy2
                System.out.println(systemLog.getClass().getName());
                System.out.println(systemLog.model());
            }
        }
    }
}

这里涉及到两个核心概念:反射机制、动态代理;反射机制可以在程序运行时获取类的完整结构信息,代理模式给目标对象提供一个代理对象,由代理对象持有目标对象的引用;

案例中通过反射机制,在程序运行时进行注解的获取和解析,值得关注的是systemLog对象的类名,输出的是代理类信息;

案例执行完毕后,会在代码工程的目录下生成代理类,可以查看$Proxy2文件;

publicfinalclass $Proxy2 extends Proxy implements SystemLog {
public final String model() throws  {
try {
return (String)super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
throw var2;
        } catch (Throwable var3) {
thrownew UndeclaredThrowableException(var3);
        }
    }
}

在对SystemLog解析的过程中,实际上是在使用注解的代理类,$Proxy2继承了Proxy类并实现了SystemLog接口,并且重写了相关方法;有关反射和代理的逻辑,在之前的内容中有详说,此处不赘述;

值得一看是代理类中invoke方法调用,具体的处理逻辑在AnnotationInvocationHandler类的invoke方法中,会对注解原生方法和自定义方法做判断,并对原生方法提供实现;

总结一下:

使用反射操作注解

反射可以获取到Class对象,进而获取到Constructor、Field、Method等实例,点开源码结构发现Class、Constructor、Field、Method等均实现了AnnotatedElement接口,AnnotatedElement接口的方法如下

// 判断该元素是否包含指定注解,包含则返回true
boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)
// 返回该元素上对应的注解,如果没有返回null
<T extends Annotation> T getAnnotation(Class<T> annotationClass);
// 返回该元素上的所有注解,如果没有任何注解则返回一个空数组
Annotation[] getAnnotations();
// 返回指定类型的注解,如果没有返回空数组
T[] getAnnotationsByType(Class<T> annotationClass)
// 返回指定类型的注解,如果没有返回空数组,只包含直接标注的注解,不包含inherited的注解
T getDeclaredAnnotation(Class<T> annotationClass)
// 返回指定类型的注解,如果没有返回空数组,只包含直接标注的注解,不包含inherited的注解
T[] getDeclaredAnnotationsByType
// 返回该元素上的所有注解,如果没有任何注解则返回一个空数组,只包含直接标注的注解,不包含inherited的注解
Annotation[] getDeclaredAnnotations();

这就说明以上元素均可以通过反射获取该元素上标注的注解。

注解的底层实现-动态代理

可以跟踪一下Excel。

复杂注解

重点是掌握标准注解,其一:理解和掌握标准注解;其二:在合适的场景写自定义注解。

1、JDK注解

在JDK中有多个注解是经常使用的,例如Override、Deprecated、SuppressWarnings等;

  • Override:判断方法是否为重写方法;
  • Deprecated:标记过时的API,继续使用会警告;
  • FunctionalInterface:检验是否为函数式接口;
  • SuppressWarnings:代码的警告会静默处理;

这里注意FunctionalInterface注解,从1.8开始引入,检验是否为函数式接口,即接口只能有一个抽象方法,否则编译报错;

2、Lombok注解

在具体的看Lombok组件之前,需要先了解一个概念:代码编译;在open-jdk的描述文档中大致分为三个核心阶段;

第一步:读取命令行上指定的所有源文件,解析为语法树,进行符号表填充;

第二步:调用注解处理器,如果处理器生成任何新的源文件或类文件,编译会重新启动;

第三步:分析器创建的语法树被分析并转换为类文件;

更多细节说明可以参考openjdk文档中Compiler模块的内容,下面再回到Lombok组件上;

Lombok组件在代码工程中的使用非常频繁,通过注解的方式极大的简化Java中Bean对象的编写,提高了效率并且让源码显得简洁;

这里用一段简单的代码演示其效果,在IdKey的类中通过三个常用的Lombok注解,替代了类中很多基础方法的显式生成,查看编译后的文件实际是存在相关方法的;

@Data
@AllArgsConstructor
@NoArgsConstructor
publicclass IdKey {
private Integer id ;
private String key ;

public static void main(String[] args) {
        IdKey idKey01 = new IdKey(1,"cicada") ;
        System.out.println(idKey01);
        idKey01.setId(2);
        idKey01.setKey("smile");
        System.out.println(idKey01);
    }
}

这里需要了解JDK中注解处理器的相关源码,AbstractProcessor作为超类,编译器在编译时会去检查该类的子类,子类中最核心的是process方法;

-- 1、Lombok处理器
@SupportedAnnotationTypes("*")
publicclass LombokProcessor extends AbstractProcessor {
private JavacTransformer transformer;
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
         transformer.transform(prio, javacProcessingEnv.getContext(), cusForThisRound, cleanup);
    }
}

-- 2、AST抽象树
publicclass JavacTransformer {
public void transform(long priority, Context context, List<JCTree.JCCompilationUnit> compilationUnits,
                          CleanupRegistry cleanup) {
        JavacAST ast = new JavacAST(messager, context, unit, cleanup);
        ast.traverse(new AnnotationVisitor(priority));
        handlers.callASTVisitors(ast, priority);
    }
}

-- 3、注解处理抽象类
publicabstractclass JavacAnnotationHandler<T extends Annotation> {
public abstract void handle(AnnotationValues<T> annotation, JCAnnotation ast, JavacNode annotationNode);
}

-- 4、Getter注解处理
publicclass HandleGetter extends JavacAnnotationHandler<Getter> {
@Override
public void handle(AnnotationValues<Getter> annotation, JCTree.JCAnnotation ast, JavacNode annotationNode) {
        JavacNode node = annotationNode.up();
        List<JCTree.JCAnnotation> onMethod = unboxAndRemoveAnnotationParameter(ast, "onMethod", "@Getter(onMethod", annotationNode);
switch (node.getKind()) {
case FIELD:
                createGetterForFields(level, fields, annotationNode, true, lazy, onMethod);
break;
        }
    }
}

IdKey类从简洁的源码编译为复杂的字节码文件,通过注解对结构处理时关联一个核心概念,叫AST抽象树,会涉及到很多语法、词法的解析逻辑;

常用注解

1、Spring Bean类的注解

1.1 @Component

通用的注解,可标注任意类为 Spring 组件。如果一个 Bean 不知道属于哪个层,可以使用@Component 注解标注。

1.2 @Repository

对应持久层即 Dao 层,主要用于数据库相关操作。

1.3 @Service

对应服务层,主要涉及一些复杂的逻辑,需要用到 Dao 层。

1.4 @Controller

对应 Spring MVC 控制层,主要用户接受用户请求并调用 Service 层返回数据给前端页面。

1.5@RestController

@RestController = @Controller + @ResponseBody

写这一个注解就相当于写了后面的两个注解,在返回值是json串的非常方便,但同时也会有一个问题,加了这个注解就不能返回jsp或者html页面,这时可以通过返回视图数据的方式来返回页面。

ModelAndView mv = new ModelAndView("index");

return mv;

1.6 @Configuration

一般用来声明配置类。

指示一个类声明一个或多个@Bean方法,并且可以由Spring容器处理,以便在运行时为这些bean生成BeanDefinition和服务请求。

@Configuration

public class AppConfig {

@Bean

public MyBean myBean() {

// instantiate, configure and return bean ...

}

}

注意:

1.@Configuration不可以是final类型;

2.@Configuration不可以是匿名类;

3.嵌套的configuration必须是静态类。

2、注入数据注解

2.1 @Autowired

自动按照类型注入,只要容器中有唯一一个 bean 对象类型和要注入的变量类型匹配,就可以注入成功。

如果有多个类型匹配,先匹配类型,然后根据变量名称去对应,如果变量名称和 bean 名称相同,就注入。

2.2 @Resource

byName就是变量名去匹配bean的id属性,而byType则是变量类型去匹配bean的class属性。

<bean id="userService" class="com.test.UserServiceImpl">

</bean>

@Autowired和@Resource的区别:

@Autowired注解是Spring提供的,而@Resource注解是J2EE本身提供的;

@Autowird注解默认通过byType方式注入,而@Resource注解默认通过byName方式注入;

@Autowired注解注入的对象需要在IOC容器中存在,否则需要加上属性required=false,表示忽略当 前要注入的bean,如果有直接注入,没有跳过,不会报错。

2.3 @Value

用于注入基本类型和 String 类型的数据。

3、请求常用注解

3.1 @RequestMapping

它用于映射客户端的访问地址,可以被应用于类和方法上面。

可以指定请求的类型get,post,put,delete

@Controller

@RequestMapping("/index")

public class HelloWorldController {

@RequestMapping(value = "/hello", method = RequestMethod.GET)

public String hello() {

return "/WEB-INF/views/success.jsp";

}

@RequestMapping(value = "/world", method = RequestMethod.POST)

public String world() {

return "/WEB-INF/views/success.jsp";

}

}

@RequestMapping 还可以将多个请求映射到一个方法上,只需要给 value 来指定一个包含多个路径的列表。

@Controller

@RequestMapping("/index")

public class HelloWorldController {

@RequestMapping(value = {"/hello", "/world", "/helloworld"})

public String hello() {

return "/WEB-INF/views/success.jsp";

}

}

3.2 @GetMapping

等价于@RequestMapping(value="",method=RequestMethod.GET)

3.3 @PostMapping

等价于@RequestMapping(value="",method=RequestMethod.POST)

3.4 @PutMapping(value="/users/{userId}")

等价于@RequestMapping(value="/users/{userId}",method=RequestMethod.PUT)

3.5 @DeleteMapping("/users/{userId}")

等价于@RequestMapping(value="/users/{userId}",method=RequestMethod.DELETE)

4、前后端传值

4.1 @PathVariable

用于获取路径参数,通过 @PathVariable 可以将 URL 中占位符参数绑定到控制器处理方法的入参中。

RequestMapping("user/get/mac/{macAddress}")

public String getByMacAddress(@PathVariable String macAddress){

//can do something;

}

4.2 @RequestParam

用于controller层,是Spring的注解,用于获取查询参数,解决前台参数名称与后台接收参数变量名称不一致的问题。

public String login(@RequestParam(value = "username") final String username,

@RequestParam(value = "password",required = false) final String password,

@RequestParam(value = "valcode",required = false) final String valcode) {

}

value:参数名字,即入参的请求参数名字,如username表示请求的参数区中的name为username的参数的值将传入;

required:是否必须,默认是true,表示请求中一定要有相应的参数,否则将报404错误码;

defaultValue:默认值,表示如果请求中没有同名参数时的默认值,默认值可以是SpEL表达式,如“#{systemProperties['java.vm.version']}”。

4.3 @Param

用于dao层,是mybatis中的注解。

使得mapper.xml中的参数与后台的参数对应上,也增强了可读性。

如果两者参数名一致得话,spring会自动进行封装,不一致的时候就需要手动去使其对应上。 即:用注解来简化xml配置的时候,@Param注解的作用是给参数命名,参数命名后就能根据名字得到参数值,正确的将参数传入sql语句中 。

List<Employee> getAllEmployeeByPage(@Param("page") Integer page,

@Param("size") Integer size);

5、表相关注解

5.1 @Entity

声明一个类对应一个数据库实体。

@Entity说明这个class是实体类,并且使用默认的orm规则,即class名就是数据库表中表明,class字段名即表中字段名。@Entity注解指明这是一个实体Bean。在项目启动时会根据该类自动生成一张表,表的名称即@Entity注解中name的值,如果不配置name,默认表明为类名所有的实体类都要有主键,@Id注解表示该属性是一个主键,@GeneratedValue注解表示注解自动生成,strategy则表示主键的生成策略。

@Data

@Entity(name = "t_book")

public class Book {

@Id

@GeneratedValue(strategy = GenerationType.IDENTITY)

private Integer id;

@Column(name = "book_name")

private String name;

@Column(name = "book_author")

private String author;

private Float price;

@Transient

private String description;

}

5.2 @Table

@Table注解默认情况下只会完成表和实体之间的映射,声明才对象映射到数据库的数据表,通过它可以为实体指定表(table)。

常用属性:name 指定表 @Table(name = "book")

@Data

@Table(name="book")

public class Book{

@Id

private Integer id;

@Column(name="book_name")

private String name;

...

}

5.3 @Id

声明一个字段为主键。所有的实体类都要有主键,@Id注解表示该属性是一个主键。

5.4 @Column

声明字段,可以设置字段的属性,字段名、是否为空。

生成的表中字段的名称就是实体类中属性的名称,通过@Column注解可以定制生成的字段属性,name表示该属性对应的数据表中字段的名称,nullable表示该字段非空。

5.5 @Transient

声明不需要与数据库映射的字段,在保存的时候不需要保存进数据库。

@Transient注解表示在生成数据库中的表时,该属性被忽略,即不生成对应的字段。

6、事务注解@Transactional

@Transactional(rollbackFor = Exception.class) //这里回滚进行定义

public int update(Prdtv prdtv) throws RuntimeException{

//注意在这里处理业务时,不要使用Try ...异常捕获,否则不回滚

return prdtvMapper.update(prdtv);

}

@Transactional:事务注解,注解一般用在可以作用在类或者方法上

1.类上,表明类中所有 public 方法都启用事务 2.方法上,最常用的一种 3.接口上,不推荐使用

@Transactional注解只能在抛出RuntimeException或者Error时才会触发事务的回滚,常见的非RuntimeException是不会触发事务的回滚的。但是我们平时做业务处理时,需要捕获异常,所以可以手动抛出RuntimeException异常或者添加rollbackFor = Exception.class(也可以指定相应异常)

事务的隔离级别:是指若干个并发的事务之间的隔离程度

1. @Transactional(isolation = Isolation.READ_UNCOMMITTED):读取未提交数据(会出现脏读,

不可重复读) 基本不使用

2. @Transactional(isolation = Isolation.READ_COMMITTED):读取已提交数据(会出现不可重复读和幻读)

3. @Transactional(isolation = Isolation.REPEATABLE_READ):可重复读(会出现幻读)

4. @Transactional(isolation = Isolation.SERIALIZABLE):串行化

7、AOP注解

7.1 @After 在方法执行之后执行(方法上)

@After("pt()")

public void after(){

}

7.2 @Before 在方法执行之前执行(方法上)

@Before("pt()")

public void before(){

}

7.3 @Around 在方法执行之前与之后执行(方法上)

@Around("pt()")

public Object around(ProceedingJoinPoint pjp) throws Throwable {

Object ret = pjp.proceed();

return ret;

}

7.4 @AfterReturning当前方法作为返回后通知

@AfterReturning(value="pt()",returning = "ret")

public void afterReturning(Object ret) {

}

7.5 @AfterThrowing当前方法作为异常后通知

@AfterThrowing(value="pt()",throwing = "t")

public void afterThrowing(Throwable t){

}

@Aspect:声明一个切面

@PointCut:声明切点

@EnableAspectJAutoProxy:开启Spring对AspectJ代理的支持

8、Lombok

8.1 @Setter

注解在属性上。为属性提供 setting 方法。

8.2 @Getter

注解在属性上。为属性[欢迎转载听雨的人博客]提供 getting 方法。

8.3 @Data

注解在类上。等同于添加如下注解: @Getter/@Setter生成get,set方法 @ToString 生成toString方法 @EqualsAndHashCode 生成equals和hashcode方法 @RequiredArgsConstructor 生成一个指定名称的静态方法

public class Programmer{

@Getter

@Setter

private String name;

@Setter(AccessLevel.PROTECTED)

private int age;

@Getter(AccessLevel.PUBLIC)

private String language;

}

相当于:

public class Programmer{

private String name;

private int age;

private String language;

public void setName(String name){

this.name = name;

}

public String getName(){

return name;

}

protected void setAge(int age){

this.age = age;

}

public String getLanguage(){

return language;

}

}

9、MyBatis中常用注解

9.1 @Insert(sql语句):实现新增

public interface UserDAO{

@Options(useGeneratedKeys = true,keyColumn = "uid", keyProperty = "id")

@Insert("insert into t_user(username,password,gender,birth) values(#{username},#{password},#{gender},#{birth})")

public void insertUser(User user);

}

设置@Options属性userGeneratedKeys的值为true,并指定实例对象中主键的属性名keyProperty=“id”,以及在数据库中的字段名keyColumn=“uid”。这样在user插入数据后,userId属性会被自动赋值

9.2 @Update(sql语句):实现更新

public interface UserDAO{

@Update("update t_user set username=#{usernmae},password=#{password}")

public void updateUser(User user);

}

9.3 @Delete(sql语句):实现删除

public interface UserDAO{

@Delete("delete from t_user where id = #{id}")

public void deleteUserById(@Param("id") int id);

}

9.4 @Select(sql语句):实现查询

public interface UserDAO{

@Select("select * from t_user where id = #{id} and username = {#username}")

public User queryUserbyId(@Param("id") int id,@Param("username") String username);

}

其他MyBatis注解:

@Result(property="实体类属性的名称",jdbcType=数据库字段类型):实现结果集封装

@Results(column="数据库字段名",porperty="实体类属性名",jdbcType="数据库字段数据类型"):封装多个结果集(当数据库字段名与实体类对应的属性名不一致时,可以使用@Results映射来将其对应起来)

@ResultMap(@Results的id值):实现引用@Results 定义的封装

@One:实现一对一结果集封装

@Many:实现一对多结果集封装

@CacheNamespace:实现注解二级缓存的使用

10、其他

1.@Scope注解默认的singleton单例模式。

@Scope注解是springIoc容器中的一个作用域,在 Spring IoC 容器中具有以下几种作用域:基本作用域singleton(单例)、prototype(多例),Web 作用域(reqeust、session、globalsession),自定义作用域。

2.Enable注解说明*

@EnableAsync: 开启异步方法的支持。

@EnableScheduling: 开启计划任务的支持。

@EnableAspectAutoProxy:开启对AspectJ自动代理的支持

@EnableAsync:开启异步方法的支持

@EnableScheduling:开启计划任务的支持

@EnableWebMvc:开启web MVC的配置支持

@EnableConfigurationProperties:开启对@ConfigurationProperties注解配置Bean的支持

@EnableJpaRepositories:开启对SpringData JPA Repository的支持

@EnableTransactionManagement:开启注解式事务的支持

@EnableCaching:开启注解式的缓存支持

参考复制材料

Java的一些常用注解及其作用_java注解用法和作用-CSDN博客

百度安全验证

Java注解(批注)的基本原理 - 知乎

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1333344.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++ 之LeetCode刷题记录(二)

&#x1f604;&#x1f60a;&#x1f606;&#x1f603;&#x1f604;&#x1f60a;&#x1f606;&#x1f603; 从今天开始cpp刷题之旅&#xff0c;多学多练&#xff0c;尽力而为。 先易后难&#xff0c;先刷简单的。 9、回文数 给你一个整数 x &#xff0c;如果 x 是一个…

nacos配置中心配置已经常见错误总结

&#x1f4bb;目录 前言1、基础架构2、依赖3、配置文件3.1、bolg-product配置文件3.1.1、application.yml配置文件3.1.2、bootstrap.yml配置文件3.1.3、nacos远程配置 3.2、bolg-system3.1.1、application.yml配置文件3.1.2、bootstrap.yml配置文件3.2.3、nacos远程配置 4、测试…

饥荒Mod 开发(二二):显示物品信息

饥荒Mod 开发(二一)&#xff1a;超大便携背包&#xff0c;超大物品栏&#xff0c;永久保鲜 饥荒中的物品没有详细信息&#xff0c;基本上只有一个名字&#xff0c;所以很多物品的功能都不知道&#xff0c;比如浆果吃了也不知道恢复什么&#xff0c; 采集的胡萝卜也不知道什么功…

DataProcess-VOC数据图像和标签一起进行Resize

VOC数据图像和标签一起进行Resize 参加检测比赛的时候&#xff0c;很多时候工业原始数据尺度都比较大&#xff0c;如果对数据不提前进行处理&#xff0c;会导致数据在加载进内存时花费大量的时间&#xff0c;所以在执行训练程序之前需要将图像提前进行预处理。对于目标检测的数…

Log4net 教程

一、Log4net 教程 在CodeProject上找到一篇关于Log4net的教程&#xff1a;log4net Tutorial&#xff0c;这篇博客的作者是&#xff1a;Tim Corey &#xff0c;对应源代码地址为&#xff1a; https://github.com/TimCorey/Log4netTutorial&#xff0c;视频地址为&#xff1a;Ap…

案例144:基于微信小程序的自修室预约系统

文末获取源码 开发语言&#xff1a;Java 框架&#xff1a;SSM JDK版本&#xff1a;JDK1.8 数据库&#xff1a;mysql 5.7 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.5.4 小程序框架&#xff1a;uniapp 小程序开发软件&#xff1a;HBuilder X 小程序…

C/C++图形化编程(2)

归纳编程学习的感悟&#xff0c; 记录奋斗路上的点滴&#xff0c; 希望能帮到一样刻苦的你&#xff01; 如有不足欢迎指正&#xff01; 共同学习交流&#xff01; &#x1f30e;欢迎各位→点赞 &#x1f44d; 收藏⭐ 留言​&#x1f4dd; 站在巨人的肩上是为了超过巨人&#x…

esp32使用lvgl,给图片取模显示图片

使用LVGL官方工具。 https://lvgl.io/tools/imageconverter 上传图片&#xff0c;如果想要透明效果&#xff0c;那么选择 输出格式C array&#xff0c;点击Convert进行转换。 下载.c文件放置到工程下使用即可。

Py之tensorflow-addons:tensorflow-addons的简介、安装、使用方法之详细攻略

Py之tensorflow-addons&#xff1a;tensorflow-addons的简介、安装、使用方法之详细攻略 目录 tensorflow-addons的简介 tensorflow-addons的安装 tensorflow-addons的使用方法 1、使用 TensorFlow Addons 中的功能&#xff1a; tensorflow-addons的简介 TensorFlow Addon…

本地搜索文件太慢怎么办?用Everything搜索秒出结果(附安装包)

每次用电脑本地的搜索都慢的一批&#xff0c;后来发现了一个搜索利器 基本上搜索任何文件都不用等待。 并且页面非常简洁&#xff0c;也没有任何广告&#xff0c;用起来非常舒服。 软件官网如下&#xff1a; voidtools 官网提供三个版本&#xff0c;用起来差别不大。 网盘链…

Javacv-利用Netty实现推流直播复用(flv)

前言 上一篇文章《JavaCV之rtmp推流&#xff08;FLV和M3U8&#xff09;》介绍了javacv的基本使用&#xff0c;今天来讲讲如何实现推流复用。 以监控摄像头的直播为例&#xff0c;通常分为三步&#xff1a; 从设备获取音视频流利用javacv进行解码&#xff08;例如flv或m3u8&am…

(2021|CoRR,AugCLIP,优化)FuseDream:通过改进的 CLIP+GAN 空间优化实现免训练文本到图像生成

FuseDream: Training-Free Text-to-Image Generation with Improved CLIPGAN Space Optimization 公众&#xff1a;EDPJ&#xff08;添加 VX&#xff1a;CV_EDPJ 或直接进 Q 交流群&#xff1a;922230617 获取资料&#xff09; 目录 0. 摘要 1. 简介 2. CLIPGAN 文本到图…

如何使用kali来进行一次ddos攻击

本文章用于记录自己的学习路线&#xff0c;不用于其他任何途径! ! ! 哈喽啊&#xff01;又是好久不见&#xff0c;本博主在之前发过一个ddos攻击的介绍。 emm…虽然那篇文章也提到了ddos攻击的方式&#xff0c;但太过于简陋&#xff0c;好像也没有什么用&#xff0c;so&#…

金蝶云星空权限项表结构

文章目录 金蝶云星空权限项表结构BOS平台【权限项】MSSQL脚本使用场景优点减少手工一个个创建的人工成本&#xff0c;还容易出错保留内码&#xff0c;可以在代码层级使用&#xff0c;方便 金蝶云星空权限项表结构 BOS平台【权限项】 MSSQL脚本 --权限项主表 SELECT * FROM db…

快速学习 webpack

目录 1. webpack基本概念 webpack能做什么&#xff1f; 2. webpack的使用步骤 2.1_webpack 更新打包 3. webpack的配置 3.1_打包流程图 3.2_案例-webpack隔行变色 3.3_插件-自动生成html文件 3.4_加载器 - 处理css文件问题 3.5_加载器 - 处理css文件 3.6_加载器 - 处…

大数据----基于sogou.500w.utf8数据的MapReduce编程

目录 一、前言二、准备数据三、编程实现3.1、统计出搜索过包含有“仙剑奇侠传”内容的UID及搜索关键字记录3.2、统计rank<3并且order>2的所有UID及数量3.3、上午7-9点之间&#xff0c;搜索过“赶集网”的用户UID3.4、通过Rank&#xff1a;点击排名 对数据进行排序 四、参…

jQuery: 整理4---创建元素和添加元素

1.创建元素&#xff1a;$("内容") const p "<p>这是一个p标签</p>" console.log(p)console.log($(p)) 2. 添加元素 2.1 前追加子元素 1. 指定元素.prepend(内容) -> 在指定元素的内部的最前面追加内容&#xff0c;内容可以是字符串、…

代码随想录算法训练营 | day60 单调栈 84.柱状图中最大的矩形

刷题 84.柱状图中最大的矩形 题目链接 | 文章讲解 | 视频讲解 题目&#xff1a;给定 n 个非负整数&#xff0c;用来表示柱状图中各个柱子的高度。每个柱子彼此相邻&#xff0c;且宽度为 1 。 求在该柱状图中&#xff0c;能够勾勒出来的矩形的最大面积。 1 < heights.len…

动态规划系列 | 最长上升子序列模型(上)

文章目录 最长上升子序列回顾题目描述问题分析程序代码复杂度分析 怪盗基德的滑翔翼题目描述输入格式输出格式 问题分析程序代码复杂度分析 登山题目描述输入格式输出格式 问题分析程序代码复杂度分析 合唱队形题目描述输入格式输出格式 问题分析程序代码复杂度分析 友好城市题…

基于Java SSM框架实现医院挂号上班打卡系统项目【项目源码+论文说明】计算机毕业设计

基于java的SSM框架实现医院挂号上班打卡系统演示 摘要 在网络发展的时代&#xff0c;国家对人们的健康越来越重视&#xff0c;医院的医疗设备更加先进&#xff0c;医生的医术、服务水平也不断在提高&#xff0c;给用户带来了很大的选择余地&#xff0c;而且人们越来越追求更个…