上节 : spring boot简介

接着上章节的spring boot简介,我们会发现boot是基于spring的,其中最重要的就是spring容器了。那么本章着重介绍spring容器装配自定义bean的几种形式。

并在装配的时候能够学会做一些处理。

1. 新建一个maven项目

2. 引入spring依赖

  可以在search.mavem.org这个网站中查找maven的依赖

3. 更改项目对jdk的依赖,这里我们使用jdk1.8

  在pom文件中修改为:

<!-- 2. 把版本改成类似java的1.8版本的 -->
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>

(我会在最后粘贴出整个pom.xml文件的)

4. 简单使用注入bean的方式来输出下 :

  首先,我们在App.java当中创建一个注解的容器,然后在此构造函数当中注入配置类MyConfig.class,在配置类当中注入bean为MyBean.java;总体结构图如下 :

然后在App.java文件当中获取当前容器,把刚才注入进来的MyBean输出下看看 :

  

5. 我们也可以根据在MyConfig当中的方法的名称来获取,默认的就是方法名 :

  MyConfig代码如下 :

  

    @Bean
public MyBean createMyBean(){
return new MyBean();
}

6. 那么,我们也可以在这个方法上的Bean后面声明名称,这样我们通过名称取的时候就是通过这里定义的来取了 :

  

    public static void main( String[] args ){
// System.out.println( "Hello World!" );
// 1. 往构造参数中传递配置类,把配置类当中的bean都注入到上下文当中去
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class); // MyConfig.class中注入了MyBean
// System.out.println(context.getBean(MyBean.class)); // 根据类型来获取 // 2. 我们也可以根据名称来获取, 默认的就是方法 的名称
// System.out.println(context.getBean("createMyBean")); // MyConfig当中的方法名 // 3. 我们通过Bean那里指定的名称来获取(这个是比较普遍的做法)
System.out.println(context.getBean("myLxfBean")); // 一旦指定了这个名称,通过代码这里的2 默认方法名称是获取不到的了
context.close();
}

7. 默认情况下,这个Bean是单例的 :

  

  那么,我们可以通过在Bean的定义那里来声明scope变量来达到多例的模式 :

  

@Configuration    // 声明类是配置类(可以装入到容器当中去)
public class MyConfig { @Bean(name="myLxfBean") // 定义Bean的名称
@Scope("prototype") // 转变成多例
public MyBean createMyBean(){
return new MyBean();
}
}

8. 上面举例说明了 使用配置类注入到容器当中,进而从容器可以获取到bean的操作,同时还能改变单例多例模式,接下来,我们也可以实现FactoryBean这个接口,

  注入自己想要的实体,就可以完成自己的Bean工厂。然后把Bean工厂放入到配置类当中(MyConfig.class),但是我们要改变一些在配置类的名称,因为我们写了

  2个一样的bean必须要声明不同的name,要不然我们通过getBean(MyBean.class)是会报错说找到多个bean,如下:

  

public class MyBeanFactory implements FactoryBean<MyBean>{

    //  FactoryBean 接口也是创建bean的

    @Override
public MyBean getObject() throws Exception {
return new MyBean(); // 这里不new 就生成不了对象的
} // 获取到Bean的类型
@Override
public Class<?> getObjectType() {
return MyBean.class; // 改写成我们想要放回的Bean的类型
} public boolean isSingleton(){
return false; // 重写接口的 是不是单例 false不是单例
} }
        // System.out.println( "Hello World!" );
// 1. 往构造参数中传递配置类,把配置类当中的bean都注入到上下文当中去
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class); // MyConfig.class中注入了MyBean
System.out.println(context.getBean(MyBean.class)); // 根据类型来获取 // 2. 我们也可以根据名称来获取, 默认的就是方法 的名称
// System.out.println(context.getBean("createMyBean")); // MyConfig当中的方法名 // 3. 我们通过Bean那里指定的名称来获取(这个是比较普遍的做法)
System.out.println(context.getBean("myLxfBean")); // 4. 我们通过往MyConfig当中注入我们的MyBeanFactory
System.out.println(context.getBean(MyBeanFactory.class)); context.close();
@Configuration    // 声明类是配置类(可以装入到容器当中去)
public class MyConfig { @Bean(name="myLxfBean") // 定义Bean的名称
@Scope("prototype") // 转变成多例
public MyBean createMyBean(){
return new MyBean();
} @Bean // 不命名的话 ,容器 会说有找到多个bean会报错的
public MyBeanFactory createMyBeanFactory(){
return new MyBeanFactory();
}
}

报错截图 : 很明显截图说找到2个bean了。

所以我们要在同一个配置类当中注入同样的bean的时候要注意给其命名。并且在容器中获取的时候要根据命名来获取,不要根据class的类型来获取。

如下 :

9. 那么我们怎么获取到MyBeanFactory这个类呢?而不是从中获取到MyBean呢,很明显,第一种答案就直接在App当中根据类型指定为MyBeanFactory就能

  获取到。如果要通过名称的话,我们通过方法名获取到的是MyBean,我们给其加个&符号获取到的就是MyBeanFactory。

  

        // 3. 我们通过Bean那里指定的名称来获取(这个是比较普遍的做法)
System.out.println(context.getBean("myLxfBean")); -- com.CTO_Boot.snapshot.CTO.boot.MyBean@120d6fe6 // 4. 我们通过往MyConfig当中注入我们的MyBeanFactory中的MyBean
System.out.println(context.getBean("myFactoryMyBean")); --com.CTO_Boot.snapshot.CTO.boot.MyBean@4ba2ca36
     // 5. 获取到MyBeanFactory
System.out.println(context.getBean(MyBeanFactory.class)); -- com.CTO_Boot.snapshot.CTO.boot.MyBeanFactory@25359ed8 // 6. 我们通过往MyConfig当中注入我们的MyBeanFactory,在容器中使用&符号获取源
System.out.println(context.getBean("&myFactoryMyBean")); --com.CTO_Boot.snapshot.CTO.boot.MyBeanFactory@25359ed8(因为使用了单例)

  这个&符号可以在BeanFactory这个interface当中找到定义。(嗯,没事多看看源码),也就是说&获取本身,不去获取MyBeanFactory生产的Bean。

10. 如果我们不继承FactoryBean这个接口,我们直接使用普通的Factory呢?首先,我们创建一个JeepFactory,写一个方法为create,此方法是创建Jeep实体类。所以,很显然,

  我们得把JeepFactory注入到MyConfig当中,然后再写个方法调用create方法即可放回Jeep这个Bean。

  

在MyConfig.java当中增加如下代码:
@Bean
public JeepFactory createJeepFactory(){
return new JeepFactory();
} /* @Bean
public Jeep createJeep(){
return createJeepFactory().createJeep(); // 或者改造成传入JeepFactory都是行的
}*/ @Bean
public Jeep createJeep(JeepFactory jeepFactory){
return jeepFactory.createJeep(); // 或者改造成传入JeepFactory都是行的 // 那么这个参数spring会默认从容器当中去获取的
}

11. 上面写了一些往容器中注入Bean再获取的一些做法。接下来我们介绍一些在bean生成、销毁等要做一些处理的时候该怎么做。

  11.1 我们可以实现spring的一些接口,如实现 InitializingBean 接口

// 要处理的bean
public class HandlingBean implements InitializingBean { @Override
public void afterPropertiesSet() throws Exception {
// 在属性设置之后输出
System.out.println("--- HandlingBean的属性已经设置完毕!!! ----");
} }

输出来的结果是 :

信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@3d646c37: startup date [Tue Apr 24 17:47:15 CST 2018]; root of context hierarchy
--- HandlingBean的属性已经设置完毕!!! ----
com.CTO_Boot.snapshot.CTO.boot.MyBean@59717824
com.CTO_Boot.snapshot.CTO.boot.MyBean@146044d7
com.CTO_Boot.snapshot.CTO.boot.MyBeanFactory@3c0a50da
com.CTO_Boot.snapshot.CTO.boot.MyBeanFactory@3c0a50da
com.CTO_Boot.snapshot.CTO.boot.Jeep@646be2c3
com.CTO_Boot.snapshot.CTO.boot.HandlingBean@797badd3

11.2 我们要在销毁之前做处理的话,可以实现 DisposableBean 接口 :

// 要处理的bean
public class HandlingBean implements InitializingBean,DisposableBean{ @Override
public void afterPropertiesSet() throws Exception {
// 在属性设置之后输出
System.out.println("--- HandlingBean的属性已经设置完毕!!! ----");
} @Override
public void destroy() throws Exception {
// 马上要销毁这个Bean了
System.out.println(this.getClass()+"-----该bean马上要销毁了------");
} }
--- HandlingBean的属性已经设置完毕!!! ----
com.CTO_Boot.snapshot.CTO.boot.MyBean@146044d7
com.CTO_Boot.snapshot.CTO.boot.MyBean@1e9e725a
com.CTO_Boot.snapshot.CTO.boot.MyBeanFactory@646be2c3
com.CTO_Boot.snapshot.CTO.boot.MyBeanFactory@646be2c3
com.CTO_Boot.snapshot.CTO.boot.Jeep@797badd3
com.CTO_Boot.snapshot.CTO.boot.HandlingBean@77be656f
四月 24, 2018 5:52:02 下午 org.springframework.context.support.AbstractApplicationContext doClose
信息: Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@3d646c37: startup date [Tue Apr 24 17:52:01 CST 2018]; root of context hierarchy
class com.CTO_Boot.snapshot.CTO.boot.HandlingBean-----该bean马上要销毁了------

  11.3 我们如果不实现这些接口的话,单纯的一个javaBean的话,我们也可以在装配到配置类当中的时候在@Bean这个当中指定init跟destroy方法

  

  

  通过截图看到,我们我效果已经出来了,值得一提的是虽然我们主食了HandlingBean,但是我们依然给其注入到了容器中,所以容器装配了配置类他就会被New。

  11.4 除了实现接口之外,在bean注解中指定之外,我们还可以在普通javaBean当中的方法上写注解来指定init跟destory方法:

  

public class AnimalHandlingBean {

    // 通过注解指定init跟destroy方法

    @PostConstruct     // 初始化
public void initDefine(){
System.out.println("--------AnimalHandlingBean已经初始化完成了----------");
} @PreDestroy // 销毁
public void destoryDefine(){
System.out.println("--------AnimalHandlingBean马上要销毁了----------");
} }

  我们在把它装配到MyConfig.java当中,在APP当中打印下。

--- HandlingBean的属性已经设置完毕!!! ----
--------DefineHandlingBean已经初始化完成了----------
--------AnimalHandlingBean已经初始化完成了----------
四月 24, 2018 6:13:12 下午 org.springframework.context.support.AbstractApplicationContext doClose
信息: Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@3d646c37: startup date [Tue Apr 24 18:13:11 CST 2018]; root of context hierarchy
com.CTO_Boot.snapshot.CTO.boot.MyBean@23a5fd2
com.CTO_Boot.snapshot.CTO.boot.MyBean@78a2da20
com.CTO_Boot.snapshot.CTO.boot.MyBeanFactory@7bc1a03d
com.CTO_Boot.snapshot.CTO.boot.MyBeanFactory@7bc1a03d
com.CTO_Boot.snapshot.CTO.boot.Jeep@70b0b186
com.CTO_Boot.snapshot.CTO.boot.DefineHandlingBean@ba8d91c
com.CTO_Boot.snapshot.CTO.boot.AnimalHandlingBean@7364985f
--------AnimalHandlingBean马上要销毁了----------
--------DefineHandlingBean马上要销毁了----------
class com.CTO_Boot.snapshot.CTO.boot.HandlingBean-----该bean马上要销毁了------

  总结下:我们上面使用了3种方式完成bean初始化后和销毁前的操作,第一种使用spring的接口,第二种在bean装配那里指定,第三种基于注解

12. 我们继续来讲解bean的装配,先前我们使用 @Configuration 来把配置类装配到容器的,我们也可以使用 @Compent

@Component
public class CompentBean { }

App.java:
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class,CompentBean.class); // MyConfig.class中注入了MyBean // 11. 通过@Compent注解获取到bean
System.out.println(context.getBean(CompentBean.class));

13. 我们也可以使用context.getBeansOfType 来获取到指定class的map集合 :

14. 我们如果不想把compentBean注入到容器的话,我们把compentBean写到MyConfig当中,然后就只用在context当中注入MyConfig.java就行。

  其实compent这个注解一般是在我们对该类没有一个明确的角色的划分我们就使用compent这个注解。

15. 在dao层使用 @Repository 注解,然后也要注入到context当中,再从容器当中获取(所以是不是想起了我们学习spring的时候,什么ssm框架都要扫描包的):

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

16. 同理,我们也可以使用@Service注解,注入到容器。

17. 同理,我们也可以使用@Controller注解,注入到容器当中,再从中获取出来。

18. 我们写一个AopTest的compent组件类,我们往里面写一个属性为TempService的类,我们再把aopTest注入到容器中,我们发现通过:

        // 16. 测试能否获取到 在组件类当中不使用autowired注解能否获取到tempService
TempService ts = context.getBean(AopTest.class).getTempService();
System.out.println(ts);

通过这种方式是获取不到tempService的,只有:

//  我们测试下 能够直接获取到 TempService对象
@Component
public class AopTest { @Autowired
private TempService tempService; // 有了autowired之后下面的get、set可有可无
public TempService getTempService() {
return tempService;
} public void setTempService(TempService tempService) {
this.tempService = tempService;
} }

  只有写了autowired才能获取到。  所以,从这里我们可以看出@Bean跟@Autowired的区别了,当我们想要在容器中直接搞个组件,从组件获取到对象的时候一般都是@Bean;

而我们业务都是一套流程,所以一般都用@Autowired,因为谁想在开发业务的时候还要提前写好生成Bean的配置类,整个工作就让spring的AOP去完成就好了。也就是你可以这样理解autowired在运行时帮我们完成了这个Bean形式的new,就是Bean的创建。

19. 有人这个时候想做个骚操作了,我们在A这个组件类当中写Autowired注解,在B这个组件当中使用Bean的方式,然后把A跟B都注入到容器中,这个时候就会报错获取2个同样的类了。解决这个问题,可以在B这个里面使用 primary注解完成。

    @Bean(name="myLxfBean")      // 定义Bean的名称
@Scope("prototype") // 转变成多例
@Primary
public MyBean createMyBean(){
return new MyBean();
}

  或者在A当中指定名称,感觉其底层还是针对Bean的不同命名,到时候根据名称获取就行 :

    @Autowired
@Qualifier("zhidingmingcheng")
private TempService tempService;

20. 如果你在上述19当中,往容器中直接注入了什么serivce类,dao类,那么他最先得到的是这个,而不是从A,B当中new出来的。(补充一点,有个跟autowired差不多的resource注解)

21. 其实写了这么多,我们往往在开发当中是把配置类是在一个包当中写的,而我们写了多个配置类肯定不愿意一个一个去扫描配置类,所以容器当中是有个扫描包的构造方法的 :

    public static void main(String[] args) {
// 使用扫描的形式,并且拒绝掉一些bean
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext("com.CTO_Boot.snapshot.CTO.boot");
System.out.println(context.getBean(HandlingBean.class)); context.close();
}

22. 我们还有一种方法,就是在配置类当中写compoentScan,在配置类当中扫描包 :

@ComponentScan("com.CTO_Boot.snapshot.CTO.boot")
@Configuration
public class ConfigScan { }
public class App3 {

    public static void main(String[] args) {
// 使用扫描的形式,并且拒绝掉一些bean
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(ConfigScan.class);
System.out.println(context.getBean(HandlingBean.class)); context.close();
}
}

23. 如果我们在上面22的基础上要排除一些Bean不让其注入进来呢,这个时候我们可以看@CompentScan的源码,里面有excludeFilters跟includeFilters;

  我们怎么使用excludeFiters ,它告诉我们要传入filter数组,fitler是什么,我们继续点击进去,发现默认的有5种类型:

  ANNOTATION,ASSIGNABLE_TYPE(某个具体的bean或者配置类),ASPECTJ,REGEX(正则表达式),CUSTOM(用户自定义);

  我们测试下排除掉MyConfig.class,然后看能不能从容器当中获取到HandlingBean.class:

  

@ComponentScan(basePackages="com.CTO_Boot.snapshot.CTO.boot",excludeFilters=@Filter(type=FilterType.ASSIGNABLE_TYPE,
classes={MyConfig.class,TempController.class})) // 我们这里是可以写上 TempController,因为@Controller底层就有组件注解啊
@Configuration
public class ConfigScan { }

  

public class App3 {

    public static void main(String[] args) {
// 使用扫描的形式,并且拒绝掉一些bean
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(ConfigScan.class);
System.out.println(context.getBean(HandlingBean.class)); context.close();
}
}

SPRING-BOOT系列之Spring4快速入门的更多相关文章

  1. Spring Boot 2.0 的快速入门(图文教程)

    摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢! Spring Boot 2.0 的快速入门(图文教程) 大家都 ...

  2. Spring Boot (一)快速入门

    一.关于Spring Boot 在开始了解Spring Boot之前,我们需要先了解一下Spring,因为Spring Boot的诞生和Spring是息息相关的,Spring Boot是Spring发 ...

  3. Spring Boot GraphQL 实战 01_快速入门

    hello,大家好,我是小黑,又和大家见面啦~ 新开一个专题是关于 GraphQL 的相关内容,主要是通过 Spring Boot 来快速开发 GraphQL 应用,希望对刚接触 GraphQL 的同 ...

  4. Spring Boot系列—(一)入门

    前言 因为项目组需要进行微服务改造,而微服务开发中需要以Spring Boot为基础.因此需要先弄懂SpringBoot. 我们先来看看SpringBoot的背景由来,SpringBoot是什么,一个 ...

  5. Spring Boot 系列总目录

    一.Spring Boot 系列诞生原因 上学那会主要学的是 Java 和 .Net 两种语言,当时对于语言分类这事儿没什么概念,恰好在2009年毕业那会阴差阳错的先找到了 .Net 的工作,此后就开 ...

  6. Spring4 快速入门

    Spring4 快速入门 1 Spring简介 1.1 Spring是什么? Spring 是一个 IOC 和 AOP 容器的开源框架,为简化企业级应用而生. IOC(Inversion of Con ...

  7. Spring Boot 系列教程18-itext导出pdf下载

    Java操作pdf框架 iText是一个能够快速产生PDF文件的java类库.iText的java类对于那些要产生包含文本,表格,图形的只读文档是很有用的.它的类库尤其与java Servlet有很好 ...

  8. spring boot系列01--快速构建spring boot项目

    最近的项目用spring boot 框架 借此学习了一下 这里做一下总结记录 非常便利的一个框架 它的优缺点我就不在这背书了 想了解的可以自行度娘谷歌 说一下要写什么吧 其实还真不是很清楚,只是想记录 ...

  9. spring boot 系列之五:spring boot 通过devtools进行热部署

    前面已经分享过四篇随笔: spring boot 系列之一:spring boot 入门 spring boot 系列之二:spring boot 如何修改默认端口号和contextpath spri ...

随机推荐

  1. UVA 10887 Concatenation of Languages 字符串hash

    题目链接:传送门 题意: 给你两个集合A,B,任意组合成新的集合C(去重) 问你最后C集合大小 题解: 暴力 组成的新串hash起来 #include<bits/stdc++.h> usi ...

  2. Codeforces Round #422 (Div. 2) D. My pretty girl Noora 数学

    D. My pretty girl Noora     In Pavlopolis University where Noora studies it was decided to hold beau ...

  3. 减肥 day1

    今天是我减肥第一天,现在体重是147斤, 早晨吃了一碗面,喝了一碗奶,中午吃了一个apple. 6点钟去打篮球,晚上去食堂稍微吃一点东西.

  4. Duplicate Observed Data

    在翻看<重构-改善既有代码的设计>这本经典的书,书中就介绍了一个重构方法--Duplicate Observed Data 复制被监视数据的重构方法,使用这种方法能够使界面和对数据的操作隔 ...

  5. Redis持久化(RDB和AOF)

    什么是Redis持久化 什么是Redis持久化,就是将内存数据保存到硬盘. Redis 持久化存储 (AOF 与 RDB 两种模式) RDB持久化 RDB 是以二进制文件,是在某个时间 点将数据写入一 ...

  6. 接口_简单get接口_第一个接口

    import flask,json # print(__name__) ##__name__代表当前这个python文件 server = flask.Flask(__name__) #把咱们当前的这 ...

  7. Python小练习_将数据库中表数据存到redis里

    # ##练习:将xxx数据库中my_user表中数据存到redis里面# 分析: pymysql.json.redis# 1.连接数据库,查到数据库里面所有的数据,游标类型要用pymysql.curs ...

  8. java nio的一个严重BUG

    java nio的一个严重BUG Posted on 2009-09-28 19:27 dennis 阅读(4588) 评论(5)  编辑  收藏 所属分类: java .源码解读      这个BU ...

  9. phpMVC框架的核心启动类定义

    <?php//核心启动类class Framework { //定义一个run方法 public static function run(){ // echo "hello,wrold ...

  10. 使用strtok_s函数从一个字符串中分离出单词

    下面的代码从含有多个结束符的字符串中分离出单词来,需要对strtok_s有清楚的认识.这段代码是我在写一个处理文件中单词个数时用来分离读取到的字符串中的单词时写的,亲测可用~ 1 2 3 4 5 6 ...