Skip to content

Commit

Permalink
feat(AssembleMethod): support for creating target instance through sp…
Browse files Browse the repository at this point in the history
…ring's BeanFactory
  • Loading branch information
Createsequence committed Apr 28, 2024
1 parent 1f035ac commit 54c0c68
Show file tree
Hide file tree
Showing 4 changed files with 147 additions and 54 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package cn.crane4j.core.util;

/**
* A functional interface that can be used to perform a computation that may throw an exception.
*
* @author huangchengxing
* @param <T> the type of the argument
* @param <R> the type of the result
*/
@FunctionalInterface
public interface CheckedFunction<T, R> {

/**
* Apply this function to the given argument.
*
* @param t the function argument
* @return the function result
* @throws Throwable the exception that may be thrown when applying the function
*/
@SuppressWarnings("java:S112")
R apply(T t) throws Throwable;
}
79 changes: 68 additions & 11 deletions crane4j-core/src/main/java/cn/crane4j/core/util/Try.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@

import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.checkerframework.checker.nullness.qual.Nullable;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;

Expand All @@ -19,16 +21,57 @@
@RequiredArgsConstructor
public class Try<T> {

/**
* Create an action that does nothing.
*
* @param runnable the runnable
* @return the try action instance
*/
public static Try<Void> of(CheckedRunnable runnable) {
Asserts.isNotNull(runnable, "runnable must not be null");
return new Try<>(runnable.toSupplier());
}

/**
* Create an action that does nothing and returns a result.
*
* @param supplier the supplier of the result
* @param <R> the type of the result
* @return the try action instance
*/
public static <R> Try<R> of(CheckedSupplier<R> supplier) {
Asserts.isNotNull(supplier, "supplier must not be null");
return new Try<>(supplier);
}

/**
* Create a successful action.
*
* @param result the result
* @param <R> the type of the result
* @return the try action instance
*/
public static <R> Try<R> success(R result) {
Try<R> action = new Try<>(() -> result);
action.performed = true;
action.result = result;
return action;
}

/**
* Create a failed action.
*
* @param ex the exception
* @param <R> the type of the result
* @return the try action instance
*/
public static <R> Try<R> failure(Throwable ex) {
Try<R> action = new Try<>(() -> { throw ex; });
action.performed = true;
action.cause = ex;
return action;
}

/**
* The supplier of the result.
*/
Expand Down Expand Up @@ -104,23 +147,13 @@ public boolean isFailure() {
return Objects.nonNull(cause);
}

/**
* Get the result.
*
* @return the result
* @see #isSuccess()
*/
public T getResult() {
perform();
return result;
}

/**
* Get the cause of the exception.
*
* @return the exception
* @see #isFailure()
*/
@Nullable
public Throwable getCause() {
perform();
return cause;
Expand All @@ -131,6 +164,7 @@ public Throwable getCause() {
*
* @param subscriber the subscriber of the result
* @return this
* @throws IllegalStateException if the computation has been performed
*/
public Try<T> subscribeFailure(Consumer<Throwable> subscriber) {
Asserts.isFalse(performed, "the computation has been performed");
Expand All @@ -147,6 +181,7 @@ public Try<T> subscribeFailure(Consumer<Throwable> subscriber) {
*
* @param subscriber the subscriber of the result
* @return this
* @throws IllegalStateException if the computation has been performed
*/
public Try<T> subscribeSuccess(Consumer<T> subscriber) {
Asserts.isFalse(performed, "the computation has been performed");
Expand Down Expand Up @@ -178,11 +213,23 @@ public T get() throws Throwable {
* Get the result or null if the supplier throws an exception.
*
* @return the result or null
* @see #getOptional()
*/
public T getOrNull() {
return isSuccess() ? result : null;
}

/**
* Get the optional result.
*
* @return the optional result
* @see #getOrNull()
*/
public Optional<T> getOptional() {
return isSuccess() ?
Optional.ofNullable(result) : Optional.empty();
}

/**
* Get the result or the default value.
*
Expand Down Expand Up @@ -218,6 +265,16 @@ public <X extends Throwable> T getOrElseThrow(Function<Throwable, X> function) t
throw function.apply(cause);
}

/**
* Get this or the other instance if this action is failed.
*
* @param function the function to get the other instance
* @return this or the other instance
*/
public Try<T> getOrElseTry(CheckedFunction<Throwable, T> function) {
return isSuccess() ? this : of(() -> function.apply(cause));
}

// ======== run operations ========

/**
Expand Down
47 changes: 45 additions & 2 deletions crane4j-core/src/test/java/cn/crane4j/core/util/TryTest.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package cn.crane4j.core.util;

import cn.crane4j.core.exception.Crane4jException;
import org.junit.Assert;
import org.junit.Test;

import java.util.Objects;
import java.util.Optional;

/**
* test for {@link Try}
Expand All @@ -23,7 +25,6 @@ public void testApply() {
Assert.assertFalse(objectTry.isPerformed());
Assert.assertTrue(objectTry.isSuccess());
Assert.assertFalse(objectTry.isFailure());
Assert.assertSame(object, objectTry.getResult());
Assert.assertNull(objectTry.getCause());
Assert.assertTrue(objectTry.isPerformed());

Expand All @@ -35,6 +36,14 @@ public void testApply() {
Assert.assertSame(object, objectTry.getOrNull());
Assert.assertSame(object, objectTry.getOrElse(new Object()));
Assert.assertSame(object, objectTry.getOrElseThrow(ex -> new IllegalStateException()));


Try<Object> throwTry = Try.of(() -> {
throw new IllegalArgumentException();
}).getOrElseTry(e -> object);
Optional<Object> optional = throwTry.getOptional();
Assert.assertTrue(optional.isPresent());
Assert.assertSame(object, optional.get());
}

@Test
Expand All @@ -51,14 +60,23 @@ public void testApplyOnFailure() {
Assert.assertFalse(objectTry.isPerformed());
Assert.assertFalse(objectTry.isSuccess());
Assert.assertTrue(objectTry.isFailure());
Assert.assertNull(objectTry.getResult());
Assert.assertSame(ex, objectTry.getCause());
Assert.assertTrue(objectTry.isPerformed());

Assert.assertThrows(IllegalArgumentException.class, objectTry::get);
Assert.assertSame(object, objectTry.getOrElseGet(e -> object));
Assert.assertSame(object, objectTry.getOrElse(object));
Assert.assertThrows(IllegalStateException.class, () -> objectTry.getOrElseThrow(e -> new IllegalStateException()));

Try<Object> throwTry = Try.of(() -> {
throw ex;
}).getOrElseTry(e -> {
Assert.assertSame(ex, e);
throw new IllegalStateException();
});
Assert.assertFalse(throwTry.isSuccess());
Assert.assertTrue(throwTry.isFailure());
Assert.assertThrows(IllegalStateException.class, throwTry::get);
}

@Test
Expand All @@ -83,4 +101,29 @@ public void runTest() {
Assert.assertThrows(IllegalStateException.class, () -> throwTry.runOrThrow(e -> new IllegalStateException()));
}

@Test
public void successTest() {
Object object = new Object();
Try<Object> success = Try.success(object);
Assert.assertTrue(success.isPerformed());
Assert.assertTrue(success.isSuccess());
Assert.assertFalse(success.isFailure());
Assert.assertNull(success.getCause());

Assert.assertThrows(Crane4jException.class, () -> success.subscribeFailure(e -> {}));
Assert.assertThrows(Crane4jException.class, () -> success.subscribeSuccess(e -> {}));
}

@Test
public void failureTest() {
Throwable ex = new IllegalArgumentException();
Try<Object> failure = Try.failure(ex);
Assert.assertTrue(failure.isPerformed());
Assert.assertFalse(failure.isSuccess());
Assert.assertTrue(failure.isFailure());
Assert.assertSame(ex, failure.getCause());

Assert.assertThrows(Crane4jException.class, () -> failure.subscribeFailure(e -> {}));
Assert.assertThrows(Crane4jException.class, () -> failure.subscribeSuccess(e -> {}));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@
import cn.crane4j.core.support.AnnotationFinder;
import cn.crane4j.core.support.Crane4jGlobalConfiguration;
import cn.crane4j.core.support.container.MethodContainerFactory;
import cn.crane4j.core.util.Try;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.springframework.aop.support.AopUtils;
import org.springframework.context.ApplicationContext;

import java.util.Collection;
import java.util.Objects;
import java.util.function.Supplier;
import java.util.Optional;

/**
* An {@link AssembleMethodAnnotationHandler} implementation,
Expand Down Expand Up @@ -56,9 +56,9 @@ public BeanAwareAssembleMethodAnnotationHandler(
@NonNull
@Override
protected Class<?> resolveTargetType(AssembleMethod annotation) {
Object target = findTargetFromSpring(annotation.targetType(), annotation.target());
return Objects.isNull(target) ?
super.resolveTargetType(annotation) : AopUtils.getTargetClass(target);
return findTargetFromSpring(annotation.targetType(), annotation.target())
.<Class<?>>map(AopUtils::getTargetClass)
.orElseGet(() -> super.resolveTargetType(annotation));
}

/**
Expand All @@ -71,43 +71,14 @@ protected Class<?> resolveTargetType(AssembleMethod annotation) {
@Nullable
@Override
protected Object getTargetInstance(Class<?> targetType, AssembleMethod annotation) {
Object target = findTargetFromSpring(targetType, annotation.target());
if (target != null) {
return target;
}

// TODO replace to applicationContext.getAutowireCapableBeanFactory().createBean()
return super.getTargetInstance(targetType, annotation);
}

@Nullable
private Object findTargetFromSpring(Class<?> beanType, String beanName) {
// try to get bean from application context by name
Object target = tryGet(() -> applicationContext.getBean(beanName));
if (Objects.nonNull(target)) {
return target;
}

// try to get bean from application context by name and type
target = tryGet(() -> applicationContext.getBean(beanName, beanType));
if (Objects.nonNull(target)) {
return target;
}

// try to get bean from application context by type
target = tryGet(() -> applicationContext.getBean(beanType));
if (Objects.nonNull(target)) {
return target;
}
return null;
return findTargetFromSpring(targetType, annotation.target())
.orElseGet(() -> applicationContext.getAutowireCapableBeanFactory().createBean(targetType));
}

private static <T> T tryGet(Supplier<T> supplier) {
try {
return supplier.get();
} catch (Exception ex) {
// ignore
}
return null;
private Optional<Object> findTargetFromSpring(Class<?> beanType, String beanName) {
return Try.of(() -> applicationContext.getBean(beanName))
.getOrElseTry(ex -> applicationContext.getBean(beanName, beanType))
.getOrElseTry(ex -> applicationContext.getBean(beanType))
.getOptional();
}
}

0 comments on commit 54c0c68

Please sign in to comment.