上节 : 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. Cocos2d-js异步图片加载

    这里说的是在需要的使用加载图片,比如游戏中的某个关卡的图片,不用在游戏一开始就加载(万一用户玩不到那关,岂不是很冤,流量费了那么多),否则 载入速度也慢.这种方式加载资源要用到cc.loader官方文 ...

  2. 怎么显示隐藏Mac上的隐藏文件

    打开终端,输入:defaults write com.apple.finder AppleShowAllFiles -bool true 此命令显示隐藏文件defaults write com.app ...

  3. 启动vmware中的虚拟机的时候,提示Failed to lock the file

    http://www.vixual.net/blog/archives/842 VMware Server 當掉後重新啟動 Guest OS 時,出現 cannot open the disk '*. ...

  4. cassandra在服务端像leveldb一样进行插入初试成功

    经过研究,决定在 cql3/QueryProcessor.java 里面下手. 这里有两个函数,第一个是 public ResultMessage process(String queryString ...

  5. docker容器安装使用

    window安装 1 下载    http://mirrors.aliyun.com/docker-toolbox/windows/docker-toolbox/ docker toolbox 是一个 ...

  6. 创建app前的环境配置/AppIcon/启动图片

    1.真机调试http://blog.csdn.net/tht2009/article/details/48580569 2.创建app前的环境配置

  7. Codeforces-707D:Persistent Bookcase (离线处理特殊的可持久化问题&&Bitset)

    Recently in school Alina has learned what are the persistent data structures: they are data structur ...

  8. 「LuoguP3191」 [HNOI2007]紧急疏散EVACUATE(最大流

    Description 发生了火警,所有人员需要紧急疏散!假设每个房间是一个N M的矩形区域.每个格子如果是’.’,那么表示这是一块空地:如果是’X’,那么表示这是一面墙,如果是’D’,那么表示这是一 ...

  9. Ordered Fractions

    链接 分析:遍历一下,求个gcd即可,最后按照ans排序并去重 /* PROB:frac1 ID:wanghan LANG:C++ */ #include "iostream" # ...

  10. angularjs 获得当前元素属性

    先用 console.log(this)查看下当前被点击元素的 this 属性,然后可以看见里面有个$index属性,该属性指向的就是DOM元素列表中当前被点击的那个DOM的下标,只需要使用this. ...