SSM速通1(1/2)

Spring核心注解详解

接下来是对ssm的学习笔记的发布,发布完之后将发布对黑马点评的优化

一、spring

Spring 是一个开源的、轻量级的 Java 企业级应用开发框架,它的核心是 IoC(控制反转)AOP(面向切面编程),旨在简化 Java 开发,尤其是构建大型、复杂、可维护的系统。

Spring 是一个帮你 管理对象、处理依赖、提供基础设施 的“大管家”,让你专注于写业务逻辑,而不是处理底层细节。

1、Spring 的核心功能:

功能说明
IoC(控制反转)你不再用 new 创建对象,Spring 帮你创建并管理(也叫 DI:依赖注入)。
AOP(面向切面编程)把“日志、事务、权限”等横切关注点从业务代码中抽离出来,解耦。
事务管理不用手动写 beginTransaction()commit(),Spring 帮你统一管理。
MVC 框架Spring MVC 提供了 Web 层开发的支持,替代传统的 Servlet/JSP。
集成支持轻松整合 MyBatis、JPA、Redis、RabbitMQ、Kafka 等。

Spring的MVC架构

层级职责一句话核心注解 / 组件开发提示
表现层Web / Controller接收 HTTP 请求,参数校验,返回响应@Controller / @RestController<br>@RequestMapping 系列<br>@RequestBody / @PathVariable<br>@ExceptionHandler<br>HandlerInterceptor只写“与协议相关”代码,不写业务
业务层Service核心业务逻辑、事务、规则校验@Service<br>@Transactional<br>领域模型 / BusinessException事务注解放在这一层;一个 Service 只干一件事
数据访问层 (持久层)DAO / Repository与数据库交互,CRUD@Repository<br>MyBatis / JPA / Hibernate<br>Entity / Mapper / DataSource不出现任何 Web 类;SQL 只在这一层
基础设施层 Infrastructure跨层通用技术能力:配置、缓存、安全、监控、第三方集成@Configuration<br>CacheManager<br>Security / 日志 / 消息队列 / 邮件被上层通过 Spring 容器注入,不反向依赖

2、Spring 的模块结构(部分)

  • Spring Core:IoC 容器,最核心。

  • Spring AOP:面向切面编程。

  • Spring MVC:Web 开发框架。

  • Spring Boot:快速构建 Spring 应用,约定大于配置

  • Spring Cloud:微服务架构一站式解决方案。

  • Spring Data:简化数据库访问。

  • Spring Security:安全框架,认证+授权。

3、IoC 和 DI

IoC 和 DI 是 Spring 框架的 核心思想,它们不是新技术,而是一种 设计思想,目的是 解耦

术语全称中文本质一句话理解
IoCInversion of Control控制反转设计思想“你别主动 new 对象,交给容器来管。”
DIDependency Injection依赖注入实现手段“容器把依赖‘送’进来,而不是你自己去‘拿’。”

IoC 是思想,DI 是手段;Spring 通过 DI 实现了 IoC,让你不再 new 对象,专心写业务。

二、容器

1、注册组件

注册 Bean(告诉容器“这个类你要管”)

以下提供部分常见注解

注解写在哪儿典型场景备注
@Bean配置类的方法上第三方 jar 里的类、需要复杂构造逻辑的类方法返回值=Bean,方法名=默认 id
@Configuration类上标记“这个类是配置类”,内部常含 @Bean本身也会被 @ComponentScan 扫到
@Component自己写的类通用型组件衍生三个子注解:@Service、@Repository、@Controller
@ComponentScan配置类上指定扫包路径,批量注册不写 value 默认扫当前包及其子包
@Import配置类上显式导入另一个配置类用于拆分大型配置
@Conditional...与上面任意注解组合按条件注册如 @ConditionalOnClass、 @ConditionalOnProperty
1.1、@Bean
  package org.example.spring01ioc;
  ​
  import org.example.spring01ioc.bean.Animal;
  import org.example.spring01ioc.bean.Person;
  import org.springframework.boot.SpringApplication;
  import org.springframework.boot.autoconfigure.SpringBootApplication;
  import org.springframework.context.ConfigurableApplicationContext;
  import org.springframework.context.annotation.Bean;
  ​
  import java.util.Map;
  ​
  @SpringBootApplication
  // 启动类的名字和项目的名字一致
  public class Spring01IocApplication {
  ​
  //  所有组件都是单例的,在 ioc 容器启动时就会创建好所有组件对象,每次从容器中获取组件都是从容器中直接获取到组件对象的引用
      public static void main(String[] args) {
  //  启动 Spring Boot 应用,每次启动都会生成一个新的 ApplicationContext(Spring 应用上下文对象)IOC 容器
  //  可使用 ConfigurableApplicationContext 来获取 IOC 容器
          ConfigurableApplicationContext ioc = SpringApplication.run(Spring01IocApplication.class, args);
  //  容器创建出来即有默认组件,使用 getBean 方法可以获取到一个默认组件,使用 getBeanDefinition 方法可以获取到多个默认组件
          Object object = ioc.getBean("spring01IocApplication");
          System.out.println("getBean获取单个默认组件:" + object);
          String[] beanNames = ioc.getBeanDefinitionNames();
          for (String beanName : beanNames) {
              System.out.println("name= " + beanName);
          }
  ​
  //        组件名字全局唯一;组件名重复了,一定只会给容器中放一个最先声明的那个。
  //        小结:从容器中获取组件
  //        1)、组件不存在,抛异常:NoSuchBeanDefinitionException
  //        2)、组件不唯一:
  //              - 按照类型只要一个:否则抛异常:NoUniqueBeanDefinitionException
  //              - 按照名字只要一个:精确获取到指定对象
  //              - 多个:返回所有组件的 List 集合
  //        3)、组件唯一存在,正确返回。
  ​
  ​
  //  按照组件名字获取一个组件
          Person result1 = ioc.getBean("person1", Person.class); // 相当于获取后再强转为 Person 类型
          System.out.println("getBean 获取 person1 组件:" + result1);
  //  按照组件类型获取一个组件
  //        Animal result2 = ioc.getBean(Animal.class); // 当容器中只有一个组件时可以直接按照组件类型获取组件
  //        System.out.println("getBean 获取 animal2 组件:" + result2);
  //  按照组件类型获取该类型所有组件
          Map<String, Person> result3 = ioc.getBeansOfType(Person.class); // 当容器中只有一个组件时可以直接按照组件类型获取组件
          System.out.println("getBean 获取 person2 组件:" + result3);
  //  按照组件类型+组件名字获取一个组件
          Person result4 = ioc.getBean("person2", Person.class); // 当容器中只有一个组件时可以直接按照组件类型获取组件
          System.out.println("getBean 获取 person2 组件:" + result4);
      }
  ​
  //  给 ioc 容器创建自己的组件,组件名就是我们创建的方法名或者是我们 @Bean 中指定的组件名
  //  1、创建一个名为 person1 的组件,使用 @Bean 注解添加到 ioc 容器中
      @Bean
      public Person person1() {
          Person person = new Person();
          person.setName("张三");
          person.setAge(18);
          person.setGender("男");
          return person;
      }
  //  2、创建一个名为 person2 的组件,使用 @Bean 注解添加到 ioc 容器中
      @Bean("person2")
      public Person person() {
          Person person = new Person();
          person.setName("李四");
          person.setAge(18);
          person.setGender("男");
          return person;
      }
  //  3、创建一个名为 animal1 的组件,使用 @Bean 注解添加到 ioc 容器中
      @Bean("animal1")
      public Animal animal() {
          Animal animal = new Animal();
          animal.setName("狗");
          animal.setAge(8);
          animal.setGender("公");
          return animal;
      }
  }
1.2、@Configuration

当要配置的组件过多时,可使用@Configuration来说明类为配置类,即将原来所有的@Bean组件分类放入对应的配置类中

1.3、@Component及其衍生的三个注解

在Spring框架中,@Component 是一个核心注解,用于自动检测和配置Spring容器中的Bean。它是Spring组件扫描的基础,任何被 @Component 标注的类都会被Spring识别为一个Bean,并注册到应用上下文中。

注解说明使用场景
@Component通用型组件注解任何Spring管理的组件
@Controller用于表现层(Spring MVC)标注控制层组件(如Spring MVC的控制器)
@Service用于业务逻辑层标注服务层组件
@Repository用于持久层标注DAO层组件(数据库访问层)

简单来说,该类注解是给我们程序员自己看的,方便我们管理并识别代码用途,分别对应 Spring 中的 MVC 架构

即完全可以使用 @Component 代替所有其他衍生出来的三个注解,本质上都是Spring的Bean。

虽然 @Component 可以替代 @Controller@Service@Repository,但使用衍生注解有以下好处:

优点说明
语义清晰明确区分层次结构,代码更易读
功能增强比如 @Repository 还能自动转换持久层异常为Spring的 DataAccessException
AOP支持不同层可以应用不同的切面逻辑(如事务、日志、安全等)

1.4、@ComponentScan

默认我们的分层注解 (@Component及其衍生的三个注解) 要想生效的前提,我们的组件必须在主程序所在包及其子包结构下,否则无法将其注入我们的 ioc容器,为了解决这个问题,提出@ComponentScan,可自定义你要扫描的包的范围,不过不建议使用,建议老实将组件放在主程序所在包及其子包结构下,条理清晰,可读性好

1.5、@Import

在Java中,@Import注解主要用于Spring框架中,用于导入配置类或组件类。只要在整个项目中导入过一次即可,因为组件是单实例的,建议在config包下单独定义一个类用于使用类似注解

  @Configuration
  @Import(OtherConfig.class)
  public class AppConfig {
      // ...
  }
1.6、其他注解
①、@Scope

@Scope 是 Spring 框架专门用来“定制 Bean 的生命周期与可见范围”的注解(XML 里也有 <bean scope="..."> 等价写法)。默认情况下,Spring 容器中所有 Bean 都是单例singleton),即容器启动时创建一次,之后人人共享。通过 @Scope 可以把某个 Bean 改成“每次新建”、“每个 Web 请求一个”、“每个会话一个”等策略。

Ⅰ、基本语法
  @Component
  @Scope("prototype")          // 重点在这行
  public class ShopCart {
      // 每次注入都拿到全新对象
  }
  ​
  // 或是 Java 的配置类
  @Configuration
  public class AppConfig {
      @Bean
      @Scope("prototype")
      public ShopCart shopCart() {
          return new ShopCart();
      }
  }
Ⅱ、范围
取值含义适用场景
singleton默认:容器里只存在一份,线程间共享无状态 Service、DAO、工具类
prototype每次 getBean() / 注入都新建实例有状态、非线程安全对象
request每个 HTTP 请求一个实例(Web 环境)同一次请求内共享数据
session每个 HTTP Session 一个实例用户级数据,如登录购物车
application整个 ServletContext 生命周期一个实例全局应用数据,极少用
websocket每个 WebSocket 连接一个实例STOMP 全双工通信

其中常见的是 singleton 和 prototype

  • singleton 创建的是单实例的组件对象,容器启动时会创建单实例对象,容器启动完成前创建完成,且容器中只存在一份

  • prototype 创建的是非单实例的组件对象,容器启动时不会创建非单实例对象,什么时候获取什么时候创建

②、@Lazy

@Lazy 是 Spring 的“懒加载”开关:默认单例 Bean 在容器启动时就被实例化;加上 @Lazy 后,第一次真正用到时才创建(延迟初始化)。

@Lazy 在单例模式下可实现组件什么时候获取什么时候创建,而不是在容器启动完成前创建完成,所以可配合 @Scope(singleton)实现懒加载,

Ⅰ、基本语法
  
  @Component
  @Lazy          // 延迟初始化
  public class HeavyService {
      public HeavyService() {
          System.out.println("HeavyService 实例化 —— 很耗时!");
      }
  }
  ​
  // 或是 Java 的配置类
  @Configuration
  public class AppConfig {
    @Bean
    @Lazy
    public HeavyService heavyService() {
        return new HeavyService();
    }
  }
Ⅱ、控制范围
  • 只加在 Bean 定义上:仅对该 Bean 生效

  • 加在配置类上:整个配置类里所有 @Bean 都变懒

      
      @Configuration
      @Lazy
      public class AppConfig {
          @Bean public BeanA a(){ return new BeanA(); }
          @Bean public BeanB b(){ return new BeanB(); } // 全部懒加载
      }

  • 加在 @ComponentScan:扫描到的组件统一懒加载

      @SpringBootApplication
      @Lazy  // 整个项目单例都懒加载(慎用)
      public class App { }

③、@FactoryBean

FactoryBean是Spring框架中的一个重要接口,它提供了一种更灵活的方式来创建和管理Bean实例。当需要复杂对象的创建逻辑或需要隐藏某些创建细节时,FactoryBean非常有用。

实现它后,Spring 容器会先实例化你的工厂对象,然后调用工厂方法得到真正的业务 Bean,最终把工厂产出的实例注册进容器。

即并非将FactoryBean 注入容器,而是其生产的实例注入容器

Ⅰ、接口中的方法

FactoryBean是一个接口,其中定义了三个方法

  
  public interface FactoryBean<T> {
      T getObject() throws Exception;   // 返回由FactoryBean创建的实例,即生产真正的 Bean
      Class<?> getObjectType();         // 返回创建对象 Bean 的类型
      boolean isSingleton();            // 指示是否是单例,默认 true,即返回的是单例的
  }
Ⅱ、举例

基本实现示例

 
  public class ConnectionFactoryBean implements FactoryBean<Connection> {
  ​
      private String url = "jdbc:mysql://localhost:3306/demo";
  ​
      @Override
      public Connection getObject() throws Exception {
          return DriverManager.getConnection(url, "root", "root");
        // 返回实例,即真正的 Bean 是
      }
  ​
      @Override
      public Class<?> getObjectType() {
          return Connection.class;
        // 返回的是 Connection 的类型
      }
  ​
      @Override
      public boolean isSingleton() {
          return false;   
        // 每次都要新连接,即最终返回的不是单例的,是多实例的,什么时候获取什么时候创建
      }
  }

在Spring配置中注册

  
  @Configuration
  public class AppConfig {
      @Bean
      public ConnectionFactoryBean connectionFactoryBean() {
          return new ConnectionFactoryBean();   // 先造工厂
      }
  }

使用端在应用中可以通过以下方式获取FactoryBean创建的Bean

  @Autowired
  // 直接注入目标对象
  private Connection conn;   // 这里注入的是工厂产出的 Connection,而非工厂本身!

最终我们是通过接口中三个方法的 getObject 方法获取对象

④、@Conditonal

@Conditional是Spring 4.0引入的核心注解,它允许条件化地注册Bean,即只有满足特定条件时,相关的Bean定义才会被注册到Spring容器中。

Ⅰ、基本用法
  
  @Configuration
  public class AppConfig {
      
      @Bean
      @Conditional(MyCondition.class)
      public MyService myService() {
          return new MyServiceImpl();
      }
  }

其中MyCondition需要实现Condition接口:

 
  public class MyCondition implements Condition {
      @Override
      public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
          // 在这里实现条件判断逻辑
          return true; // 返回true则创建Bean,false则不创建
      }
  }
Ⅱ、Spring Boot提供的条件注解
  • @ConditionalOnClass: 当类路径存在指定类时生效

  • @ConditionalOnMissingClass: 当类路径不存在指定类时生效

  • @ConditionalOnBean: 当容器中存在指定Bean时生效

  • @ConditionalOnMissingBean: 当容器中不存在指定Bean时生效

  • @ConditionalOnProperty: 根据配置属性决定是否生效

  • @ConditionalOnResource: 当指定资源存在时生效

  • @ConditionalOnWebApplication: 当是Web应用时生效

  • @ConditionalOnNotWebApplication: 当不是Web应用时生效

  • @ConditionalOnExpression: 基于SpEL表达式

当然除了spring boot创建的条件注解,我们也可以自定义条件注解

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值