Spring事物(二)----基于注解源码分析之2

本文深入解析了JdkDynamicAopProxy的工作原理,包括如何生成代理类、调用带有事务的方法,以及动态代理机制在Spring AOP中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

JdkDynamicAopProxy如何生成代理类

接着上一篇,这篇我们分析内容:
1、如何通过JdkDynamicAopProxy生成代理类
2、方法在调用时如何调用带事物的方法

什么是代理类?
    代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。

代理模式主要有两种:静态代理和动态代理
静态代理:由程序编码创建,在进行编译,再程序运行前,生成的代理类.class 文件已存在。
动态代理:在程序运行时,利用反射机制动态创建而成的字节码,已存在于内存当中。动态代理分为JDK动态代理和CGLIB动态代理

在分析JdkDynamicAopProxy之前,我们先来学习JDK动态代理的。先看例子:
我们需求就是对学生小明在执行跑步前后进行其它事情的处理,比如在小明跑步前要换上运动鞋,跑完步之后洗澡。

定义个Person接口类

public interface Person {
	public void run(String name);
}

定义一个Person接口实现Student 类

public class Student implements Person {
	@Override
	public void run(String name) {
		System.out.println("====="+name + " 正在跑步=====");
	}
}

定义Handler类实现InvocationHandler接口

public class MyHandler implements InvocationHandler {

	private Object target;//这其实业务实现类对象,用来调用具体的业务方法
	/**
	 * 绑定业务对象并返回一个代理类
	 */
	public MyHandler(Object target) {
		this.target = target;
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		System.out.println("=========before 换运动鞋========");
		method.invoke(target,args); //目标方法
		System.out.println("=========after 洗澡========");
		return null;
	}
}

测试类

public class ProxyTest {
	@Test
	public void test(){
	  //生成代理类
		Person person = (Person) Proxy.newProxyInstance(
		       Person.class.getClassLoader(),  --加载器
				new Class<?>[] {Person.class},  --代理接口类
				new MyHandler(new Student()));  --待处理类
		person.walk("小明");
	}
}

接下我们运行test方法,打印结果:

=========before 换运动鞋========
=====小明 正在跑步=====
=========after 洗澡========

在这个例子中,我们看到在不修改跑步run方法的基础上还添加一些跑步前和跑步后的处理事件功能。其实在很多场景经常用到这种思想,比如需要打印某一些方法的执行时间,当然我们可以在这些方法里面直接编写打印时间的逻辑处理。但是如果有好几百个方法需要打印呢?这工作量得多大?后续的维护难度有多大?可想而知,而代理模式在不需要修改原方法的基础上,还能实现方法的执行时间打印。

由上面的例子可以看出实现一个jdk动态代理只需要三步
1、创建被代理的接口和类
2、创建实现InvocationHandler接口的处理类,覆写invoke方法并实现代理逻辑
3、通过Proxy的静态方法newProxyInstance创建代理类

下面来分析如何生成代理类
代理类通过Proxy.newProxyInstance方法生成,打开这个方法

    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
       //校验待处理回调是否为空
        Objects.requireNonNull(h);

        final Class<?>[] intfs = interfaces.clone();
        //安全方面的验证
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }
        //生成代理类
        Class<?> cl = getProxyClass0(loader, intfs);

        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl); //权限校验
            }
         //获取代理类的构造方法
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            //实例化代理类
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
    }

我们发现newProxyInstance做两件事:
1、getProxyClass0方法生成代理类
2、newInstance实例化代理类

接下来我们分析getProxyClass0方法,接口数不得超过65535个,从proxyClassCache缓存获取代理类,如果缓存没有,就从ProxyClassFactory生成代理类

    private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }
         return proxyClassCache.get(loader, interfaces);
    }

看proxyClassCache定义,是反射包下的WeakCache类,由此猜想,代理类的生成离不开的反射机制

   private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
        proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

接下看WeakCache的get方法,这里WeakCache的map是一个二级map结构:第一级map(key,map<subkey,Supplier>),第二级map(subkey,Supplier),其中Supplier就是生成的代理类对象,具体生成流程
1、通过传进来的Classloader进行包装后的对象key查找一级map,如果没有,则新建
2、通过subKeyFactory生成subKey ,从二级map中找supplier ,第一次while循环如果没有supplier ,则先创建一个Factory对象,并将Factory赋值给Factory,此时第二次while循环,supplier不为空,调用supplier.get()获取到代理对象

WeakCache类局部变量定义
  private final ReferenceQueue<K> refQueue
        = new ReferenceQueue<>();
   
  private final ConcurrentMap<Object, ConcurrentMap<Object, Supplier<V>>> map
        = new ConcurrentHashMap<>();
  private final ConcurrentMap<Supplier<V>, Boolean> reverseMap
        = new ConcurrentHashMap<>();
        
  private final BiFunction<K, P, ?> subKeyFactory;
  private final BiFunction<K, P, V> valueFactory;

  public WeakCache(BiFunction<K, P, ?> subKeyFactory,
                     BiFunction<K, P, V> valueFactory) {
        this.subKeyFactory = Objects.requireNonNull(subKeyFactory);
        this.valueFactory = Objects.requireNonNull(valueFactory);
    }
 public V get(K key, P parameter) {
        //校验接口是否为空
        Objects.requireNonNull(parameter);
         //清理缓存
        expungeStaleEntries();
        //从缓存获取一级key
        Object cacheKey = CacheKey.valueOf(key, refQueue);
         //从map通过一级key得到代理类
        ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
        if (valuesMap == null) {
            //如果为空。则创建一个新的map
            ConcurrentMap<Object, Supplier<V>> oldValuesMap
                = map.putIfAbsent(cacheKey,
                                  valuesMap = new ConcurrentHashMap<>());
            if (oldValuesMap != null) {
                valuesMap = oldValuesMap;
            }
        }
        //生成subkey,sub-key是由WeakCache构造函数传人的KeyFactory()生成的
        Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
        //如果获取代理类
        Supplier<V> supplier = valuesMap.get(subKey);
        Factory factory = null;
        //轮询获取代理类,知道有为止
        while (true) {
            if (supplier != null) { 
            //调用factory的的get方法
                V value = supplier.get();
                if (value != null) {
                    return value;
                }
            }
            //如果没有代理类,则创建一个Factory对象
            if (factory == null) {
                factory = new Factory(key, parameter, subKey, valuesMap);
            }

            if (supplier == null) {
                supplier = valuesMap.putIfAbsent(subKey, factory);
                if (supplier == null) {
                    //将factory赋值给supplier 
                    supplier = factory;
                }
            } else {
                if (valuesMap.replace(subKey, supplier, factory)) {
                    supplier = factory;
                } else {
                    supplier = valuesMap.get(subKey);
                }
            }
        }
    }

3、前面我们知道Factory是Supplier的实例,那么调用supplier.get()方法就是Factory类中的get方法,而Factory的get方法是通过valueFactory.apply方法生成代理对象,由前面初始化知道valueFactory的实例为ProxyClassFactory,那么调用的就是ProxyClassFactory的apply方法。

        // 所有代理类统一的名称前缀
      private static final String proxyClassNamePrefix = "$Proxy";
        //生成代理类唯一序号
      private static final AtomicLong nextUniqueNumber = new AtomicLong();

       public synchronized V get() { // serialize access
           //同步做检查,防止多线程情况下,拿到的代理对象不是同一个
            Supplier<V> supplier = valuesMap.get(subKey);
            if (supplier != this) {
                return null;
            }

            V value = null;
            try {
               
                value = Objects.requireNonNull(valueFactory.apply(key, parameter));
            } finally {
                if (value == null) { // remove us on failure
                    valuesMap.remove(subKey, this);
                }
            }
            assert value != null;
            CacheValue<V> cacheValue = new CacheValue<>(value);
            if (valuesMap.replace(subKey, this, cacheValue)) {
                reverseMap.put(cacheValue, Boolean.TRUE);
            } else {
                throw new AssertionError("Should not reach here");
            }
            return value;
        }
    }

4、打开ProxyClassFactory的apply方法,而生成代理类字节码文件又主要通过ProxyGenerate的generateProxyClass(proxyName,interfaces),生成代理类通过defineClass0方法加载字节码到内存

   public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
   
          Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
          for (Class<?> intf : interfaces) {
              //验证指定的类加载器(loader)加载接口所得到的Class对象(interfaceClass)是否对象相同
              Class<?> interfaceClass = null;
              try {
                  interfaceClass = Class.forName(intf.getName(), false, loader);
              } catch (ClassNotFoundException e) {
              }
              if (interfaceClass != intf) {
                  throw new IllegalArgumentException(
                      intf + " is not visible from class loader");
              }
             //验证该Class对象是不是接口
              if (!interfaceClass.isInterface()) {
                  throw new IllegalArgumentException(
                      interfaceClass.getName() + " is not an interface");
              }
             //验证该Class接口是否重复
              if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                  throw new IllegalArgumentException(
                      "repeated interface: " + interfaceClass.getName());
              }
          }
          //定义代理类的包名
          String proxyPkg = null;    
          int accessFlags = Modifier.PUBLIC | Modifier.FINAL;

          /*
           * 记录下非public代理接口必须定义在同一个包中,影响生成路径
           */
          for (Class<?> intf : interfaces) {
              int flags = intf.getModifiers();
              if (!Modifier.isPublic(flags)) {
                  accessFlags = Modifier.FINAL;
                  String name = intf.getName();
                  int n = name.lastIndexOf('.');
                  String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                  if (proxyPkg == null) {
                      proxyPkg = pkg;
                  } else if (!pkg.equals(proxyPkg)) {
                      throw new IllegalArgumentException(
                          "non-public interfaces from different packages");
                  }
              }
          }

          if (proxyPkg == null) {
             //如果都是public接口,那么生成的代理类就在com.sun.proxy默认路径下面
              proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
          }

          //生成代理类唯一序号标志
          long num = nextUniqueNumber.getAndIncrement();
          String proxyName = proxyPkg + proxyClassNamePrefix + num;

          /*
           * 最终生成代理类的方法
           */
          byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
              proxyName, interfaces, accessFlags);
          try {
              return defineClass0(loader, proxyName,
                                  proxyClassFile, 0, proxyClassFile.length);
          } catch (ClassFormatError e) {
              throw new IllegalArgumentException(e.toString());
          }
      }
  }

5、ProxyGenerator的generateProxyClass方法最终调用了generateClassFile生成代理类。终于盼到头了,generateClassFile才是真正生成代理类字节码文件的方法

public static byte[] generateProxyClass(final String name,
											Class<?>[] interfaces,
											int accessFlags)
	{
	     //生成文件代理类
		ProxyGenerator gen = new ProxyGenerator(name, interfaces, accessFlags);
		final byte[] classFile = gen.generateClassFile();

		if (saveGeneratedFiles) {
			java.security.AccessController.doPrivileged(
					new java.security.PrivilegedAction<Void>() {
						public Void run() {
							try {
								int i = name.lastIndexOf('.');
								Path path;
								if (i > 0) {
									Path dir = Paths.get(name.substring(0, i).replace('.', File.separatorChar));
									Files.createDirectories(dir);
									path = dir.resolve(name.substring(i+1, name.length()) + ".class");
								} else {
									path = Paths.get(name + ".class");
								}
								Files.write(path, classFile);
								return null;
							} catch (IOException e) {
								throw new InternalError(
										"I/O exception saving generated file: " + e);
							}
						}
					});
		}
		return classFile;
	}

打开generateClassFile方法,生成步骤如下:
定义一个代理方法proxyMethods的map集合
1、将hashCode,equals,toString添加代理方法集合
2、把接口的所有方法添加到代理方法集合
3、对所有的代理方法进行校验
4、生成代理类的构造方法,入参为InvocationHandler的实现类
5、生成代理类的方法
6、生成初始静态块
7、生成代理文件相关信息

private byte[] generateClassFile() {

       /* ============================================================
        * Step 1: 将hashCode,equals,toString添加到代理方法集合(proxyMethods)
        */
   	addProxyMethod(hashCodeMethod, Object.class);
   	addProxyMethod(equalsMethod, Object.class);
   	addProxyMethod(toStringMethod, Object.class);

       /*
        * 把接口的所有方法添加到集合
        */
   	for (Class<?> intf : interfaces) {
   		for (Method m : intf.getMethods()) {
   			addProxyMethod(m, intf);
   		}
   	}

       /*
        *校验相同前面的方法的返回值是否一致
        */
   	for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
   		checkReturnTypes(sigmethods);
   	}

   	try {
   	   //增加代理类的构造方法
   		methods.add(generateConstructor());

   		for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
   			for (ProxyMethod pm : sigmethods) {

   				// 添加所有属性字段都是private static Method XXX
   				fields.add(new FieldInfo(pm.methodFieldName,
   						"Ljava/lang/reflect/Method;",
   						ACC_PRIVATE | ACC_STATIC));
   				// 生成代理类的方法
   				methods.add(pm.generateMethod());
   			}
   		}
                    //静态类的生成
   		methods.add(generateStaticInitializer());

   	} catch (IOException e) {
   		throw new InternalError("unexpected I/O Exception", e);
   	}

   	if (methods.size() > 65535) {
   		throw new IllegalArgumentException("method limit exceeded");
   	}
   	if (fields.size() > 65535) {
   		throw new IllegalArgumentException("field limit exceeded");
   	}

       /* 
        * 写代理文件
        */
   	cp.getClass(dotToSlash(className));
   	cp.getClass(superclassName);
   	for (Class<?> intf: interfaces) {
   		cp.getClass(dotToSlash(intf.getName()));
   	}

   	cp.setReadOnly();
   	ByteArrayOutputStream bout = new ByteArrayOutputStream();
   	DataOutputStream dout = new DataOutputStream(bout);
   	try {
   		dout.writeInt(0xCAFEBABE);
   		dout.writeShort(CLASSFILE_MINOR_VERSION);
   		dout.writeShort(CLASSFILE_MAJOR_VERSION);
   		cp.write(dout);
   		dout.writeShort(accessFlags);
   		dout.writeShort(cp.getClass(dotToSlash(className)));
   		dout.writeShort(cp.getClass(superclassName));
   		dout.writeShort(interfaces.length);
   		for (Class<?> intf : interfaces) {
   			dout.writeShort(cp.getClass(
   					dotToSlash(intf.getName())));
   		}
   		dout.writeShort(fields.size());
   		for (FieldInfo f : fields) {
   			f.write(dout);
   		}
   		dout.writeShort(methods.size());
   		for (MethodInfo m : methods) {
   			m.write(dout);
   		}
   		dout.writeShort(0); 

   	} catch (IOException e) {
   		throw new InternalError("unexpected I/O Exception", e);
   	}
   	return bout.toByteArray();
   }

6、生成好代理类的字节码后,由于是加载到内存的,没法看到。但是我们可以通过某一些方法来生成文件,在测试类中增加如下代码,意思生成$Proxy0代理类,这里我指定的是自己电脑的F盘

  public void proxyTest() throws Exception {
  	byte[] bs = ProxyGenerator.generateProxyClass("$Proxy0", new Class<?>[] {Person.class});
  	new FileOutputStream(new File("f:/$Proxy0.class")).write(bs);
  }

7、找到F盘的生成$Proxy0.class文件,

  /**
  *$Proxy0继承了Proxy类,实现了Person接口
  */
  public final class $Proxy0 extends Proxy implements Person {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;
    //构造方法里面传入InvocationHandler的实例
    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }
   /**
   *Object的equals方法
   */
    public final boolean equals(Object var1) throws  {
        try {
            return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue();
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }
   /**
   *Object的toString方法
   */
    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
   /**
    *接口person的walk的方法,这里的h代表是案例中的MyHandler,
    那就是调用 MyHandler的invoke方法,前面我们看到invoke出了调用委托类的方法之外。还做了一些其它功能的加强。比如walk方法之前还做了换运动鞋的功能等
    */
    public final void walk(String var1) throws  {
        try {
            super.h.invoke(this, m3, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }
 /**
   *Object的hashCode方法
   */
    public final int hashCode() throws  {
        try {
            return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue();
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
 /**
   *静态类的初始化
   */
    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
            m3 = Class.forName("org.springframework.aop.framework.jdkProxy.Person").getMethod("walk", new Class[]{Class.forName("java.lang.String")});
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

我们分析这个$Proxy0代理类。既然是实现Person接口。必须实现walk方法,里面调用super.h.invoke(this, m3, new Object[]{var1}),super.h是什么?回到最开始的Proxy.newProxyInstance方法,先生成代理类后,接着调用代理类的构造方法cons.newInstance(new Object[]{h})实例化代理类,代理类的构造方法入参就是h。最后又调用了super(h),调用的是Proxy的构造方法。将h赋值给了Proxy的h变量。
那么super.h就是构造方法传进来的h,这个h其实就是MyHandler。那么invoke就是调用MyHandler的invoke方法,我们来看个图:

Person(父类)$Proxy0(代理类)MyHandler(处理类)Student(委托类)walk方法invoke方法before方法walk方法after方法Person(父类)$Proxy0(代理类)MyHandler(处理类)Student(委托类)

终于轮到我们的主题了JdkDynamicAopProxy主角出场了,JdkDynamicAopProxy封装了newProxyInstance方法,还实现了InvocationHandler接口,并重写了invoke方法
	public Object getProxy(@Nullable ClassLoader classLoader) {
		if (logger.isDebugEnabled()) {
			logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
		}
		Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
		findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
		return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
	}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		MethodInvocation invocation;
		Object oldProxy = null;
		boolean setProxyContext = false;

		TargetSource targetSource = this.advised.targetSource;
		Object target = null;

		try {
			if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
				return equals(args[0]);
			}
			else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
				return hashCode();
			}
			else if (method.getDeclaringClass() == DecoratingProxy.class) {
				return AopProxyUtils.ultimateTargetClass(this.advised);
			}
			else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
					method.getDeclaringClass().isAssignableFrom(Advised.class)) {
				return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
			}

			Object retVal;

			if (this.advised.exposeProxy) {
				oldProxy = AopContext.setCurrentProxy(proxy);
				setProxyContext = true;
			}
			target = targetSource.getTarget();
			Class<?> targetClass = (target != null ? target.getClass() : null);

			List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
			if (chain.isEmpty()) {
				Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
				retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
			}
			else {
				invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
				retVal = invocation.proceed();
			}
			Class<?> returnType = method.getReturnType();
			if (retVal != null && retVal == target &&
					returnType != Object.class && returnType.isInstance(proxy) &&
					!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
				retVal = proxy;
			}
			else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
				throw new AopInvocationException(
						"Null return value from advice does not match primitive return type for: " + method);
			}
			return retVal;
		}
		finally {
			if (target != null && !targetSource.isStatic()) {
				targetSource.releaseTarget(target);
			}
			if (setProxyContext) {
				AopContext.setCurrentProxy(oldProxy);
			}
		}
	}

由此可总结:
1、基于JdkDynamicAopProxy通过getProxy方法生成代理类。
2、当调用事物A方法时,先调用代理类中的A方法,由代理类A方法调用JdkDynamicAopProxy的invoke方法,invoke方法进行一系列的处理后。最终调用委托类的A方法。

显而易见,在invoke方法调用的执行前后,就可以进行一些加强操作。比如事物。缓存等功能。

资源下载链接为: https://pan.quark.cn/s/22ca96b7bd39 在当今的软件开发领域,自动化构建与发布是提升开发效率和项目质量的关键环节。Jenkins Pipeline作为一种强大的自动化工具,能够有效助力Java项目的快速构建、测试及部署。本文将详细介绍如何利用Jenkins Pipeline实现Java项目的自动化构建与发布。 Jenkins Pipeline简介 Jenkins Pipeline是运行在Jenkins上的一套工作流框架,它将原本分散在单个或多个节点上独立运行的任务串联起来,实现复杂流程的编排与可视化。它是Jenkins 2.X的核心特性之一,推动了Jenkins从持续集成(CI)向持续交付(CD)及DevOps的转变。 创建Pipeline项目 要使用Jenkins Pipeline自动化构建发布Java项目,首先需要创建Pipeline项目。具体步骤如下: 登录Jenkins,点击“新建项”,选择“Pipeline”。 输入项目名称和描述,点击“确定”。 在Pipeline脚本中定义项目字典、发版脚本和预发布脚本。 编写Pipeline脚本 Pipeline脚本是Jenkins Pipeline的核心,用于定义自动化构建和发布的流程。以下是一个简单的Pipeline脚本示例: 在上述脚本中,定义了四个阶段:Checkout、Build、Push package和Deploy/Rollback。每个阶段都可以根据实际需求进行配置和调整。 通过Jenkins Pipeline自动化构建发布Java项目,可以显著提升开发效率和项目质量。借助Pipeline,我们能够轻松实现自动化构建、测试和部署,从而提高项目的整体质量和可靠性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值