spring ioc容器有哪些(Spring IOC官方文档学习笔记(十二)之基于Java的容器配置)
1.@Bean与@Configuration
(1) 标注于类之上的@Configuration注解与标注于方法之上的@Bean注解是支持基于Java的容器配置的核心,被@Bean注解标注的方法用于实例化bean并将其注入至容器中,它与基于xml配置中的<bean/>标签起着相同的作用,@Bean可用在任何被@Component注解标注的类中,不过绝大部分情况下它们都被用于被@Configuration注解标注的类中;被@Configuration注解标注的类通常作为bean的定义源,如同基于xml配置中的<beans/>标签,此外,还可在@Configuration标注的类中配置bean之间的依赖关系,如下
//两个普通的类,其中ExampleB依赖ExampleA public class ExampleA { } public class ExampleB { private ExampleA exampleA; public ExampleB(ExampleA exampleA) { this.exampleA = exampleA; } } //配置类 @Configuration public class Config { //注入bean ExampleA @Bean public ExampleA exampleA() { return new ExampleA(); } //调用exampleA()方法来配置ExampleB @Bean public ExampleB exampleB() { return new ExampleB(exampleA()); } //上下这两个exampleB方法等价,注意,在下面这个例子中,Spring会自动帮我们注入ExampleA对象 // @Bean // public ExampleB exampleB(ExampleA exampleA) { // return new ExampleB(exampleA); // } } //启动容器,打印注入的对象是否相同 AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class); System.out.println(ctx.getBean(ExampleB.class).getExampleA() == ctx.getBean(ExampleA.class)); //观察结果,可见Spring执行了依赖注入,注入了容器中的ExampleA,而非new出来了一个新的ExampleA true(2) @Bean也可用在任何被@Component注解标注的类中,此时,我们称其为lite @Bean模式,而在这种lite模式下,我们不能配置bean之间的依赖关系,如下所示
//取上面的例子,其他保持不变,只将Config类上的@Configuration注解变更为@Component注解,此时其中的@Bean注解就处于lite @Bean模式 @Component public class Config { //保持不变... } //启动容器,观察打印结果,为false,可见此时Spring并没有执行依赖注入,而是直接new出来了一个新的ExampleA给ExampleB,因此在这种lite模式下,我们不能配置bean之间的依赖关系2.通过AnnotationConfigApplicationContext实例化Spring容器
(1) AnnotationConfigApplicationContext作为Spring的容器,它不仅可以接受@Configuration类(同时这个类本身也会被注册为一个bean)作为参数 ,还可以接受@Component类或用JSR-330注解标注的类作为参数,如下
//例一: 一个Spring配置类 @Configuration public class Config { } public static void main(String[] args) { //使用@Configuration类作为输入,完全摆脱掉基于xml的配置 AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class); //打印,会观察到Config也被注入到了容器中 Arrays.stream(ctx.getBeanDefinitionNames()).forEach(System.out::println); } //例二: ExampleB被JSR 330标准注解标注 @Component public class ExampleA { } @Named public class ExampleB { @Inject private ExampleA exampleA; public ExampleA getExampleA() { return exampleA; } } public static void main(String[] args) { //使用@Component类或用JSR-330注解标注的类作为参数,容器会对它们进行依赖注入 AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ExampleA.class, ExampleB.class); System.out.println(ctx.getBean(ExampleB.class).getExampleA()); }(2) 可以使用AnnotationConfigApplicationContext类的register(Class<?>…)方法编程式的向容器中注册,如下
public class ExampleA { } public static void main(String[] args) { //使用无参构造函数 AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); //编程式的向容器中注册bean ExampleA ctx.register(ExampleA.class); //refresh()方法用于处理bean,一定要调用此方法,否则容器将抛出异常 ctx.refresh(); System.out.println(ctx.getBean(ExampleA.class)); }(3) 我们可以使用@ComponentScan(basePackages = "...")注解或<context:component-scan base-package="..."/>标签来开启注解扫描,此外,AnnotationConfigApplicationContext也提供了scan(String…)方法来开启注解扫描,如下
//ExampleA位于cn.example.spring.boke包下 @Component public class ExampleA { } public static void main(String[] args) { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); //开启注解扫描,扫描cn.example.spring.boke"包下的bean ctx.scan("cn.example.spring.boke"); ctx.refresh(); System.out.println(ctx.getBean(ExampleA.class)); }(4) 在web环境中 ,可以使用AnnotationConfigWebApplicationContext ,来配置ContextLoaderListener监听器或DispatcherServlet ,具体配置细节可参考官方文档
3.使用@Bean注解
(1) @Bean注解是一个方法级别的注解 ,类似于基于xml配置中的<bean/>标签 ,我们可以在@Configuration类或@Component类中使用该注解 ,它常用于声明一个bean ,如下
@Configuration public class Config { //@Bean注解用于将方法的返回值注册为容器中的一个bean ,方法返回类型就是该bean的类型 ,默认情况下 ,方法名就是bean的名称 //下面这个例子:向容器中注入一个类型为ExampleA ,名称为exampleA的bean @Bean public ExampleA exampleA() { return new ExampleA(); } }(2) 针对@Bean方法的返回类型,也有些细节需要注意 ,如下所示
//有两个接口A和B public interface A { } public interface B { } //ExampleA实现了这两个接口 public class ExampleA implements A, B { } //而另一个bean ExampleB ,它依赖了类型为B的bean @Component public class ExampleB { @Autowired private B b; public B getB() { return b; } } //配置类 @Configuration @ComponentScan(basePackages = "cn.example.spring.boke") public class Config { //注意,我们返回了ExampleA的实例 ,不过却将返回类型声明为了A @Bean public A exampleA() { return new ExampleA(); } } //接着 ,启动容器 ,会发现容器抛出NoSuchBeanDefinitionException: No qualifying bean of type cn.example.spring.boke.B的异常 ,可见容器将返回的ExampleA仅视作了A类型然后就用于注入 ,虽然它也可以被视作B类型 AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class); System.out.println(ctx.getBean(ExampleB.class).getB()); //针对上面的情况 ,有两种办法解决: //方法一:最直接 ,直接返回类型B而非A ,如下 @Bean public B exampleA() { return new ExampleA(); } //方法二:其他的不变 ,只在我们的ExampleB上 ,加上@Lazy注解 ,让容器有时间去充分的识别ExampleA ,使它意识到ExampleA还可以视作B类型 @Component @Lazy public class ExampleB { //省略... }(3) @Bean方法可以拥有任意数量的参数,这些参数就是这个bean的依赖项 ,如下
@Configuration public class Config { @Bean public ExampleB exampleB() { return new ExampleB(); } //ExampleA依赖了ExampleB ,Spring会为我们自动注入容器中的这个ExampleB对象,类似于基于构造函数的依赖注入 @Bean public ExampleA exampleA(ExampleB exampleB) { return new ExampleA(exampleB); } }(4) 同普通的bean ,由@Bean方法注入的bean也支持由JSR-250所定义的生命周期回调注解 ,支持InitializingBean ,DisposableBean或Lifecycle等接口 ,支持各种Aware接口用于注入容器内置组件 ,同时@Bean还提供了initMethod和destroyMethod属性用于配置初始化和销毁回调 ,同<bean/>标签的属性 ,如下
public class ExampleA implements ApplicationContextAware { private ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } @PostConstruct public void init() { System.out.println("init..."); } public void destroy() { System.out.println("destroy..."); } } @Configuration public class Config { @Bean(destroyMethod = "destroy") public ExampleA exampleA() { return new ExampleA(); } }(5) 默认情况下 ,当我们使用基于Java的配置来定义一个bean的时候 ,如果在这个bean中有声明public修饰的close或shutdown方法 ,那么这些方法会随着该bean的销毁回调的触发而同时被调用 ,这是Spring提供的一个默认机制 ,无需任何的配置都会生效,但如果我们想关闭掉这一机制 ,可通过设置@Bean中的属性destroyMethod=""来达到这一目的 ,如下
//设置destroyMethod = "",禁用掉Spring会自动触发public修饰的close或shutdown方法的机制 @Bean(destroyMethod = "") public ExampleA exampleA() { return new ExampleA(); }(6) 我们可以通过使用@Scope注解来指定bean的作用域 ,如下
@Configuration public class Config { //由@Bean方法所声明的bean的作用域默认为singleton //@Scope注解还可用于类上 @Bean @Scope("prototype") public ExampleA exampleA() { return new ExampleA(); } }创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!