ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

Spring 源码详解(一)

2020-12-18 18:03:40  阅读:111  来源: 互联网

标签:String Spring private public 源码 spring class 详解 Users


一、所需依赖
1、Spring核心依赖
2、Spring DAO依赖
3、Spring Web依赖
4、Spring Test依赖
二、XML命名空间
三、IOC
1、什么是IOC
2、什么是DI
3、DI的实现方式
3.1、构造器注入
3.2、Setter注入
4、IOC容器
4.1、IOC容器的设计
4.2、ApplicationContext
4.3、注解注入方式
4.3.1、@Autowired 自动装配的歧义性
4.3.2、@Autowired 为什么作用在接口上

一、所需依赖

<!-- Spring依赖 -->
    <!-- 1.Spring核心依赖 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>5.2.9.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>5.2.9.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.2.9.RELEASE</version>
    </dependency>
<!-- 2.Spring dao依赖 -->
<!-- spring-jdbc包括了一些如jdbcTemplate的工具类 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>5.2.9.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
        <version>5.2.9.RELEASE</version>
    </dependency>
    <!-- 3.Spring web依赖 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-web</artifactId>
        <version>5.2.9.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.2.9.RELEASE</version>
    </dependency>
    <!-- 4.Spring test依赖:方便做单元测试和集成测试 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>5.2.9.RELEASE</version>
  </dependency>

1、Spring核心依赖
spring-core、spring-beans、spring-context
2、Spring DAO依赖
spring-jdbc (JDBCTemplate模板)、spring-tx
3、Spring Web依赖
spring-web、spring-webmvc
4、Spring Test依赖
spring-test

二、XML命名空间

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
        
</beans>

三、IOC
1、什么是IOC
“控制反转”, 将创建对象的过程交给Spring, 比如我们不需要了解Redis或Mybatis的构建过程, 只需简单的配置即可使用, 又比如多个团队开发不同组件, 我们不需要其他团队开发的组件构建过程, 我们这需要的时候直接拿来用即可

2、什么是DI
“依赖注入” IOC的实现方式

3、DI的实现方式
构造器注入
Setter注入
接口注入

目前最常用的是构造器注入与Setter注入, 接口注入一般是使用第三方API时使用, 底层Spring都是通过反射来实现的, 这个我们后面再讨论

3.1、构造器注入
创建 Entity实体类

@Data
public class Users {
    private Long id;
    private String username;
    private String password;
    private String email;

    public Users(Long id, String username, String password, String email) {
        this.id = id;
        this.username = username;
        this.password = password;
        this.email = email;
    }
}

配置XML

<bean id="users" class="com.chenjiaxin.spring.entity.Users">
    <constructor-arg index="0" value="1"/>
    <constructor-arg index="1" value="zhangsan"/>
    <constructor-arg index="2" value="zhangsan123"/>
    <constructor-arg index="3" value="18297828@sinl.com"/>
</bean>

3.2、Setter注入
该方式是Spring比较推荐的方式, 优点是灵活性高, 由于当我们构造参数足够多时, 构造器注入就显得十分冗余
首先,默认情况下 要注入的对象必须要有无参构造器

@Data
public class Users {
    private Long id;
    private String username;
    private String password;
    private String email;
    // 可以省略, 这里为了说明
    public Users() {
    }
}

XML配置

<bean id="users" class="com.chenjiaxin.spring.entity.Users">
    <property name="email" value="18297828@sinl.com"/>
</bean>

4、IOC容器
从上面的例子中我们知道IOC的作用, 它可以容纳我们开发的各种Bean

4.1、IOC容器的设计
IOC容器的设计主要依赖于 BeanFactory与ApplicationContext两个接口, 其中 ApplicationContext是BeanFactory的子接口, 换就话说
BeanFactory是IOC最底层的接口, 但是工作中我们一般使用 ApplicationContext, 因为它对BeanFactory的功能又做了许多扩展

  1. BeanFactory源码
public interface BeanFactory {
    String FACTORY_BEAN_PREFIX = "&";

    Object getBean(String var1) throws BeansException;

    <T> T getBean(String var1, Class<T> var2) throws BeansException;

    Object getBean(String var1, Object... var2) throws BeansException;

    <T> T getBean(Class<T> var1) throws BeansException;

    <T> T getBean(Class<T> var1, Object... var2) throws BeansException;

    <T> ObjectProvider<T> getBeanProvider(Class<T> var1);

    <T> ObjectProvider<T> getBeanProvider(ResolvableType var1);

    boolean containsBean(String var1);

    boolean isSingleton(String var1) throws NoSuchBeanDefinitionException;

    boolean isPrototype(String var1) throws NoSuchBeanDefinitionException;

    boolean isTypeMatch(String var1, ResolvableType var2) throws NoSuchBeanDefinitionException;

    boolean isTypeMatch(String var1, Class<?> var2) throws NoSuchBeanDefinitionException;

    @Nullable
    Class<?> getType(String var1) throws NoSuchBeanDefinitionException;

    @Nullable
    Class<?> getType(String var1, boolean var2) throws NoSuchBeanDefinitionException;

    String[] getAliases(String var1);
}

由于这个接口的重要性, 笔者有必要进行一些基本的阐述

  • getBean 的多个方法用于获取配置给Spring IOC容器的Bean, 从参数类型可以看出可以是字符串也可以是Class类型
    isSingleton 判断是否为单例 true 为单例 isPrototype 判断是否为非单例 true 为非单例
    getAliases 获取别名

4.2、ApplicationContext

  1. ApplicationContext源码
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory, MessageSource, ApplicationEventPublisher, ResourcePatternResolver {
    @Nullable
    String getId(); //获取ID

    String getApplicationName(); //获取应用名称

    String getDisplayName(); //获取应用显示名称

    long getStartupDate(); //获取应用启动时间

    @Nullable
    ApplicationContext getParent(); //获取父级应用上下文

    AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException; //获取bean工厂(DefaultListableBeanFactory)
}

4.2.1、ClassPathXmlApplicationContext
会在启动时加载指定XML配置, 初始化容器, 比如通过上面的 DI进行Setter或构造器注入之后 我们想得到这个Bean可以这样做

public static void main(String[] args) {
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");
    Users users = context.getBean("users", Users.class);
    System.out.println(users);
}

4.2.2、AnnotationConfigApplicationContext
使用AnnotationConfigApplicationContext可以实现基于Java的配置类加载Spring的应用上下文。避免使用application.xml进行配置。相比XML
配置,更加便捷。

  1. 创建 configuration配置类
@Configuration注解就相当于XML中的<beans/> 标签
@Configuration
public class UserConfig {
    @Bean
    public Users getUsers() {
        Users users = new Users();
        users.setId(1L);
        users.setUsername("lisi");
        return users;
    }
}
  1. 读取该bean
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(UserConfig.class); //注册配置类
context.refresh(); //刷新容器
Users bean = context1.getBean(Users.class);

4.3、注解注入方式
组件扫描 @Component && @ComponentScan
自动装配 @Autowired

注解注入 等同于

组件扫描

  1. 在POJO类上面添加 @Component 注解
  2. 添加配置类,使用 @ComponentScan 使用包扫描, 扫描指定包下的 @Component 注解
  3. 使用 AnnotationConfigApplicationContext类获取对象
@Component
public class Users{
    @Value("1")
    private Long id;
    @Value("zhangsan")
    private String username;
}

@ComponentScan(basePackages = "com.chenjiaxin")
public class UserConfig {
}

public class SpringMain {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(UserConfig.class);
        Users bean = context.getBean(Users.class);
        System.out.println(bean);
    }
}

组件扫描 只能作用在简单数据类型之上, 如果要作用在复杂对象上是不可取的, 比如下面的例子
//角色类

@Data
public class Role {
    private Long id;
    private String roleName;
}

//用户类
@Data
public class Users{
    private String email;
    private Role role;
}

//XML
<bean id="role" class="com.chenjiaxin.spring.entity.Role">
    <property name="id" value="1"/>
    <property name="roleName" value="管理员"/>
</bean>

<bean id="users" class="com.chenjiaxin.spring.entity.Users">
    <property name="email" value="18297828@sinl.com"/>
    <property name="role" ref="role"/>
</bean>

//获取对象
public class SpringMain {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");
        Users bean = context.getBean(Users.class);
        System.out.println(bean);
    }
}

@Autowired 自动装配 原理是Set注入, 对上方测试代码进行改造

//角色POJO
@Data
@Component
public class Role {
    @Value("1")
    private Long id;
    @Value("管理员")
    private String roleName;
}

//用户POJO
@Data
@Component
public class Users{
    @Autowired  
    private Role role;
    @Value("981928@sign.com")
    private String email;
}

//配置注解扫描
@ComponentScan(basePackages = "com.chenjiaxin")
public class UserConfig {
}

//测试
public class SpringMain {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(UserConfig.class);
        Users bean = context.getBean(Users.class);
        System.out.println(bean);
    }
}

@Autowired 是按照类型 获取Bean的

public interface BeanFactory {
    ...
    <T> T getBean(Class<T> var1) throws BeansException;
    ...
}

4.3.1、@Autowired 自动装配的歧义性
先看下面一个例子, Uservice接口有两个实现类, 在Test中 使用Autowired 会出现歧义性

public interface UserService {
    // 读取数据库伪代码业务接口
    Users selectOne();
}

//实现类一
@Service
public class UserServiceImpl implements UserService {
   ...
}

//实现类二
@Service
public class UserServiceImplTwo implements UserService {
   ...
}

//测试类
@RunWith(SpringRunner.class)
@ContextConfiguration(classes=UserConfig.class)
public class SpringMain {
    @Autowired
    private UserService userService;
    
    @Test
    public void  test1() {
        System.out.println(userService.selectOne());
    }
}

解决方案:

  • @Primary
  • @Qualifier

方式一: 被 @Primary标注的实现类 ,当spring 进行装配时 会被优先处理。

@Service
@Primary
public class UserServiceImpl implements UserService {
  ...
}

方式二: 也可以在装配的时候 强制让spring 按照名称进行装配 也就是XML中bean标签的ID

@Autowired
@Qualifier("userServiceImpl")
private UserService userService;

4.3.2、@Autowired 为什么作用在接口上
如果Spring配置了<context:component-scan base-package=“com.*.service”></context:component-scan>,并且要注入的接口只有一个实现类的话,那么spring框架可
以自动将interface与其实现类组装起来。如果没有配置component scan,那么我们必须在application-config.xml(或等同的配置文件)定义这个bean。
一般情况下一个接口我们只写一个实现类,这个时候我们只需要在实现类上注解@service

@Service
public class UserServiceImpl implements UserService {
	……
}

在这种情况下,我们要使用这个实现类的时候也只需要用@Autowired即可 Spring会自动组装userservice与其实现类UserServiceImpl

@Autowired
private Userservice userservice;

标签:String,Spring,private,public,源码,spring,class,详解,Users
来源: https://blog.csdn.net/weixin_46784756/article/details/111386594

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有