Spring基础总结(上)

news2024/9/20 20:45:43

Spring基础总结(上)

1. Spring 如何创建一个 Bean 对象

通过调用对象的无参构造方法创建一个新对象,然后通过依赖注入得到bean对象(默认单例)依赖注入这一步对新对象中添加了 @Autowired 或者@Resource 等注解的属性赋值,得到 Bean 对象,如下是一段伪代码

//1.通过无参构造函数创建一个新对象 userService
UserService userService = new UserService();

//2.遍历对象的属性,给添加了 @Autowired 注解的属性赋值
for(Field field : userService.getClass().getDeclaredFields()) {
    if(field.isAnnotationPresent(Autowired.class)) {
	field.set(userService, ??)
    }
}
... ...

2. 什么是单例池?作用是什么?先看如下代码

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

UserService userService1 = (UserService)context.getBean("userService");
UserService userService2 = (UserService)context.getBean("userService");
System.out.println(userService1.equals(userService2)); // true

我们可以发现获取到的两个 UserService 对象是相同的,这说明 spring 有一个类似 Map 的集合来保存单例 Bean,key 值为 bean 的 name 属性,value 的值为 bean 本身,这个保存 bean 的集合就是单例池,它的作用就是用来存储单例 Bean 对象;如果是多例 Bean,就不会被放入到单例池

3. @PostConstruct 注解的作用

先看如下代码



public class UserService {    
    private User user;
}

我们在 UserService 中定义了一个 User(id, name) 属性,假如有这样的需求:我们需要在使用 userService 这个 Bean 的时候,获取 user 属性的值;
可能很多人觉得,那可以将 user 也定义成一个 Bean,然后注入到 UserService 不就好了么;但是如果这个 user 的信息需要从数据库查询出来并赋值呢?
这样就比较麻烦了;如果在 UserService 初始化成 Bean 的时候,让 User 对象也初始化属性就好了,这个解决办法就是定义一个初始化 User 属性的方法,并添加 @PostConstruct 让 spring 在初始化前去初始化 User 的属性,如下调用对象的无参构造方法 --> UserService 对象 --> 依赖注入 --> 初始化前(调用初始化方法 initUser()) --> 初始化 --> 初始化后 --> 放入单例池(单例 Bean 对象) 所以可改造代码如下



public class UserService {
    private User user;
	
    
    private void initUser() {
        user = new User();
        user.setId(1);
        user.setName("dufu");
    }
}

注意:这个注解在某些 JDK(高版本,例如:1.8)版本没有,这实际上是 JDK 自己的注解!如果找不到可以添加如下依赖

<dependency>    
    <groupId>javax.annotation</groupId>    
    <artifactId>javax.annotation-api</artifactId>    
    <version>1.3.2</version>
</dependency>

除此之外,spring 也提供了一个解决方案,就是实现 InitializingBean 接口并实现 afterPropertiesSet() 方法,所以也可以改造代码如下



public class UserService implements InitializingBean {
    private User user;
	
    public void afterPropertiesSet() throws Exception {
        user = new User();
	    user.setId(1);
	    user.setName("dufu");
    }
}

但要注意 afterPropertiesSet() 方法是在初始化的时候执行,而 @PostConstruct 注解下的方法是在初始化前执行;调用对象的无参构造方法 --> UserService 对象 --> 依赖注入 --> 初始化前(执行 initUser()) --> 初始化(执行 afterPropertiesSet()) --> 初始化后 --> 放入单例池(单例 Bean 对象)

4. Bean 的实例化和 Bean 的初始化的区别

Bean 的实例化:调用对象的无参构造方法 --> 对象

Bean 的初始化:调用对象指定的方法初始化对象,也就是 spring 的 initializeBean() 方法做的事情

5. 推断构造方法

  1. 在实例化 Bean 的时候,默认会调用 Bean 的无参构造方法(即时有多个构造方法);如下代码,我们可以验证一下


public class UserService {
    
    private User user;
	
    /**
     * 无参构造函数
     */
    public UserService() {
        System.out.println("调用了无参构造方法");
    }
	
    /**
     * 有参构造函数1
     */
    public UserService(User user) {
        System.out.println("调用了有参构造函数1");
    }
}
// 调用
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = (UserService)context.getBean("userService");
context.close();
  1. 如果定义了多个构造方法但没有无参构造方法会报错,如下去掉代码中的无参构造方法,则会抛出异常:No default constructor found … …


public class UserService {
    
    private User user;
	
    /**
     * 有参构造函数1
     */
    public UserService(User user) {
        System.out.println("调用了有参构造函数1");
    }
}

3)如果要指定实例化 Bean 的时候定义了多个构造方法中的某一个,需要在该构造方法上添加 @Autowired 注解(此时有没有其他构造方法都没有影响)



public class UserService {
    private User user;
	
    /**
     * 有参构造函数1
     */
    public UserService() {
    	System.out.println("调用了无参构造函数");
    }
	
    /**
     * 有参构造函数1,添加 @Autowired 注解, 指定调用该构造方法
     */
    
    public UserService(User user) {
        System.out.println("调用了有参构造函数1");
    }
}

6. 从单例池中获取 Bean 的步骤

先根据 type 获取,然后再根据 name 获取在单例池中,可能会存在 bean 的 name 不一样,但 bean 的类型相同的情况,如下代码


public User user1() {
    return new User();
}

public User user2() {
    return new User();
}

public User user3() {
    return new User();
}
// 调用
System.out.println(context.getBean("user1"));
System.out.println(context.getBean("user2"));
System.out.println(context.getBean("user3"));

可以看到这是 3 个类型一样但又不相同的 Bean;那么在初始化 Bean 的构造函数中,如下


public class UserService {
    private User user;

    
    public UserService(User user1) {
        System.out.println(user1);
        this.user = user1;
    }
}

在初始化 UserService 并调用 UserService 的构造方法的时候,先根据类型找出 User 类型的 Bean(3个),然后再根据 name(user1) 找出到底使用哪一个进行初始化操作;所以我们将构造函数改为


public UserService(User user4) {
    System.out.println(user4);
    this.user = user4;
}

就会报错,No qualifying bean of type [com.spring.demo.entity.User] is defined: expected single matching bean but found 3: user1,user2,user3
因为单例池中虽然有 User 类型的 Bean,但没有 name = user4 的 Bean;但是如果单例池中只有一个 User 类型的 Bean,因为 Spring 根据类型已经能找到 User 类型的 Bean,就不会根据名字再找,那么上面写成 user4 就不会报错

7. AOP实现原理

在 Bean 初始化后,如果有 AOP,则通过 AOP 得到一个代理对象,然后把代理对象放入到单例池中(注意不是普通对象),否则放入的是 Bean 实例化后的普通对象,流程如下

调用对象的无参构造方法 --> UserService 对象 --> 依赖注入 --> 初始化前(执行 initUser()) --> 初始化(执行 afterPropertiesSet()) --> 初始化后(AOP) --> 代理对象 --> 放入单例池(单例 Bean 对象)

如下,我们添加了一个 AOP 实现



public class SystemAop {
    private final String pointCut = "execution(* com.spring.demo.service.*.*(..))";
	
    (pointCut)
    public void before(JoinPoint jp) {
        System.out.println("Aop Before ... ...");
    }
	
    (pointCut)
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        Object result = pjp.proceed();
        System.out.println("Aop Around ... ...");
        return result;
    }
	
    (pointCut)
    public void after() {
        System.out.println("Aop After ... ...");
    }
}

在 UserService 中添加如下代码


public class UserService {
    
    private User user;
	
    public void print() {
        System.out.println(user);
    }
}

在调用如下代码的时候

public static void main(String[] args) {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
    UserService userService = (UserService)context.getBean("userService");
    userService.print(); // 输出:com.spring.demo.entity.User@3d34d211
    context.close();
}

在 UserService 中注入了 User 对象;如果在 userService.print(); 这句代码打断点,可以看到,其 user 属性是 Null;这是因为代理对象中没有 user 属性;但最终却能打印出 user 属性的值,这是为什么呢?Spring 的 AOP 是通过 CGLIB 实现的,其实现是基于继承的方式,伪代码如下

public UserServiceProxy extends UserService {
    // 实例池中的普通对象
    UserService target;
	
    // 重载父类中的 test() 方法
    public void test() {
        切面逻辑 
        target.test(); // 此处的 target 就是实例池中的普通对象
        切面逻辑 
    }
}

可以看到,代理对象继承了普通对象,这样就可以在 context.getBean(“userService”) 的时候实现强制转化(UserService userService = (UserService)context.getBean(“userService”));代理对象中的 target 属性的值就是普通对象,这样在调用 test() 方法的时候,就可以通过普通对象去调用;所以在代理对象中,user 属性 = null,但普通对象中 user 有值
在这里插入图片描述

(pointCut)
public void before(JoinPoint jp) {
    System.out.println("Aop Before ... ...");
    System.out.println(jp.getThis().getClass().getName()); // 代理对象
    System.out.println(jp.getTarget().getClass().getName()); // 被代理的对象
}

8. Spring 事务实现原理

如下我们实现了一个基本的 Spring 的事务示例

  1. 在 AppConfig 配置上添加 @EnableTransactionManagement 注解,打开事务;并添加数据源和 JdbcTemplate



("com.spring.demo")
public class AppConfig {
    
    public JdbcTemplate jdbcTemplate() {
        return new JdbcTemplate(dataSource());
    }
	
    
    public PlatformTransactionManager transactionManager() {
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
        transactionManager.setDataSource(dataSource());
        return transactionManager;
    }
	
    
    public DataSource dataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setUrl("xxx");
        dataSource.setUsername("xxx");
        dataSource.setPassword("xxx");
        return dataSource;
    }
}
  1. 在 UserService 中,注入 JdbcTemplate 并添加 insert() 方法;代码如下

public class UserService {
    
    private JdbcTemplate jdbdTemplate;
	
    
    public void insert() {
        jdbdTemplate.execute("insert into user(id, name) values(1, 'dufu')");
        throw throw new RuntimeException();
    }
}

注意在 insert() 方法上我们添加了 @Transactional 注解,表示支持事务,在方法中执行了插入操作后,再抛出异常;如果事务生效的话,那么这条记录将不会被插入到数据库中,事实也确实如此;如下是一段 Spring 中实现事务的伪代码

public UserServiceProxy extends UserService {
    UserService target;
	
    // 重载父类中的 insert() 方法
    public void insert() {
        ... ...
        // 如果方法上面添加了 @Transactional 注解,就处理事务
        if(method.isAnnotationPresent(Transactional.class)) {
            // 开启事务
            try {
                // 1.通过事务管理器新建一个 connection;
                Connection conn = xxx.getConnection();
                // 2.关闭 autocommit 属性
                conn.autocommit = false;
                // 3.普通对象执行数据操作
                target.insert();
                ... ...
            } catch(Exception e) {
                // 如果执行异常, 就回滚操作
                conn.rollback();
            }
        }
    }
}

注意:如果我们去掉了 AppConfig 配置类上的 @Configuration 注解的话,事务就会失效!这是为什么呢?这是因为在事务管理器中创建 Connection 的时候,是通过 ThreadLocal<Map<DataSource, Connection>>为了保证可以使用多数据源,这里使用 Map 来存储 Connection,必须要保证事务中的 dataSource 和 jdbcTemplate 中的 dataSource 是同一个才能保证事务有效!如果没有添加 @Configuration 注解,事务和 jdbcTemplate 拿到的
dataSource 是两个 new 出来的对象(看下面的代码,两个 Bean 中都有:new DataSource()),不是同一个;添加了 @Configuration 注解,AppConfig 会被动态动态代理,在代理对象中,拿其他的 Bean 都是从 Spring 容器的单例池中拿的,就能保证拿到的 dataSource 是同一个对象


public JdbcTemplate jdbcTemplate() {
    return new JdbcTemplate(dataSource());
}


public PlatformTransactionManager transactionManager() {
    DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
    transactionManager.setDataSource(dataSource());
    return transactionManager;
}

了解了 Spring 的事务实现原理后,再看下面这段代码


public class UserService {
    
    private JdbcTemplate jdbdTemplate;
	
    
    public void insert() {
        jdbdTemplate.execute("insert into user(id, name) values(1, 'dufu')");
	insert1();
    }
	
    // 不提交事务, 也就是说不会执行下面的插入操作
    (propagation = Propagation.NEVER)
    public void insert() {
        jdbdTemplate.execute("insert into user(id, name) values(1, 'dufu')");
    }
}

按照正常逻辑,只会插入 insert() 方法中的数据,insert1() 方法中的数据是不会插入的,但是最终发现,两条数据都被插入了,也就是说 insert1() 方法的事务失效了,这是什么原因呢?这是因为,在上面伪代码中,target.insert(); 这句代码是普通对象执行的,insert() 方法下的 insert1() 方法也是普通对象执行的,这样的话就 insert1() 方法根本没有进入代理对象执行 insert() 方法的逻辑;而 target.insert() 方法可以,是因为外层包裹了代理对象重载的insert() 方法!

那如何让 insert() 方法下的 insert1() 方法能被事务托管呢?其解决办法就是让 insert1() 方法被代理对象调用即可,所以可修改代码如下


public class UserService {
    
    private JdbcTemplate jdbdTemplate;
	
    // Spring 中可以注入自己
    
    private UserService userService;
	
    
    public void insert() {
        jdbdTemplate.execute("insert into user(id, name) values(1, 'dufu')");
        userService.insert1();
    }
	
    // 不提交事务, 也就是说不会执行下面的插入操作
    (propagation = Propagation.NEVER)
    public void insert() {
        jdbdTemplate.execute("insert into user(id, name) values(2, 'dufu')");
    }
}

9. Spring 为什么使用三级缓存解决循环依赖

循环依赖就是相互依赖,A 注入 B,B 注入 A 这种情况就是循环依赖的一个场景;在 Spring 中,通过三级缓存解决了循环依赖的问题;三级缓存分别为

单例池:singletonObjects 保存的是经过完整的生命周期后的Bean
第二级缓存:earlySingletonObjects 保存没有经过完整生命周期的单例对象
第三级缓存:singletonFactories 保存根据 Bean 的信息生成的 Lambda 表达式,用于循环依赖时候判断是否需要提前 AOP

A 的创建生命周期如下

  1. 创建一个 A 普通对象(同时记录到 creatingSet(“a”)) --> 存储到 singletonFactories<a, lambda>
  2. 填充 B 属性 --> 去单例池中找 B 对象 --> 找不到就创建 B 对象
  3. 填充其他属性4. 其他操作5. 初始化后(AOP)6. 放入到单例池

B 的创建生命周期如下

  1. 创建一个 B 普通对象 --> 存储到 singletonFactories<a, lambda>
  2. 填充 A 属性 --> 去单例池中找 A 对象 --> 通过 creatingSet 确定 A 正在创建中(即出现了循环依赖的情况) --> 到 earlySingletonObjects 中寻找 Bean 的信息 lambda 表达式(如果找到了,返回并赋值,没有的话下一步) --> 到 singletonFactories 找,提前 AOP(如果需要 AOP) 得到 A 的代理对象(如果不需要 AOP 得到普通对象) --> 放到 earlySingletonObjects
  3. 填充其他属性
  4. 其他操作
  5. 初始化后(AOP)
  6. 放入到单例池

只要出现了循环依赖的情况,都会去二级缓存中去找 Bean 对象,三级缓存的目的就是为了怎么生成对应的 Bean 对象(普通对象还是代理对象) 并放入到二级缓存中

10. 在循环依赖的情况下,添加了 @Async 会报错

  1. 例如,AService 和 BService 相互依赖注入,并在 AService 下定义了一个添加了 @Async 注解的方法如下

public class AService {
    
    private BService bService;
	
    
    public void print() {
        System.out.println(bService);
    }
}

在调用 AService 下的 print 方法的时候会报错

public static void main(String[] args) {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
    AService aService = (AService)context.getBean("AService");
    aService.print();
    context.close();
}

报错信息:Error creating bean with name ‘AService’: Bean with name ‘AService’ has been injected into other beans [BService] in its raw version as part of a circular reference, but has eventually been wrapped. This means that said other beans do not use the final version of the bean. This is often the result of over-eager type matching - consider using ‘getBeanNamesOfType’ with the ‘allowEagerInit’ flag turned off, for example.

这是因为,添加了 @Async 注解后,Spring 会生成 AService 的代理对象,而在循环依赖的情况下,Spring 也创建了一个 AService 的代理对象,这两个代理对象是不一样的,所以会报错;在开发中,应尽量避免这种既有循环依赖又有 @Async 的情况,如果实在没有办法避免,可以在注入的 BService 对象上添加一个 @Lazy 注解即可,如下


public class AService {
    
    
    private BService bService;
	
    
    public void print() {
        System.out.println(bService);
    }
}

这是因为,添加了 @Lazy 注解后,注入的 BService 是一个通过 @Lazy 注解逻辑直接生成的代理对象(不是从单例池中获取的),在 print() 方法中使用的时候,才会从单例池中去取出真正要使用的 BService 对象

  1. 在循环依赖的情况下通过构造方法注入同样会产生这样的问题(甚至连普通对象都无法生成),如下

public class AService {
    private BService bService;
	
    /**
     * 构造方法注入 BService
     */
    public AService(BService bService) {
        this.bService = bService;
    }
	
    public void print() {
        System.out.println(bService);
    }
}

解决办法同样是在构造方法上添加 @Lazy 注解

3)在循环依赖的情况下,如果 AService 和 BService 是多例 Bean 的话,也会报错

11. @Value 的使用

在 AppConfig 配置类中,添加一个 Bean,指定读取的 properties 文件地址;如果 Spring 的版本在 5 及以上,则可省略该步骤


public PreferencesPlaceholderConfigurer configurer() {
    PreferencesPlaceholderConfigurer configurer = new PreferencesPlaceholderConfigurer();
    configurer.setLocation(new ClassPathResource("/application.properties"));
    return configurer;
}

在其他类中,就可以通过 @Value(${xxx}) 的方式获取到 properties 配置文件中的值


public class AService {
    ("${appName}")
    private String appName;
	
    public void print() {
        System.out.println(appName);
    }
}

如果使用 @Value(#{Bean的name}) 这种方式,可以实现像 @Resource 注解的功能;例如:


public class AService {
    ("#{BService}")
    private BService bService;
	
    public void test() {
        System.out.println(bService);
    }
}

假如在 properties 配置文件中有一个很多地方都在使用的配置,如果还是在各个使用的地方添加 @Value(“${keyName}”) 这种方式,假如修改了 keyName 的话,很容易因为漏改而出现问题;有两个简单的解决办法

新建一个类,把常用的值注入到这个类的属性中
新建一个注解,在该注解上添加 @Value(“${keyName}”) 注解,在使用到这个值的地方,使用我们新建的这个注解因为新建的这个注解不需要添加属性,所以不存在修改的问题;如下

({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})
(RetentionPolicy.RUNTIME)
(${xxx})
public  MyValue {}

12. @Conditional 注解

该注解可用于在注册 Bean 的时候,根据自定义条件让 Spring 容器注册 Bean;或者对 Bean 做一些操作

  1. 首先新建 MyCondition 类并实现 Condition 接口,重新其 matches 方法,这里我们给 BService 这个 Bean 设置 name 属性的值
public class MyCondition implements Condition {
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        try {
            // 读取本地配置文件
            Resource resource = context.getResourceLoader().getResource("/application.properties");
            Properties properties = new Properties();
            properties.load(resource.getInputStream());		
            BeanFactory beanFactory = context.getBeanFactory();
            if(beanFactory.containsBean("BService")) {
                // 给 BService 的  name 属性赋值
                BService bService = (BService) beanFactory.getBean(BService.class);
                bService.setName((String)properties.getProperty("name"));
            }
        } catch (IOException e) {
            return false;
        }
        return true;
    }
}
  1. BService 类的代码如下,注意我们添加了 @Conditional(MyCondition.class) 注解


(MyCondition.class)
public class BService {    
    private String name;
}
  1. 在 AService 中注入 BService,并打印 BService 的 name 属性的值

public class AService {
    
    private BService bService;
	
    public void print() {
        System.out.println(bService.getName());
    }
}

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

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

相关文章

openOffice pdf.js spring boot 微信在线预览office pdf文件

下载openoffice 并安装//pdf.js 案例 https://mozilla.github.io/pdf.js/examples/index.html#interactive-examples//openoffice 连接不上 进入安装目录 cmd 运行以下命令 soffice -headless -accept"socket,host127.0.0.1,port8100;urp;" -nofirststartwizard<!…

技术管理之产品管理

一、产品相关概念 1.1 产品的定义 作为商品提供给市场&#xff0c;被人们使用和消费&#xff0c;并能满足人们某种需求的任何东西&#xff0c;包括有形的物品和无形的服务、组织、观念或者它们的组合&#xff1b;简单点产品就是解决某一类问题的东西。 1.2 产品思维 产品思…

安全研发人员能力模型窥探

能力 是一个比较抽象的概念&#xff0c;不同的行业、管理者、研发人员对能力的认知都会有差异。另外&#xff0c;作为研发团队的相应的职级定级、绩效考核的基础&#xff0c;一个“大家普遍认可”的能力的模型是非常重要的。这是比职级模型更高层的一个基本模型&#xff0c;所谓…

漏洞之S2-048 远程代码执行漏洞(CVE-2017-9791)

一、漏洞详情二、环境搭建1、使用vulhub搭建&#xff0c;搭建方法详见&#xff1a;https://blog.csdn.net/qq_32393893/article/details/129027549?spm1001.2014.3001.55012、切换到vulhub/struts2/s2-0483、启动容器 docker-compose up -d4、访问虚拟机IP:8080端口&#xff0…

智能家居项目(三)之框架设计及框架代码文件工程建立

目录 一、智能家居项目框架设计草图 二、框架代码文件工程建立 三、添加声音识别模块的串口读取功能 一、智能家居项目框架设计草图 代码思路讲解&#xff1a; 1、一个指令工厂&#xff0c;一个控制工厂&#xff0c;实际上就是通过链表链起来的数据。具体怎么链接起来&…

dockerfile自定义镜像安装jdk8,nginx,后端jar包和前端静态文件,并启动容器访问

dockerfile自定义镜像安装jdk8,nginx,后端jar包和前端静态文件&#xff0c;并启动容器访问简介centos7系统里面我准备的服务如下:5gsignplay-web静态文件内容如下:nginx.conf配置文件内容如下:Dockerfile内容如下:run.sh启动脚本内容如下:制作镜像并启动访问简介 通过用docker…

将SpringBoot项目部署到云服务器上面

将jar包部署到云服务器上面在项目中直接双击点击maven里面的package当控制台输出创建成功以后找到target目录下面打好的jar包然后找到jar包所在的文件目录&#xff0c;将该jar包放到服务器里面的apache-tomcat-8.5.82目录里面的webapps目录里面打开安全组开放访问端口服务器里面…

【2023进阶自动化测试第一步】什么是自动化测试基础?

01、自动化测试的定义 使用一种自动化测试工具来验证各种软件测试的需求&#xff0c;它包括测试活动的而管理与实施、测试脚本的开发与执行。 自动化测试只是策是工作的一部分&#xff0c;是对手工测试的一种补充&#xff1a;自动化测试决不能代替手工测试&#xff1b;多数情…

Word处理控件Aspose.Words功能演示:使用 Java 拆分 MS Word 文档

Aspose.Words 是一种高级Word文档处理API&#xff0c;用于执行各种文档管理和操作任务。API支持生成&#xff0c;修改&#xff0c;转换&#xff0c;呈现和打印文档&#xff0c;而无需在跨平台应用程序中直接使用Microsoft Word。此外&#xff0c;API支持所有流行的Word处理文件…

ASE28N50-ASEMI高压N沟道MOS管ASE28N50

编辑-Z ASE28N50在TO-247封装里的静态漏极源导通电阻&#xff08;RDS(ON)&#xff09;为200mΩ&#xff0c;是一款N沟道高压MOS管。ASE28N50的最大脉冲正向电流ISM为110A&#xff0c;零栅极电压漏极电流(IDSS)为1uA&#xff0c;其工作时耐温度范围为-55~150摄氏度。ASE28N50功…

【实战场景二】如何设计一个分布式锁?

如何优雅的设计一个分布式锁&#xff1f;如何设计一个分布式锁&#xff1f;1、什么是分布式锁2、那么分布式锁&#xff0c;具备什么条件呢&#xff1f;3、设计分布式锁有哪些方式&#xff1f;3.1 利用redis实现分布式锁原理3.2 基于数据库做分布式锁3.3 基于zookeeper实现分布式…

L1-064 估值一亿的AI核心代码

以上图片来自新浪微博。 本题要求你实现一个稍微更值钱一点的 AI 英文问答程序&#xff0c;规则是&#xff1a; 无论用户说什么&#xff0c;首先把对方说的话在一行中原样打印出来&#xff1b;消除原文中多余空格&#xff1a;把相邻单词间的多个空格换成 1 个空格&#xff0c…

如何有效提升微信小程序的排名?

微信小程序排名提升的方法有很多&#xff0c;今天厦门巨神峰给大家分享几点&#xff1a; 1. 加强小程序的用户体验&#xff0c;提升用户满意度&#xff1b; 2. 加强小程序的口碑宣传&#xff0c;提升小程序的知名度&#xff1b; 3. 加强小程序的技术支持&#xff0c;提升小程…

血糖高不高,看皮肤也能知道

糖尿病是一种以高血糖为特征的代谢性疾病。长期存在的高血糖&#xff0c;导致各种组织&#xff0c;特别是眼、肾、心脏、血管、神经的慢性损害、功能障碍。很多人不知道&#xff0c;皮肤也会受到血糖影响。以下就是皮肤发出的警告&#xff1a;1.黑棘皮病&#xff1a;后脖颈、腋…

Docker安全防护与配置

author: aming email: jikcheng163.com title: Docker安全防护与配置 creation_date: 2023-02-08 12:26 Last modified date: 2023-02-08 14:09 tags: Docker安全防护与配置 File Folder with relative path: reading notes/doc remark: other: 本章背景知识 运行在容器内部的…

【C++修行之路】C/C++内存管理

文章目录程序区域内存划分C语言动态内存分配&#xff1a;new和delete&#xff1a;new、delete和malloc、free的区别:程序区域内存划分 C/C程序内存区域划分非常相似。 C语言动态内存分配&#xff1a; malloc、calloc、realloc都是C语言动态开辟内存的常用函数 其中 malloc 开…

【Mybatis源码解析】一级缓存和二级缓存源码解析

文章目录缓存使用缓存源码测试代码上一篇《【Mybatis源码解析】mapper实例化及执行流程源码分析》&#xff0c;主要讲解了Mybatis的基本原理一级执行的流程&#xff0c;这一章来讲一下Mybatis的两个缓存&#xff1a;一级缓存和二级缓存。 因为网上大部分都是使用xml配置的方式…

函数编程之Function

文章目录前言一、Function是什么&#xff1f;二、Function 怎么用?1.简单使用2.真正的强大之处总结前言 在java8之后,我已经习惯了开始用stream()方式编程,但是对于新引入的其他功能,还是不清楚,今天经历了一个编程问题后,让我对于Function() 这个函数有了新的认知; 一、Func…

渲染农场优势是什么_云渲染农场怎么用?

在回答渲染农场的优势这个问题之前&#xff0c;我先申明一下本文中提到的渲染农场/云渲染平台/云渲染农场&#xff0c;都特指CG领域内的专业3D渲染平台&#xff0c;有一些文章会强调这个叫法的区别&#xff0c;但是业内一般都不会分这么细&#xff0c;所以也就不赘述了。渲染农…

【Spark分布式内存计算框架——Spark SQL】7. 数据处理分析案例

4.3 案例&#xff1a;电影评分数据分析 使用电影评分数据进行数据分析&#xff0c;分别使用DSL编程和SQL编程&#xff0c;熟悉数据处理函数及SQL使用&#xff0c;业务需求说明&#xff1a; 对电影评分数据进行统计分析&#xff0c;获取Top10电影&#xff08;电影评分平均值最高…