Spring:Ioc

news2024/11/18 20:37:00

目录

一、Spring的两大核心机制

1、IOC/ DI:控制反转和依赖注入

2、AOP:面向切面编程

二、IOC/ DI:控制反转 / 依赖注入:

1、bean的基础配置:

2、bean的实例化

 3、配置bean

4、依赖注入方式

 三、注解开发定义bean

注解依赖注入


Spring设计理念

Spring是面向Bean的编程

一、Spring的两大核心机制

1、IOC/ DI:控制反转和依赖注入

控制反转:将对象的创建由代码本身转移到外部容器———解耦

配置文件中配置bean

依赖注入:在使用时,从外部容器获取需要的对象

在bean中注入属性值/普通数据/对象、构造方法的参数值/普通数据/对象

2、AOP:面向切面编程

将散布在程序中的公共功能集中处理

在不改变原有逻辑/功能的前提下对系统/功能做增强处理(前置、后置、异常、最终、

环绕)

Spring的优点

1、低侵入式设计

2、独立于各种应用服务器

3、依赖注入特性将组件关系透明化,降低了耦合度

4、面向切面编程特性允许将通用任务进行集中式处理

5、与第三方框架的良好整合

二、IOC/ DI:控制反转 / 依赖注入:

将组件对象的控制权从代码本身转移到外部容器组件化的思想:分离关注点,使用接口,不再关注

实现

依赖的注入:将组件的构建和使用分开

目的:解耦合,实现每个组件时只关注组件内部的事情

首先要加Spring的依赖

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>5.1.9.RELEASE</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.1.9.RELEASE</version>
    </dependency>

创建applicationContext.xml文件

在applicationContext.xml文件中编写bean

1、bean的基础配置:

类型:标签

所属:beans标签

功能:定义Spring核心容器管理对象

格式:

<beans>
    <bean/>
    <bean></bean>
</beans>

属性列表:

idbean的id,使用容器可以通过id值获取对应的bean,在一个容器中id值唯一
classbean的类型,即配置的bean的全路径类名

示例:

<bean id="bookDao" class="com.demo.dao.impl.BookDaoImpl" />
<bean id="bookService" class="com.demo.service.impl.BookServiceImpl"></bean>

bean别名配置

实用name属性,所属bean标签

功能:定义bean的别名,可定义多个,用逗号(,)或分号(;)或空格分隔

示例:

<bean id="bookDao" name="dao bookDaoImpl" class="com.demo.dao.imp1.BookDaoImpl" />
<bean name="service, bookServiceImpl" class="com.demo.service.impl.BookServiceImpl" />

 注意:

获取bean无论是通过id还是name获取,如果无法获取到,将抛出异常NoSuchBeanDefinitionExceptionNoSuchBeanDefinitionException: No bean named 'bookServiceImpl' available

bean的作用范围

scope:所属bean标签

功能:singleton:单例            prototype:非单例

示例:

<bean id="bookDao" class="com.demo.dao.impl.BookDaoImpl" scope="prototype"/>

2、bean的实例化

bean本质上就是对象,创建bean使用构造方法完成

bean实例化的四种方式

1、使用构造方法(默认无参方法)常用

提供可访问的构造方法

public class BookDaoImpl implements BookDao {
    public void save() {
        system.out.println( "book dao save ..." );
    }
}

配置bean

<bean id="bookDao" class="com.demo.dao.impl.BookDaoImpl“/>

注意:无参构造方法如果不存在,将抛出异常BeanCreationException

2、使用静态工厂(了解)

public class OrderDaoFactory {
    public static orderDao getorderDao(){
        return new OrderDaoImpl();
    }
}

配置bean

<bean id="orderDao" factory-method="getorderDao" 
class="com.itheima.factory.orderDaoFactory"/>

3、使用实例工厂(了解)

public class UserDaoFactory {
    public UserDao getUserDao(){
        return new UserDao1mpl();
    }
}

配置bean

<bean id="userDaoFactory" class="com.demo.factory.UserDaoFactory" />
<!--userDaoFactory  上面的和下面的是一个-->
<bean id="userDao"
factory-method="getUserDao"factory-bean="userDaoFactory"/>

 4、使用FactoryBean

public class UserDaoFactoryBean implements FactoryBean<UserDao>{
    public UserDao getobject() throws Exception {
        return new UserDaoImpl();
}
    public class<?> getObjectType() {
        return UserDao.class;
    }
}

 3、配置bean

<bean id="userDao" class="com.demo.factory.UserDaoFactoryBean"/>

bean的生命周期:bean从创建到销毁的整体过程

初始化容器

1.创建对象(内存分配)

2.执行构造方法

3.执行属性注入( set操作)

4.执行bean初始化方法

使用bean:执行业务操

关闭/销毁容器:执行bean销毁方法

bean生命周期控制:在bean创建后到销毁前做一些事情

提供生命周期空值方法

public class BookDaoImpl implements BookDao {

    public void save( ) {
            system.out.println("book dao save ..." );
    }

    public void init(){
        system.out.print1n( "book init ..." );
    }

    public void destory(){
    system.out.println( "book destory ..." );
    }
}

配置生命周期控制方法

<bean id="bookDao" class="com.demo.dao.impl.BookDaoImpl" init-method="init" destroy-method="destory "/>

bean销毁时机

容器关闭前触发bean的销毁

关闭容器方式∶

1、手工关闭容器:configurableApplicationContext接口close()操作

2、注册关闭钩子,在虚拟机退出前先关闭容器再退出虚拟机:ConfigurableApplicationContext接口registerShutdownHook()操作
 

public class AppForLifecycle {
    public static void main( string[] args ) {
        ClassPathXmlApplicationContext ctx = new classPathXmlApplicationContext(     
             "applicationContext.xml");
    ctx.close();
    }
}

4、依赖注入方式

setter注入:简单类型  引用类型

构造器注入:简单类型   引用类型

setter注入-简单类型

在bean中定义引用类型属性并提供可访问的set方法

public class BookDaoImpl implements BookDao {
    private int connectionNumber;
    public void setConnectionNumber(int connectionNumber) {
        this.connectionNumber = connectionNumber;
    }
}

配置中使用property标签value属性注入简单类型数据

<bean id="bookDao" class="com.demo.dao.impl.BookDaoImpl">
    <property name="connectionNumber" value="10" />
</bean>

setter注入-引用类型

在bean中定义引用类型属性并提供可访问的set方法

public class BookServiceImpl implements BookService{
    private BookDao bookDao;
    public void setBookDao(BookDao bookDao) {
        this.bookDao = bookDao;
    }
}

配置中使用property标签ref属性注入引用类型对象

<bean id="bookService" class="com.demo.service.impl.BookServiceImpl">
    <property name="bookDao" ref="bookDao" / >
</bean>
<bean id="bookDao" class="com.demo.dao.impl.BookDaoImpl" />

构造器注入-引用类型(了解)

在bean中定义引用类型属性并提供可访问的构造方法

public class BookServiceImpl implements BookService{
    private BookDao bookDao;
    public BookServiceImpl(BookDao bookDao) {
        this.bookDao = bookDao;
    }
}

配置中使用constructor-arg标签ref属性注入引用类型对象

<bean id="bookService" class="com.demo.service.impl.BookServiceImpl">
<constructor-arg name="bookDao" ref="bookDao" />
</bean>
<bean id="bookDao" class="com.demo.dao.imp1.BookDaoImpl" />

构造器注入-简单类型(了解)

在bean中定义引用类型属性并提供可访问的set方法

public class BookDaoImpl implements BookDao {
    private int connectionNumber;
    public void setConnectionNumber(int connectionNumber) {
        this.connectionNumber = connectionNumber;
}

配置中使用constructor-arg标签value属性注入简单类型数据

<bean id="bookDao" class="com.demo.dao.impl.BookDaoImpl">
    <constructor-arg name="connectionNumber" value="10"/>
</bean>

依赖注入方式选择

1、强制依赖使用构造器进行,使用setter注入有概率不进行注入导致null对象出现

2、可选依赖使用setter注入进行,灵活性强

3、 Spring框架倡导使用构造器,第三方框架内部大多数采用构造器注入的形式进行数据初始化,相对严谨

4、如果有必要可以两者同时使用,使用构造器注入完成强制依赖的注入,使用setter注入完成可选

依赖的注入

5、实际开发过程中还要根据实际情况分析,如果受控对象没有提供setter方法就必须使用构造器注入
6、自己开发的模块推荐使用setter注入

依赖自动装配

IOC容器根据bean所依赖的资源在容器中自动查找并注入到bean中的过程称为自动装配·

自动装配方式:1、按类型(常用)2、按名称3、按构造方法4,不启用自动装配

配置中使用bean标签autowire属性设置自动装配的类型

<bean id="bookDao" class="com.demo.dao.impl.BookDaoImpl" />
<bean id="bookService" class=" com.demo.service.impl.BookServiceImpl" autowire="byType" />

依赖自动装配特征

自动装配用于引用类型依赖注入,不能对简单类型进行操作

使用按类型装配时( byType ) 必须保障容器中相同类型的bean唯一,推荐使用

使用按名称装配时 ( byName )必须保障容器中具有指定名称的bean,因变量名与配置耦合,不推

荐使用

自动装配优先级低于setter注入与构造器注入,同时出现时自动装配配置失效

注入集合对象

注入list对象

<property name="list">
    <list>
        <value>itcast</value>
        <value>demo</value>
    </list>
</property>

注入set对象

<property name="set">
    <set>
        value>itcast</value>
        <value>demo</value>
     < /set>
</ property>

注入map对象

<property name="map" >
    <map>
        <entry key=" country" value="china" />
        <entry key="province" value="henan" />
        <entry key="city" value="kaifeng" />
    </map>
</property>

注入Properties

<property name="properties">
    <props>
        <prop key="country">china</ prop>
        <prop key="province" >henan</ prop>
        <prop key="city">kaifeng</prop>
    </props>
</property>

数据源对象管理

导入druid坐标

<dependency>
    <groupId>com.alibaba</ groupId>
    <artifactId>druid</artifactId>
    <version>1.1.16</version>
</dependency>

配置数据源对象作为spring管理的bean

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver" />
    <property name="url" value="jdbc:mysql://127.0.0.1:3306/spring_db" />
    <property name="username" value="root" />
    <property name="password" value="root" />
</bean>

加载properties文件

开启context命名空间

 使用context命名空间,加载指定properties文件

<context:property-placeholder location="jdbc.properties"/>

使用${ }读取加载的属性值

<property name="username" value="${jdbc.username}"/>

不加载系统属性

<context:property-placeholder location="jdbc.properties" system-properties-mode="NEVER"/>

加载多个properties文件

<context:property-placeholder location="jdbc.properties,msg.properties"/>

加载所有properties文件

<context:property-placeholder location="*.properties"/>

加载properties文件标准格式

<context:property-placeholder location="classpath:*.properties" />

从路径或jar包中搜索并加载properties文件

<context:property-placeholder location="classpath*:*.properties" />

创建容器

方式一:类路径加载配置文件

ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xm1");

方式二:文件路径加载配置文件

ApplicationContext ctx = new FileSystemxmlApplicationContext("D:\\applicationContext.xml" );

方式三:加载多个配置文件

ApplicationContext ctx = new ClassPathXmlApplicationContext("bean1.xm1","bean2.xml" );

获取bean

方式一:使用bean名称获取

BookDao bookDao = (BookDao) ctx.getBean("bookDao");

方式二:使用bean名称获取并指定类型

BookDao bookDao = ctx.getBean("bookDao",BookDao.class);

方式三:使用bean类型获取

BookDao bookDao = ctx.getBean(BookDao.class);

BeanFactory初始化

类路径加载配置文件

Resource resources = new ClassPathResource("applicationContext.xml");
BeanFactory bf = new Xm1BeanFactory(resources);
BookDao bookDao = bf.getBean("bookDao",BookDao.class);
bookDao.save();

BeanFactory创建完毕后,所有的bean均为延迟加载

bean的属性

 依赖注入属性

 三、注解开发定义bean

使用@Component定义bean

@Component("bookDao")
public class BookDaoImpl implements BookDao {
}
@Component
public class BookServiceImpl implements BookService {
}

核心配置文件中通过组件扫描加载bean

<context:component-scan base-package="com.demo"/>

Spring提供@Component注解的三个衍生注解

@Controller:用于表现层bean定义

@service :用于业务层bean定义

@Repository :用于数据层bean定义

@Repository( "bookDao")
public class BookDaoImpl implements BookDao {
}
@Service
public class BookServiceImpl implements BookService {
}

1、纯注解开发

Spring3.0开启了纯注解开发模式,使用Java类替代配置文件,开启了Spring快速开发赛道

Java类代替Spring核心配置文件,

@Configuration注解用于设定当前类为配置类
@ComponentScan注解用于设定扫描路径,此注解只能添加一次,多个数据请用数组格式

@ComponentScan({com.demo.service" , "com.demo.dao"})

读取Spring核心配置文件初始化容器对象切换为读取Java配置类初始化容器对象

//加载配置文件初始化容器
ApplicationContext ctx = new classPathXmlApplicationContext( "applicationContext.xml" );
//加载配置类初始化容器
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);

注解定义bean的作用范围

使用@Scope定义bean作用范围

@Repository
@scope("singleton")
public class BookDaoImpl implements BookDao {
}

注解定义bean生命周期

使用@PostConstruct、@PreDestroy定义bean生命周期

@Repository
@scope("singleton")
public class BookDaoImpl implements BookDao {
    public BookDaoImpl() {
        system.out.println( "book dao constructor ..." );
    }
    @PostConstruct
    public void init(){
        system.out.println( " book init ..." );
    }
    @PreDestroy
    public void destroy(){
        system.out.println( "book destory ..." );
    }
}

注解依赖注入

使用@Autowired注解开启自动装配模式(按类型)

@Service
public class BookServiceImpl implements BookService {
    @Autowired
    private BookDao bookDao;
    public void save() {
        system.out.println("book service save ..." );
        bookDao.save();
    }
}

注意:

1、自动装配基于反射设计创建对象并暴力反射对应属性为私有属性初始化数据,因此无需提供setter方法

2、自动装配建议使用无参构造方法创建对象(默认),如果不提供对应构造方法,请提供唯一的构造方法

使用@Qualifier注解开启指定名称装配bean

@Service
public class BookServiceImpl implements BookService {
    @Autowired
    @Qualifier( "bookDao")
    private BookDao bookDao;
}

注意:@Qualifier注解无法单独使用,必须配合@Autowired注解使用

使用@Value实现简单类型注入

@Repository ("bookDao")
public class BookDaoImpl implements BookDao {
    @Value("10")
    private string connectionNum;
}

使用@PropertySource注解加载properties文件

@Configuration
@ComponentScan("com.demo")
@PropertySource("classpath:jdbc.properties")
public class SpringConfig {
}

注意:路径仅支持单一文件配置,多文件请使用数组格式配置,不允许使用通配符*

第三方bean管理

使用独立的配置类管理第三方bean

方式一:导入式

public class JdbcConfig {
    @Bean
    public DataSource dataSource(){
        DruidDatasource ds = new DruidDataSource();
        ds.setDriverClassName("com.mysql.jdbc.Driver");
        ds.setUrl("jdbc:mysql://localhost: 3306/spring_db");
        ds.setUsername("root");
        ds.setPassword("root");
        return ds;
    }
}

使用@Import注解手动加入配置类到核心配置,此注解只能添加一次,多个数据请用数组格式

@Configuration
@Import(JdbcConfig.class)
public class SpringConfig {
}

方式二:扫描式

使用@ComponentScan注解扫描配置类所在的包,加载对应的配置类信息

@Configuration
@ComponentScan({"com.demo.config","com.demo.service"," com.demo.dao"})
public class springConfig {
}

第三方bean依赖注入

简单类型依赖注入

public class jdbcConfig {
    @Value("com.mysql.jdbc.Driver" )
    private String driver;

    @Value("jdbc:mysql://localhost:3306/spring_db")
    private String url;

    @Value("root")
    private String userName;

    @Value("root")
    private String password;

    @Bean
    public DataSource dataSource(){
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName(driver);
        ds.setUrl(url);
        ds.setUsername(userName);ds.setPassword(userName);
        return ds;
    }
}

引用类型依赖注入

@Bean
public DataSource dataSource(BookService bookService){
    system.out.println(bookService);
    DruidDataSource ds = new DruidDataSource();
    //属性设置
    return ds;
}

引用类型注入只需要为bean定义方法设置形参即可,容器会根据类型自动装配对象

XML配置比对注解配置

功能xml配置注解
定义bean

bean标签

id属性

class属性

@Component

        @Controller

        @Service

        @Repository

@ComponentScan

设置依赖注入setter注入( set方法)
        引用/简单
构造器注入(构造方法)
        引用/简单
自动装配

@Autowired

        @Qualifier

@Value

配置第三方beanbean标签
静态工厂、实例工、FactoryBeanscope属性
@Bean
作用范围scope属性@Scope
生命周期标准接口
        init-method
        destroy-method

@PostConstructor

@PreDestroy



 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/81125.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Spring推断构造方法源码深度解析

文章目录前言思考目标一、bean的实例化入口-createBeanInstance1、源码逻辑思路**核心点&#xff0c;主要做了几件事情**2、instantiateBean-默认无参实例化3、instantiate-实例化4、instantiateClass-实例化类二、获取构造函数候选数组-determineConstructorsFromBeanPostProc…

这十套练习,教你如何用Pandas做数据分析(04)

练习4-Apply函数 探索1960 - 2014 美国犯罪数据 步骤1 导入必要的库 运行以下代码 import numpy as np import pandas as pd 步骤2 从以下地址导入数据集 运行以下代码 path4 ‘…/input/pandas_exercise/pandas_exercise/exercise_data/US_Crime_Rates_1960_2014.csv’…

微信小程序|入门进阶

接下来点击新建项目就可以在主界面中预览到我们的豆瓣电影示例了 小程序开发初体验 Hello world 希望是一个从零到一的转换过程~ 创建项目 接下来创建一个新的项目,理解小程序项目的基本结构和一些基础语法。 官方的开发工具为此准备了一个QuickStart项目。在创建过程中,…

利用jrebel与arthas在centos7 springboot热部署

jrebel 热部署 jrebel在本地是可以class xml一起热部署&#xff0c;但是远程热部署xml不行&#xff0c;所以用arthas代替去热部署xml 1.jrebel 反向代理 因为jrebel是收费插件&#xff0c;所以要高一些小动作咱们才能‘正常’使用&#xff0c;当然你也可以拿别人代理好的操作…

并发基础(五):ThreadPoolExecutor源码解析

尺有所短&#xff0c;寸有所长&#xff1b;不忘初心&#xff0c;方得始终。 请关注公众号&#xff1a;星河之码 在JDK提供的几种线程池技术&#xff0c;除了JDK8新加的newWorkStealingPool之外&#xff0c;其余的几种线程池都是通过ThreadPoolExecutor 来实现线程池技术&#x…

本周大新闻|FDA公布XR认证医疗名单,索尼推数字孪生平台Mapray

本周大新闻&#xff0c;AR方面&#xff0c;苹果首款MR头显或推迟至2023年下半年发布&#xff1b;FDA官网公布经过认真的AR/VR医疗方案名单&#xff1b;索尼预测AR/VR光学发展路径&#xff1b;索尼公布3D数字孪生平台Mapray&#xff1b;索尼公布ToF AR SDK。 VR方面&#xff0c…

Docker简介与安装

一、Docker 简介 1.1 为什么选择Docker? 更高效的利用系统资源更快速的启动时间一致的运行环境持续交付和部署更轻松的迁移更轻松的维护和扩展 1.2 Docker组件 1.2.1 Docker服务器与客户端 Docker是一个客户端-服务器&#xff08;cs&#xff09;架构程序。Docker客户端只…

linux下共享内存和消息队列实现多进程间数据收发

linux下进程通信的方式有很多&#xff0c;共享内存&#xff0c;消息队列&#xff0c;管道等。共享内存可以传输大量数据&#xff0c;但是多个进程同时读取共享内存就会出现脏读&#xff0c;可以借助消息队列实现多进程消息发送和接收。这种组合方式在实际开发中应用还是很多的&…

计算机网络复习(五)

考点&#xff1a;UDP 拥塞控制 TCP三次握手四次握手 P247 熟知端口号 P215 TCP报文计算5-36.假定TCP采用一种仅使用线性增大和乘法减小的简单拥塞控制算法&#xff0c;而不使用慢开始。发送窗口不采用字节为计算单位&#xff0c;而是使用分组pkt为计算单位。在一开始发送窗口为…

时序预测 | MATLAB实现VAR和GARCH时间序列预测

时序预测 | MATLAB实现VAR和GARCH时间序列预测 目录 时序预测 | MATLAB实现VAR和GARCH时间序列预测预测效果基本介绍程序设计VARGARCH参考资料预测效果 基本介绍 机器学习可其用于时间序列问题的分类和预测。在探索时间序列的机器学习方法之前,尝试统计时间序列预测方法,它列…

SQL开窗函数之percent_rank、first_value、nth的用法

开窗函数 当我们需要进行一些比较复杂的子查询时&#xff0c;聚合函数就会非常的麻烦&#xff0c;因此可以使用开窗函数进行分组再运用函数查询。窗口函数既可以显示聚集前的数据&#xff0c;也可以显示聚集后的数据&#xff0c;可以在同一行中返回基础行的列值和聚合后的结果…

微信小程序|反编译

一、下载网易模拟器 MuMu模拟器官网_安卓模拟器_网易手游模拟器 根据自己的系统选择对应的软件进行安装。 安装成功后,如下: 二、再模拟器上面安装对应的软件(微信、RE文件管理器) 1. 打开应用中心,搜索 RE文件管理器和微信,分别进行下载 2. 打开微信,输入帐号进行…

Windows 文件比较工具winmerge

今天下载了一个非常强大的文件比较工具推荐给大家。开源免费的&#xff01;&#xff01;&#xff01; 什么是WinMerge&#xff1f; WinMerge是Windows的开源差异和合并工具。WinMerge 可以比较文件夹和文件&#xff0c;以易于理解和处理的可视文本格式呈现差异。 官方下载地…

代码随想录算法训练营第六十天| LeetCode84. 柱状图中最大的矩形

一、LeetCode84. 柱状图中最大的矩形 1&#xff1a;题目描述&#xff08;84. 柱状图中最大的矩形&#xff09; 给定 n 个非负整数&#xff0c;用来表示柱状图中各个柱子的高度。每个柱子彼此相邻&#xff0c;且宽度为 1 。 求在该柱状图中&#xff0c;能够勾勒出来的矩形的最大…

学习Typescript(第二弹)

接口 对象类型接口 先用interface定义一个List接口,成员有id时number类型&#xff0c;name是string类型再定义一个Result&#xff0c;成员是List数组定义一个render函数&#xff0c;接收参数是result // 先用interface定义一个List接口 interface List {id:number,name:strin…

安卓APP源码和设计报告——个人通讯录

摘 要 随着移动设备制造技术和移动通信网络的迅猛发展,全球手机用户日益增加,手机成为了很多人日常生活中必不可少的一部分,手机业在日益发展的同时,人们对手机的功能需求和体验需求也越来越高,因此各种智能手机相继而出&#xff0c;当前市场上最流行的智能手机的操作系统非An…

RabbitMQ--重试机制

原文网址&#xff1a;RabbitMQ--重试机制_IT利刃出鞘的博客-CSDN博客 简介 说明 本文介绍RabbitMQ的重试机制。 问题描述 消费者默认是自动提交&#xff0c;如果消费时出现了RuntimException&#xff0c;会导致消息直接重新入队&#xff0c;再次投递&#xff08;进入队首&am…

【iOS】—— MVVM模式

MVVM模式 文章目录MVVM模式为什么使用MVVM&#xff1f;MVVM分别代表什么含义&#xff1f;MVVM通信关系MVVM模式的优缺点优点:缺点:概括总结MVVM文件分类为什么使用MVVM&#xff1f; iOS中&#xff0c;我们使用的大部分都是MVC架构。虽然MVC的层次明确&#xff0c;但是由于功能日…

C# 11新特性之file关键字

C#11 添加了文件作用域类型功能&#xff1a;一个新的 file 修饰符&#xff0c;可以应用于任何类型定义以限制其只能在当前文件中使用。这样&#xff0c;我们可以在一个项目中拥有多个同名的类。 目录示例file不可以与其他修饰符一起使用file可以修饰的类型file 不可修饰的类型f…

主流报表开发工具FastReport.Net全新发布,邀您体验最新版试用

FastReport .Net是一款适用于 WinForms、Blazor Server、ASP.NET、MVC、.NET 6 和 .NET Core 的报告生成工具。FastReport代表着“速度”、“可靠”和“品质”&#xff0c;是当今主流的报表开发工具。 该产品在本月进行了重大版本v2023的发布&#xff0c;接下来让我们一起看看…