@Autowired Spring与原生代码对比 Spring底层源码实现原理

目录

  • 一 Spring的作用
    • 1. 依赖注入(Dependency Injection):
    • 2. 面向切面编程(Aspect-Oriented Programming):
    • 3. 事务管理(Transaction Management):
    • 4. 数据库访问(Database Access):
    • 5. 安全性(Security):
  • 二 原生代码对比
    • 1. **依赖注入**:
    • 2. **面向切面编程**:
    • 3. **事务管理**:
    • 4. **数据库访问**:
    • @Autowired 详细过程
    • @Autowired工作原理
    • @AutoWired原生代码对比
  • 三 Spring底层实现原理
    • 1. **类路径扫描和组件注册**:
    • 2. **Bean的定义**:
    • 3. **依赖注入**:
    • 4. **依赖解析**:
    • 5. **循环依赖处理**:
    • 6. **Bean的生命周期管理**:

一 Spring的作用

Spring出现后解决了什么问题,有具体的代码例子吗?
加粗样式
Spring是一个开源的Java框架,它解决了许多企业应用程序开发中的常见问题,包括依赖注入、面向切面编程、事务管理、数据库访问、安全性等等。以下是一些Spring解决的主要问题以及示例代码:

1. 依赖注入(Dependency Injection):

Spring允许通过依赖注入将对象之间的依赖关系解耦,这有助于更容易地测试和维护应用程序。

// 示例:使用Spring进行依赖注入
@Service
public class MyService {
    private final MyRepository repository;

    @Autowired
     // 构造函数注入,通过构造函数接收 MyRepository 的实例
    public MyService(MyRepository repository) {
        this.repository = repository;
    }

    // ...
}

在上述示例中,@MyService类的构造函数接收一个@MyRepository的实例作为参数,这就是构造函数注入的方式。当您创建@MyService的实例时,Spring容器会查找匹配的@MyrRepository的Bean定义,并自动将其注入到@MyService的构造函数中。

构造函数注入有助于确保类在实例化时拥有所有必需的依赖项,从而使类更加可靠和可测试。同时,它也使依赖关系在类的外部清晰可见,提高了代码的可读性。

"依赖关系"在编程中通常指的是一个对象需要另一个对象的服务或数据以完成其工作。当一个类(或对象)依赖于另一个类(或对象)时,它需要引用(使用)那个类(或对象)的实例来执行特定的操作。

在Spring框架中,依赖关系通常表现为一个组件(例如,一个服务类或一个控制器)需要引用其他组件(例如,一个数据访问类或一个服务类)的实例来完成某些操作。这些依赖项可以通过构造函数注入、Setter方法注入或字段注入等方式进行管理和注入。

使用@Autowired注解告诉Spring自动注入依赖项,这意味着Spring会负责在运行时创建依赖项的实例,并将它们注入到需要它们的类中。这使得代码更加模块化和松耦合,同时提高了可维护性和可测试性。

所以,当说一个类有依赖关系时,它通常需要引用其他类的实例以完成其任务。Spring的依赖注入机制帮助管理和解决这些依赖关系。

2. 面向切面编程(Aspect-Oriented Programming):

Spring提供了AOP支持,可以轻松地实现横切关注点,如日志记录、事务管理等。

// 示例:使用Spring AOP配置日志切面
@Aspect
@Component
public class LoggingAspect {
    @Before("execution(* com.example.service.*.*(..))")
    public void logMethodCall(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("Calling method: " + methodName);
    }
}

3. 事务管理(Transaction Management):

Spring简化了事务的管理,可以通过注解或XML配置进行声明式事务管理。

// 示例:使用Spring注解配置声明式事务管理
@Service
public class MyService {
    @Autowired
    private MyRepository repository;

    @Transactional
    public void performTransactionalOperation() {
        // 执行数据库操作
    }
}

4. 数据库访问(Database Access):

Spring提供了JDBC模板和对象关系映射(ORM)框架集成,简化了数据库访问和操作。

// 示例:使用Spring JDBC模板执行查询
@Autowired
private JdbcTemplate jdbcTemplate;

public List<User> getAllUsers() {
    return jdbcTemplate.query("SELECT * FROM users", new BeanPropertyRowMapper<>(User.class));
}

5. 安全性(Security):

Spring Security是Spring的子项目,提供了强大的身份验证和授权机制,用于保护应用程序。

// 示例:配置Spring Security以保护REST端点
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/admin/**").hasRole("ADMIN")
                .antMatchers("/user/**").hasRole("USER")
                .and()
            .formLogin()
                .loginPage("/login")
                .permitAll()
                .and()
            .logout()
                .permitAll();
    }
}

这些示例只是Spring框架提供的功能的一小部分,Spring还有许多其他功能,用于解决企业应用程序开发中的各种问题。Spring的核心目标之一是提高开发的效率并降低代码的复杂性,使开发人员能够更容易地构建可维护和可扩展的应用程序。

二 原生代码对比

当谈到Spring框架时,与原生Java代码相比,Spring提供了更高级、更抽象的方式来解决问题,以提高开发效率和可维护性。以下是一些原生Java代码与Spring代码的对比示例:

1. 依赖注入

原生Java

public class MyService {
    private MyRepository repository;

    public MyService() {
        this.repository = new MyRepository();
    }

    // ...
}

使用Spring

@Service
public class MyService {
    private final MyRepository repository;

    @Autowired
    public MyService(MyRepository repository) {
        this.repository = repository;
    }

    // ...
}

使用Spring,您可以通过注解(如@Autowired)将依赖项注入到类中,而无需在类内部创建它们的实例。这样可以更轻松地管理和替换依赖项。

2. 面向切面编程

原生Java

public class MyService {
    public void doSomething() {
        // 执行业务逻辑
        // 记录日志
        System.out.println("Logging: Method doSomething() is called.");
        // 处理事务
        try {
            // 开始事务
            // 执行业务逻辑
            // 提交事务
        } catch (Exception ex) {
            // 回滚事务
        }
    }
}

使用Spring AOP

@Aspect
@Component
public class LoggingAspect {
    @Before("execution(* com.example.service.MyService.*(..))")
    public void logMethodCall(JoinPoint joinPoint) {
        System.out.println("Calling method: " + joinPoint.getSignature().getName());
    }
}

Spring AOP使您能够将横切关注点(如日志记录和事务管理)从业务逻辑中分离出来,通过注解或XML配置进行切面处理。

3. 事务管理

原生Java

public class MyService {
    public void performTransactionalOperation() {
        // 开始事务
        try {
            // 执行业务逻辑
            // 提交事务
        } catch (Exception ex) {
            // 回滚事务
        }
    }
}

使用Spring声明式事务管理

@Service
public class MyService {
    @Transactional
    public void performTransactionalOperation() {
        // 执行业务逻辑
    }
}

Spring的声明式事务管理允许您通过注解或XML配置来管理事务,而不必手动编写事务管理代码。

4. 数据库访问

原生Java

public class MyRepository {
    public List<User> getAllUsers() {
        // 使用JDBC连接数据库,执行查询等操作
        // 处理结果集
        // 关闭数据库连接
    }
}

使用Spring JDBC

@Autowired
private JdbcTemplate jdbcTemplate;

public List<User> getAllUsers() {
    return jdbcTemplate.query("SELECT * FROM users", new BeanPropertyRowMapper<>(User.class));
}

Spring JDBC模板提供了一种更简单和更安全的方式来执行数据库操作,它封装了连接管理、异常处理等细节。

这些示例只是简单地对比了原生Java代码和Spring框架代码的一些方面。Spring旨在简化企业应用程序开发,提供更高级别的抽象和便利性,使开发更加高效和可维护。但请注意,Spring并不一定适用于所有项目,具体选择是否使用Spring取决于项目的需求和规模。

@Autowired 详细过程

当使用Spring框架时,@Autowired注解用于将依赖项自动注入(或装配)到类中。这意味着Spring会负责创建依赖项的实例并将其传递给需要它们的类,而不需要手动创建依赖项的实例。让我提供一个更加详细的示例,希望这次能更清楚:

假设我们有一个简单的用户管理应用,其中有两个关键的类:UserServiceUserRepositoryUserService需要使用UserRepository来获取用户信息。

  1. 创建用户类 User
public class User {
    private String name;

    public User(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}
  1. 创建用户仓库类 UserRepository,它模拟从数据库中获取用户数据:
public class UserRepository {
    public List<User> getAllUsers() {
        // 模拟从数据库中获取用户数据
        return Arrays.asList(new User("Alice"), new User("Bob"));
    }
}
  1. 创建用户服务类 UserService,该类依赖于 UserRepository 来获取用户数据:
public class UserService {
    private UserRepository userRepository;

    @Autowired // 使用 @Autowired 注解来告诉 Spring 自动注入依赖
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public List<User> getAllUsers() {
        return userRepository.getAllUsers();
    }
}
  1. 配置Spring:要使 @Autowired 注解生效,需要配置Spring来扫描包并进行自动装配。
@Configuration
@ComponentScan(basePackages = "com.example") // 告诉Spring扫描包路径以寻找组件
public class AppConfig {
    // 这个配置类用于启用注解扫描和自动装配
}
  1. 启动应用程序:在应用程序的入口点中,创建Spring应用程序上下文并获取 UserService 的实例:
public class MainApp {
    public static void main(String[] args) {
        // 创建Spring应用程序上下文,加载配置
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

        // 获取UserService实例,它会自动注入UserRepository
        UserService userService = context.getBean(UserService.class);

        // 调用UserService的方法来获取用户数据
        List<User> users = userService.getAllUsers();

        // 打印用户信息
        for (User user : users) {
            System.out.println("User: " + user.getName());
        }
    }
}

在这个示例中,@Autowired注解告诉Spring自动将UserRepository注入到UserService的构造函数中。这就是依赖注入的核心概念,它使得我们无需手动创建UserRepository的实例,Spring会自动创建并注入它。这种方式有助于降低代码的耦合度,提高可维护性,并使测试更容易进行,因为您可以轻松地模拟依赖项。

@Autowired工作原理

更详细地解释一下@Autowired注解和Spring自动依赖注入的工作原理。

在Spring框架中,@Autowired注解是一种用于自动注入(自动装配)依赖关系的方式。当您在类的构造函数、成员变量、Setter方法或方法参数上标记@Autowired注解时,Spring会负责在运行时查找匹配的依赖项并将其注入到类中。

让我们通过一个示例来说明这个过程。

假设有以下两个类:

public class UserRepository {
    public List<User> getAllUsers() {
        // 模拟从数据库中获取用户数据
        return Arrays.asList(new User("Alice"), new User("Bob"));
    }
}

public class UserService {
    private UserRepository userRepository;

    @Autowired // 使用 @Autowired 注解来告诉 Spring 自动注入 userRepository 依赖
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public List<User> getAllUsers() {
        return userRepository.getAllUsers();
    }
}

在上面的代码中,UserService类有一个依赖项userRepository,它使用了构造函数注入。通过在构造函数上添加@Autowired注解,我们告诉Spring要将UserRepository的实例注入到UserService中。

Spring的工作原理:

当您创建一个Spring应用程序上下文并初始化它时,Spring会扫描应用程序中的所有类,查找被@Autowired注解标记的依赖关系,并尝试自动解析并注入这些依赖关系。

在这个示例中,当您调用context.getBean(UserService.class)来获取UserService的实例时,Spring会首先查找UserRepository的实例并创建它。然后,Spring会自动将UserRepository的实例传递给UserService的构造函数,因为构造函数上标记有@Autowired注解。

这样,UserService类就拥有了一个userRepository的实例,可以使用它来执行数据库操作,而无需手动创建UserRepository的实例。

总之,@Autowired注解告诉Spring要在类中自动注入依赖项,从而简化了依赖管理的过程,提高了代码的可维护性和可测试性。Spring会负责创建并注入依赖项,使您的代码更加模块化和灵活。如果仍有疑问,请随时提出。

@AutoWired原生代码对比

如果没有使用Spring的@Autowired注解,您可以使用原生的Java代码手动进行依赖注入。以下是一个简单的示例,展示了如何在原生Java中手动进行构造函数注入:

假设有以下两个类:UserServiceUserRepository

public class UserRepository {
    public List<User> getAllUsers() {
        // 模拟从数据库中获取用户数据
        return Arrays.asList(new User("Alice"), new User("Bob"));
    }
}

public class UserService {
    private UserRepository userRepository;

    // 构造函数注入,手动传入 UserRepository 的实例
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public List<User> getAllUsers() {
        return userRepository.getAllUsers();
    }
}

在这个示例中,UserService类的构造函数接受一个UserRepository的实例作为参数,手动进行了依赖注入。当您需要创建UserService的实例时,您需要提供一个合适的UserRepository实例:

public class MainApp {
    public static void main(String[] args) {
        UserRepository userRepository = new UserRepository(); // 手动创建 UserRepository 实例
        UserService userService = new UserService(userRepository); // 手动注入 UserRepository 实例

        List<User> users = userService.getAllUsers();

        for (User user : users) {
            System.out.println("User: " + user.getName());
        }
    }
}

在原生Java中,您需要手动创建并管理依赖项的实例,并将它们传递给需要它们的类的构造函数。这种方式可以实现依赖注入,但相对于使用Spring的@Autowired注解来说,会更加繁琐和不灵活,尤其在有大量依赖关系的复杂应用程序中。Spring的依赖注入机制大大简化了这个过程,提高了代码的可读性和可维护性。

三 Spring底层实现原理

Spring框架的核心功能是依赖注入(DI),其中@Autowired注解是实现依赖注入的关键之一。让我们来分析Spring框架底层是如何实现自动查找和创建依赖项的。

1. 类路径扫描和组件注册

Spring在启动时会扫描指定的包路径以查找类,特别是那些被@Component及其派生注解(如@Service@Repository等)标记的类。这些标记表示这些类是Spring管理的组件。Spring将这些组件注册到一个内部的容器中,以便后续的依赖注入和其他操作。

2. Bean的定义

在Spring中,被注册的组件称为Bean。每个Bean都有一个与之关联的Bean定义(BeanDefinition),该定义包含有关Bean的元数据,如类名、作用域、构造函数参数、属性等信息。

3. 依赖注入

当您在类的构造函数、成员变量、Setter方法或方法参数上使用@Autowired注解时,Spring会根据Bean定义中的信息来确定要注入的依赖项。

  • 构造函数注入:如果构造函数上使用了@Autowired,Spring会查找匹配的Bean定义,并使用构造函数注入Bean。

  • 成员变量注入:如果成员变量上使用了@Autowired,Spring会通过反射设置成员变量的值,将匹配的Bean注入到成员变量中。

  • Setter方法注入:如果Setter方法上使用了@Autowired,Spring会调用Setter方法并将匹配的Bean注入到属性中。

  • 方法参数注入:如果方法参数上使用了@Autowired,Spring会查找匹配的Bean定义,并将匹配的Bean注入到方法的参数中。

4. 依赖解析

Spring使用Bean定义的信息来解析依赖关系。它会检查每个标有@Autowired的地方,然后查找匹配的Bean定义,创建相应的Bean实例,并将其注入到需要的位置。

5. 循环依赖处理

Spring还处理了循环依赖的情况。如果A类依赖于B类,而B类又依赖于A类,Spring会使用代理对象来解决这种循环依赖,确保每个Bean都能够正常创建。

6. Bean的生命周期管理

一旦依赖项被注入,Spring会管理Bean的生命周期,包括初始化和销毁阶段。

总的来说,Spring框架通过反射、Bean定义、组件扫描和依赖解析等技术,实现了@Autowired注解的功能,从而使依赖项的自动查找、创建和注入成为可能。这个过程在Spring的核心容器中进行,主要包括ApplicationContextBeanFactory等组件的协作。虽然具体的实现涉及大量的代码和细节,但这个过程的核心思想是在组件扫描的基础上,自动处理依赖关系,使应用程序的开发更加便捷和模块化。

你可能感兴趣的:(spring,java,后端)