spring-ioc容器(基础应用)

IOC

基于官方5.2.0文档

https://docs.spring.io/spring/docs/5.2.0.RELEASE/spring-framework-reference/core.html#spring-core

1.1 概念

控制反转(Inversion of Control,缩写为IoC),
是面向对象编程中的一种设计原则,编程目标或者标准.可以用来减低计算机代码之间的耦合度。

其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),
还有一种方式叫“依赖查找”(Dependency Lookup)

我们平常对于一个对象的实例化一般是通过手动去new,这样对象的实例化控制权是由应用程序控制的

所谓控制反转:就是把这种权利反转给一个容器,而应用程序只需要提供对象的类型即可。


实现IOC有很多种方式,我们利用Spring来实现IOC

将类的管理交给Spring

IOC的好处:
    1.对象的实例化不是一件简单的事情,比如对象的关系比较复杂,依赖关系往往需要程序员去维护,这是一件非常头疼的事
    2.解耦,由容器去维护具体的对象
    3.托管了类的产生过程,比如我们需要在类的产生过程中做一些处理,最直接的例子就是代理,如果有容器程序可以把这部分过程交给容器,应用程序则无需去关心类是如何完成代理的

1.2 IOC容器

Spring实现IOC的思路

`org.springframework.context.ApplicationContext`接口管理SpringIOC容器和负责将容器实例化.
配置元数据里面,描述对象之间的依赖关系
然后由容器解析这些配置信息,继而维护对象之间的依赖关系
前提条件是依赖关系必须在类中定义好

定位到代码中的步骤

    1.应用程序中提供类,提供依赖关系(属性或者构造方法)
        eg.定义一个A类,依赖B类


    2.把需要交给容器管理的对象通过配置信息告诉容器(xml,annotation,javaConfig)
        xml中<bean id="A" \/>
        <bean id="B" \/>


    3.把各个类之间的依赖关系通过配置信息告诉容器
        xml中<bean id="A" ref="B" \/>
        <bean id="B" />
Spring三种编码方式
配置元数据有三种编码方式
1.xml           	XML-based metadata
        https://docs.spring.io/spring/docs/5.2.0.RELEASE/spring-framework-reference/core.html#aop-schema
2.接口              Annotation-based configuration
        https://docs.spring.io/spring/docs/5.2.0.RELEASE/spring-framework-reference/core.html#beans-annotation-config
3.java配置类        Java-based configuration
        https://docs.spring.io/spring/docs/5.2.0.RELEASE/spring-framework-reference/core.html#beans-java

这三种方式的目的都是一样的,目的都是为了定义(声明)Bean对象
他们可以互相兼容存在
并不强制用哪一种
XML的方式配置元数据

IOC容器通常需要定义被Spring管理的Bena对象,通过<bean/> 元素在一个顶级的<beans/>内的形式表示

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

    <bean id="..." class="...">  
        <!-- collaborators and configuration for this bean go here -->
    </bean>

    <bean id="..." class="...">
        <!-- collaborators and configuration for this bean go here -->
    </bean>

    <!-- more bean definitions go here -->

</beans>

1️⃣:id属性为定义的Bean的标识(名称)
2️⃣:class填写类的全限定类名

获取容器

java

ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");

services.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"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- services -->

    <bean id="petStore" class="org.springframework.samples.jpetstore.services.PetStoreServiceImpl">
        <property name="accountDao" ref="accountDao"/>
        <property name="itemDao" ref="itemDao"/>
        <!-- additional collaborators and configuration for this bean go here -->
    </bean>

    <!-- more bean definitions for services go here -->

</beans>


1.3 Bean

一个Spring IoC容器管理着一个或者多个Bean,Bean通过配置原数据创建,并提供给容器以供使用,
在容器内部,这些Bean被定义成BeanDefinition对象
创建一个Bean可以定义:
    1.全限定类名,以具体实现类来创建Bean
    2.Bean的作用范围,声明周期,回调函数...
    3.Bean与其他Bean之间的依赖关系
    4....
配置元数据里面的配置属性将会转换为一个Bean的属性
定义bean用到的属性

Class:全限定类名

Name:Bean的名称,没有写的话id就是name

Scope:Bean的作用域

Constructor argume:构造函数注入

Properties:属性注入

Autowiring mode:自动装配

Lazy initialization mode:懒加载

Initialization method:初始化的回调

Destruction method:销毁后的回调

Bean的命名空间
在xml配置中,你可以用id或者name属性来指定一个Bean的的名称
id只允许存在唯一一个不重复

如果你没有给Bean指定一个id或者name
容器将会给他生成一个唯一的名称
但是,如果你想用ref来指向一个Bean,那个Bean就必须有一个名称

实例化Bean
Bean本质上会被创建成对象,
容器根据Bean的命名/配置文件等Bean的描述,将Bean实例化成一个真实的对象
...
构造器实例化
<bean id="exampleBean" class="examples.ExampleBean"/>

<bean name="anotherExample" class="examples.ExampleBeanTwo"/>

静态工厂方法实例化

xml配置中,factory-method属性中指定他的静态工厂方法
在类中,声明一个静态实例,
调用静态方法返回

<bean id="clientService"
    class="examples.ClientService"
    factory-method="createInstance"/>
public class ClientService {
    private static ClientService clientService = new ClientService();
    private ClientService() {}

    public static ClientService createInstance() {
        return clientService;
    }
}
工厂方法实例化

使用工厂方法实例
调用非静态方法,使用现有的bean容器创建一个新的bean。
使用这种机制,类属性保留为空,
在factory-bean属性中指定他的工厂
factory-method属性中指定他的工厂方法
指定bean的名称在当前(或父母或祖先)容器,其中包含要调用的实例方法创建对象。

<!-- the factory bean, which contains a method called createInstance() 
    定义一个Bean,以它作为工厂
-->
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
    <!-- inject any dependencies required by this locator bean -->
</bean>

<!-- the bean to be created via the factory bean \
这个类是工程类创建出来的
-->
<bean id="clientService"
    factory-bean="serviceLocator"
    factory-method="createClientServiceInstance"/>

public class DefaultServiceLocator {

    private static ClientService clientService = new ClientServiceImpl();

    public ClientService createClientServiceInstance() {
        return clientService;
    }
}

1.4 Dependencies 依赖管理

两个注入风格
通过构造函数或者
set方法来依赖注入(Dependency Injection)
1.4.1.1 构造方法依赖注入

定义一个Pojo

package x.y;

public class ThingOne {

    public ThingOne(ThingTwo thingTwo, ThingThree thingThree) {
        // ...
    }
}

使用<constructor-arg/> 标签

<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>

使用<value>xxx</value>来制定变量的值,使用type属性指定参数是哪个

package examples;

public class ExampleBean {

    // Number of years to calculate the Ultimate Answer
    private int years;

    // The Answer to Life, the Universe, and Everything
    private String ultimateAnswer;

    public ExampleBean(int years, String ultimateAnswer) {
        this.years = years;
        this.ultimateAnswer = ultimateAnswer;
    }
}
<bean id="exampleBean" class="examples.ExampleBean">
    <constructor-arg type="int" value="7500000"/>
    <constructor-arg type="java.lang.String" value="42"/>
</bean>

index属性指定是第几个参数,注意从0开始,这样可以解决使用type时候,
构造函数有两个相同类型的参数

<bean id="exampleBean" class="examples.ExampleBean">
    <constructor-arg index="0" value="7500000"/>
    <constructor-arg index="1" value="42"/>
</bean>

!!!

根据构造函数的name,通常使用这个,因为既方便也不容易出错

<bean id="exampleBean" class="examples.ExampleBean">
    <constructor-arg name="years" value="7500000"/>
    <constructor-arg name="ultimateAnswer" value="42"/>
</bean>

你可以使用@ConstructorProperties这个jdk注解来声明你的构造参数
没用过,很少用

package examples;

public class ExampleBean {

    // Fields omitted

    @ConstructorProperties({"years", "ultimateAnswer"})
    public ExampleBean(int years, String ultimateAnswer) {
        this.years = years;
        this.ultimateAnswer = ultimateAnswer;
    }
}

1.4.1.2 Set方法依赖注入

@Required注解应用于Bean对象的属性的Set方法,
这个注解表示这个属性必须被显示的定义或者自动装配,5.1被弃用…

public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Required
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // ...
}
依赖注入的过程
  1. ApplicationContext对象创建/初始化,所有的Bean对象通过1️⃣xml2️⃣注解3️⃣java配置类的形式被定义在容器内,提供依赖关系
  2. 每个Bean会根据配置元数据内的配置的方式注入依赖,包括构造函数注入,set方法注入,静态工程等等方法…当Bean被真正创建时候,这些依赖已经被注入到Bean
  3. 在容器内:每个set的属性或者构造函数参数,或者是ref方式引用其他的对象的参数,都会变成Bean的属性
  4. 值的每个属性或构造函数参数都从其指定的格式转换为该属性或构造函数参数的实际类型。默认情况下,Spring可以将以字符串格式提供的值转换为所有内置类型,如int、long、string、boolean等。
在创建容器时,Spring容器验证每个bean的配置。
但是,每个bean的属性值不会被马上注入,只到这个Bean被真正创建
将创建单例作用域的bean并将其设置为预实例化( pre-instantiated)(默认值)。
否则,仅在请求bean时才创建它。当bean的依赖项及其依赖项的依赖项(等等)被创建和分配时
1.4.1.3 循环依赖
                如果使用构造函数注入,可能会导致循环依赖

比如,
类A依赖于通过构造函数注入的类B,类B也依赖于通过构造函数注入的类A

SpringIOC容器在运行时会发现这种循环依赖,并抛出BeanCurrentlyInCreationException

一种合理的解决方案是将构造函数注入改成Set方法注入,不用构造函数注入,只使用Set方法注入



1.4.1.4 依赖注入案例

通过构造函数和Set方法注入

<bean id="exampleBean" class="examples.ExampleBean">
    <!-- setter injection using the nested ref element -->
    <property name="beanOne">
        <ref bean="anotherExampleBean"/>
    </property>

    <!-- setter injection using the neater ref attribute -->
    <property name="beanTwo" ref="yetAnotherBean"/>
    <property name="integerProperty" value="1"/>
</bean>

<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
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"/>

public class ExampleBean {

    private AnotherBean beanOne;

    private YetAnotherBean beanTwo;

    private int i;

    public ExampleBean(
        AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) {
        this.beanOne = anotherBean;
        this.beanTwo = yetAnotherBean;
        this.i = i;
    }
}

静态工厂类的注入
静态工厂方法返回一个Bean,方法的参数将成为Bean的依赖

<bean id="exampleBean" class="examples.ExampleBean" factory-method="createInstance">
    <constructor-arg ref="anotherExampleBean"/>
    <constructor-arg ref="yetAnotherBean"/>
    <constructor-arg value="1"/>
</bean>

<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
public class ExampleBean {

    // a private constructor
    private ExampleBean(...) {
        ...
    }

    // a static factory method; the arguments to this method can be
    // considered the dependencies of the bean that is returned,
    // regardless of how those arguments are actually used.
    public static ExampleBean createInstance (
        AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) {

        ExampleBean eb = new ExampleBean (...);
        // some other operations...
        return eb;
    }
}
1.4.2 依赖注入的不同类型
基础类型和String
ref注入其他Bean
InnerBean类型
集合类型
null/空字符串
p/c命名空间
...
1.4.2.1 基础类型和String
<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <!-- results in a setDriverClassName(String) call -->
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/mydb"/>
    <property name="username" value="root"/>
    <property name="password" value="masterkaoli"/>
</bean>

开启P命名标签,xmlns:p代表property
c代表构造函数

<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
    https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource"
        destroy-method="close"
        p:driverClassName="com.mysql.jdbc.Driver"
        p:url="jdbc:mysql://localhost:3306/mydb"
        p:username="root"
        p:password="masterkaoli"/>

</beans>

使用java.util.Properties

<bean id="mappings"
    class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">

    <!-- typed as a java.util.Properties -->
    <property name="properties">
        <value>
            jdbc.driver.className=com.mysql.jdbc.Driver
            jdbc.url=jdbc:mysql://localhost:3306/mydb
        </value>
    </property>
</bean>

Spring容器规定,在<value/>标签内写的文本会被识别成java.util.Properties并被PropertyEditor解析?
这是一种很骚气的缩写?
这也是Spring团队推荐的值注入书写风格?

  • idref标签
  • 没用过,不知道干啥用的/.//
<bean id="theTargetBean" class="..."/>

<bean id="theClientBean" class="...">
    <property name="targetName">
        <idref bean="theTargetBean"/>
    </property>
</bean>

完全等价于

<bean id="theTargetBean" class="..." />

<bean id="client" class="...">
    <property name="targetName" value="theTargetBean"/>
</bean>
1.4.2.2 ref注入其他类型
1.4.2.3 InnerBean

<property/> 或者 <constructor-arg/>标签内,<bean/>标签作为内容



<bean id="outer" class="...">
   <!-- instead of using a reference to a target bean, simply define the target bean inline -->
   <property name="target">
       <bean class="com.example.Person"> <!-- this is the inner bean -->
           <property name="name" value="Fiona Apple"/>
           <property name="age" value="25"/>
       </bean>
   </property>
</bean>

inner bean并不强制要求定义name或者id,因为容器调用这个InnerBean并不需要他的标识符
而且匿名内部类在创建的时候忽略了scope标识
因为这个内部类都是匿名的,而且它随着外部类的创建而创建.
所以不能独立的访问InnerBean或者在别的地方调用它

1.4.2.4 集合类型

<list/>,<set/>,<map/>, <props/>标签可以注入java集合类型:Collection types, List, Set,Map, and Properties

很少用把…

<bean id="moreComplexObject" class="example.ComplexObject">
    <!-- results in a setAdminEmails(java.util.Properties) call -->
    <property name="adminEmails">
        <props>
            <prop key="administrator">administrator@example.org</prop>
            <prop key="support">support@example.org</prop>
            <prop key="development">development@example.org</prop>
        </props>
    </property>
    <!-- results in a setSomeList(java.util.List) call -->
    <property name="someList">
        <list>
            <value>a list element followed by a reference</value>
            <ref bean="myDataSource" />
        </list>
    </property>
    <!-- results in a setSomeMap(java.util.Map) call -->
    <property name="someMap">
        <map>
            <entry key="an entry" value="just some string"/>
            <entry key ="a ref" value-ref="myDataSource"/>
        </map>
    </property>
    <!-- results in a setSomeSet(java.util.Set) call -->
    <property name="someSet">
        <set>
            <value>just some string</value>
            <ref bean="myDataSource" />
        </set>
    </property>
</bean>
map类型,set类型,可以用这些标签注入

bean | ref | idref | list | set | map | props | value | null

1.4.2.5 增强集合
public class SomeClass {

    private Map<String, Float> accounts;

    public void setAccounts(Map<String, Float> accounts) {
        this.accounts = accounts;
    }
}

通过set方法注入map

<beans>
    <bean id="something" class="x.y.SomeClass">
        <property name="accounts">
            <map>
                <entry key="one" value="9.99"/>
                <entry key="two" value="2.75"/>
                <entry key="six" value="3.99"/>
            </map>
        </property>
    </bean>
</beans>
1.4.2.6 null和空字符串

空字符串

<bean class="ExampleBean">
    <property name="email" value=""/>
</bean>

等价于

exampleBean.setEmail("");

null

<bean class="ExampleBean">
    <property name="email">
        <null/>
    </property>
</bean>

等价于

exampleBean.setEmail(null);
1.4.2.7 p命名空间

p表示属性
Spring内部提供, 不需要提供XSD

<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
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean name="classic" class="com.example.ExampleBean">
        <property name="email" value="someone@somewhere.com"/>
    </bean>

    <bean name="p-namespace" class="com.example.ExampleBean"
        p:email="someone@somewhere.com"/>
</beans>

p:spouse-ref表示spouse是ref类型

<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
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean name="john-classic" class="com.example.Person">
        <property name="name" value="John Doe"/>
        <property name="spouse" ref="jane"/>
    </bean>

    <bean name="john-modern"
        class="com.example.Person"
        p:name="John Doe"
        p:spouse-ref="jane"/>

    <bean name="jane" class="com.example.Person">
        <property name="name" value="Jane Doe"/>
    </bean>
</beans>
1.4.2.8 c命名空间

c表示构造函数

<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"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="beanTwo" class="x.y.ThingTwo"/>
    <bean id="beanThree" class="x.y.ThingThree"/>

    <!-- traditional declaration with optional argument names -->
    <bean id="beanOne" class="x.y.ThingOne">
        <constructor-arg name="thingTwo" ref="beanTwo"/>
        <constructor-arg name="thingThree" ref="beanThree"/>
        <constructor-arg name="email" value="something@somewhere.com"/>
    </bean>

    <!-- c-namespace declaration with argument names -->
    <bean id="beanOne" class="x.y.ThingOne" c:thingTwo-ref="beanTwo"
        c:thingThree-ref="beanThree" c:email="something@somewhere.com"/>

</beans>

c命名空间与index


<!-- c-namespace index declaration -->
<bean id="beanOne" class="x.y.ThingOne" c:_0-ref="beanTwo" c:_1-ref="beanThree"
    c:_2="something@somewhere.com"/>
1.4.2.9 组合属性名

设置Bean属性的时候可以使用组合属性名,

<bean id="something" class="things.ThingOne">
    <property name="fred.bob.sammy" value="123" />
</bean>

设置fred属性里面的bob属性里面的sammy属性,值为123

1.4.3 使用depends-on
如果一个bean是其他bean的依赖,
这通常意味着一个bean被设置为他的属性。
通常你在基于XML的元数据配置的<REF />元素实现此目的

但是有时候:
实例化一个类之前要实例化另一个类
然而他们之间并不是依赖关系。


比如说:

用于数据库驱动程序初始化在一个静态类的初始化程序...(瞎写的)

单例模式下面的bean是prototype类型(瞎写的)

<bean id="beanOne" class="ExampleBean" depends-on="manager"/>
<bean id="manager" class="ManagerBean" />

为了表示对多个Bean的依赖,使用depends-on属性(逗号,空格,分号是有效的分隔符)

<bean id="beanOne" class="ExampleBean" depends-on="manager,accountDao">
    <property name="manager" ref="manager" />
</bean>

<bean id="manager" class="ManagerBean" />
<bean id="accountDao" class="x.y.jdbc.JdbcAccountDao" />
1.4.4 懒加载

IOC容器将在第一次被请求的时候创建Bean,而不是在容器被创建的时候

就是说,在容器创建的时候不会调用Bean的构造方法,只有在getBean时候才会被调用

如果你想为所有的对都实现懒加载可以使用官网的配置

default-lazy-init = true

<bean id="lazy" class="com.something.ExpensiveToCreateBean" lazy-init="true"/>
<bean name="not.lazy" class="com.something.AnotherBean"/>
1.4.5 自动装配
IOC的注入有两个地方需要提供依赖关系,
一是类的定义中,
二是在spring的配置中需要去描述。

那么,既然我们在类里面定义好了对象之间的依赖关系
我们为什么还要在配置信息里面再定义依赖关系呢??

自动装配则把第二个取消了,即我们仅仅需要在类中提供依赖,继而把对象交给容器管理即可完成注入。
1.4.5.1 自动装配的优点
1.节省参数的配置
2.当我们对象发生更新的时候,如果你需要添加一个对象到依赖中,这个对象会自动去更新

当使用xml配置元数据的时候,你可以使用给<bean/>标签加上autowire属性,有四种模式…

no 
    Default
    不适用自动装配,Bean定义必须使用ref

byName
    根据属性名:
    自动装配的byName
    和你类属性的名称没有关系
    是去找类里面的setXxx,xxx昨晚name

byType
    用的最多的
    

constructor


使用方法

1.全局开启自动装配

<beans defau-autowire="byType">

2.在 标签加上autoWire="byName",即给这个Bean单独开启

自动装配的局限性
    在实际开发中,描述类之间的依赖关系通常是大篇幅的,
    
    如果使用自动装配则省去了很多配置,并且如果对象的依赖发生更新我们可以不需要去更新配置,
    
    但是也带来了一定的缺点

  1.无法自动装配基础类型,`Strings`,`Classes`和基础类型的数组,这是设计上的缺陷

  2.没有具体注入来的明确,对象间的依赖关系不再具体的表现出来

  3.Wiring information不再是有效的工具,Spring容器生成文档?

  4.容器里面定义的大多数Bean,使用set方法或者构造器方法匹配他们的类型从而自动注入,如果是数据/集合/`Map`类型,可能没有问题,但是,开发者只希望匹配单一目标值,当不是当单一的时候,会抛出一个错误
1.4.6 方法注入

在大多数设计场景中,容器中的Bean大多数是单例的.
一个单例Bean需要取依赖另一个单例Bean,或者一个非单例Bean需要去依赖另一个非单例Bean,通常可以通过将一个Bean定义为另一个Bean的属性来处理依赖性

但是当Bean的生命周期不同时,会出现问题

假设单例Bean A需要去使用非单例(prototype)Bean B,也许在Bean A的每个方法都会调用Bean B,

容易只会创建这个单例Bean A一次,所以只有一次机会去设置他的属性

容器无法为Bean A每次都提供一个新的Bean B

一种解决方法是放弃控制反转,
使Bean A 实现 ApplicationContextAware这个接口
当Bean A需要Bean B时候,通过getBean("B")告诉容器来获取Bean B

// a class that uses a stateful Command-style class to perform some processing
package fiona.apple;

// Spring-API imports
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

public class CommandManager implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    public Object process(Map commandState) {
        // grab a new instance of the appropriate Command
        Command command = createCommand();
        // set the state on the (hopefully brand new) Command instance
        command.setState(commandState);
        return command.execute();
    }

    protected Command createCommand() {
        // notice the Spring API dependency!
        return this.applicationContext.getBean("command", Command.class);
    }

    public void setApplicationContext(
            ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

!!
当是这种方式并不是合适的,业务代码与Spring框架耦合…一些Spring IOC容器的高级特效,可以使你更轻易的处理这种情况…??Lookup方法注入

Lookup方法注入

注意.scope默认是单例模式

package fiona.apple;

// no more Spring imports!

public abstract class CommandManager {

    public Object process(Object commandState) {
        // grab a new instance of the appropriate Command interface
        Command command = createCommand();
        // set the state on the (hopefully brand new) Command instance
        command.setState(commandState);
        return command.execute();
    }

    // okay... but where is the implementation of this method?
    protected abstract Command createCommand();
}

在注入的的方法中,将被注入的方法需要以下形式的声明:

xml<public|protected> [abstract] <return-type> theMethodName(no-arguments);


<!-- a stateful bean deployed as a prototype (non-singleton) 非单例模式 -->
<bean id="myCommand" class="fiona.apple.AsyncCommand" scope="prototype">
    <!-- inject dependencies here as required -->
</bean>

<!-- commandProcessor uses statefulCommandHelper 单例模式 -->
<bean id="commandManager" class="fiona.apple.CommandManager">
    <lookup-method name="createCommand" bean="myCommand"/>
</bean>


基于注解的模式

public abstract class CommandManager {

    public Object process(Object commandState) {
        Command command = createCommand();
        command.setState(commandState);
        return command.execute();
    }

    @Lookup("myCommand")
    protected abstract Command createCommand();
}

可以根据lookup方法的返回值类型

public abstract class CommandManager {

    public Object process(Object commandState) {
        MyCommand command = createCommand();
        command.setState(commandState);
        return command.execute();
    }

    @Lookup
    protected abstract MyCommand createCommand();
}

通常情况下使用注解来Lookup,

修改方法
public class MyValueCalculator {

    public String computeValue(String input) {
        // some real code...
    }

    // some other methods...
}

实现org.springframework.beans.factory.support.MethodReplacer这个接口
,提供了新的方法定义

/**
 * meant to be used to override the existing computeValue(String)
 * implementation in MyValueCalculator
 */
public class ReplacementComputeValue implements MethodReplacer {

    public Object reimplement(Object o, Method m, Object[] args) throws Throwable {
        // get the input value, work with it, and return a computed result
        String input = (String) args[0];
        ...
        return ...;
    }
}

开始操作,你可以使用多个<arg-type/>标签在<replaced-method/>标签内部

<bean id="myValueCalculator" class="x.y.z.MyValueCalculator">
    <!-- arbitrary method replacement -->
    <replaced-method name="computeValue" replacer="replacementComputeValue">
        <arg-type>String</arg-type>
    </replaced-method>
</bean>

<bean id="replacementComputeValue" class="a.b.c.ReplacementComputeValue"/>

1.5 Bean作用域

singleton
    默认值
    单例模式:每个IOC容器一个Bean只实例化一次
    单例模式依赖多例模式:see Method Injection

prototype
    多例模式:一个Bean会被实例化多次
    通常情况下data access object (DAO) 不该被定义为多例模式,因为DAO不该持有任何会话状态


session
    作用于HTTP Session生命周期
    只有在基于web-aware Spring ApplicationContext情形下有效

application
    ServletContext 生命周期
    只有在web-aware Spring ApplicationContext 情形下有效

websocket
    WebSocket 生命周期
    只有在web-aware Spring ApplicationContext 情形下有效
Bean作用域与依赖间的关系
容器不仅仅管理着对象(Bean)的实例化,还将依赖联合依赖?
比如说
你想将一个HTTP request-scoped Bean 注入进一个longer-lived scope Bean
你将会选择注入一个AOP代理?

也就是说,你需要注入暴露出一样的公共接口作用域对象的代理对象,但也可以检索相关范围的真正目标对象(如HTTP请求),并委托方法调用到真正的对象。

<?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:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- an HTTP Session-scoped bean exposed as a proxy -->
    <bean id="userPreferences" class="com.something.UserPreferences" scope="session">
        <!-- instructs the container to proxy the surrounding bean -->
        <aop:scoped-proxy/> 
    </bean>

    <!-- a singleton-scoped bean injected with a proxy to the above bean -->
    <bean id="userService" class="com.something.SimpleUserService">
        <!-- a reference to the proxied userPreferences bean -->
        <property name="userPreferences" ref="userPreferences"/>
    </bean>
</beans>


<bean id="userPreferences" class="com.something.UserPreferences" scope="session"/>

<bean id="userManager" class="com.something.UserManager">
    <property name="userPreferences" ref="userPreferences"/>
</bean>


自定义作用域

1.6 Bean的生成过程

1.6.1 生命周期回调

你可以实现InitializingBean接口或者DisposableBean接口,但是不推荐

@PostConstruct and @PreDestroy注解在现代Spring应用中有较好的体验
使用注解,你将不再和Spring接口耦合

如果你不使用注解,你可以在xml配置元数据里面定义init-methoddestroy-method

1.6.1.1 初始化生命周期回调

在java配置类中
你可以使用 @Bean 的initMethod 属性

xml配置方式

<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>
public class ExampleBean {

    public void init() {
        // do some initialization work
    }
}

实现接口的方式

public class AnotherExampleBean implements InitializingBean {

    @Override
    public void afterPropertiesSet() {
        // do some initialization work
    }
}
销毁生命周期

xml配置方式

<bean id="exampleInitBean" class="examples.ExampleBean" destroy-method="cleanup"/>
public class ExampleBean {

    public void cleanup() {
        // do some destruction work (like releasing pooled connections)
    }
}

实现接口的方式

public class AnotherExampleBean implements DisposableBean {

    @Override
    public void destroy() {
        // do some destruction work (like releasing pooled connections)
    }
}
默认的初始化/销毁 生命周期方法

默认的方法: init(), initialize(), dispose(),

xml配置方法

<beans default-init-method="init">

    <bean id="blogService" class="com.something.DefaultBlogService">
        <property name="blogDao" ref="blogDao" />
    </bean>

</beans>
public class DefaultBlogService implements BlogService {

    private BlogDao blogDao;

    public void setBlogDao(BlogDao blogDao) {
        this.blogDao = blogDao;
    }

    // this is (unsurprisingly) the initialization callback method
    public void init() {
        if (this.blogDao == null) {
            throw new IllegalStateException("The [blogDao] property must be set.");
        }
    }
}
联合声明周期机制
Spring 2.5中

有三种方法控制声明周期行为

    实现InitializingBean and DisposableBean 接口

    自定义init() and destroy()方法

     @PostConstruct and @PreDestroy annotations. 注解


相同的Bean被配置不同的生命周期,用不同的初始化方法

1.方法加上@PostConstruct注解
2.定义afterPropertiesSet()方法  InitializingBean 
3.自定义init() 方法

--------------------------------

1.方法加上@PreDestroy注解
2.destroy() as defined by the DisposableBean
3.自定义destroy()方法


1.9 注解编码风格

@AutoWired 默认使用的是byType,如果byType没找到,就根据byName
@Resource() 默认使用的是name,如果括号里面的name没有填,是根据属性名来的,跟set方法没有关系
如果没找到,就byType

通过xml中的<context:annotation-config/>来隐式的注册他们

<?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:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

    <context:annotation-config/>

</beans>

隐式注册的包括
AutowiredAnnotationBeanPostProcessor, 
CommonAnnotationBeanPostProcessor,
 PersistenceAnnotationBeanPostProcessor, and the
  aforementioned RequiredAnnotationBeanPostProcessor.
<context:annotation-config/> 仅在定义的相同的应用程序上下文中寻找Bean上的注解

如果是在WebApplicationContext 中,它只会检查你控制器中的@Autowired beans

而不是服务
1.9.1 @Required

5.1开始被弃用

1.9.2 @Autowired
  1. 使用@Autowired注解在构造方法上

从Spring Framework 4.3开始,如果目标bean只定义一个构造函数,那么就不再需要在这样的构造函数上使用@Autowired注解.
但如果是多个构造器存在,为了指示容器使用哪个容器,至少必须使用@Autowired进行注释

public class MovieRecommender {

    private final CustomerPreferenceDao customerPreferenceDao;

    @Autowired
    public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
        this.customerPreferenceDao = customerPreferenceDao;
    }

    // ...
}
  1. @Autowired注解定义在属性的set方法上
public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Autowired
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // ...
}
  1. 你也可以将@Autowired注解定义任意名称的方法上,他有许多参数
public class MovieRecommender {

    private MovieCatalog movieCatalog;

    private CustomerPreferenceDao customerPreferenceDao;

    @Autowired
    public void prepare(MovieCatalog movieCatalog,
            CustomerPreferenceDao customerPreferenceDao) {
        this.movieCatalog = movieCatalog;
        this.customerPreferenceDao = customerPreferenceDao;
    }

    // ...
}
  1. 你也可以使用@Autowired注解在字段上,甚至可以和构造器混用
public class MovieRecommender {

    private final CustomerPreferenceDao customerPreferenceDao;

    @Autowired
    private MovieCatalog movieCatalog;

    @Autowired
    public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
        this.customerPreferenceDao = customerPreferenceDao;
    }

    // ...
}
注意事项
确保你的目标组件(比如说:MovieRecommender/CustomerPreferenceDao)类型是一致的


注入可能由于运行时“没有找到类型匹配”错误而失败

  1. 作用于数组集合类型
public class MovieRecommender {

    @Autowired
    private MovieCatalog[] movieCatalogs;

    // ...
}
public class MovieRecommender {

    private Set<MovieCatalog> movieCatalogs;

    @Autowired
    public void setMovieCatalogs(Set<MovieCatalog> movieCatalogs) {
        this.movieCatalogs = movieCatalogs;
    }

    // ...
}
public class MovieRecommender {

    private Map<String, MovieCatalog> movieCatalogs;

    @Autowired
    public void setMovieCatalogs(Map<String, MovieCatalog> movieCatalogs) {
        this.movieCatalogs = movieCatalogs;
    }

    // ...
}
1.9.3 @Primary

因为根据类型自动注入可能会导向多个实现类,所以有必要在选择的过程中加以控制.

其中的一种方式就是使用@Primary注解.当有多个实现类的时候,加了这个注解的将会成为自动注入的值.

@Configuration
public class MovieConfiguration {

    //当有两个实现类的时候

    @Bean
    @Primary
    public MovieCatalog firstMovieCatalog() { ... }

    @Bean
    public MovieCatalog secondMovieCatalog() { ... }

    // ...
}

@Autowired选取的是加了@Primary

public class MovieRecommender {

    @Autowired
    private MovieCatalog movieCatalog;

    // ...
}

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:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

    <context:annotation-config/>

    <bean class="example.SimpleMovieCatalog" primary="true">
        <!-- inject any dependencies required by this bean -->
    </bean>

    <bean class="example.SimpleMovieCatalog">
        <!-- inject any dependencies required by this bean -->
    </bean>

    <bean id="movieRecommender" class="example.MovieRecommender"/>

</beans>
1.9.4 自动装配使用筛选限定符

当有多个实现类时候使用自动写入,@Primary是有效的方式去选择生效的

当你需要更多的控制过程,你可以尝试 @Qualifier注解

您可以将限定符值与特定的参数关联起来,缩小类型匹配,以便为每个参数选择特定的bean。在最简单的情况下,这可以是一个简单的描述性值

public class MovieRecommender {

    @Autowired
    @Qualifier("main")
    private MovieCatalog movieCatalog;

    // ...
}

你可以在单个构造函数参数或者方法参数上加上@Qualifier注解

public class MovieRecommender {

    private MovieCatalog movieCatalog;

    private CustomerPreferenceDao customerPreferenceDao;

    @Autowired
    public void prepare(@Qualifier("main") MovieCatalog movieCatalog,
            CustomerPreferenceDao customerPreferenceDao) {
        this.movieCatalog = movieCatalog;
        this.customerPreferenceDao = customerPreferenceDao;
    }

    // ...
}

xml形式的Bean定义

<?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:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

    <context:annotation-config/>

    <bean class="example.SimpleMovieCatalog">
        <qualifier value="main"/> 

        <!-- inject any dependencies required by this bean -->
    </bean>

    <bean class="example.SimpleMovieCatalog">
        <qualifier value="action"/> 

        <!-- inject any dependencies required by this bean -->
    </bean>

    <bean id="movieRecommender" class="example.MovieRecommender"/>

</beans>

你可以新建自己的筛选注解

@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Genre {

    String value();
}

然后你可以在自动写入的参数/字段上使用该注解

public class MovieRecommender {

    @Autowired
    @Genre("Action")
    private MovieCatalog actionCatalog;

    private MovieCatalog comedyCatalog;

    @Autowired
    public void setComedyCatalog(@Genre("Comedy") MovieCatalog comedyCatalog) {
        this.comedyCatalog = comedyCatalog;
    }

    // ...
}

你可以给<bean/>标签加上子标签<qualifier/> ,然后指明type和value
,去匹配你的自定义注解
type匹配的是注解的全限定类名
如果不存在命名冲突的情况,可以使用 short class name(只写类名)

<?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:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

    <context:annotation-config/>

    <bean class="example.SimpleMovieCatalog">
        <qualifier type="Genre" value="Action"/>
        <!-- inject any dependencies required by this bean -->
    </bean>

    <bean class="example.SimpleMovieCatalog">
        <qualifier type="example.Genre" value="Comedy"/>
        <!-- inject any dependencies required by this bean -->
    </bean>

    <bean id="movieRecommender" class="example.MovieRecommender"/>

</beans>

案例:提供一个目录,在没有网络的时候可以搜索这个目录

  1. 定义一个简单的注解
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Offline {
        1️⃣
}
  1. 添加注解到定义了自动写入的字段或者属性
public class MovieRecommender {

    @Autowired
    @Offline   2️⃣ 
    private MovieCatalog offlineCatalog;

    // ...
}
  1. 现在定义这个Bean只需要qualifier 的type
<bean class="example.SimpleMovieCatalog">
    <qualifier type="Offline"/> 
    <!-- inject any dependencies required by this bean -->
</bean>

您还可以定义自定义限定符注释,除简单值属性外,它还接受命名属性。如果在要自动装配的字段或参数上指定了多个属性值,则bean定义必须匹配所有这些属性值,才能被视为自动装配候选

@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface MovieQualifier {

    String genre();

    Format format();
}
public enum Format {
    VHS, DVD, BLURAY
}
public class MovieRecommender {

    @Autowired
    @MovieQualifier(format=Format.VHS, genre="Action")
    private MovieCatalog actionVhsCatalog;

    @Autowired
    @MovieQualifier(format=Format.VHS, genre="Comedy")
    private MovieCatalog comedyVhsCatalog;

    @Autowired
    @MovieQualifier(format=Format.DVD, genre="Action")
    private MovieCatalog actionDvdCatalog;

    @Autowired
    @MovieQualifier(format=Format.BLURAY, genre="Comedy")
    private MovieCatalog comedyBluRayCatalog;

    // ...
}

最后,Bean应该包含限定符,使用<qualifier/>标签


<?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:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

    <context:annotation-config/>

    <bean class="example.SimpleMovieCatalog">
        <qualifier type="MovieQualifier">
            <attribute key="format" value="VHS"/>
            <attribute key="genre" value="Action"/>
        </qualifier>
        <!-- inject any dependencies required by this bean -->
    </bean>

    <bean class="example.SimpleMovieCatalog">
        <qualifier type="MovieQualifier">
            <attribute key="format" value="VHS"/>
            <attribute key="genre" value="Comedy"/>
        </qualifier>
        <!-- inject any dependencies required by this bean -->
    </bean>

    <bean class="example.SimpleMovieCatalog">
        <meta key="format" value="DVD"/>
        <meta key="genre" value="Action"/>
        <!-- inject any dependencies required by this bean -->
    </bean>

    <bean class="example.SimpleMovieCatalog">
        <meta key="format" value="BLURAY"/>
        <meta key="genre" value="Comedy"/>
        <!-- inject any dependencies required by this bean -->
    </bean>

</beans>

1.9.5 使用泛型作为限定符

没用过

配置类定义Bean

@Configuration
public class MyConfiguration {

    @Bean
    public StringStore stringStore() {
        return new StringStore();
    }

    @Bean
    public IntegerStore integerStore() {
        return new IntegerStore();
    }
}
@Autowired
private Store<String> s1; // <String> qualifier, injects the stringStore bean

@Autowired
private Store<Integer> s2; // <Integer> qualifier, injects the integerStore bean

// Inject all Store beans as long as they have an <Integer> generic
// Store<String> beans will not appear in this list
@Autowired
private List<Store<Integer>> s;
1.9.6 使用 CustomAutowireConfigurer

CustomAutowireConfigurerBeanFactoryPostProcessor
https://lab.ccb.com/user/login
让你自定义过滤注解

<bean id="customAutowireConfigurer"
        class="org.springframework.beans.factory.annotation.CustomAutowireConfigurer">
    <property name="customQualifierTypes">
        <set>
            <value>example.CustomQualifier</value>
        </set>
    </property>
</bean>
1.9.7 Injection with @Resource

使用@Resource来注入

@AutoWired 默认使用的是byType,如果byType没找到,就根据byName
@Resource() 默认使用的是name,如果括号里面的name没有填,是根据属性名来的,跟set方法没有关系
如果没找到,就byType

public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Resource(name="myMovieFinder") 
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }
}

如果括号里面的name没有填 ,将会以set方法后面的单词小写开头作为name

public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Resource
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }
}
1.9.8 使用 @Value

@Value通常用于注入外部属性

Bean

@Component
public class MovieRecommender {

    private final String catalog;

    public MovieRecommender(@Value("${catalog.name}") String catalog) {
        this.catalog = catalog;
    }
}

配置类

@Configuration
@PropertySource("classpath:application.properties")
public class AppConfig { }

配置文件:application.properties

catalog.name=MovieCatalog

以上代码,在Bean中${catalog.name}就等于MovieCatalog

1.9.9 @PostConstruct and @PreDestroy

加方法上表明这是初始化/销毁生命周期回调方法

public class CachingMovieLister {

    @PostConstruct
    public void populateMovieCache() {
        // populates the movie cache upon initialization...
    }

    @PreDestroy
    public void clearMovieCache() {
        // clears the movie cache upon destruction...
    }
}

1.10 类搜索与管理

本章中大部分的案例代码都是用xml形式来创建配置元数据,在Spring容器中,这些元数据生成每个bean的属性依赖等等…

在下面的案例中,你们可以使用注解来表达自己的自定义表达式,来定义容器可以注册哪些Bean

从Spring3.0开始,Spring JavaConfig项目提供的许多特性都是Spring核心框架的一部分

这允许你使用java,而不是xml

查看@Configuration、@Bean、@Import和@DependsOn注释,了解如何使用这些新特性。
1.10.1 @Component和类似的注解
1.10.1 @Component 和其他注解的关系

@Repository注解,标识于 Data Access Object 或者 DAO,该标记的用途之一是自动转换异常??

Spring提供了更多的原型注释:@Component,@Service,@Controller

  1. @Component,是通用的

  2. @Repository, @Service, and @Controller是特俗化的@Component,具有更特殊的用途.,分别用在持久层,服务层,控制器层…因此,@Component用于 用其他注释都不太合适的地方,

  3. 其他三个注解中包含 @Component

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component 
public @interface Service {

    // ...
}

@Service注解将会以@Component一样的方式处理

您还可以组合元注释来创建组合注释。
例如,Spring MVC中的@RestController注释由@Controller和@ResponseBody组成。

@ComponentScan(basePackages = "org.example"),basePackages这个属性定义了包扫描的基础路径,
如果有且仅有basePackages这个属性,可以这样简写: @ComponentScan("org.example")

使用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:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="org.example"/>

</beans>

当使用<context:component-scan>的时候,会自动开启<context:annotation-config>,就不用写他了

1.10.4 定义搜索包的筛选条件
@Configuration
@ComponentScan(basePackages = "org.example",
        includeFilters = @Filter(type = FilterType.REGEX, pattern = ".*Stub.*Repository"),
        excludeFilters = @Filter(Repository.class))
public class AppConfig {
    ...
}

xml形式

<beans>
    <context:component-scan base-package="org.example">
        <context:include-filter type="regex"
                expression=".*Stub.*Repository"/>
        <context:exclude-filter type="annotation"
                expression="org.springframework.stereotype.Repository"/>
    </context:component-scan>
</beans>
1.10.6 Bean名称自动生成规则
1.10.8 注解限定符
1.10.9 类搜索候选应用列表

1.11 使用JSR330标准注解

没用过,跳过

1.12 java配置类编码风格

1.12.1 基础:@Configuration和@Bean
@Configuration
public class AppConfig {

    @Bean
    public MyService myService() {
        return new MyServiceImpl();
    }
}

转化为xml

<beans>
    <bean id="myService" class="com.acme.services.MyServiceImpl"/>
</beans>
1.12.2 使用AnnotationConfigApplicationContext来实例化Spring容器

绝大多数xml风格的实例化采用ClassPathXmlApplicationContext,

你可以使用带有@Configuration的类作为参数传给AnnotationConfigApplicationContext,然后就不用写xml了

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
    MyService myService = ctx.getBean(MyService.class);
    myService.doStuff();
}
1.12.3 使用@Bean注解

在方法上面加@Bean,里面new对象,并return出去,就把这个Bean给容器管理了

1.12.3.2 Bean的依赖

1.13 环境变量!这个重要

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值