A fast dependency injector for Java and Android that generates plain Java source code without using reflection or runtime bytecode generation
—
Assisted injection in Dagger enables the creation of objects that require both injected dependencies and runtime parameters. This pattern uses factory interfaces to create instances where some parameters are provided by Dagger's dependency injection and others are passed at runtime.
Annotates constructors that require assisted injection, mixing dependencies injected by Dagger with parameters provided at runtime.
/**
* Annotates constructors for assisted injection (runtime parameter passing)
*/
@Target(ElementType.CONSTRUCTOR)
@Retention(RetentionPolicy.RUNTIME)
@interface AssistedInject {}Key Features:
Usage Examples:
public class UserSession {
private final UserService userService; // Injected by Dagger
private final User user; // Passed at runtime
private final String sessionId; // Passed at runtime
@AssistedInject
public UserSession(
UserService userService, // Dagger-provided
@Assisted User user, // Runtime parameter
@Assisted String sessionId // Runtime parameter
) {
this.userService = userService;
this.user = user;
this.sessionId = sessionId;
}
}Annotates factory interfaces or abstract classes that create instances with assisted injection. The factory provides a clean API for creating objects with mixed parameter sources.
/**
* Annotates factory interfaces/abstract classes for creating assisted injection types
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface AssistedFactory {}Requirements:
Usage Examples:
@AssistedFactory
public interface UserSessionFactory {
UserSession create(User user, String sessionId);
}
// Usage
public class LoginService {
private final UserSessionFactory sessionFactory;
@Inject
public LoginService(UserSessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
public UserSession login(String username, String password) {
User user = authenticateUser(username, password);
String sessionId = generateSessionId();
return sessionFactory.create(user, sessionId);
}
}Annotates constructor parameters that are passed at runtime rather than injected by Dagger. Each @Assisted parameter must have a corresponding parameter in the factory method.
/**
* Annotates constructor parameters that are passed at runtime (not injected)
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@interface Assisted {
/**
* Identifier for distinguishing parameters of the same type
*/
String value() default "";
}Key Features:
Usage Examples:
public class DatabaseConnection {
private final ConnectionPool pool; // Injected
private final String host; // Runtime parameter
private final int port; // Runtime parameter
private final String database; // Runtime parameter
@AssistedInject
public DatabaseConnection(
ConnectionPool pool,
@Assisted String host,
@Assisted int port,
@Assisted String database
) {
this.pool = pool;
this.host = host;
this.port = port;
this.database = database;
}
}
@AssistedFactory
public interface DatabaseConnectionFactory {
DatabaseConnection create(String host, int port, String database);
}When multiple parameters have the same type, use identifiers to distinguish them:
public class Rectangle {
private final Graphics graphics; // Injected
private final int width; // Runtime parameter
private final int height; // Runtime parameter
@AssistedInject
public Rectangle(
Graphics graphics,
@Assisted("width") int width,
@Assisted("height") int height
) {
this.graphics = graphics;
this.width = width;
this.height = height;
}
}
@AssistedFactory
public interface RectangleFactory {
// Parameters must match identifiers and types
Rectangle create(@Assisted("width") int width, @Assisted("height") int height);
}Assisted injection works with generic types:
public class Repository<T> {
private final DatabaseService databaseService; // Injected
private final Class<T> entityClass; // Runtime parameter
private final String tableName; // Runtime parameter
@AssistedInject
public Repository(
DatabaseService databaseService,
@Assisted Class<T> entityClass,
@Assisted String tableName
) {
this.databaseService = databaseService;
this.entityClass = entityClass;
this.tableName = tableName;
}
}
@AssistedFactory
public interface RepositoryFactory {
<T> Repository<T> create(Class<T> entityClass, String tableName);
}
// Usage
public class UserService {
private final Repository<User> userRepository;
@Inject
public UserService(RepositoryFactory repositoryFactory) {
this.userRepository = repositoryFactory.create(User.class, "users");
}
}Multiple Factories:
public class ReportGenerator {
@AssistedInject
public ReportGenerator(
ReportService reportService,
@Assisted ReportType type,
@Assisted DateRange dateRange,
@Assisted("format") String format
) { /* ... */ }
}
// Different factories for different use cases
@AssistedFactory
public interface DailyReportFactory {
ReportGenerator create(
ReportType type,
@Assisted("format") String format
);
}
@AssistedFactory
public interface CustomReportFactory {
ReportGenerator create(
ReportType type,
DateRange dateRange,
@Assisted("format") String format
);
}Factory with Nullable Parameters:
public class EmailSender {
@AssistedInject
public EmailSender(
EmailService emailService,
@Assisted String recipient,
@Assisted @Nullable String cc,
@Assisted @Nullable String bcc
) { /* ... */ }
}
@AssistedFactory
public interface EmailSenderFactory {
EmailSender create(
String recipient,
@Nullable String cc,
@Nullable String bcc
);
}Assisted factories must be explicitly bound in components:
@Module
public abstract class AssistedInjectModule {
// No explicit binding needed - Dagger generates the factory implementation
}
@Component(modules = AssistedInjectModule.class)
public interface ApplicationComponent {
UserSessionFactory userSessionFactory();
DatabaseConnectionFactory databaseConnectionFactory();
RepositoryFactory repositoryFactory();
}Clear Parameter Naming:
// Good: Clear parameter purposes
@AssistedInject
public FileProcessor(
Logger logger, // Injected
@Assisted("input") File inputFile, // Runtime
@Assisted("output") File outputFile // Runtime
) { /* ... */ }
// Avoid: Ambiguous parameters
@AssistedInject
public FileProcessor(
Logger logger,
@Assisted File file1,
@Assisted File file2
) { /* ... */ }Factory Interface Design:
// Good: Descriptive factory methods
@AssistedFactory
public interface HttpClientFactory {
HttpClient createWithTimeout(int timeoutSeconds);
HttpClient createWithAuth(String token, int timeoutSeconds);
}
// Avoid: Generic create method
@AssistedFactory
public interface HttpClientFactory {
HttpClient create(Object... params);
}Error Handling:
public class SafeAssistedInjection {
@AssistedInject
public SafeAssistedInjection(
Validator validator,
@Assisted String input
) {
// Validate runtime parameters
if (input == null || input.trim().isEmpty()) {
throw new IllegalArgumentException("Input cannot be null or empty");
}
this.validator = validator;
this.input = validator.validate(input);
}
}Install with Tessl CLI
npx tessl i tessl/maven-com-google-dagger--dagger