前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Spring入门:The IoC Container,实践篇(下)

Spring入门:The IoC Container,实践篇(下)

作者头像
WEBJ2EE
发布2019-09-17 15:57:47
5410
发布2019-09-17 15:57:47
举报
文章被收录于专栏:WebJ2EEWebJ2EE

8. 基于【注解(Annotation)】的容器配置

8.1. 应该用 XML 还是 Annotation?

  • 各有特点,应由开发人员根据情况挑选适合自己的方案。
  • 注解配置更简洁,会侵入业务逻辑;(XML 恰好反过来);
  • Spring 支持注解与 XML 配置混合使用;
  • Spring 通过 BeanPostProcessor 实现基于注解的配置;
  • <context:annotation-config/> 可隐式注册那些用于实现注解配置的 BeanPostProcessor:AutowiredAnnotationBeanPostProcessor, CommonAnnotationBeanPostProcessor, PersistenceAnnotationBeanPostProcessor, RequiredAnnotationBeanPostProcessor
  • <context:annotation-config/> 的影响范围近限它所在的容器;
  • 基于注解注入的执行时机要比基于XML的注入靠前;

8.2. @Required

  • 应用于 Bean 属性的 setter 方法上。实现必填的依赖项控制;
  • Spring 5.1 中废弃了,建议使用【构造器注入】或【InitializingBean.afterPropertiesSet()】解决;

示例1:

代码语言:javascript
复制
package webj2ee;

import org.springframework.beans.factory.annotation.Required;

public class ExampleBean {
    private String name;

    @Required
    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "ExampleBean{" +
                "name='" + name + '\'' +
                '}';
    }
}
代码语言:javascript
复制
<?xml version="1.0" encoding="UTF-8"?>
<beans ...>
    <bean class="org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor"/>
    <bean id="exampleBean" class="webj2ee.ExampleBean"/>
</beans>
代码语言:javascript
复制
package webj2ee;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("context.xml");
        ExampleBean exampleBean = context.getBean("exampleBean", ExampleBean.class);
        System.out.println(exampleBean);
        context.close();
    }
}

8.3. @Autowired

  • @Autowired 注解可用于构造函数、setter方法、字段;
  • @Autowired 可以收集 ApplicationContext 中匹配某种特殊类型的所有 Bean;(注1:可收集为 Map、Set、Array)(注2:可通过 @Order 注解,调整顺序)
  • @Autowired 标记的方法或字段,默认是 required 的,可通过 @Autowired(required = false) 调整为非必填字段。也可以通过@Nullable、java.util.Optional (JDK8)作为替代方案;
  • 当 @Autowired 标记到构造函数上时:如果只有一个构造函数,实际没必要通过@Autowired标注。如果存在多个构造函数,最多只能有一个构造函数被标记为(required=true);多个构造函数可同时被标记为(required=false),都将作为候选,满足最大依赖项的构造函数将会被选用。
  • 构造函数参数中的【多元素注入(arrays、set、map)】的注入比较特殊,如果没有找到可注入项,则会被解析为空(Empty)
  • @Autowired 可以直接解析一些Spring 内部依赖项:BeanFactory, ApplicationContext, Environment, ResourceLoader, ApplicationEventPublisher, and MessageSource。

示例1:(只有一个构造函数,没必要标记 @Autowired)

代码语言:javascript
复制
package webj2ee;
public class MovieFinder {
    @Override
    public String toString() {
        return "MovieFinder{}";
    }
}
代码语言:javascript
复制
package webj2ee;

public class SimpleMovieLister {
    private MovieFinder movieFinder;

    public SimpleMovieLister(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    @Override
    public String toString() {
        return "SimpleMovieLister{" +
                "movieFinder=" + movieFinder +
                '}';
    }

    // ...
}
代码语言:javascript
复制
<?xml version="1.0" encoding="UTF-8"?>
<beans ...>
    <context:annotation-config/>
    <bean id="movieFinder" class="webj2ee.MovieFinder"/>
    <bean id="simpleMovieLister" class="webj2ee.SimpleMovieLister"/>
</beans>
代码语言:javascript
复制
package webj2ee;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("context.xml");
        SimpleMovieLister simpleMovieLister = context.getBean("simpleMovieLister", SimpleMovieLister.class);
        System.out.println(simpleMovieLister);
        context.close();
    }
}

示例2:(@Autowired + setter)

代码语言:javascript
复制
package webj2ee;
public class MovieFinder {
    @Override
    public String toString() {
        return "MovieFinder{}";
    }
}
代码语言:javascript
复制
package webj2ee;

import org.springframework.beans.factory.annotation.Autowired;

public class SimpleMovieLister {
    private MovieFinder movieFinder;

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

    @Override
    public String toString() {
        return "SimpleMovieLister{" +
                "movieFinder=" + movieFinder +
                '}';
    }

    // ...
}
代码语言:javascript
复制
<?xml version="1.0" encoding="UTF-8"?>
<beans ...>
    <context:annotation-config/>
    <bean id="movieFinder" class="webj2ee.MovieFinder"/>
    <bean id="simpleMovieLister" class="webj2ee.SimpleMovieLister"/>
</beans>
代码语言:javascript
复制
package webj2ee;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("context.xml");
        SimpleMovieLister simpleMovieLister = context.getBean("simpleMovieLister", SimpleMovieLister.class);
        System.out.println(simpleMovieLister);
        context.close();
    }
}

示例3:(@Autowired + required=false)

代码语言:javascript
复制
package webj2ee;
public class MovieFinder {
    @Override
    public String toString() {
        return "MovieFinder{}";
    }
}
代码语言:javascript
复制
package webj2ee;

import org.springframework.beans.factory.annotation.Autowired;

public class SimpleMovieLister {
    private MovieFinder movieFinder;

    @Autowired(required = false)
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }
    
//    @Autowired
//    public void setMovieFinder(@Nullable MovieFinder movieFinder) {
//        this.movieFinder = movieFinder;
//    }

//    @Autowired
//    public void setMovieFinder(Optional<MovieFinder>  movieFinder) {
//        this.movieFinder = movieFinder.orElse(null);
//    }

    @Override
    public String toString() {
        return "SimpleMovieLister{" +
                "movieFinder=" + movieFinder +
                '}';
    }

    // ...
}
代码语言:javascript
复制
<?xml version="1.0" encoding="UTF-8"?>
<beans ...>
    <context:annotation-config/>
    <bean id="simpleMovieLister" class="webj2ee.SimpleMovieLister"/>
</beans>
代码语言:javascript
复制
package webj2ee;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("context.xml");
        SimpleMovieLister simpleMovieLister = context.getBean("simpleMovieLister", SimpleMovieLister.class);
        System.out.println(simpleMovieLister);
        context.close();
    }
}

示例4:(@Autowired + Set、Array、Map)

代码语言:javascript
复制
package webj2ee;
public interface MovieCatalog {
}
代码语言:javascript
复制
package webj2ee;

import org.springframework.core.annotation.Order;

@Order(1)
public class Comedy implements MovieCatalog {
    @Override
    public String toString() {
        return "Comedy{}";
    }
}
代码语言:javascript
复制
package webj2ee;

import org.springframework.core.annotation.Order;

@Order(2)
public class Dracula implements MovieCatalog {
    @Override
    public String toString() {
        return "Dracula{}";
    }
}
代码语言:javascript
复制
package webj2ee;

import org.springframework.beans.factory.annotation.Autowired;

import java.util.Arrays;
import java.util.Map;
import java.util.Set;

public class MovieRecommender {

    @Autowired
    private MovieCatalog[] movieCatalogsArr;

    @Autowired
    private Set<MovieCatalog> movieCatalogsSet;

    @Autowired
    private Map<String, MovieCatalog> movieCatalogsMap;

    @Override
    public String toString() {
        return "MovieRecommender{" +
                "movieCatalogsArr=" + Arrays.toString(movieCatalogsArr) +
                ", movieCatalogsSet=" + movieCatalogsSet +
                ", movieCatalogsMap=" + movieCatalogsMap +
                '}';
    }
}
代码语言:javascript
复制
<?xml version="1.0" encoding="UTF-8"?>
<beans ...>
    <context:annotation-config/>
    <bean id="dracula" class="webj2ee.Dracula"/>
    <bean id="comedy" class="webj2ee.Comedy"/>
    <bean id="movieRecommender" class="webj2ee.MovieRecommender"/>
</beans>
代码语言:javascript
复制
package webj2ee;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("context.xml");
        MovieRecommender movieRecommender = context.getBean("movieRecommender", MovieRecommender.class);
        System.out.println(movieRecommender);
        context.close();
    }
}

示例5:(@Autowired + Constructors + Multi-Elements)

代码语言:javascript
复制
package webj2ee;

import org.springframework.beans.factory.annotation.Autowired;

import java.util.Arrays;
import java.util.Map;
import java.util.Set;

public class MovieRecommender {
    private MovieCatalog[] movieCatalogsArr;
    private Set<MovieCatalog> movieCatalogsSet;
    private Map<String, MovieCatalog> movieCatalogsMap;

    @Autowired(required = true)
    public MovieRecommender(
            MovieCatalog[] movieCatalogsArr,
            Set<MovieCatalog> movieCatalogsSet,
            Map<String, MovieCatalog> movieCatalogsMap) {
        this.movieCatalogsArr = movieCatalogsArr;
        this.movieCatalogsSet = movieCatalogsSet;
        this.movieCatalogsMap = movieCatalogsMap;
    }

    @Override
    public String toString() {
        return "MovieRecommender{" +
                "\nmovieCatalogsArr=" + Arrays.toString(movieCatalogsArr) +
                ", \nmovieCatalogsSet=" + movieCatalogsSet +
                ", \nmovieCatalogsMap=" + movieCatalogsMap +
                '}';
    }
}
代码语言:javascript
复制
<?xml version="1.0" encoding="UTF-8"?>
<beans ...>
    <context:annotation-config/>
    <bean id="movieRecommender" class="webj2ee.MovieRecommender"/>
</beans>

示例6:(@Autowired + ApplicationContext ...)

代码语言:javascript
复制
package webj2ee;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;

public class SimpleMovieLister {

    @Autowired
    private ApplicationContext context;

    @Override
    public String toString() {
        return "SimpleMovieLister{" +
                "context=" + context +
                '}';
    }
}
代码语言:javascript
复制
<?xml version="1.0" encoding="UTF-8"?>
<beans ...>
    <context:annotation-config/>
    <bean id="simpleMovieLister" class="webj2ee.SimpleMovieLister"/>
</beans>
代码语言:javascript
复制
package webj2ee;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("context.xml");
        SimpleMovieLister simpleMovieLister = context.getBean("simpleMovieLister", SimpleMovieLister.class);
        System.out.println(simpleMovieLister);
        context.close();
    }
}

8.4. @Autowired + @Primary

  • @Autowired 是按【类型】装配,如果存在多个同类型候选Bean,就会报错;
  • 可通过 @Primary 在一堆同类型 Bean 中标记其中某一个是主要的;
代码语言:javascript
复制
package webj2ee;

import org.springframework.beans.factory.annotation.Autowired;

public class MovieRecommender {
    @Autowired
    private MovieCatalog movieCatalog;

    @Override
    public String toString() {
        return "MovieRecommender{" +
                "movieCatalog=" + movieCatalog +
                '}';
    }
}
代码语言:javascript
复制
<?xml version="1.0" encoding="UTF-8"?>
<beans ...>
    <context:annotation-config/>

    <bean id="dracula" class="webj2ee.Dracula" primary="true"/>
    <bean id="comedy" class="webj2ee.Comedy"/>
    <bean id="movieRecommender" class="webj2ee.MovieRecommender"/>
</beans>
代码语言:javascript
复制
package webj2ee;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("context.xml");
        MovieRecommender movieRecommender = context.getBean("movieRecommender", MovieRecommender.class);
        System.out.println(movieRecommender);
        context.close();
    }
}

8.5. @Autowired + @Qualifier

  • @Qualifier 用于进一步精确限制候选 Bean 范围;

标准用法:

代码语言:javascript
复制
<?xml version="1.0" encoding="UTF-8"?>
<beans ...>
    <context:annotation-config/>

    <bean id="dracula" class="webj2ee.Dracula">
        <qualifier value="level1"></qualifier>
    </bean>

    <bean id="comedy" class="webj2ee.Comedy">
        <qualifier value="level2"></qualifier>
    </bean>

    <bean id="movieRecommender" class="webj2ee.MovieRecommender"/>
</beans>
代码语言:javascript
复制
package webj2ee;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;

public class MovieRecommender {
    @Autowired
    @Qualifier("level1")
    private MovieCatalog movieCatalog;

    @Override
    public String toString() {
        return "MovieRecommender{" +
                "movieCatalog=" + movieCatalog +
                '}';
    }
}
  • @Qualifier(value) 的回退匹配特性:如果在候选 Bean 中没有找到复合 qualifier value 的 Bean,则会去找一个 id 与 qualifier value 相等的 Bean;但!不要将 @Qualifier(value) 的这种回退匹配的特性用于实现 byName 的依赖注入,应考虑其语义化特征。如果要实现 byName,应当选择@Resource。

8.6. @Resource

  • 隶属 JSR-250 规范。
  • 可应用于字段、Bean 的 setter 方法。
  • 语义是 by-name。
  • @Resource 的回退匹配策略,会寻找主类型匹配元素 Bean。
  • @Resource 也可以解析像 BeanFactory, ApplicationContext, ResourceLoader, ApplicationEventPublisher, MessageSource 这些 Spring 依赖。

示例:

代码语言:javascript
复制
package webj2ee;

import org.springframework.context.ApplicationContext;

import javax.annotation.Resource;

public class SimpleMovieLister {
    private MovieFinder movieFinder;

    @Resource
    private ApplicationContext context;

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

    @Override
    public String toString() {
        return "SimpleMovieLister{" +
                "movieFinder=" + movieFinder +
                ", context=" + context +
                '}';
    }
}
代码语言:javascript
复制
<?xml version="1.0" encoding="UTF-8"?>
<beans ...>
    <context:annotation-config/>
    <bean id="movieFinder" class="webj2ee.MovieFinder"/>
    <bean id="simpleMovieLister" class="webj2ee.SimpleMovieLister"/>
</beans>

9. Classpath Scanning and Managed Components

  • 前面提到的 @Autowired、@Resource 等只是解决了注入层面问题,但还是要在 XML 中手写 Bean 定义。
  • @Component 是标记 Bean 的基础注解;其衍生有:@Repository(持久层), @Service(服务层), @Controller(展示层)。
  • <context:component-scan> 用于实现 Bean 的扫描与注册;
  • <context:component-scan> 隐式启用了 <context:annotation-config>;
  • 可通过 include-filter、exclude-filter 定制扫描策略;
代码语言:javascript
复制
<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>

下一波就轮到写

AOP实践篇咯

参考:

Spring Framework Runtime: https://docs.spring.io/spring/docs/4.2.x/spring-framework-reference/html/overview.html The IoC Container: https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#spring-core Inversion of Control Containers and the Dependency Injection pattern: https://martinfowler.com/articles/injection.html 《Java Web 高级编程技术》 《Spring 入门经典》 《精通 Spring 4.x 企业应用开发实战》 《Spring5 高级编程》 《Spring实战(第3版)》

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-09-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 WebJ2EE 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档