Java笔记(八):spring IoC

2023/4/15 更新

对spring进行重新认识

1、spring是什么?

spring是一个开源的java框架 用于提高开发效率和系统的可维护性 它的显著特点是支持IoC和AOP,可以很方便的对数据库进行访问。

Spring5.x版本包含的组件
2、Spring主要组件的功能

Core Container

spring-core : Spring框架基本的核心工具类
spring-beans : 提供对bean的创建、配置、管理等功能的支持
spring-context : 提供对国际化、事件传播、资源加载等功能的支持
spring-expression : 提供对表达式语言SpEL的支持 只依赖于core模块 不依赖于其它模块

AOP

spring-aspects 为AspectJ的集成提供支持
spring-aop 提供面向切面的编程实现
spring-instrument 提供为JVM添加代理的功能

Data Access/Integration

spring-jdbc 提供对数据库访问的抽象JDBC
spring-tx 提供对事务的支持
spring-orm 提供对Hibernate JPA iBatis等ORM框架的支持(解决面向对象与关系型数据库存在的互不匹配现象的技术,主要包括Hibernate iBATIS mybatis eclipseLink JFinal)

Spring Web

spring-web 对Web功能的实现提供一些最基础的支持
spring-webmvc 提供对Spring mvc的实现
spring-websocket 提供对WebSocket的支持 WebSocket可以让客户端和服务段双向通信
spring-webflux 提供对WebFlux的支持 WebFlux是Spring Framework 5.0中引入的新的响应式框架

3、什么是MVC?

MVC是一种架构

MVC
spring MVC提供对MVC的支持

4、spring boot是什么?

Spring的开发配置非常繁琐 Spring boot用于简化Spring的开发 尤其是减少配置文件


IoC容器

4.15更新 重新理解IoC

为什么叫控制反转?按理说,在程序中生成一个对象与否是我们也就是程序员来控制的。

比如你需要new一个对象,那么就应该写 A a = new A();

如果发现 A 这个类没有B不行,会报错,那么还需要把B对象也实例化出来,才能让程序跑起来。

没有IoC
这样做恐怕在现在的项目是非常难用的,一个service的对象实例化,恐怕光是new各种依赖的对象就会让程序员疯掉了。

那么如果让Spring来帮我们管理创建对象的过程呢?

有IoC容器的情况
IoC容器帮助我们把需要实例化的对象给创建好并且存储起来了 需要的时候直接提供给需要的对象,直接组装就完事了。这就是在使用spring的过程中常见的依赖注入的操作。

以前使用XML来配置bean,现在我们更多的使用各种注解来告诉Spring,哪些对象是需要被自动装配的,比如@Component @Bean 注入的时候则使用@Autowired @Resource来进行依赖注入,这样就大大简化了代码,也减少了各种繁琐的操作。

什么是Spring bean?

Bean就是被IoC容器管理的对象

org.springframework.bean和org.springframework.context实现了IoC。

注入Bean的注解有@Autowired 和@Resource

Autowired是spring提供的注解,Resource是JDK提供的注解


容器是一种为某种特定组件的运行提供必要支持的一个软件环境 IoC指的是Inversion of Control

组件在IoC容器中被装配出来需要某种“注入”机制

比如某个组件需要datasource 它自己并不会去创建datasource 而是等待外部去注入datasource

这样不同的组件就可以实现资源的共享 另一个组件同样可以注入datasource

依赖注入方法1 set函数

public class BookService {
    private DataSource dataSource;

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }
}

方法2 构造方法

public class BookService {
    private DataSource dataSource;

    public BookService(DataSource dataSource) {
        this.dataSource = dataSource;
    }
}

方法3 在配置文件中组装bean

<bean id="userService" class="com.itranswarp.learnjava.service.UserService">
        <property name="mailService" ref="mailService" />
</bean>

在编写好配置文件后 可以加载配置文件自动生成bean

ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");

取出bean

// 获取Bean:
UserService userService = context.getBean(UserService.class);
// 正常调用:
User user = userService.login("bob@example.com", "password");

除了ApplicationContext之外,还有一种容器叫BeanFactory

BeanFactory factory = new XmlBeanFactory(new ClassPathResource("application.xml"));
MailService mailService = factory.getBean(MailService.class);

Annotation配置

使用配置文件的方法非常繁琐 我们可以使用注解来简化这个过程

@Component
public class MailService {
	...
}

使用该注解定义了一个Bean 默认名称为mailservice

接着给另一个bean添加注解@Autowired

@Component
public class UserService {
    @Autowired
    MailService mailService;

    ...
}

使用@Autowired可以简化注入

@Configuration为配置类
@ComponentScan告诉容器自动搜索当前类所在的包以及子包 把标注为@component的Bean自动创建出来 根据@Autowired进行装配

使用Annotation配合自动扫描能大幅简化Spring的配置,我们只需要保证:

  • 每个Bean被标注为@Component并正确使用@Autowired注入;
  • 配置类被标注为@Configuration@ComponentScan
  • 所有Bean均在指定包以及子包内。

注入List

如何把接口相同但实现不同的类注入到list中

首先定义一个接口

public interface Validator {
    void validate(String email, String password, String name);
}

接着对该接口进行不同的实现

@Component
public class EmailValidator implements Validator {
    public void validate(String email, String password, String name) {
        if (!email.matches("^[a-z0-9]+\\@[a-z0-9]+\\.[a-z]{2,10}$")) {
            throw new IllegalArgumentException("invalid email: " + email);
        }
    }
}

@Component
public class PasswordValidator implements Validator {
    public void validate(String email, String password, String name) {
        if (!password.matches("^.{6,20}$")) {
            throw new IllegalArgumentException("invalid password");
        }
    }
}

@Component
public class NameValidator implements Validator {
    public void validate(String email, String password, String name) {
        if (name == null || name.isBlank() || name.length() > 20) {
            throw new IllegalArgumentException("invalid name: " + name);
        }
    }
}

我们在注入这些bean时这样写

@Component
public class Validators {
    @Autowired
    List<Validator> validators;

    public void validate(String email, String password, String name) {
        for (var validator : this.validators) {
            validator.validate(email, password, name);
        }
    }
}

所有类型为Validator的Bean会配装为一个List注入进来

使用oder注解可以指定list中bean的顺序

使用@Autowired(required=false)来表示该注入是可选的

创建第三方bean

我们可以在@Configuration类中编写一个java方法创建并返回它 使用@Bean注解

@Configuration
@ComponentScan
public class AppConfig {
    // 创建一个Bean:
    @Bean
    ZoneId createZoneId() {
        return ZoneId.of("Z");
    }
}

初始化和销毁

@PostConstruct用于初始化 @PreDestroy用于销毁

@Component
public class MailService {
    @Autowired(required = false)
    ZoneId zoneId = ZoneId.systemDefault();

    @PostConstruct
    public void init() {
        System.out.println("Init mail service with zoneId = " + this.zoneId);
    }

    @PreDestroy
    public void shutdown() {
        System.out.println("Shutdown mail service");
    }
}

这样的Bean的执行流程为
1、构造方法
2、根据@Autowired注入
3、初始化
4、销毁容器

别名

类型相同的Bean 在创建Bean时可以使用别名区分

@Configuration
@ComponentScan
public class AppConfig {
    @Bean("z")
    ZoneId createZoneOfZ() {
        return ZoneId.of("Z");
    }

    @Bean
    @Qualifier("utc8")
    ZoneId createZoneOfUTC8() {
        return ZoneId.of("UTC+08:00");
    }
}

在注入时

@Component
public class MailService {
	@Autowired(required = false)
	@Qualifier("z") // 指定注入名称为"z"的ZoneId
	ZoneId zoneId = ZoneId.systemDefault();
    ...
}

可以把某个bean指定为@Primary

@Configuration
@ComponentScan
public class AppConfig {
    @Bean
    @Primary // 指定为主要Bean
    @Qualifier("z")
    ZoneId createZoneOfZ() {
        return ZoneId.of("Z");
    }

    @Bean
    @Qualifier("utc8")
    ZoneId createZoneOfUTC8() {
        return ZoneId.of("UTC+08:00");
    }
}

没有指定bean的名字时将注入primary的bean

FactoryBean

spring提供了工厂模式 使用FactoryBean接口

@Component
public class ZoneIdFactoryBean implements FactoryBean<ZoneId> {

    String zone = "Z";

    @Override
    public ZoneId getObject() throws Exception {
        return ZoneId.of(zone);
    }

    @Override
    public Class<?> getObjectType() {
        return ZoneId.class;
    }
}

注入配置

使用@PropertySource可以自动读取配置文件 再使用@Value注入

@Configuration
@ComponentScan
@PropertySource("app.properties") // 表示读取classpath的app.properties
public class AppConfig {
    @Value("${app.zone:Z}")
    String zoneId;

    @Bean
    ZoneId createZoneId() {
        return ZoneId.of(zoneId);
    }
}
  • "${app.zone}"表示读取key为app.zone的value,如果key不存在,启动将报错;
  • "${app.zone:Z}"表示读取key为app.zone的value,但如果key不存在,就使用默认值Z。

条件装配

根据注解@Profile可以决定在当前环境下是否创建bean

使用注解Conditionnal则可以要求在满足某个条件时才创建bean


4.15更新

什么是spring AOP

面向切面编程,将与业务无关,但一些公用的部分封装起来,比如事务处理、日志管理、权限控制等等。

一些常见术语:

AOP术语
AspectJ是目前java生态系统中最完整的AOP框架,并且相对简单易用。

AspectJ定义的通知类型有

  • Before:前置通知
  • After:目标对象的方法调用之后触发
  • AfterReturning:返回通知,目标对象的方法调用完成,返回结果值后触发
  • AfterThrowing:异常通知,目标对象的方法运行中抛出/触发异常后触发。
  • Around:环绕通知,在目标对象的方法调用前后触发。

待更新具体例子

什么是spring mvc?

mvc
MVC模式下 我们一般把后端拿项目分为Service层 Dao层 Entity层 Controller层

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值