Spring-IOC

语雀链接:https://www.yuque.com/nlwrno/xfkcgp/xmncyq

什么是IOC

控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)

为什么要使用spring IOC

在日常程序开发过程当中,我们推荐面向抽象编程,面向抽象编程会产生类的依赖,当然如果你够强大可以自己写一个管理的容器,但是既然spring以及实现了,并且spring如此优秀,我们仅仅需要学习spring框架便可。
当我们有了一个管理对象的容器之后,类的产生过程也交给了容器,至于我们自己的app则可以不需要去关系这些对象的产生了。为什么要使用spring来管理自己的bean对象呢,比如说,如果我们的Singer对象,我们需要对其代理,Singer只管唱歌,在哪唱,谁负责收钱,都需要动态代理实现,但如果我们每次调用都自己new一个对象,每次也就需要重新代理一次,而交给spring管理,spring则可以帮我们做这些操作。

spring实现IOC的思路和方法

spring实现IOC的思路是提供一些配置信息用来描述类之间的依赖关系,然后由容器去解析这些配置信息,继而维护好对象之间的依赖关系,前提是对象之间的依赖关系必须在类中定义好,比如A.class中有一个B.class的属性,那么我们可以理解为A依赖了B。既然我们在类中已经定义了他们之间的依赖关系那么为什么还需要在配置文件中去描述和定义呢?
spring实现IOC的思路大致可以拆分成3点

  1. 应用程序中提供类,提供依赖关系(属性或者构造方法)
  2. 把需要交给容器管理的对象通过配置信息告诉容器(xml、annotation,javaconfig)
  3. 把各个类之间的依赖关系通过配置信息告诉容器
    配置这些信息的方法有三种分别是xml,annotation和javaconfig
    维护的过程称为自动注入,自动注入的方法有两种构造方法和setter
    自动注入的值可以是对象,数组,map,list和常量比如字符串整形等

spring编程的风格

schemal-based-------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:c="http://www.springframework.org/schema/c"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                            http://www.springframework.org/schema/beans/spring-beans.xsd
                            http://www.springframework.org/schema/context
                            http://www.springframework.org/schema/context/spring-context.xsd"
                            default-autowire="byType"  ><!--全局定义自动装配-->
    <!--自动装配的方式,byTyp  byName  -->
  
        <!-- 开启注解 <context:annotation-config/>
                             开启扫描注解包括开启注解   -->
    <bean id="dao" class="com.spring.ioc.dao.IndexDaoImpl" autowire="byType"></bean>
    
        
    <context:component-scan base-package="com.spring.ioc"/>
   <bean id="service" class="com.spring.ioc.dao.IndexService">
        &lt;!&ndash;<property name="dao" ref="dao"></property>&ndash;&gt;
        <constructor-arg ref="dao"></constructor-arg>
    </bean>
</beans>

annotation-based-----annotation

@Service("service")
public class IndexService {
    /*@Autowired*/
    private IndexDao dao;
    public IndexService(IndexDao dao){
        this.dao = dao;
    }
    /*byName自动注入是根据setDao -->去set小写D --> dao来确定的e*/
    public void setDao(IndexDao dao) {
        this.dao = dao;
    }
    public void sayHello(){
        dao.sayHello();
    }
}
public class Test {
    public static void main(String[] args) {
        /*ClassPathXmlApplicationContext classPathXmlApplicationContext
                = new ClassPathXmlApplicationContext("classpath:spring.xml");
        IndexService service = (IndexService)classPathXmlApplicationContext.getBean("service");
        */
        AnnotationConfigApplicationContext an = new AnnotationConfigApplicationContext(SpringConfig.class);
        IndexService service = (IndexService)an.getBean("service");
        service.sayHello();
    }
}

java-based----java Configuration

@Configuration
public class ConfigA {
    @Bean
    public A a() {
        return new A();
    }
}
@Configuration
@Import(ConfigA.class)
public class ConfigB {
    @Bean
    public B b() {
        return new B();
    }
}

注入的两种方法

spring注入详细配置(字符串、数组等)参考文档:
https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#beans-factory-properties-detailed
接下的示例都是官方文档种的示例:

Constructor-based Dependency Injection

构造器注入,参考文档:
https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#beans-constructor-injection

package x.y;
public class ThingOne {
    public ThingOne(ThingTwo thingTwo, ThingThree thingThree) {
        // ...
    }
}
<beans>
    <bean id="beanOne" class="x.y.ThingOne">
        <constructor-arg ref="beanTwo"/>
        <constructor-arg ref="beanThree"/>
    </bean>
    <bean id="beanTwo" class="x.y.ThingTwo"/>
    <bean id="beanThree" class="x.y.ThingThree"/>
</beans>

Setter-based Dependency Injection

setter参考文档:
https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#beans-setter-injection

public class ExampleBean {
    private AnotherBean beanOne;
    private YetAnotherBean beanTwo;
    private int i;
    public void setBeanOne(AnotherBean beanOne) {
        this.beanOne = beanOne;
    }
    public void setBeanTwo(YetAnotherBean beanTwo) {
        this.beanTwo = beanTwo;
    }
    public void setIntegerProperty(int i) {
        this.i = i;
    }
}
<bean id="exampleBean" class="examples.ExampleBean">
    <!-- constructor injection using the nested ref element -->
    <constructor-arg>
        <ref bean="anotherExampleBean"/>
    </constructor-arg>
    <!-- constructor injection using the neater ref attribute -->
    <constructor-arg ref="yetAnotherBean"/>
    <constructor-arg type="int" value="1"/>
</bean>
<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>

自动装配

上面说过,IOC的注入有两个地方需要提供依赖关系,一是类的定义中,二是在spring的配置中需要去描述。自动装配则把第二个取消了,即我们仅仅需要在类中提供依赖,继而把对象交给容器管理即可完成注入。
在实际开发中,描述类之间的依赖关系通常是大篇幅的,如果使用自动装配则省去了很多配置,并且如果对象的依赖发生更新我们可以不需要去更新配置,但是也带来了一定的缺点

自动装配的优点

参考文档:
https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#beans-factory-autowire

// 官方文档给出的解释
Autowiring can significantly reduce the need to specify properties or constructor arguments. (Other mechanisms such as a bean template discussed elsewhere in this chapterare also valuable in this regard.)

Autowiring can update a configuration as your objects evolve. For example, if you need to add a dependency to a class, that dependency can be satisfied automatically without you needing to modify the configuration. Thus autowiring can be especially useful during development, without negating the option of switching to explicit wiring when the code base becomes more stable.

// 看不懂对吧,我也看不懂,然后我们有翻译,感觉翻译解释的都不需要加工处理了

自动装配可以大大减少指定属性或构造函数参数的需要。(本章其他地方讨论的其他机制,如bean模板,在这方面也很有价值。)
自动装配可以在你的目标进化时更新配置。例如,如果需要向类添加依赖项,则可以自动满足该依赖项,而不需要修改配置。因此,自动装配在开发过程中特别有用,当代码库变得更加稳定时,自动装配可以避
免切换到显式连接的选项。

自动装配的缺点

参考文档:
https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#beans-autowired-exceptions
// 依旧官网解释
Explicit dependencies in property and constructor-arg settings always override autowiring.You cannot autowire simple properties such as primitives, Strings, and Classes (and arraysof such simple properties). This limitation is by-design.

Autowiring is less exact than explicit wiring. Although, as noted in the earlier table,Spring is careful to avoid guessing in case of ambiguity that might have unexpected results.The relationships between your Spring-managed objects are no longer documented explicitly.

Wiring information may not be available to tools that may generate documentation from a Spring container.

Multiple bean definitions within the container may match the type specified by the setter method or constructor argument to be autowired. For arrays, collections, or Map instances,this is not necessarily a problem. However, for dependencies that expect a single value, this ambiguity is not arbitrarily resolved. If no unique bean definition is available, anexception is thrown.
// 依旧是翻译
属性和构造函数设置中的显式依赖项总是覆盖自动装配无法自动装配简单属性,如原语、字符串和类(以及此类简单属性的数组)。这种限制是由设计造成的。

自动装配不如显式连接精确。尽管,如前面表格中所述。Spring小心地避免在可能产生意外结果的歧义情况下进行猜测:Spring管理对象之间的关系不再明确地记录。—> 不是缺点好吧

连接信息可能对从Spring容器生成文档的工具不可用。

容器中的多个bean定义可以与要自动装配的sette方法或构造函数参数指定的类型相匹配。对于数组、集合或映射实例,这不一定是个问题。然而,对于期望单个值的依赖项,这种模糊性不是任意解决的。如果没有唯一的bean定义可用,则抛出异常

自动装配的方法

参考文档:
https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#beans-factory-autowire
在这里插入图片描述

@Autowired以及@Resource

参考文档:
https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#beans-autowired-annotation

引发问题:

@Autowired默认按照Type进行注入,但加入一个接口有多个实现,这个时候注入会出现什么问题呢?下面以代码的形式来进行展示。
public interface IndexDao {
public void sayHello();
}

@Repository
public class IndexDaoImpl implements  IndexDao{
    @Override
    public void sayHello() {
        System.out.println("hello IndexDaoImpl...");
    }
}
@Repository
public class IndexDaoImpl2 implements IndexDao{
    @Override
    public void sayHello() {
        System.out.println("hello IndexDaoImpl2...");
    }
}
@Service("service2")
public class IndexService2 {
    @Autowired
    private IndexDao indexDao;
    public void sayHello(){
        indexDao.sayHello();
    }
}
public class Test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext an = new AnnotationConfigApplicationContext(SpringConfig.class);
        IndexService2 service = (IndexService2)an.getBean("service2");
        service.sayHello();
    }
}
Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'service2': Unsatisfied dependency expressed through field 'indexDao'; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.spring.ioc.dao.IndexDao' available: expected single matching bean but found 2: indexDaoImpl,indexDaoImpl2
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:643)
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:130)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:399)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1422)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:594)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:517)
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:323)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:321)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:879)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:878)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:550)
    at org.springframework.context.annotation.AnnotationConfigApplicationContext.<init>(AnnotationConfigApplicationContext.java:89)
    at com.spring.ioc.dao.Test.main(Test.java:22)
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.spring.ioc.dao.IndexDao' available: expected single matching bean but found 2: indexDaoImpl,indexDaoImpl2
    at org.springframework.beans.factory.config.DependencyDescriptor.resolveNotUnique(DependencyDescriptor.java:220)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1265)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1207)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:640)
    ... 14 more

解决方式:

方式一:使用@Primary注解来定义,优先使用IndexDaoImpl

@Repository
@Primary
public class IndexDaoImpl implements  IndexDao{
    @Override
    public void sayHello() {
        System.out.println("hello IndexDaoImpl...");
    }
}

在这里插入图片描述
方式二:使用@Qualifier(“indexDaoImpl2”)用ByName的方式注入

@Service("service2")
public class IndexService2 {
    @Autowired
    @Qualifier("indexDaoImpl2")
    private IndexDao indexDao;
    public void sayHello(){
        indexDao.sayHello();
    }
}

在这里插入图片描述
方式三:使用@Resource注解进行注入(在这里可以看和@Autowire的区别)

// 使用@Resource可以指定以Type/name来进行注入,如果只有一个实现也可以直接@Resource
// 不具体指定,也会按照ByType方式注入 
@Service("service2")
public class IndexService2 {
    
    @Resource(type = IndexDaoImpl2.class)
    private IndexDao indexDao;
    public void sayHello(){
        indexDao.sayHello();
    }
}
@Service("service2")
public class IndexService2 {
    
    @Resource(name = "indexDaoImpl2")
    private IndexDao indexDao;
    public void sayHello(){
        indexDao.sayHello();
    }
}

在这里插入图片描述

Spring懒加载

参考文档:
https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#beans-factory-lazy-init
默认情况下,Applicationcontext实现将创建和配置所有的单例bean作为初始化过程的一部分。也就是说启动时,就会将bean初始化。通常,这种预实例化是可取的,因为配置或周围环境中的错误会立即被发现,而不是几小时甚至几天之后。当此行为不可取时,您可以通过将bean定义标记为延迟初始化来防止单例bean的预实例化。延迟初始化的bean告诉loC容器在第一次请求时创建bean实例,而不是在启动时。

<!-- 局部配置懒加载bean lazy-init="true" -->
<bean id="lazy" class="com.something.ExpensiveToCreateBean" lazy-init="true"/>
<bean name="not.lazy" class="com.something.AnotherBean"/>
<!-- 全局配置懒加载bean default-lazy-init="true" -->
<beans default-lazy-init="true">
    <!-- no beans will be pre-instantiated... -->
</beans>

springbean的作用域

参考文档:
https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#beans-factory-scopes
在这里插入图片描述

<!-- XML方式定义springbean的作用域 
    默认为singleton单例模式
    scope="prototype"  定义为prototype原型
 -->
<bean id="accountService" class="com.something.DefaultAccountService" scope="prototype"/>
@Service("service")
@Scope("prototype")  // 注解方式实现定义作用域
public class IndexService {
    /*@Autowired*/
    private IndexDao dao;
    public IndexService(IndexDao dao){
        this.dao = dao;
    }
    /*byName自动注入是根据setDao -->去set小写D --> dao来确定的e*/
    public void setDao(IndexDao dao) {
        this.dao = dao;
    }
    public void sayHello(){
        dao.sayHello();
    }
}

Singleton Beans with Prototype-bean Dependencies

意思是在Singleton 当中引用了一个Prototype的bean的时候引发的问题
参考文档:
https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#beans-factory-method-injection
这里解释一下问题就是,当我们单例中引用了非单例/原型的时候,我们引入的原型对象是单例的还是原型的呢?通过下方代码来看一下。

@Component("dao")
@Scope("prototype")
public class IndexDaoImpl implements  IndexDao{
    @Override
    public void sayHello() {
        System.out.println("hello spring");
    }
}
@Service("service")
@Scope("singleton")  //可以不写,默认singleton
public class IndexService {
    @Autowired
    private IndexDao dao;
    
     public IndexDao getIndexDao(){
        return dao;
     }
    
    public void sayHello(){
        dao.sayHello();
    }
}
public class Test {
    public static void main(String[] args) {
        /*ClassPathXmlApplicationContext classPathXmlApplicationContext
                = new ClassPathXmlApplicationContext("classpath:spring.xml");
        IndexService service = (IndexService)classPathXmlApplicationContext.getBean("service");
        */
        AnnotationConfigApplicationContext an = new AnnotationConfigApplicationContext(SpringConfig.class);
        IndexService service = (IndexService)an.getBean("service");
        IndexService service1 = (IndexService)an.getBean("service");
        // 思考一下结果会什么呢  service定义的为singleton,hashCode肯定是相同的
        // 但是IndexDao定义的prototype,结果会是什么样的呢?
        System.out.println("service.hashCode:"+ service.hashCode());
        System.out.println("service1.hashCode:"+ service1.hashCode());
        System.out.println("service.indexDao.hashCode:"+ service.getIndexDao().hashCode());
        System.out.println("service1.indexDao.hashCode:"+ service1.getIndexDao().hashCode());
        service.sayHello();
    }
}

在这里插入图片描述
结果确实相同的,为什么呢,因为service的单例覆盖掉了内部引用的原型,如果场景需要IndexDao每次是原型的方式来进行,我们应该怎么处理呢。
方式一:

// 这种方式算然可以解决,但是代码侵入性太高,不算一个很好的方式
@Service("service")
public class IndexService  implements ApplicationContextAware {
    private ApplicationContext applicationContext;
   
    protected IndexDao getIndexDao() {
        // notice the Spring API dependency!
        return this.applicationContext.getBean("dao", IndexDao.class);
    }
    public void setApplicationContext(
            ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
    public void sayHello(){
        getIndexDao().sayHello();
    }
}

方式二:

// 这样的话代码侵入性就降低很多
@Service("service")
public abstract class IndexService {
    @Lookup
    protected abstract IndexDao getIndexDao();
    public void sayHello(){
        getIndexDao().sayHello();
    }
}

Spring声明周期和回调

参考文档:
https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#beans-factory-lifecycle
1、Methods annotated with @PostConstruct
2、afterPropertiesSet() as defined by the InitializingBean callback interface
3、A custom configured init() method

Spring指定环境

参考文档:
https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#beans-definition-profiles-java
使用@profile注解,具体参照官方文档即可。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值