栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

二、Spring

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

二、Spring

IOC和DI 1、IOC:反转控制

Ioc—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想。在Java开发中,IOC意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。如何理解好IOC呢?理解好IOC的关键是要明确“谁控制谁,控制什么,为何是反转(有反转就应该有正转了),哪些方面反转了”,那我们来深入分析一下:

●谁控制谁,控制什么:传统Java SE程序设计,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象;而IOC是有专门一个容器来创建这些对象,即由IOC容器来控制对象的创建;谁控制谁?当然是IOC容器控制了对象;控制什么?那就是主要控制了外部资源获取(不只是对象包括比如文件等)。

●为何是反转,哪些方面反转了:有反转就有正转,传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象;为何是反转?因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转;哪些方面反转了?依赖对象的获取被反转了。

2、DI:依赖注入

IOD的另一种表述方式:即组件以一些预先定义好的方式(入setter方法)接受来自于容器的资源注入。相对于IOC而言,这种表述更直接。

3、IOC容器在Spring中的实现

3.1、在通过IOC容器读取Bean的实例之前,需要将IOC容器本身实例化。

Spring提供了IOC容器的两种实现方式

3.1.1、BeanFactory :IOC容器的基本实现,是Spring内部的基础设施,是面向Spring本身的,不是给开发人员使用的。

3.1.2、ApplicationContext:BeanFactory 的子接口,提供了更多高级特性。面向Spring的使用者,几乎所有场合都使用ApplicationContext而不是BeanFactory 。

ApplicationContext高级特性:
1、与 Spring 的 AOP 功能轻松集成
2、消息资源处理(用于国际化)
3、Event publication
4、特定于应用程序层的上下文,例如用于 Web 应用程序的WebApplicationContext。

ApplicationContext的主要实现类

ClassPathXmlApplicationContext:对应类路径下的XML格式的配置文件FileSystemXmlApplicationContext:对应文件系统中的XML格式的配置文件在初始化时就创建单例的bean,也可以通过配置的方式指定创建的Bean是多实例的。ConfigurableApplicationContext 是ApplicationContext的子接口,包含一些扩展方法refresh()和close()让ApplicationContext具有启动、关闭和刷新上下文的能力。WebApplicationContext 专为WEB应用而准备的,它允许从相对于WEB根目录的路径中完成初始化工作 4、IOC配置的三种方式 xml 配置

顾名思义,就是将bean的信息配置.xml文件里,通过Spring加载文件为我们创建bean。这种方式出现很多早前的SSM项目中,将第三方类库或者一些配置工具类都以这种方式进行配置,主要原因是由于第三方类不支持Spring注解。

优点: 可以使用于任何场景,结构清晰,通俗易懂

缺点: 配置繁琐,不易维护,枯燥无味,扩展性差

Java 配置

将类的创建交给我们配置的JavcConfig类来完成,Spring只负责维护和管理,采用纯Java创建方式。其本质上就是把在XML上的配置声明转移到Java配置类中

优点:适用于任何场景,配置方便,因为是纯Java代码,扩展性高,十分灵活

缺点:由于是采用Java类的方式,声明不明显,如果大量配置,可读性比较差

举例: 创建一个配置类, 添加@Configuration注解声明为配置类 创建方法,方法上加上@bean,该方法用于创建实例并返回,该实例创建后会交给spring管理,方法名建议与实例名相同(首字母小写)。注:实例类不需要加任何注解

@Configuration
public class BeansConfig {

    @Bean("userDao")
    public UserDaoImpl userDao() {
        return new UserDaoImpl();
    }

    @Bean("userService")
    public UserServiceImpl userService() {
        UserServiceImpl userService = new UserServiceImpl();
        userService.setUserDao(userDao());
        return userService;
    }
}

注解配置

通过在类上加注解的方式,来声明一个类交给Spring管理,Spring会自动扫描带有@Component,@Controller,@Service,@Repository这四个注解的类,然后帮我们创建并管理,前提是需要先配置Spring的注解扫描器。

优点:开发便捷,通俗易懂,方便维护。

缺点:具有局限性,对于一些第三方资源,无法添加注解。只能采用XML或JavaConfig的方式配置

举例: 对类添加@Component相关的注解,比如@Controller,@Service,@Repository 设置ComponentScan的basePackage, 比如, 或者@ComponentScan("xxx.xxx.xxx")注解,或者 new AnnotationConfigApplicationContext("xxx.xxx.xxx")指定扫描的basePackage.

@Service
public class UserServiceImpl {

    @Autowired
    private UserDaoImpl userDao;

    public List findUserList() {
        return userDao.findUserList();
    }

}

5、依赖注入的三种方式 setter方式

在XML配置方式中,property都是setter方式注入,比如下面的xml:



    
    
        
        
    
    

本质上包含两步: 第一步,需要new UserServiceImpl()创建对象, 所以需要默认构造函数 第二步,调用setUserDao()函数注入userDao的值, 所以需要setUserDao()函数 所以对应的service类是这样的:

public class UserServiceImpl {

    
    private UserDaoImpl userDao;

    
    public UserServiceImpl() {
    }

    
    public List findUserList() {
        return this.userDao.findUserList();
    }

    
    public void setUserDao(UserDaoImpl userDao) {
        this.userDao = userDao;
    }
}

在注解和Java配置方式下

public class UserServiceImpl {

    
    private UserDaoImpl userDao;

    
    public List findUserList() {
        return this.userDao.findUserList();
    }

    
    @Autowired
    public void setUserDao(UserDaoImpl userDao) {
        this.userDao = userDao;
    }
}

构造函数

在XML配置方式中,是通过构造函数参数注入,比如下面的xml:



    
    
        
        
    
    


本质上是new UserServiceImpl(userDao)创建对象, 所以对应的service类是这样的:

public class UserServiceImpl {

    
    private final UserDaoImpl userDao;

    
    public UserServiceImpl(UserDaoImpl userDaoImpl) {
        this.userDao = userDaoImpl;
    }

    
    public List findUserList() {
        return this.userDao.findUserList();
    }

}

在注解和Java配置方式下

 @Service
public class UserServiceImpl {

    
    private final UserDaoImpl userDao;

    
    @Autowired // 这里@Autowired也可以省略
    public UserServiceImpl(final UserDaoImpl userDaoImpl) {
        this.userDao = userDaoImpl;
    }

    
    public List findUserList() {
        return this.userDao.findUserList();
    }

}

注解注入

以@Autowired(自动注入)注解注入为例,修饰符有三个属性:Constructor,byType,byName。默认按照byType注入。

constructor:通过构造方法进行自动注入,spring会匹配与构造方法参数类型一致的bean进行注入,如果有一个多参数的构造方法,一个只有一个参数的构造方法,在容器中查找到多个匹配多参数构造方法的bean,那么spring会优先将bean注入到多参数的构造方法中。

byName:被注入bean的id名必须与set方法后半截匹配,并且id名称的第一个单词首字母必须小写,这一点与手动set注入有点不同。

byType:查找所有的set方法,将符合符合参数类型的bean注入。

@Service
public class UserServiceImpl {

    
    @Autowired
    private UserDaoImpl userDao;

    
    public List findUserList() {
        return userDao.findUserList();
    }

}

6、为什么推荐构造器注入方式

The Spring team generally advocates constructor injection as it enables one to implement application components as immutable objects and to ensure that required dependencies are not null. Furthermore constructor-injected components are always returned to client (calling) code in a fully initialized state.

简单翻译:构造器注入的方式能够保证注入的组件不可变,并且确保需要的依赖不为空。此外,构造器注入的依赖总是能够在返回客户端(组件)代码的时候保证完全初始化的状态。

依赖不可变:其实说的就是final关键字依赖不为空:省去了我们对其检查,当要实例化实例的时候,由于自己实现了有参数的构造函数,所以不会调用默认的构造函数,那么就需要Spring容器传入所需要的参数,所以就两种情况:1.有该类型的参数,传入,OK。2.无该类型的参数,报错。完全初始化的状态:向构造器传参之前,要确保注入的内容不为空,那么肯定要调用依赖组件的构造方法完成实例化。而在Java类加载实例化的过程中,构造方法是最后的一步(之前如果有父类先初始化父类,然后自己的成员变量,最后才是构造方法),所以返回来的都是初始化之后的状态。

所以通常是这样的

 @Service
public class UserServiceImpl {

    
    private final UserDaoImpl userDao;

    
    public UserServiceImpl(final UserDaoImpl userDaoImpl) {
        this.userDao = userDaoImpl;
    }

}

如果使用setter注入,缺点显而易见,对于IOC容器以外的环境,除了使用反射来提供它需要的依赖之外,无法复用该实现类。而且将一直是个潜在的隐患,因为你不调用将一直无法发现NPE的存在。

// 这里只是模拟一下,正常来说我们只会暴露接口给客户端,不会暴露实现。
UserServiceImpl userService = new UserServiceImpl();
userService.findUserList(); // -> NullPointerException, 潜在的隐患

循环依赖的问题:使用field注入可能会导致循环依赖,即A里面注入B,B里面又注入A:

public class A {
    @Autowired
    private B b;
}

public class B {
    @Autowired
    private A a;
}

如果使用构造器注入,在spring项目启动的时候,就会抛出:BeanCurrentlyInCreationException:Requested bean is currently in creation: Is there an unresolvable circular reference?从而提醒你避免循环依赖,如果是field注入的话,启动的时候不会报错,在使用那个bean的时候才会报错。

7、使用构造器注入方式时注入了太多的类导致Bad Smell怎么办?

比如当你一个Controller中注入了太多的Service类,会给你提示相关告警

对于这个问题,说明你的类当中有太多的责任,那么你要好好想一想是不是自己违反了类的单一性职责原则,从而导致有这么多的依赖要注入。

8、@Autowired和@Resource以及@Inject等注解注入有何区别

@Autowired

1、@Autowired是Spring自带的注解,通过AutowiredAnnotationBeanPostProcessor 类实现的依赖注入

2、@Autowired可以作用在CONSTRUCTOR、METHOD、PARAMETER、FIELD、ANNOTATION_TYPE

3、@Autowired默认是根据类型(byType )进行自动装配的

4、如果有多个类型一样的Bean候选者,需要指定按照名称(byName )进行装配,则需要配合@Qualifier。 指定名称后,如果Spring IOC容器中没有对应的组件bean抛出NoSuchBeanDefinitionException。也可以将@Autowired中required配置为false,如果配置为false之后,当没有找到相应bean的时候,系统不会抛异常

@Resource

1、@Resource是JSR250规范的实现,在javax.annotation包下

2、@Resource可以作用TYPE、FIELD、METHOD上

3、@Resource是默认根据属性名称进行自动装配的,如果有多个类型一样的Bean候选者,则可以通过name进行指定进行注入

@Inject

1、@Inject是JSR330 (Dependency Injection for Java)中的规范,需要导入javax.inject.Inject jar包 ,才能实现注入

2、@Inject可以作用CONSTRUCTOR、METHOD、FIELD上

3、@Inject是根据类型进行自动装配的,如果需要按名称进行装配,则需要配合@Named;

@Autowired、@Resource和@Inject注解的区别(最详细)

IOC体系结构

BeanDefinition

IOC容器管理了我们定义的各种Bean对象及其相互的关系,Bean对象在Spring实现中是以BeanDefinition来描述的。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/760546.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号