Explain Java DI, AOP, and transactions
Company: TikTok
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: medium
Interview Round: Technical Screen
Quick Answer: This question evaluates understanding of Java framework internals and related competencies including dependency injection, aspect-oriented programming and proxy mechanisms, transaction management, annotation processing, bean lifecycle, classpath scanning, bean scopes, and circular dependency detection or resolution.
Constraints
- 1 <= number of discovered classes <= 10^5 in the general case, but your algorithm should be near-linear in the number of classes and dependencies
- Class names are unique
- Each dependency name is a string
- annotations may include 'component' and/or 'transactional'
- A cycle is resolvable only when every bean in that cycle has scope 'singleton' and injection_type 'setter'
Examples
Input: ([('Repo', ['component'], [], 'constructor', 'singleton', False, False), ('Service', ['component', 'transactional'], ['Repo'], 'constructor', 'singleton', True, False), ('IgnoredUtil', [], [], 'constructor', 'singleton', False, False)],)
Expected Output: {'status': 'OK', 'order': ['Repo', 'Service'], 'proxies': {'Repo': 'NONE', 'Service': 'JDK'}}
Explanation: Only Repo and Service are registered because IgnoredUtil is not a component. Service depends on Repo, so Repo is created first. Service is transactional and has an interface, so it uses a JDK proxy.
Input: ([('A', ['component'], ['B'], 'setter', 'singleton', False, False), ('B', ['component', 'transactional'], ['A'], 'setter', 'singleton', False, False), ('C', ['component'], ['A'], 'constructor', 'singleton', False, False)],)
Expected Output: {'status': 'OK', 'order': ['A', 'B', 'C'], 'proxies': {'A': 'NONE', 'B': 'CGLIB', 'C': 'NONE'}}
Explanation: A and B form a cycle, but it is allowed because both are singleton beans using setter injection. Their SCC is emitted in lexicographical order. B is transactional, has no interface, and is not final, so it uses CGLIB.
Input: ([('A', ['component'], ['B'], 'constructor', 'singleton', False, False), ('B', ['component'], ['A'], 'setter', 'singleton', False, False)],)
Expected Output: {'status': 'CIRCULAR', 'beans': ['A', 'B']}
Explanation: The cycle A <-> B is not resolvable because A uses constructor injection.
Input: ([('A', ['component'], ['B'], 'constructor', 'singleton', False, False), ('B', [], [], 'constructor', 'singleton', False, False)],)
Expected Output: {'status': 'MISSING_DEPENDENCY', 'bean': 'A', 'dependency': 'B'}
Explanation: B exists in the discovered class list but is not a component, so it is not registered as a bean. A therefore has a missing dependency.
Input: ([('FinalService', ['component', 'transactional'], [], 'constructor', 'singleton', False, True)],)
Expected Output: {'status': 'UNPROXYABLE', 'bean': 'FinalService'}
Explanation: FinalService is transactional, has no interface, and is final. It cannot be proxied with JDK proxies or CGLIB.
Input: ([],)
Expected Output: {'status': 'OK', 'order': [], 'proxies': {}}
Explanation: With no discovered classes, startup succeeds trivially with no beans.
Hints
- Model beans and dependencies as a directed graph. Strongly connected components help identify cycles.
- After collapsing each strongly connected component into one node, the remaining graph is a DAG that can be topologically sorted.