Java基础 - 底层了解

Java基础 - 底层了解

1 Junit单元测试

Junit 单元测试框架

  • Junit 是使用Java语言实现的单元测试框架,它是开源的,Java开发者都应当学习学习使用Junit编写单元测试
  • 此外,几乎所有的IDE工具都集成了Junit,这样就可以直接在IDE中编写并运行Junit测试

Junit优点

  • Junit可以灵活的选择执行那些测试方法,可以一键执行全部测试方法
  • Junit可以生成全部方法的测试报告
  • 单元测试中的某个方法测试失败了,不会影响其他测试方法的测试

单元测试快速入门

  • 将Junit 的 jar包导入到项目中

    • IDEA 通常整合好了 Junit框架,一般不需要导入
    • 如果IDEA没有整合好,需要自己手工导入如下2个Junit的jar包到模块
  • 编写测试方法:该测试方法必须是公共的无参数无返回值的非静态方法

  • 在测试方法上使用@Test 注解:标注该方法是一个测试方法

  • 在测试方法中完成被测试方法的预期正确性测试

  • 选中测试方法,选择”Junit运行“,如果测试良好则是绿色;如果测试失败,则是红色

Junit常用注解(Junit 4.xxxx版本)

注解说明
@Test测试方法
@Before用来修饰实例方法,该方法会在每一个测试方法执行之前执行一次
@After用来修饰实例方法,该方法会在每一个测试方法执行之后执行一次
@BeforeClass用来修饰静态方法,该方法会在所有测试方法之前,只执行一次
@AfterClass用来修饰静态方法,该方法会在所有测试方法之后,只执行一次
  • 开始执行的方法:初始化资源
  • 执行完之后的方法:释放资源
public class TestUserService {
    //修饰实例方法 Before , After
    @Before
    public void before(){
        System.out.println("before");
    }

    @After
    public void after(){
        System.out.println("after");
    }

    //修改静态方法
    @BeforeClass
    public static void beforeClass(){
        System.out.println("BeforeClass");
    }

    @AfterClass
    public static void afterClass(){
        System.out.println("AfterClass");
    }
    /**
        测试方法:
              1;必须是公开的,无参数,无返回值
              2:测试方法必须是 @Test注解标记
     */
    @Test
    public void testLoginName(){
        UserService user = new UserService();
        String msg = user.loginName("admin", "123456");

        //进行预期结果的正确性测试:断言
        /**
             org.junit.ComparisonFailure: 您的登录业务可能出现问题
         */
        Assert.assertEquals("您的登录业务可能出现问题","登陆成功" , msg);

    }

    @Test
    /**
          java.lang.ArithmeticException: / by zero
     */
    public void testSelectUsers(){
        UserService user = new UserService();
        user.selectUsers();
    }
}
public class UserService {
    /**
     * 匹配 账户名 与 密码
     * @param loginName 账户名
     * @param passWord  密码
     * @return  登录信息
     */
    public String loginName(String loginName , String passWord){
        if ("admin".equals(loginName) && "123456".equals(passWord)){
            return "登陆成功" ;
        }else {
            return "您输入有误,请重新输入";
        }
    }
    public void selectUsers(){
        System.out.println(10 / 0);
        System.out.println("查询到所有用户");
    }
}
image-20240211144731143
2 反射
  • 反射:加载类,并允许 以编程的方式解剖类中的各种成分(成员变量、方法、构造器等),常用于框架

  • 反射就是把java类中的各种成分映射成一个个的Java对象

    • 例如:一个类有:成员变量、方法、构造方法、包等等信息,利用反射技术可以对一个类进行解剖,把个个组成部分映射成一个个对象。
  • 加载过程:Class对象 的 由来是将class文件读入内存,并为之创建一个Class对象

image-20240211170703298

第一步:获取字节码对象

image-20240211171246441
public class StudentTest {
    public static void main(String[] args) throws Exception{
        //1.类名.class
        Class c1 = Student.class;
        System.out.println(c1.getName());      //edu.ecjtu.d2_reflect.Student
        System.out.println(c1.getSimpleName());//Student
        //2.Class 静态方法forName 包名.类名
        Class c2 = Class.forName("edu.ecjtu.d2_reflect.Student");   
        System.out.println(c1 == c2);                               //true
		//3.对象.class
        Student s = new Student();
        Class c3 = s.getClass();
        System.out.println(c3 == c2);								//true
    }
}
  • 在运行期间,一个类,只有一个Class对象产生。
2.1 Class的常用API
//获取包名、类名
clazz.getPackage().getName()//包名
clazz.getSimpleName()//类名
clazz.getName()//完整类名
 
//获取构造方法定义信息
getConstructor(参数类型列表)//获取公开的构造方法
getConstructors()//获取所有的公开的构造方法
getDeclaredConstructors()//获取所有的构造方法,包括私有
getDeclaredConstructor(int.class,String.class)
    
//获取成员变量定义信息
getFields()//获取所有公开的成员变量,包括继承变量
getDeclaredFields()//获取本类定义的成员变量,包括私有,但不包括继承的变量
getField(变量名)
getDeclaredField(变量名)
 
//获取方法定义信息
getMethods()//获取所有可见的方法,包括继承的方法
getMethod(方法名,参数类型列表)
getDeclaredMethods()//获取本类定义的的方法,包括私有,不包括继承的方法
getDeclaredMethod(方法名,int.class,String.class)
 
//反射新建实例
clazz.newInstance();//执行无参构造创建对象
clazz.newInstance(222,"韦小宝");//执行有参构造创建对象
clazz.getConstructor(int.class,String.class)//获取构造方法
 
//反射调用成员变量
clazz.getDeclaredField(变量名);//获取变量
clazz.setAccessible(true);//使私有成员允许访问
f.set(实例,);//为指定实例的变量赋值,静态变量,第一参数给null
f.get(实例);//访问指定实例变量的值,静态变量,第一参数给null

//反射调用成员方法
Method m = Clazz.getDeclaredMethod(方法名,参数类型列表);
m.setAccessible(true);//使私有方法允许被调用
m.invoke(实例,参数数据);//让指定实例来执行该方法

类的构造器

  • 获取类的构造器,并对其进行操作

    • Constructor<?>[] getConstructors() 获取全部构造器(只能是public修饰的)
    • Constructor<?>[] getDeclaredConstructors() 获取全部构造器(只要存在就能拿到)
    • Constructor<?> getConstructor(Class < ?> … parameterTypes) 获取某个构造器(public 修饰)
    • Constructor<?> getDeclaredConstructor(Class < ?> … parameterTypes) 获取某个构造器(只要存在就能拿到)
  • 获取类构造器的作用:依然是初始化对象并返回

    • T newInstance (Object … initargs) 调用此构造器对象表示的构造器,并传入参数,完成对想的初始化并返回
    • public void setAccessible(boolean flag) 设置为 true ,表示禁止检查访问控制(暴力反射)
//省略成员变量setXxx和getXxx
public class Cat {
    public static char sex = '雄';
    public static final int COUNT = 2;
    private String name;
    private int age;

    public Cat(){
        System.out.println("咖啡猫来了~~~");
    }

    public Cat(String name, int age) {
        this.name = name;
        this.age = age;
    }

    private String sleep(){
        return "💤";
    }

    private void run(){
        System.out.println("🐱喜欢跑步");
    }

    private void eat(){
        System.out.println("🐱喜欢吃猫粮");
    }
    private void eat(String food){
        System.out.println("🐱喜欢吃"+food);
    }

    @Override
    public String toString() {
        return "Cat{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
public class ConstructorsTest {
    @Test
    public void testConstructors(){
        //1.第一步:获取类的Class对象   !!!!
        Class c = Cat.class;
        //2.获取全部构造器(public)
//        Constructor[] constructors = c.getConstructors();
        //2.获取全部构造器(任意)
        Constructor[] constructors = c.getDeclaredConstructors();
        //3.遍历获取每个构造器
        for (Constructor constructor : constructors) {
            System.out.println(constructor.getName() + "------>"
                    + constructor.getParameterCount());
        }
    }

    @Test
    public void testConstructor() throws Exception{
        //1.获取类的Class对象
        Class c = Cat.class;
        //2.获取某个构造器(public)
        Constructor<Cat> constructor = c.getConstructor(String.class,int.class);
        System.out.println(constructor.getName() + "------------>"
                + constructor.getParameterCount());
        constructor.setAccessible(true);//禁止检查访问控制
        //获取对象
        Cat ci = constructor.newInstance("咖啡猫",2);
        System.out.println(ci);

        //3.获取任意的某个构造器
        Constructor constructor1 = c.getDeclaredConstructor();
        System.out.println(constructor1.getName() + "------------>"
                + constructor1.getParameterCount());
        constructor1.setAccessible(true);
        Cat ci1 = (Cat) constructor1.newInstance();
    }
}
  • 一般先 构造器 setAccessible(true) 有访问权限,然后 newInstance(…)创建对象

类的成员变量

  • 获取类的成员变量

    • public Field[] getFields() 获取类的全部成员变量(public )
    • public Field[] getDeclaredFields() 获取类的全部成员变量(只要能存在就能拿到)
    • public Field getField(String name “成员变量名”) 获取类的某个成员变量(public )
    • public Field getDeclaredField(String name) 获取 类的某个成员变量(任意)
  • 获取到成员变量的作用:依然是赋值、取值

    • void set(Object obj,Object value) 赋值
    • Object get(Object obj) 取值
    • public void setAccessible(boolean flag) 设置为true,表示禁止检查访问控制(暴力反射)
public class FieldTest {
    @Test
    public void testField() throws Exception{
        //1.定义类的Class对象
        Class c = Cat.class;
        //2.获取类的成员变量(public)
//        Field[] fields = c.getFields();
        //获取任意的成员变量
        Field[] fields = c.getDeclaredFields();
        //3.遍历
        for (Field field : fields) {
            System.out.println(field.getName() + "--->"
                    + field.getType());
        }
        //4.定位某个成员变量
        Field fName = c.getDeclaredField("name");
        System.out.println(fName.getName() + "---->"
                + fName.getType());
        Field fAge  = c.getDeclaredField("age");
        System.out.println(fAge.getName() + "---->"
                + fAge.getType());
        //5.赋值
        Cat c1 = new Cat();
        fName.setAccessible(true); //禁止访问控制权限
        fName.set(c1,"咖啡猫");
        System.out.println(c1);
        //6.取值
        fAge.setAccessible(true);
        System.out.println(fName.get(c1));
        System.out.println(fAge.get(c1));   //默认值 0
    }
}

运行结果

sex—>char
COUNT—>int
name—>class java.lang.String
age—>int
name---->class java.lang.String
age---->int
咖啡猫来了~~~
Cat{name=‘咖啡猫’, age=0}
咖啡猫
0

类的成员方法

  • 获取成员方法
    • Method[] getMethods() 获取类的全部成员方法(public )
    • Method[] getDeclaredMethods() 获取类的全部成员方法(任意)
    • Method getMethod(String name ,Class<?> … parameterTypes) 获取类的某个成员方法(public )
    • Method getDeclaredMethod(String name,Class<?> … parameterTypes)获取类的某个成员方法(任意)
  • 成员方法的作用:依然是执行
    • public Object invoke(Object obj 实体类对象 ,Object … args 传参) 触发某个对象的该方法执行
    • public void setAccessible(boolean flag) 设置为true ,表示禁止检查访问控制(暴力反射)
public class MethodTest {
    @Test
    public void testMethod() throws Exception{
        //1.定义类的Class对象
        Class c = Cat.class;
        //2.获取所有的方法(public)
//        Method[] methods = c.getMethods();
        Method[] methods = c.getDeclaredMethods();
        for (Method method : methods) {
            System.out.println(method.getName() + "---->" +
                    method.getParameterCount() +
                    "---->" + method.getReturnType());
        }
        //3.获取某个方法
        Method method = c.getDeclaredMethod("eat",String.class);
        System.out.println(method.getName() + "---->" +
                method.getParameterCount() +
                "---->" + method.getReturnType());
        //4.运行
        Cat cat = new Cat();
        method.setAccessible(true);
        method.invoke(cat,"🐟");

        Method sleep = c.getDeclaredMethod("sleep");
        sleep.setAccessible(true);
        Object invoke = sleep.invoke(cat);
        System.out.println(invoke);
    }
}

运行结果:

eat---->0---->void
eat---->1---->void
setAge---->1---->void
getAge---->0---->int
run---->0---->void
toString---->0---->class java.lang.String
getName---->0---->class java.lang.String
setName---->1---->void
sleep---->0---->class java.lang.String
eat---->1---->void
咖啡猫来了~~~
🐱喜欢吃🐟
💤

2.2 自定义"框架"

利用框架存储对象的成员变量

public class ObjectSaveFrame {
    //接收 实体类对象
    public static void objectSave(Object obj) throws Exception {
        //打印流
        PrintStream ps = new PrintStream(new FileOutputStream("src/data.txt",true));
        //1.获取类的Class对象
        Class c = obj.getClass();
        String name = c.getSimpleName();
        ps.println("--------------"+name+"--------------");//打印
        //2.遍历成员变量
        Field[] fields = c.getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            String fName = field.getName();
            String s = field.get(obj)+"";//取值
            ps.println(fName+"="+s);     //打印
        }
    }
}
//测试
public class TestFrame {
    @Test
    public void testObjectSave() throws Exception {
        //目标 : 利用框架存储对象的成员变量
        Student s = new Student("小王",21,'男',180.5,"足球,篮球");//String name, int age, char sex, double height, String hobbies
        Teacher t = new Teacher("刘老师",5000.0);//String name, double salary

        ObjectSaveFrame.objectSave(s);
        ObjectSaveFrame.objectSave(t);
    }
}
image-20240211175708248
3 注解Annotation
  • Java代码里的特殊标记,比如:@Override@Test等,作用:让其他程序根据注解信息来决定怎么执行该程序
  • 注意:注解可以用在类上、构造器上、方法上、成员变量上、参数上等位置处
  • 注解本质上是一个接口,Java中所有注解都是继承了Annotation接口的
  • @注解(…):其实就是一个实现类对象,实现了该注解以及Annotation接口

注解的原理

注解本质是一个继承了Annotation的特殊接口,其具体实现类是Java运行时生成的动态代理类。而我们通过反射获取注解时,返回的是Java运行时生成的动态代理对象$Proxy1。通过代理对象调用自定义注解(接口)的方法,会最终调用AnnotationInvocationHandler的invoke方法。该方法会从memberValues这个Map中索引出对应的值。

而memberValues的来源是Java常量池
这个运行时生成的动态代理对象是可以导出到文件的,方法有两种
在代码中加入System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
在运行时加入jvm 参数 -Dsun.misc.ProxyGenerator.saveGeneratedFiles=true

原文链接:

image-20240213101934664

注解的分类

  • 1 元注解修饰注解的注解,元注解是一种基本注解,但是它能够应用到其他的注解上面(给其他普通注解进行解释)@Retention@Documented@Target@Inherited@Repeatable 5 种

    • 1.@Target 声明被修饰的注解只能在哪些位置使用,@Target(ElementType. TYPE)

      • TYPE,类、接口和枚举
      • FIELD,成员变量
      • METHOD,成员方法
      • PARAMETER,方法参数
      • CONSTRUCTOR,构造器
      • LOCAL_VARIABLE,局部变量
      • PACKAGE 可以给一个包进行注解
      • ANNOTATION_TYPE 可以给一个注解进行注解
    • 2.@Retention 声明注解的保留周期,@Retention(RetentionPolicy.RUNTIME)

      • 1.SOURCE 只作用在源码阶段,在编译器进行编译时它将被丢弃忽视 ,字节码文件中不存在
      • 2.CLASS**(默认值)**保留到字节码文件阶段,它并不会被加载到 JVM 中,运行阶段不存在
      • 3.RUNTIME**(开发常用)**一直保留到运行阶段,被加载到JVM,程序运行时可以获取它们
    • 3.@Documented 该注解和文档有关,它的作用是能够将注解中的元素包含到 Javadoc中去

    • 4.@Inherited 它并不是说注解本身可以继承,而是说如果一个超类被 @Inherited 注解过的注解 进行注解的话,那么如果它的子类没有被任何注解应用的话,那么这个子类就继承了超类的注解

      • 注解A被@Inherited修饰,A修饰超类,B继承超类且没有其他注解,则B继承了超类的注解
    • 5.@Repeatable 是 JDK1.8中新增的一种注解,作用:允许是同一个程序元素多次使用相同的注解。

      通过使用 @Repeatable 注解,我们可以在一个程序元素上多次使用相同的注解。

      在使用 @Repeatable 注解时,我们需要同时提供一个容器注解(container annotation)来封装可重复注解的多个实例

      • 一些约束:
      • @Repeatable 所声明的注解,其元注解@Target的使用范围要比@Repeatable的值声明的注解中的@Target的范围要大或相同,否则编译器错误,显示@Repeatable值所声明的注解的元注解@Target不是@Repeatable声明的注解的@Target的子集
      • @Repeatable注解声明的注解的元注解@Retention的周期要比@Repeatable的值指向的注解的@Retention得周期要小或相同
      @Target(ElementType.METHOD)
      @Retention(RetentionPolicy.RUNTIME)
      @Repeatable(Roles.class)
      public @interface Role {
          String name();
      }
      
      @Target(ElementType.METHOD)
      @Retention(RetentionPolicy.RUNTIME)
      public @interface Roles {
          Role[] value();
      }
      
      public class RoleClass {
          @Role(name = "李明")
          @Role(name = "振华")
          public void test(){
          }
          
          @Roles(value = {@Role(name = "老王"),@Role(name = "老刘")})
          public void test1(){
      
          }
      }
      
      public class RoleTest {
          @SuppressWarnings("all")   //内置注解,抑制编译器警告
          public static void main(String[] args) {
              Method[] methods = RoleClass.class.getDeclaredMethods();
              try {
                  for (Method method : methods) {
                      Annotation[] annotations = method.getDeclaredAnnotations();
                      for (Annotation annotation : annotations) {
                          System.out.println(method.getName()+"---->注解:"+annotation);
                      }
                  }
                  System.out.println(1/0);   //编译器 没有"黄色"⚠
              }catch (Exception e){
                  e.printStackTrace();
              }
          }
      }
      
      

      结果:

      test---->注解:@edu.ecjtu.d3_annotation.Roles(value=[@edu.ecjtu.d3_annotation.Role(name=李明), @edu.ecjtu.d3_annotation.Role(name=振华)])
      test1---->注解:@edu.ecjtu.d3_annotation.Roles(value=[@edu.ecjtu.d3_annotation.Role(name=老王), @edu.ecjtu.d3_annotation.Role(name=老刘)])

  • 2 内置注解

    • @Override 检测重写
    • @Deprecated 已过时,只是一个标志
    • @SuppressWarrings("all") 抑制编译器生成 警告⚠ 用于方法、类等上面,不能是具体语句上面
  • 3 自定义注解

    • 格式

      • public @interface 注解名称 {

        ​ public 属性名称 属性名() default 默认值;

        }

3.1 解析注解

什么是注解的解析

  • 就是判断类上、方法上、成员变量上是否存在注解,并把注解里的内容给解析出来

如何解析注解

  • 指导思想:要解析 谁上面的注解,就应该先拿到谁
  • 比如要解析类上面的注解,则应该先获取该类的 Class对象,再通过Class对象解析其上面的注解
  • 比如要解析成员方法上的注解,则应该先获取到该成员方法 的 Method对象,再通过Method对象再解析其上面的注解
  • ClassMethodFieldConstructor 都实现了 AnnotatedElement接口,它们都拥有解析注解的能力
AnnotatedElement接口提供了解析注解的方法说明
public Annotation[] getDeclaredAnnotations( )获取当前对象上面的注解
public T getDeclaredAnnotation(Class < T > annotationClass)获取指定的注解对象
public boolean isAnnotationPresent( Class < Annotation > annotationClass )判断当前对象上是否存在某个注解
/**
 * 自定义注解
 */
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTest4 {
    String value();
    double aaa() default 100;
    String[] bbb();
}

@MyTest4(value = "aaa" , aaa = 100.0 , bbb = {"bbb","ccc"})
public class Demo {

    @MyTest4(value = "hhh" , aaa = 105.0 , bbb = {"bbb","ccc"})
    public void test1(){

    }
}

/**
 *  解析注释
 */
public class AnnotationTest3 {
    @Test
    public void testDemo() throws Exception {
        //1.定义Class对象
        Class c = Demo.class;
        //2.解析类的注释对象
//        Annotation demo = c.getDeclaredAnnotation(Demo.class);
        if (c.isAnnotationPresent(MyTest4.class)){
            MyTest4 demo = (MyTest4) c.getDeclaredAnnotation(MyTest4.class);
            System.out.println(demo.value());
            System.out.println(demo.aaa());
            System.out.println(Arrays.toString(demo.bbb()));
        }
        //3.解析方法的注释对象
        Method method = c.getDeclaredMethod("test1");
        if (method.isAnnotationPresent(MyTest4.class)){
            MyTest4 demo = (MyTest4) method.getDeclaredAnnotation(MyTest4.class);
            System.out.println(demo.value());
            System.out.println(demo.aaa());
            System.out.println(Arrays.toString(demo.bbb()));
        }
    }
}

运行结果:

aaa
100.0
[bbb, ccc]
hhh
105.0
[bbb, ccc]

4 动态代理
image-20240213110635268

动态代理 是基于 拦截器 和 反射 实现的,不需要第三方库支持,只需要 JDK 环境即可

  • 必须实现 InvocationHandler 接口;
  • 使用 Proxy.newProxyInstance 产生代理对象;
  • 被代理的对象必须要实现接口;
  • 内部采用asm技术动态生成字节码

如何为 Java 对象创建一个代理对象

  • java.lang.reflect.Proxy类:提供了为对象产生 代理对象的方法:
    • public static Object newProxyInstance (ClassLoader loader,Class< ? > interfaces ,InvocationHandler h)
      • ClassLoader loader 用于指定用哪个类加载器,去加载生成的代理类
      • Class <?> interfaces 指定接口,这些接口用于指定生成的代理长什么样,也就有哪些方法
      • InvocationHandler 用来指定生成的代理对象要 干什么事情(将用户业务对象,定位为代理对象)
/**
 *  代理行为
 */
public interface Star {
    String sing(String name);
    void   dance();
}

public class BigStar implements Star{
    private String name;
    public BigStar(){}
    public BigStar(String name) {
        this.name = name;
    }
    @Override
    public String sing(String name) {
        System.out.println(this.name + "正在唱" + name);
        return "谢谢大家";
    }
    @Override
    public void dance() {
        System.out.println(this.name + "正在优美地跳舞~~~~");
    }
}

/**
 *  目标:实现一个代理工具类
 */
public class ProxyUtil {
    /**
     * 返回一个 代理人
     * @param bigStar 大明星对象
     * @return 明星代理人
     */
    public static Star createProxy(BigStar bigStar){
//        Object newProxyInstance(ClassLoader loader,
//                Class<?>[] interfaces,
//                InvocationHandler h)
        /**
         *  参数一:指定一个类的加载器                      通常为本类的加载器
         *  参数二:指定生成的代理长什么样子,也就是哪些方法    比较抽象
         *  参数三:用来指定 生成的代理对象要干什么事情
         */
        Star starProxy = (Star) Proxy.newProxyInstance(ProxyUtil.class.getClassLoader(),
                new Class[]{Star.class},
                new InvocationHandler() {
                    @Override
                    /**
                     *  public Object invoke(Object proxy, Method method, Object[] args)
                     *  参数一:真代理对象
                     *  参数二:代理方法
                     *  参数三:代理方法的具体参数
                     */
                    //回调方法
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //代理对象要做什么事情
                        if (method.getName().equals("sing")){
                            System.out.println("准备话筒,收款20W");
                        }else if (method.getName().equals("dance")){
                            System.out.println("准备场地,收款10W");
                        }
                        return method.invoke(bigStar,args); //对于没有返回值的方法调用,得到null
                    }
                });
        return starProxy;
    }
}

public class Test {
    public static void main(String[] args) {
        BigStar s = new BigStar("杨超越");
        //根据用户业务对象, !!!!定义一个代理对象
        Star starProxy  = ProxyUtil.createProxy(s);
		//sing
        String rs = starProxy.sing("恭喜发财");
        System.out.println(rs);
		//dance
        starProxy.dance();
    }
}

运行结果:

准备话筒,收款20W
杨超越正在唱恭喜发财
谢谢大家
准备场地,收款10W
杨超越正在优美地跳舞~~~~

参考链接

Java基础之一反射

Java反射

java中的@Repeatable

@Repeatable的作用以及如何使用

注解

动态代理实现原理

  • 22
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
基础xxl-job是一款开源的任务调度框架,能够帮助开发者实现分布式任务调度和管理。该框架极其适用于Web开发中复杂任务的调度和管理。 作为零基础使用者,我们需要先了解xxl-job的核心概念和基本使用方法。首先,xxl-job的任务调度是基于触发器来实现的,需要定义一个触发器来告诉xxl-job什么时候启动任务。任务是开发者自行编写的代码,可以是Java或者其他语言实现的。在任务执行过程中,可以通过xxl-job提供的接口获取任务的一些更多细节。 对于零基础使用者来说,首先需要下载并安装xxl-job的相关组件,然后进行初始化配置。xxl-job支持多种数据库,可以选择适合自己项目的数据库进行配置。安装完成后,我们需要定义任务,包括任务的唯一标识、任务描述、任务执行器、任务参数等信息。同时,我们还需要设置触发器,定义任务触发的时间规则。 配置完成后,我们就可以通过xxl-job的管理平台来管理和监控任务。通过管理平台,我们可以查看已经定义的任务,修改任务的触发规则,查看任务调度的历史记录和执行情况等。此外,xxl-job还提供了报警功能,可以在任务执行过程中进行异常报警。 总的来说,零基础xxl-job的使用过程需要先安装并配置相关组件,然后定义任务和触发器,最后通过管理平台进行任务的管理和监控。在实际使用中,可以根据自己的项目需求进行定制化开发。不过需要注意的是,由于xxl-job是一个比较底层的框架,对于零基础用户来说,可能需要多花一些时间去理解和学习相关知识。但是一旦掌握了基本的使用方法,xxl-job将会极大地提升任务调度的效率和可靠性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值