导语
学了ssm很久以后,再来回顾spring框架的时候发觉已经忘了不少东西了,但是这个忘了不少的情况却让我蛋疼了一天,所以今天专门花个时间来记录下spring是如何创建bean的,创建bean的方式有哪些,做一个总结
spring本质是一个对象容器,也可以认为是组件容器,总之就是用来装可重用(java)组件的,传统方式是使用new这个关键词来获取对象,有了spring后不仅可以在系统之间减少耦合性,也改变了业界构建系统的方式方法。在spring中大量使用java反射创建对象,这种创建对象的方式在jdk1.8后有了翻倍的性能提升,所以现在业界也普遍使用jdk1.8原因所在
目录
环境声明
- jdk version : 1.8
- IDE : idea community 2019.2
- maven version : 3.6.2
- spring version : 5.2.1.RELEASE
理解要点
- spring的@Component()用于标注在类上,标注上该注解的类在运行时,会被加入到spring容器中
- spring中,被@Server() 、@Controller() 、@Repository三个注解的类,在运行时也会被放入spring容器中,但是他们相比@Component又有了明显web分层概念,Component就单纯的吧该类添加进spring容器,而其他三个就还做了其他事情
- spring既然有了bean管理,那么bean在很多地方是有bean的生命周期的,比如有的bean仅仅需要使用一次就会被废弃(比如初始化组件),而有些bean是需要重复使用的(持久层组件),这个时候就spring就引入了Scope的概念,也就是
- spring的本质上是bean容器,所以他的主要目的在于管理bean(或者说在于解决代码之间的依赖关系)bean的生命周期
- spring-boot是基于spring的
java技术体系中获得类实例(对象)的方式有哪些?
1.new
在开始学习java面向对象的时候,我们都是通过new来获得一个实实在在的对象的,但是这种方式获得对象,一旦这个类不能存在的时候就麻烦了,连编译都通不过,更别说运行了!但有时候我们对这个类的依赖性有并不强,又影响整体的创建对象的方式有时候很让人头疼,而且一旦出错就影响整个线程的。
2.反射
我们知道在学jdbc操作数据库的时候有个Class.forName()这个就是通过路径来加载我们需要的类,这个时候返回一个类的信息,我们通过类信息来模拟出一个对象,也可以获得一个类实例,这就是spring创建类实例的基础。这样创建对象的方式就算类不存在也不影响程序的编译,就算出错也可以控制,不至于把整个线程搞崩。这是反射的优势所在
spring获取bean的注解
1.spring完全注解开发充要条件
要想完全通过注解的方式开发,就必须使用Configuration以及ComponentScan注解,因为这两个注解分别代表了spirng的xml配置文件,以及配置文件中指定要扫描的包范围。因为spring还是离不开配置信息,spring只有通过配置信息才能工作,这也是大部分框架为了实现解耦的常用方式。配置信息中需要注明spring要扫描哪些包,扫描包的动机就在于扫描包中的类是否有需要spring接管(装入spring容器)的类,那么spring扫描了又怎么知道需不需要spring接管呢,那就是通过注解呗,因为反射可以得到类的注解信息。
package TT.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan(basePackages = "TT")
public class AppConfig {}
2.申明类的实例化对象存入到spring容器的注解
@Component
这种方式就是单纯的将被注解的类添加到spring容器,请看实例
package TT.entity;
import org.springframework.stereotype.Component;
//申明当前的类可以由spring容器接管(被加入到spring容器中),并给当前类取名叫person1
@Component("person1")
public class Person {
public void sayHello(){
System.out.println("你好我是"+this.getClass().getName());
}
}
@Service
这个注解就是声明当前类为service层,也会被存入到spring容器,但是跟Component不一样,spring也是做过一些工作的
package TT.entity;
import org.springframework.stereotype.Service;
/**
* 分层中的service层
*/
@Service
public class ServiceTT extends Person {
}
@Repository
用来声明持久层的代码,会被存入到spring容器中,跟但是跟Component不一样,spring也是做过一些工有关持久层的工作
package TT.entity;
import org.springframework.stereotype.Repository;
@Repository
public class RepositoryTT extends Person{
}
@Controller
用来声明控制层的代码
package TT.entity;
import org.springframework.stereotype.Controller;
@Controller
public class ControllerTT extends Person{
}
通过spring容器来使用以上被注解的类
package TT;
import TT.config.AppConfig;
import TT.entity.ControllerTT;
import TT.entity.Person;
import TT.entity.RepositoryTT;
import TT.entity.ServiceTT;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Application {
public static void main(String[] args) {
//通过spring容器来使用我们之前被注解过的类
ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
//通过我们的别名获取类实例
Person p=(Person) ac.getBean("person1");
p.sayHello();
//通过传入类信息获得类实例
ServiceTT d = ac.getBean(ServiceTT.class);
d.sayHello();
ControllerTT c=ac.getBean(ControllerTT.class);
c.sayHello();
RepositoryTT r=ac.getBean(RepositoryTT.class);
r.sayHello();
}
}
结果:
你好,我是TT.entity.Person
你好,我是TT.entity.ServiceTT
你好,我是TT.entity.ControllerTT
你好,我是TT.entity.RepositoryTT
3.申明方法返回对象存入到spring容器的注解
要说通过方法返回对象的形式要追溯到设计模式中去,这个设计模式就是工厂模式
假设我们代码中需要调用别人已经写好的工具包,而且这个工具包中的代码也已经无法修改了,他们代码中也没有使用spring框架,所以我们没有办法通过@ComponentScan扫面到别人的代码加入spring,因为别人的代码中没有@service、@Component、@Controller以及@Repository。
但我们可以通过调用工具包中的工厂方法来调用实例类,也可以通过直接实例化来调用别人写好的类,工具包中的工厂代码如下:
package TT.old_code.factory;
import TT.old_code.entity.ControllerTT;
import TT.old_code.entity.Person;
import TT.old_code.entity.RepositoryTT;
import TT.old_code.entity.ServiceTT;
public class BeanFactory {
public static Person getControllerTT() {
return new ControllerTT();
}
public static Person getService(){
return new ServiceTT();
}
public static Person getRepositoryTT(){
return new RepositoryTT();
}
public static Person getPerson(){
return new Person();
}
}
所以,这时候spring的@Bean就诞生了
@Bean
我就将之前的代码改一改,现在AppConfig大概长这样
package TT.newCode.config;
import TT.old_code.entity.Person;
import TT.old_code.entity.RepositoryTT;
import TT.old_code.factory.BeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
//现在我们可以调用别人代码中的工厂对象返回的对象存入spring容器
@Bean("controllerTT1")
public Person getControllerTT() {
return BeanFactory.getControllerTT();
}
@Bean("service1")
public Person getService() {
return BeanFactory.getService();
}
//我们也可以直接实例化别人工具代码中的类存入spring容器
@Bean("repositoryTT1")
public Person getRepositoryTT() {
return new RepositoryTT();
}
@Bean("person")
public Person getPerson() {
return new Person();
}
}
现在我们仍然正常使用spring容器,跟我们写的代码没有任何区别
package TT.newCode;
import TT.newCode.config.AppConfig;
import TT.old_code.entity.ControllerTT;
import TT.old_code.entity.ServiceTT;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class ApplicationOnBeanFactory {
public static void main(String[] args) {
ApplicationContext ac= new AnnotationConfigApplicationContext(AppConfig.class);
ControllerTT configuration=(ControllerTT)ac.getBean("controllerTT1",ControllerTT.class);
configuration.sayHello();
ServiceTT s=ac.getBean(ServiceTT.class);
s.sayHello();
}
}
结果:
你好,我是TT.old_code.entity.ControllerTT
你好,我是TT.old_code.entity.ServiceTT