PracHub
QuestionsPremiumLearningGuidesInterview PrepNEWCoaches

Quick Overview

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.

  • Medium
  • TikTok
  • Coding & Algorithms
  • Software Engineer

Explain Java DI, AOP, and transactions

Company: TikTok

Role: Software Engineer

Category: Coding & Algorithms

Difficulty: Medium

Interview Round: Technical Screen

Explain how dependency injection and AOP proxies are implemented in a modern Java framework such as Spring: bean lifecycle (creation, post-processing, initialization, destruction), proxy mechanisms (JDK dynamic proxies vs. CGLIB), transaction management via proxies, and annotation processing at runtime. Describe classpath scanning, bean scopes, and how circular dependencies are detected or resolved.

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.

Modern Java frameworks such as Spring discover classes on the classpath, register beans, resolve dependencies, and wrap some beans in AOP proxies. In this problem, you will implement a simplified container startup simulator. You are given a list of discovered class definitions. Each definition is a tuple: (name, annotations, dependencies, injection_type, scope, has_interface, is_final) Rules: 1. Only classes whose annotations contain "component" are registered as beans. 2. A registered bean may depend only on other registered beans. 3. Bean creation must respect dependencies. 4. Circular dependencies are allowed only if every bean in the cycle uses setter injection and has singleton scope. 5. A bean annotated with "transactional" must be proxied: - Use "JDK" if has_interface is True - Otherwise use "CGLIB" if is_final is False - Otherwise startup fails because the bean is unproxyable 6. If startup succeeds, return the bean creation order and the proxy type for every registered bean. Failure priority: - First check for missing dependencies - Then check for unresolvable circular dependencies - Then check for unproxyable transactional beans For deterministic output: - If multiple beans have no remaining prerequisites, create the lexicographically smallest next - Inside a resolvable cycle, output bean names in lexicographical order - If multiple invalid cycles exist, report the lexicographically smallest sorted bean list

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.

Solution

def solution(classes):
    from heapq import heappush, heappop

    beans = {}
    component_order = []

    # Classpath scanning: only classes annotated with 'component' become beans.
    for item in classes:
        name, annotations, dependencies, injection_type, scope, has_interface, is_final = item
        ann = {a.lower() for a in annotations}
        if 'component' in ann:
            beans[name] = {
                'annotations': ann,
                'dependencies': list(dependencies),
                'injection_type': injection_type.lower(),
                'scope': scope.lower(),
                'has_interface': bool(has_interface),
                'is_final': bool(is_final),
            }
            component_order.append(name)

    # Failure priority 1: missing dependencies, checked in component input order.
    for name in component_order:
        for dep in beans[name]['dependencies']:
            if dep not in beans:
                return {'status': 'MISSING_DEPENDENCY', 'bean': name, 'dependency': dep}

    names = list(beans.keys())
    graph = {name: list(beans[name]['dependencies']) for name in names}

    # Tarjan's algorithm for SCCs.
    index = 0
    stack = []
    on_stack = set()
    indices = {}
    lowlink = {}
    sccs = []

    def strongconnect(v):
        nonlocal index
        indices[v] = index
        lowlink[v] = index
        index += 1
        stack.append(v)
        on_stack.add(v)

        for w in graph[v]:
            if w not in indices:
                strongconnect(w)
                lowlink[v] = min(lowlink[v], lowlink[w])
            elif w in on_stack:
                lowlink[v] = min(lowlink[v], indices[w])

        if lowlink[v] == indices[v]:
            comp = []
            while True:
                w = stack.pop()
                on_stack.remove(w)
                comp.append(w)
                if w == v:
                    break
            comp.sort()
            sccs.append(comp)

    for name in names:
        if name not in indices:
            strongconnect(name)

    # Failure priority 2: unresolvable cycles.
    problematic = None
    for comp in sccs:
        has_cycle = len(comp) > 1 or (len(comp) == 1 and comp[0] in graph[comp[0]])
        if not has_cycle:
            continue

        resolvable = all(
            beans[b]['scope'] == 'singleton' and beans[b]['injection_type'] == 'setter'
            for b in comp
        )
        if not resolvable:
            if problematic is None or tuple(comp) < tuple(problematic):
                problematic = comp

    if problematic is not None:
        return {'status': 'CIRCULAR', 'beans': problematic}

    # Failure priority 3: proxy validation and proxy type selection.
    proxies = {}
    for name in component_order:
        bean = beans[name]
        if 'transactional' in bean['annotations']:
            if bean['has_interface']:
                proxies[name] = 'JDK'
            elif not bean['is_final']:
                proxies[name] = 'CGLIB'
            else:
                return {'status': 'UNPROXYABLE', 'bean': name}
        else:
            proxies[name] = 'NONE'

    # Build SCC DAG for deterministic creation order.
    comp_id = {}
    comp_members = []
    for i, comp in enumerate(sccs):
        comp_members.append(comp)
        for b in comp:
            comp_id[b] = i

    dag = [set() for _ in range(len(sccs))]
    indegree = [0] * len(sccs)

    # If bean depends on dep, dep's component must come before bean's component.
    for bean in names:
        to_comp = comp_id[bean]
        for dep in graph[bean]:
            from_comp = comp_id[dep]
            if from_comp != to_comp and to_comp not in dag[from_comp]:
                dag[from_comp].add(to_comp)
                indegree[to_comp] += 1

    heap = []
    for i, comp in enumerate(comp_members):
        if indegree[i] == 0:
            heappush(heap, (comp[0], i))

    order = []
    while heap:
        _, cid = heappop(heap)
        order.extend(comp_members[cid])

        for nxt in sorted(dag[cid], key=lambda x: comp_members[x][0]):
            indegree[nxt] -= 1
            if indegree[nxt] == 0:
                heappush(heap, (comp_members[nxt][0], nxt))

    return {'status': 'OK', 'order': order, 'proxies': proxies}

Time complexity: O(V + E). Space complexity: O(V + E).

Hints

  1. Model beans and dependencies as a directed graph. Strongly connected components help identify cycles.
  2. After collapsing each strongly connected component into one node, the remaining graph is a DAG that can be topologically sorted.
Last updated: May 10, 2026

Loading coding console...

PracHub

Master your tech interviews with 7,500+ real questions from top companies.

Product

  • Questions
  • Learning Tracks
  • Interview Guides
  • Resources
  • Premium
  • For Universities
  • Student Access

Browse

  • By Company
  • By Role
  • By Category
  • Topic Hubs
  • SQL Questions
  • Compare Platforms
  • Discord Community

Support

  • support@prachub.com
  • (916) 541-4762

Legal

  • Privacy Policy
  • Terms of Service
  • About Us

© 2026 PracHub. All rights reserved.

Related Coding Questions

  • Parse a nested list from a string - TikTok (medium)
  • Implement stacks, streaming median, and upward path sum - TikTok (easy)
  • Maximize sum with no adjacent elements - TikTok (medium)
  • Implement stack variants and path-sum check - TikTok (medium)
  • Find the longest palindromic substring - TikTok (easy)