先看一下SpringbootBean是怎么个情况,再看一下SpringBean的配置
默认情况下,Spring项目启动时,会把Bean都创建好放在IOC容器中,如果主要获取这些Bean,可以通过如下方式:
// IOC容器对象
@Autowired
private ApplicationContext applicationContext;
@Test
public void testBean(){
// TODO 根据bean名称获取 若没指定bean名称,默认类名首字母小写
DeptController deptControllerBean1 =(DeptController) applicationContext.getBean("deptController");
System.out.println(deptControllerBean1); //com.zhangjingqi.controller.DeptController@249b54af
// TODO 根据bean的类型获取
DeptController deptControllerBean2 = applicationContext.getBean(DeptController.class);
System.out.println(deptControllerBean2);//com.zhangjingqi.controller.DeptController@249b54af
// TODO 根据bean的名称 及 类型获取
DeptController deptControllerBean3 = applicationContext.getBean("deptController",DeptController.class);
System.out.println(deptControllerBean3);//com.zhangjingqi.controller.DeptController@249b54af
}
上述所说的【Spring项目启动时,会把其中的bean创建好】还会受到作用域及延迟初始化影响,这里主要针对于默认的单例非延迟加载的bean而言。
Spring支持五中作用域,后三种在Web环境下才生效:
作用域 | 说明 |
---|---|
singleton | 容器内同名称的bean只有一个实例(单例)(默认) |
prototype | 每次使用该bean时会创建新的实例(非单例) |
request | 每个请求范围内会创建新的实例(Web环境中,了解) |
session | 每个会话范围内会创建新的实例(Web环境中,了解) |
application | 每个应用范围内会创建新的实例(Web环境中,了解) |
singleton模式下bean对象情况,并且在容器启动的时候创建好的
@Test
public void testScope(){
for (int i=0;i<10;i++){
DeptController deptControllerBean2 = applicationContext.getBean(DeptController.class);
System.out.println(deptControllerBean2);
}
}
com.zhangjingqi.controller.DeptController@586728e8
com.zhangjingqi.controller.DeptController@586728e8
com.zhangjingqi.controller.DeptController@586728e8
com.zhangjingqi.controller.DeptController@586728e8
com.zhangjingqi.controller.DeptController@586728e8
com.zhangjingqi.controller.DeptController@586728e8
com.zhangjingqi.controller.DeptController@586728e8
com.zhangjingqi.controller.DeptController@586728e8
com.zhangjingqi.controller.DeptController@586728e8
com.zhangjingqi.controller.DeptController@586728e8
我们也可以在第一次使用的时候实例化 @Lazy
代表延迟初始化,直到第一次使用的时候
@Lazy //
@RestController
@Slf4j
public class DeptController {...}
通过@Scope注解来进行配置作用域
@Scope("prototype")
@RestController
@Slf4j
public class DeptController {}
com.zhangjingqi.controller.DeptController@7f6b57f2
com.zhangjingqi.controller.DeptController@144ee8a7
com.zhangjingqi.controller.DeptController@52b32b70
com.zhangjingqi.controller.DeptController@18c820d2
com.zhangjingqi.controller.DeptController@3d3930fe
com.zhangjingqi.controller.DeptController@5e51ec2e
com.zhangjingqi.controller.DeptController@15f2a43f
com.zhangjingqi.controller.DeptController@4c65d8e3
com.zhangjingqi.controller.DeptController@382faf51
com.zhangjingqi.controller.DeptController@69ce14e6
项目开发中,自己开发的类使用@Component以及其三个衍生注解@Controller、@Service、@Repository注入即可
但是还有一种情况是第三方提供的,比如依赖
比如:dom4j就是第三方组织提供的。 dom4j中的SAXReader类就是第三方编写的。
如果我想将SAXReader对象存入到容器,需要在SAXReader类添加@Component注解,但是这是第三方bean,我们是无法修改的是不能添加注解的。
<dependency>
<groupId>org.dom4jgroupId>
<artifactId>dom4jartifactId>
<version>2.1.3version>
dependency>
如果要管理的bean对象来自于第三方(不是自定义的),是无法用@Component及衍生注解生命bean的,此时需要用到@Bean注解
启动类也是配置类,我们完全可以在这里进行注入
//Filter是javaweb三大组件之一,不是Spring提供的,如果想要使用三大组件,需要添加这个注解
@ServletComponentScan
@SpringBootApplication
public class SpringbootWebApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootWebApplication.class, args);
}
//声明第三方bean
@Bean //将当前方法的返回值对象交给IOC容器管理, 成为IOC容器bean
public SAXReader saxReader(){
return new SAXReader();
}
}
测试是否可以
创建xml文件,解析下面内容
<emp>
<name>Tomname>
<age>18age>
emp>
挺完美的
@Autowired
private SAXReader saxReader;
@Test
public void testBean2() throws DocumentException {
Document document = saxReader.read(this.getClass().getClassLoader().getResource("1.xml")
);
Element rootElement = document.getRootElement();
String name = rootElement.element("name").getText();
String age = rootElement.element("age").getText();
System.out.println(name + " : " + age); // Tom : 18
}
但是在Spring项目中,我们一般会保证启动类的纯粹性,让启动类仅仅是启动类,我们把其他的配置单独列出来
@Configuration //配置类 (在配置类当中对第三方bean进行集中的配置管理)
public class CommonConfig {
//声明第三方bean
@Bean //将当前方法的返回值对象交给IOC容器管理, 成为IOC容器bean
//通过@Bean注解的name/value属性指定bean名称, 如果未指定, 默认是方法名
public SAXReader reader(DeptService deptService) {
System.out.println(deptService);
return new SAXReader();
}
}
XML配置方式 | 功能描述 |
---|---|
Bean的id和全限定名配置 | |
通过name设置Bean的别名,通过别名也能直接获取到Bean实例 | |
Bean的作用范围,BeanFactory作为容器时取值singleton和prototype | |
Bean的实例化时机,是否延迟加载。BeanFactory作为容器时无效 | |
Bean实例化后自动执行的初始化方法,method指定方法名 | |
Bean实例销毁前的方法,method指定方法名 | |
设置自动注入模式,常用的有按照类型byType,按照名字byName | |
指定哪个工厂Bean的哪个方法完成Bean的创建 |
配置UserServiceImpl由Spring容器负责管理
<!--class 可以明确Bean在哪-->
<!--id 为Bean做一个唯一标识-->
<bean id="userService" class="com.zhangjingqi.service.impl.UserServiceImpl">
</bean>
id
唯一标识,不能与其他bean重复
我们最终要getBean,getBean的参数其实是beanName,并不是id,但是当bean对象进入到容器后,会将id转化成beanName进行存储
applicationContext.getBean("userService");
假如说我们不配置id,会出现什么情况?
这个Bean依然有BeanName,如下所示
<bean class="com.zhangjingqi.service.impl.UserServiceImpl"> </bean>
如果不配置id,则Spring会把当前Bean实例的全限定名作为beanName
applicationContext.getBean("com.zhangjingqi.service.impl.UserServiceImpl");
我们一般都是配置id,别名几乎不适用
可以为当前Bean指定多个别名,根据别名也可以获得Bean对象
<bean id="userService" name="aaa,bbb" class="com.zhangjingqi.service.impl.UserServiceImpl"/>
通过下面的方式都可以获取上面的Bean
applicationContext.getBean("userService");
applicationContext.getBean("aaa");
applicationContext.getBean("bbb");
说明:BeanName就是BeanName,别名就是别名,不在一块进行存储
下面是BeanName,依然是userService,并没有出现aaa/bbb
那为什么还能通过aaa,bbb获取到呢?
如下所示
假如我们没有配置id,但是配置了别名,会出现什么情况?
<bean name="aaa,bbb" class="com.zhangjingqi.service.impl.UserServiceImpl">
property>
bean>
如下所示,第一个别名充当BeanName
如果id,name都没有呢?
根据class全类名获取
Spring支持五中作用域,后三种在Web环境下才生效:
作用域 | 说明 |
---|---|
singleton | 容器内同名称的bean只有一个实例(单例)(默认) |
prototype | 每次使用该bean时会创建新的实例(非单例) |
request | 每个请求范围内会创建新的实例(Web环境中,了解) |
session | 每个会话范围内会创建新的实例(Web环境中,了解) |
application | 每个应用范围内会创建新的实例(Web环境中,了解) |
默认singleton的bean,在容器启动时被创建,可以使用@Lazy注解来延迟初始化(延迟到第一次使用时)
prototype,每一次使用该bean的时候都会创建一个新的实例
用完之后就找不到这个引用了,最终就释放了,被垃圾回收器给回收了
<!--class 可以明确Bean在哪-->
<!--id 为Bean做一个唯一标识-->
<bean id="userService" scope="prototype" class="com.zhangjingqi.service.impl.UserServiceImpl">
</bean>
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
Object userService1 = applicationContext.getBean("userService");
System.out.println(userService1);//com.zhangjingqi.service.impl.UserServiceImpl@43bc63a3
Object userService2 = applicationContext.getBean("userService");
System.out.println(userService2);//com.zhangjingqi.service.impl.UserServiceImpl@702657cc
Object userService3 = applicationContext.getBean("userService");
System.out.println(userService3);//com.zhangjingqi.service.impl.UserServiceImpl@6a6cb05c
当lazy-init设置为true时为延迟加载,也就是当Spring容器创建的时候,不会立即创建Bean实例,等待用到时再创建Bean实例并存储到单例池中去,后续在使用该Bean直接从单例池获取即可,本质上该Bean还是单例的
<bean id="userService" class="com.zhangjingqi.service.impl.UserServiceImpl" lazy-init="true">
bean>
lazy-init对BeanFactory是无效的
比如说lazy-init=“false”,此时BeanFactory可以直接帮我们创建嘛?
这是直接不可能的!
对ApplicationContext有效
Bean在被实例化后,可以执行指定的初始化方法完成一些初始化的操作,Bean在销毁之前也可以执行指定的销毁方法完成一些操作
也就是说能指定Bean操作的初始化方法和销毁方法的配置
public class UserServiceImpl implements UserService {
public void init() {
System.out.println("Bean实例初始化方法执行");
}
public void destroy() {
System.out.println("Bean实例销毁方法执行");
}
}
init方法先执行还是构造方法先执行?
构造方法先执行,因为构造方法的执行代表对象的创建
property>
bean>
测试
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
Object userService1 = applicationContext.getBean("userService");
System.out.println(userService1);
控制台只输出了“Bean实例初始化方法执行”
运行测试方法后,为什么只输出了“Bean实例初始化方法执行”,而没有输出“Bean实例销毁方法执行”?
因为只有容器ApplicationContext显示关闭的时候才会销毁Bean并执行销毁方法
我们程序停掉后类似于停点,容器ApplicationContext并不知道要关闭,它根本就没有来得及去执行Bean的销毁方法
要想执行销毁方法如下所示,执行applicationContext.close();语句
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
Object userService1 = applicationContext.getBean("userService");
System.out.println(userService1);
applicationContext.close();
强调一点
Bean的销毁与Bean的销毁方法的调用不是一回事
比如说容器ApplicationContext没有了,在内存中已经挂掉了,那Bean肯定也是没有了(因为没有办法维护Bean了,所以Bean也没有了)
那Bean销毁后为什么销毁方法没被调用呢?
因为Spring还没有执行到那一步,容器就已经挂掉了,已经没办法再调用销毁方法了,所以我们要显示的关闭容器,让Spring容器执行销毁方法
applicationContext.close();
我们还可以通过实现 InitializingBean 接口,完成一些Bean的初始化操作
InitializingBean 接口中有一个afterPropertiesSet方法,当类实现了这个接口之后,Spring容器会自动帮你去调用此方法
afterPropertiesSet 字面意思就是在属性设置之后执行,我们测试一下试试
UserServiceImpl中方法的执行顺序我已经按照顺序拍好了,下面有测试进行验证
public class UserServiceImpl implements UserService , InitializingBean {
public UserServiceImpl(){
System.out.println("实例对象创建了");
}
// BeanFactory去调用该方法,从容器中获得userDao设置到此处
public void setUserDao(UserDao userDao) {
System.out.println("userDao实例:"+userDao);
}
public void afterPropertiesSet() throws Exception {
System.out.println("InitializingBean...");
}
public void init() {
System.out.println("Bean实例初始化方法执行");
}
public void destroy() {
System.out.println("Bean实例销毁方法执行");
}
}
配置类
<bean id="userService" init-method="init" destroy-method="destroy"
class="com.zhangjingqi.service.impl.UserServiceImpl" >
<property name="userDao" ref="userDao">property>
bean>
<bean id="userDao" class="com.zhangjingqi.dao.impl.UserDaoImpl">
bean>
测试类
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
Object userService1 = applicationContext.getBean("userService");
applicationContext.close();
执行结果
所以我们初始化Bean的操作有两种了,一种是在bean配置是设置一个init-method属性,并指定对应的方法,另一种方式是类实现InitializingBean类,并重写afterPropertiesSet方法
如果两种操作我都编写了,那谁会先执行?
afterPropertiesSet执行后再init-method属性对应的方法执行,详细的讲解会在bean的生命周期中说明
Spring的实例化方式主要有下面两种
说明
我们现在使用Spring容器是通过ApplicationContext,它帮我们调用的BeanFactory底层的对应方法,BeanFactory本身就是工厂,但是工厂内部帮我们造Bean的时候又分为两种方式,一是通过构造方式实例化,另一种是通过工厂方式实例化
说白了就是工厂内部套工厂,第一层工厂就是我们的BeanFactory,第二层工厂就是需要我们进行提供了(第二种方式工厂方式实例化),第二层工厂可以产生一个Bean,最终由Spring容器帮我们管理
构造方式实例化Bean又分为无参构造方法实例化和有参构造方法实例化,Spring中配置的
至于是有参构造还是无参构造实例化,取决于在配置文件中是怎么配置的,默认是无参构造实例化
那这么说来,默认情况下,我们设置了一个有参构造函数,没有设置无参构造函数,那是会报错的,因为默认是无参构造实例化,有了有参构造函数后就会把无参覆盖掉,此时我们需要手动添加无参构造函数
配置文件
<bean id="userService" class="com.zhangjingqi.service.impl.UserServiceImpl">
<constructor-arg name="name" value="我叫zhangjingqi">constructor-arg>
<property name="userDao" ref="userDao">property>
bean>
<bean id="userDao" class="com.zhangjingqi.dao.impl.UserDaoImpl">bean>
UserServiceImpl类
public class UserServiceImpl implements UserService {
private UserDao userDao;
// BeanFactory去调用该方法,从容器中获得userDao设置到此处
public void setUserDao(UserDao userDao) {
this.userDao =userDao;
}
public UserServiceImpl(){
System.out.println("UserServiceImpl实例化 - 无参构造注入");
}
public UserServiceImpl(String name){
System.out.println("UserServiceImpl实例化 - 有参构造注入,name="+name);
}
}
测试程序
//参数是一个xml配置文件
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
Object userService1 = applicationContext.getBean("userService");
// System.out.println(userService1);
applicationContext.close();
测试结果
如果我们构造方法是两个参数呢?,会执行下面的哪个构造方法?
public UserServiceImpl(){
System.out.println("UserServiceImpl实例化 - 无参构造注入");
}
public UserServiceImpl(String name){
System.out.println("UserServiceImpl实例化 - 有参构造注入,name="+name);
}
public UserServiceImpl(String name,int age){
System.out.println("UserServiceImpl实例化 - 有参构造注入,name="+name+",age="+age);
}
<bean id="userService" class="com.zhangjingqi.service.impl.UserServiceImpl">
<constructor-arg name="name" value="我叫zhangjingqi">constructor-arg>
<constructor-arg name="age" value="22">constructor-arg>
<property name="userDao" ref="userDao">property>
bean>
答案: 执行public UserServiceImpl(String name,int age)构造方法
说明:
constructor-arg标签不仅仅可以用于构造函数的参数,只要是Bean创建时需要的参数,都可以用这个标签进行传输
下面在Bean工厂时会出现另外一种使用方式
工厂方式实例化Bean,又分为三种
静态工厂方法实例化Bean
自定义一个工厂,在工厂内部自定义一个静态方法,在静态方法的内部产生一个Bean,最终这个Bean会交给Spring容器管理
实例工厂方法实例化Bean
自定义一个工厂,在工厂内部自定义一个非静态方法,在非静态方法的内部产生一个Bean,最终这个Bean会交给Spring容器管理
实现FactoryBean规范延迟实例化Bean
老师说这个一句两句说不明白,不在这里说了。
**静态工厂方法实例化与实例工厂方法实例化最重要的区别? **
静态工厂方法实例化与实例工厂方法实例化最重要的区别就是方法是静态还是非静态
静态工厂方法实例化产生Bean的时候是不需要创建对象的,直接类名调用即可
实例工厂方法实例化必须需要实例化对象去调用
原先是怎么产生Bean的?
Spring容器通过全包名反射创建好对象放到容器当中
现在是怎么创建Bean?
Spring容器帮我们去调用MyBeanFactory1的静态方法userDao,最终将返回的对象存入到Spring容器之中
静态工厂
这样使用静态工厂有什么好处呢?
比如我们想在创建UserDaoImpl对象之前执行一些其他的操作,我们就可以在return new UserDaoImpl();语句之前进行编写
由此方法将第三方的对象注入Spring容器
public class MyBeanFactory1 {
public static UserDao userDao(){
return new UserDaoImpl();
}
}
配置文件进行配置
**正常情况下**,在启动时Spring容器会按照全限定名com.zhangjingqi.factory.MyBeanFactory1从无参构造创建MyBeanFactory1对象,创建完对象放入到容器当中id为userDao1,BeanName也是id为userDao1 **但是此时有 factory-method指定方法为userDao**,此时Spring在解析时就明白了,我们不是把com.zhangjingqi.factory.MyBeanFactory1创建为对象,而是把MyBeanFactory1类中userDao的返回值作为对象,再以我们指定的id作为BeanName存储到容器当中
也就是说userDao1指定是userDao方法返回值Bean的名字/id,而不是MyBeanFactory1对象的名字/id
只需要这一个配置就行,不需要在配置中配置UserDao的Bean信息
<bean id="userDao1" factory-method="userDao"
class="com.zhangjingqi.factory.MyBeanFactory1">
bean>
进行测试
//参数是一个xml配置文件
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
Object userDao1 = applicationContext.getBean("userDao1");
System.out.println("userDao1 = "+userDao1);//userDao1 = com.zhangjingqi.dao.impl.UserDaoImpl@37883b97
经过我们的debug,发现userDao1确实是UserDaoImpl类的实例对象,并不是MyBeanFactory1类的
与静态工厂方法实例化Bean的区别就是不是静态的而已。
工厂
这样使用实例工厂有什么好处呢?
在创建实例化对象之前与之后可以进行一些其他的操作
由此方法将第三方的对象注入Spring容器
public class MyBeanFactory2 {
public UserDao userDao(){
return new UserDaoImpl();
}
}
配置
我们应该先创建工厂MyBeanFactory2,让Spring容器帮我们创建工厂对象,再让工厂对象调用userDao方法产生UserDaoImpl对象
<bean id="myBeanFactory2" class="com.zhangjingqi.factory.MyBeanFactory2">
bean>
<bean id="userDao2" factory-bean="myBeanFactory2" factory-method="userDao" >
bean>
测试
成功获取Bean
//参数是一个xml配置文件
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
Object userDao2 = applicationContext.getBean("userDao2");
System.out.println("userDao2 = "+userDao2); //userDao2 = com.zhangjingqi.dao.impl.UserDaoImpl@6913c1fb
相当的完美
工厂类
public class MyBeanFactory1 {
public static UserDao userDao(String name,int age){
System.out.println("name:"+name+",age:"+age);
return new UserDaoImpl();
}
}
配置文件
<bean id="userDao1" factory-method="userDao"
class="com.zhangjingqi.factory.MyBeanFactory1">
<constructor-arg name="name" value="我叫zhangjingqi">constructor-arg>
<constructor-arg name="age" value="22">constructor-arg>
bean>
测试
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
Object userDao1 = applicationContext.getBean("userDao1");
System.out.println("userDao1 = "+userDao1);
工厂类
public class MyBeanFactory2 {
public UserDao userDao(String name,int age){
System.out.println("name:"+name+",age:"+age);
return new UserDaoImpl();
}
}
配置文件
<bean id="myBeanFactory2" class="com.zhangjingqi.factory.MyBeanFactory2">
bean>
<bean id="userDao2" factory-bean="myBeanFactory2" factory-method="userDao" >
<constructor-arg name="name" value="我叫zhangjingqi">constructor-arg>
<constructor-arg name="age" value="22">constructor-arg>
bean>
测试类
//参数是一个xml配置文件
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
Object userDao2 = applicationContext.getBean("userDao2");
System.out.println("userDao2 = "+userDao2);//userDao2 = com.zhangjingqi.dao.impl.UserDaoImpl@609cd4d8
相对复杂一点,这种方式并不是马上创建Bean,而是在用的时候才创建Bean
此种方式Spring底层用的稍微多一点,我们在开发中不经常使用,但是有时候面试会问
FactoryBean是一个接口,主要的作用在于定义工厂Bean实现规范的
比如说我们的一个类(这个类也能是工厂)实现了FactoryBean接口,那定义的这个类也会变成一个工厂Bean
工厂Bean的作用是造对象的,调用方法getObject()
public interface FactoryBean<T> { String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType"; @Nullable T getObject() throws Exception; @Nullable Class<?> getObjectType(); default boolean isSingleton() { return true; } }
实现FactoryBean接口,并重写getObject方法
public class MyBeanFactory3 implements FactoryBean<UserDao> {
// 返回的Bean是谁
public UserDao getObject() throws Exception {
return new UserDaoImpl();
}
// 返回Bean的类型是什么
public Class<?> getObjectType() {
return UserDao.class;
}
}
配置文件
<bean id="userDao3" class="com.zhangjingqi.factory.MyBeanFactory3">
bean>
测试程序
//参数是一个xml配置文件
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
Object userDao3 = applicationContext.getBean("userDao3");
System.out.println("userDao3 = "+userDao3); //userDao3 = com.zhangjingqi.dao.impl.UserDaoImpl@305b7c14
单例对象结果图
但是现在有一个疑问,userDao3对应的value是MyBeanFactory3,而不是UserDaoImpl,但是我们控制台却输出的是“userDao3 = com.zhangjingqi.dao.impl.UserDaoImpl@38102d01”,显然又是UserDaoImpl,这是怎么回事?
我们userDao3被缓存到了factoryBeanObjectCache,也就是说从下图的位置返回的
为什么说是延迟呢?
我们在执行“applicationContext.getBean(“userDao3”);”之前,factoryBeanObjectCache中是没有UserDao3对象的,但是我们执行完这条语句后,factoryBeanObjectCache中就存在了
这也就是说new ClassPathXmlApplicationContext(“beans.xml”)加载配置文件生成Spring容器的时候FactoryBean接口的getObject方法并没有执行/调用,但是在执行getBean时,才去调用的getObject方法生成userDaoImpl对象,并缓存到factoryBeanObjectCache
ref 是 reference 的缩写形式,翻译为:涉及,参考的意思,用于引用其他Bean的id。
value 用于注入普通属性值。
通过Bean的set方法注入
要有set方法
<property name="userDao" ref="userDao"/>
<property name="userDao" value="haohao"/>
<constructor-arg name="name" ref="userDao"/>
<constructor-arg name="name" value="haohao"/>
注入的数据类型有哪些?
配置文件
<bean id="userService" class="com.zhangjingqi.service.impl.UserServiceImpl">
<property name="stringList">
<list>
<value>aaaavalue>
<value>bbbvalue>
<value>cccvalue>
list>
property>
bean>
UserServiceImpl
public class UserServiceImpl implements UserService {
// 注入List
private List<String> stringList;
public void setStringList(List<String> stringList) {
this.stringList = stringList;
}
测试程序
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
UserServiceImpl userService = (UserServiceImpl) applicationContext.getBean("userService");
System.out.println("userService = "+userService.getStringList());//userService = [aaaa, bbb, ccc]
向List中注入引用参数
UserServiceImpl
private List<UserDao> userDaoList;
public void setUserDaoList(List<UserDao> userDaoList) {
this.userDaoList = userDaoList;
}
配置文件
<bean id="userService" class="com.zhangjingqi.service.impl.UserServiceImpl">
<property name="userDaoList">
<list>
<bean class="com.zhangjingqi.dao.impl.UserDaoImpl">bean>
<bean class="com.zhangjingqi.dao.impl.UserDaoImpl">bean>
<ref bean="userDao1">ref>
<ref bean="userDao2">ref>
<ref bean="userDao3">ref>
list>
property>
bean>
<bean id="userDao1" class="com.zhangjingqi.dao.impl.UserDaoImpl">bean>
<bean id="userDao2" class="com.zhangjingqi.dao.impl.UserDaoImpl">bean>
<bean id="userDao3" class="com.zhangjingqi.dao.impl.UserDaoImpl">bean>
private Set<String> setSet;
public void setSetSet(Set<String> setSet) {
this.setSet = setSet;
}
配置文件
<bean id="userService" class="com.zhangjingqi.service.impl.UserServiceImpl">
<property name="strSet">
<set>
<value>aaaaavalue>
<value>bbbbbvalue>
<value>cccccvalue>
set>
property>
bean>
private Set<UserDao> userDaoSet;
public void setUserDaoSet(Set<UserDao> userDaoSet) {
this.userDaoSet = userDaoSet;
}
配置文件
<property name="userDaoSet">
<set>
<bean class="com.zhangjingqi.dao.impl.UserDaoImpl">bean>
<ref bean="userDao1">ref>
<ref bean="userDao2">ref>
<ref bean="userDao3">ref>
set>
property>
bean>
<bean id="userDao1" class="com.zhangjingqi.dao.impl.UserDaoImpl">bean>
<bean id="userDao2" class="com.zhangjingqi.dao.impl.UserDaoImpl">bean>
<bean id="userDao3" class="com.zhangjingqi.dao.impl.UserDaoImpl">bean>
UserServiceImpl
private Map<String, UserDao> map;
public void setMap(Map<String, UserDao> map) {
this.map = map;
}
配置信息
<bean id="userService" class="com.zhangjingqi.service.impl.UserServiceImpl">
<property name="map">
<map>
<entry key="1" value-ref="userDao1">entry>
<entry key="2" value-ref="userDao2">entry>
<entry key="3" value-ref="userDao3">entry>
map>
property>
bean>
<bean id="userDao1" class="com.zhangjingqi.dao.impl.UserDaoImpl">bean>
<bean id="userDao2" class="com.zhangjingqi.dao.impl.UserDaoImpl">bean>
<bean id="userDao3" class="com.zhangjingqi.dao.impl.UserDaoImpl">bean>
UserServiceImpl
private Properties properties;
public void setProperties(Properties properties) {
this.properties = properties;
}
配置文件
<bean id="userService" class="com.zhangjingqi.service.impl.UserServiceImpl">
<property name="properties">
<props>
<prop key="p1">zhangjingqiprop>
<prop key="p2">123prop>
<prop key="p3">456prop>
props>
property>
bean>
测试
//参数是一个xml配置文件
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
UserServiceImpl userService = (UserServiceImpl) applicationContext.getBean("userService");
System.out.println(userService.getProperties());//{p3=456, p2=123, p1=zhangjingqi}
System.out.println(userService.getProperties().getProperty("p1"));//zhangjingqi
我们之间接触的Bean都是人为手动的将其注入进去的,是手动装配
自动装配方式:如果被注入的属性类型是Bean引用的话,那么可以在
不需要人为的指定properties标签去给他配置,它会自动根据你指定的某一个要求然后进行自动装配
byName:通过属性名自动装配,即去匹配 setXxx 与 id=“xxx”(name=“xxx”)是否一致
比如说我们要往UserServiceImpl类中注入一个UserDaoImpl类对象
怎么完成自动装配的呢?
UserServiceImpl类内部有一个setUserDao方法,如果把此方法名的set字符去掉,剩下的第一个单词首字母小写,也就是userDao与容器中的某个Bean的BeanName能对应上就行
如果匹配不上,所对应的对象也就是userDao对象为NULL(两种情况,容器中根本不存在该对象,或者是BeanName对应错了)
配置文件
<bean id="userService" autowire="byName"
class="com.zhangjingqi.service.impl.UserServiceImpl">
bean>
<bean id="userDao" class="com.zhangjingqi.dao.impl.UserDaoImpl">bean>
UserServiceImpl
public class UserServiceImpl implements UserService {
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao =userDao;
}
}
测试
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
UserServiceImpl userService = (UserServiceImpl) applicationContext.getBean("userService");
System.out.println(userService.getUserDao());//com.zhangjingqi.dao.impl.UserDaoImpl@1573f9fc
byType:通过Bean的类型从容器中匹配,匹配出多个相同Bean类型时,报错
类型可能会重复,比如UserDao接口可能会有很多实现类都注册了Bean,那此时使用byType就会报错
虽然是根据类型装配,但是也需要有setUserDao方法
public class UserServiceImpl implements UserService {
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao =userDao;
}
}
配置文件
此时set方法与BeanName名字不对应也是没有问题的
<bean id="userService" autowire="byType"
class="com.zhangjingqi.service.impl.UserServiceImpl">
bean>
<bean id="userDao88888" class="com.zhangjingqi.dao.impl.UserDaoImpl">bean>
Spring 的 xml 标签大体上分为两类,一种是默认标签,一种是自定义标签
下面这段代码中xmlns代表了xml name space的简写,其值就是一个地址,这个就是默认的命名空间,并且xmlns内部维护的标签,都是默认标签
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
标签 | 作用 |
---|---|
一般作为 xml 配置根标签,其他标签都是该标签的子标签 | |
Bean的配置标签,上面已经详解了,此处不再阐述 | |
外部资源导入标签 | |
指定Bean的别名标签,使用较少 |
beans标签下面还能再套beans
但是用的比较少 多套beans的话,我们可以指定用哪一套,下面会讲
怎么指定被激活的环境?
使用命令行动态参数,虚拟机参数位置加载
-Dspring.profiles.active=test
**使用代码的方式设置环境变量 **
System.setProperty("spring.profiles.active","test")
配置文件
现在我们有三个环境,默认环境、dev环境、test环境
其中userService、userDao不在任何环境下
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="userService" class="com.zhangjingqi.service.impl.UserServiceImpl"/>
<bean id="userDao" class="com.zhangjingqi.dao.impl.UserDaoImpl">bean>
<beans profile="dev">
<bean id="userServiceDev" class="com.zhangjingqi.service.impl.UserServiceImpl">bean>
beans>
<beans profile="test">
<bean id="userDaoTest" class="com.zhangjingqi.dao.impl.UserDaoImpl">bean>
beans>
beans>
测试
默认环境
没有任何设置,就是默认环境,此时只有默认环境下的配置生效,其他环境下的配置不生效
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
UserServiceImpl userService = (UserServiceImpl) applicationContext.getBean("userService");
UserDaoImpl userDao = (UserDaoImpl) applicationContext.getBean("userDao");
System.out.println(userService);//com.zhangjingqi.service.impl.UserServiceImpl@6ae5aa72
System.out.println(userDao);//com.zhangjingqi.dao.impl.UserDaoImpl@222545dc
dev环境
除此之外,默认环境的Bean也生效
// 指定运行环境
System.setProperty("spring.profiles.active", "dev");
//参数是一个xml配置文件
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
UserServiceImpl userService = (UserServiceImpl) applicationContext.getBean("userServiceDev");
test环境
除此之外,默认环境的Bean也生效
// 指定运行环境
System.setProperty("spring.profiles.active", "test");
//参数是一个xml配置文件
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
UserDaoImpl userDao = (UserDaoImpl) applicationContext.getBean("userDaoTest");
导入其他配置文件,项目变大后,就会导致一个配置文件内容过多,可以将一个配置文件根据业务某块进行拆分,拆分后,最终通过
<import resource="classpath:UserModuleApplicationContext.xml"/>
<import resource="classpath:ProductModuleApplicationContext.xml"/>
为某个Bean添加别名,与在
<bean id="userService" name="aaa,bbb" class="com.itheima.service.impl.UserServiceImpl">
<property name="userDao" ref="userDao"/>
bean>
<alias name="userService" alias="xxx"/>
<alias name="userService" alias="yyy"/>
并且xmlns:context="http://www.springframework.org/schema/context"内部维护的标签(此内部的约束在对应的jar中存在,所以一定要导入坐标),我们都叫做自定义标签
Spring的自定义标签需要引入外部的命名空间,并为外部的命名空间指定前缀,使用 <前缀:标签> 形式的标签,称之为自定义标签
如下所示,xmlns:context便是我们自己定义的一个新的命名空间
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
如果我们编写了下面的标签,一看就知道是context命名空间中的标签
<context:property-placeholder>context:property-placeholder>
方法定义 | 返回值和参数 |
---|---|
Object getBean (String beanName) | 根据beanName从容器中获取Bean实例,要求容器中Bean唯一,返回值为Object,需要强转 |
T getBean (Class type) | 根据Class类型从容器中获取Bean实例,要求容器中Bean类型唯一,返回值为Class类型实例,无需强转 |
T getBean (String beanName,Class type) | 根据beanName从容器中获得Bean实例,返回值为Class类型实例,无需强转 |
//根据beanName获取容器中的Bean实例,需要手动强转
UserService userService = (UserService) applicationContext.getBean("userService");
//根据Bean类型去容器中匹配对应的Bean实例,如存在多个匹配Bean则报错
UserService userService2 = applicationContext.getBean(UserService.class);
//根据beanName获取容器中的Bean实例,指定Bean的Type类型
UserService userService3 = applicationContext.getBean("userService", UserService.class);
在 xml 中配置的Bean都是自己定义的,例如:UserDaoImpl,UserServiceImpl。
在实际开发中有些功能类并不是我们自己定义的,而是使用的第三方jar包中的,那么,这些Bean要想让Spring进行管理,也需要对其进行配置
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.49version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>1.1.23version>
dependency>
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setDriverClassName(....);
druidDataSource.setUrl(....);
druidDataSource.setUsername(....);
druidDataSource.setPassword(....);
配置文件
property是set方法注入,name属性值就是原本的set方法名称去除掉set字符并且第一个单词的首字母小写,一定要对应,否则注入失败
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc://localhost:3306/mybatis"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
bean>
//参数是一个xml配置文件
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
Object dataSource = applicationContext.getBean("dataSource");
System.out.println(dataSource);
开发中没有这么干的,就是应用一下,巩固一下知识
Connection 的产生是通过DriverManager的静态方法getConnection获取的,所以我们要用静态工厂方式配置。
配置文件
<bean class="java.lang.Class" factory-method="forName">
<constructor-arg name="className" value="com.mysql.cj.jdbc.Driver"/>
bean>
<bean id="connection" class="java.sql.DriverManager" factory-method="getConnection" scope="prototype">
<constructor-arg name="url" value="jdbc:mysql:localhost:3306/mybatis"/>
<constructor-arg name="user" value="root"/>
<constructor-arg name="password" value="root"/>
bean>
之前编写形式
我们可以把SimpleDateFormat看成工厂,而parse就是产生 对象的工厂实例方法,Date类型就是工厂实例方法的返回值
String currentTimeStr = "2023-08-27 07:20:00";
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = simpleDateFormat.parse(currentTimeStr);
怎么交给Spring容器管理?
<bean id="simpleDateFormat" class="java.text.SimpleDateFormat" >
<constructor-arg name="pattern" value="yyyy-MM-dd HH:mm:ss">constructor-arg>
bean>
<bean id="date" class="java.util.Date" factory-bean="simpleDateFormat" factory-method="parse">
<constructor-arg name="source" value="2023-08-27 07:20:00">constructor-arg>
bean>
测试
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
Object date = applicationContext.getBean("date");
System.out.println(date);
原始方式
//加载mybatis核心配置文件,使用Spring静态工厂方式 InputStream in = Resources.getResourceAsStream(“mybatis-conifg.xml”); //创建SqlSessionFactoryBuilder对象,使用Spring无参构造方式 SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); //调用SqlSessionFactoryBuilder的build方法,使用Spring实例工厂方式 SqlSessionFactory sqlSessionFactory = builder.build(in);
导入对应坐标
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.5.5version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.19version>
dependency>
配置文件
<bean id="inputStream" class="org.apache.ibatis.io.Resources" factory-method="getResourceAsStream">
<constructor-arg name="resource" value="mybatis-config.xml"/>
bean>
<bean id="sqlSessionFactoryBuilder" class="org.apache.ibatis.session.SqlSessionFactoryBuilder"/>
<bean id="sqlSessionFactory" factory-bean="sqlSessionFactoryBuilder" factory-method="build">
<constructor-arg name="inputStream" ref="inputStream"/>
bean>
teFormat" >
测试
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
Object date = applicationContext.getBean("date");
System.out.println(date);
原始方式
//加载mybatis核心配置文件,使用Spring静态工厂方式 InputStream in = Resources.getResourceAsStream(“mybatis-conifg.xml”); //创建SqlSessionFactoryBuilder对象,使用Spring无参构造方式 SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); //调用SqlSessionFactoryBuilder的build方法,使用Spring实例工厂方式 SqlSessionFactory sqlSessionFactory = builder.build(in);
导入对应坐标
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.5.5version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.19version>
dependency>
配置文件
<bean id="inputStream" class="org.apache.ibatis.io.Resources" factory-method="getResourceAsStream">
<constructor-arg name="resource" value="mybatis-config.xml"/>
bean>
<bean id="sqlSessionFactoryBuilder" class="org.apache.ibatis.session.SqlSessionFactoryBuilder"/>
<bean id="sqlSessionFactory" factory-bean="sqlSessionFactoryBuilder" factory-method="build">
<constructor-arg name="inputStream" ref="inputStream"/>
bean>