1、Bean实例化的方式:
Spring为Bean提供了多种实例化方式,通常包括4种方式。(也就是说在Spring中为Bean对象的创建准备了多种方案,目的是:更加灵活)
- 第一种:通过构造方法实例化
- 第二种:通过简单工厂模式实例化
- 第三种:通过factory-bean实例化
- 第四种:通过FactoryBean接口实例化
配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<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">
<!--Spring提供的实例化的方式,在spring的配置文件中配值类的全路径,spring会自动调用该类的无参构造方法来实例化-->
<bean id="springBean" class="com.songzhishu.spring6.bean.SpringBean"/>
<!--简单工场模式来实例化,告诉Spring的配置文件,调用哪个类的哪个方法来获取bean-->
<!--factory-method 属性来告诉Spring框架使用这个方法可以获取bean-->
<bean id="starBean" class="com.songzhishu.spring6.bean.StarFactory" factory-method="get"/>
<!--Spring实例化方式的第三中 通过工厂模式 factory-bean 哪个bean可以找到方法 和factory-method 哪个方法可以得到bean-->
<bean id="gunFactoryBean" class="com.songzhishu.spring6.bean.GunFactory"/>
<bean id="gunBean" factory-method="get" factory-bean="gunFactoryBean"/>
<!--通过Spring的特定的接口FactoryBean来简化第三种方式 -->
<bean id="personBean" class="com.songzhishu.spring6.bean.PersonFactoryBean" />
</beans>
测试的程序就不写啦,因为博客的字数太多后面写笔记的时候很卡很卡!
1、BeanFactory和FactoryBean的区别
BeanFactorySpring: IoC容器的顶级对象,BeanFactory被翻译为“Bean工厂”,在Spring的IoC容器中,“Bean工厂”负责创建Bean对象。BeanFactory是工厂。
FactoryBean:它是一个Bean,是一个能够辅助Spring实例化其它Bean对象的一个Bean。
在Spring中,Bean可以分为两类:
- 第一类:普通Bean
- 第二类:工厂Bean(记住:工厂Bean也是一种Bean,只不过这种Bean比较特殊,它可以辅助Spring实例化其它Bean对象。)
FactoryBean是Spring提供的一种整合第三方框架的常用机制。和普通的bean不同,配置一个FactoryBean类型的bean,在获取bean的时候得到的并不是class属性中配置的这个类的对象,而是getObject()方法的返回值。通过这种机制,Spring可以帮我们把复杂组件创建的详细过程和繁琐细节都屏蔽起来,只把最简洁的使用界面展示给我们。
2、bean的生命周期:
Spring其实就是一个管理Bean对象的工厂。它负责对象的创建,对象的销毁等。所谓的生命周期就是:对象从创建开始到最终销毁的整个过程。
通过了解生命周期可以知道在哪个时间节点上调用了哪个类的哪个方法。然后我们可以根据自己的需求在特定的节点上添加功能。
user:
package com.songzhishu.spring6.bean;
public class User {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
System.out.println("第二步:属性赋值");
this.name = name;
}
public User() {
System.out.println("第一步:创建");
}
public void initBean(){
System.out.println("第四步:初始化bean");
}
public void destroyBean(){
System.out.println("第七步:销毁bean");
}
}
bean的后置处理器:
package com.songzhishu.spring6.bean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class BeanPost implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("第三步:执行bean后处理器的before方法");
return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("第五步:执行bean后处理器的after方法");
return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
}
}
配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<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">
<!--配置bean后处理 这个bean的后处理器作用配置文件的所以的bean-->
<bean class="com.songzhishu.spring6.bean.BeanPost"></bean>
<!--nit-method="指定初始化的方法" destroy-method="指定销毁bean的方法"-->
<bean id="userBean" class="com.songzhishu.spring6.bean.User" init-method="initBean" destroy-method="destroyBean">
<property name="name" value="张胜男"></property>
</bean>
</beans>
测试类:
@Test
public void test5(){
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("springbean.xml");
User userBean = applicationContext.getBean("userBean", User.class);
System.out.println("第五步:使用bean"+ userBean);
//关闭spring的容器,然后才能执行销毁方法 转型的快捷指令 castvar
ClassPathXmlApplicationContext context = (ClassPathXmlApplicationContext) applicationContext;
context.close();
}
测试结果:
1、Bean的作用域不同,管理方式不同
Spring 根据Bean的作用域来选择管理方式。
- 对于singleton作用域的Bean,Spring 能够精确地知道该Bean何时被创建,何时初始化完成,以及何时被销毁;
- 而对于 prototype 作用域的 Bean,Spring 只负责创建,当容器创建了 Bean 的实例后,Bean 的实例就交给客户端代码管理,Spring 容器将不再跟踪其生命周期。
2、自己new的对象如何让Spring管理
@Test
public void testBeanRegister(){
// 自己new的对象
User user = new User();
System.out.println(user);
// 创建 默认可列表BeanFactory 对象
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
// 注册Bean
factory.registerSingleton("userBean", user);
// 从spring容器中获取bean
User userBean = factory.getBean("userBean", User.class);
System.out.println(userBean);
}
3、Bean的循环依赖问题:
A对象中有B属性。B对象中有A属性。这就是循环依赖。我依赖你,你也依赖我。
比如:丈夫类Husband,妻子类Wife。Husband中有Wife的引用。Wife中有Husband的引用。
1、singleton+setter的模式下产生的循环依赖:
<?xml version="1.0" encoding="UTF-8"?>
<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">
<bean id="husbandBean" class="com.powernode.spring6.bean.Husband" scope="singleton">
<property name="name" value="张三"/>
<property name="wife" ref="wifeBean"/>
</bean>
<bean id="wifeBean" class="com.powernode.spring6.bean.Wife" scope="singleton">
<property name="name" value="小花"/>
<property name="husband" ref="husbandBean"/>
</bean>
</beans>
首先要明白的是singleton是单实例,也就是说在整个Spring的容器的中的这对象是唯一的,所以在Spring容器的加载的时候就会创建bean,而不是像多实例在调用getbean的时候进行实例化,所以在创建完毕后直接进行 曝光(就是先不赋值,直接曝光,就是告诉别人我创建好啦可以使用啦),然后是调用set方法进行属性的复制!如果两个bean的scope的都是prototype,同样的代码然后执行的话,会一直创建对方的,这不就是死循环!但是一个是单实列一个是多实列的就是没问题的。
如果使用的是构造方式来进行解决循环依赖的问题会怎么样? 然后我们参照使用setter+singleton时候是将注入分成两个部分,所以如果使用的是构造方式的话,实例化和属性赋值是在一起的,所以没有办法分开,就会导致出现和上面一样的问题!
4、基于注解管理Bean:
注解相当于一个标记,有这个标记的会执行相应的设置,如果没有那么就不执行!
1、开启组件扫描:
Spring 默认不使用注解装配 Bean,因此我们需要在 Spring 的 XML 配置中,通过 context:component-scan 元素开启 Spring Beans的自动扫描功能。开启此功能后,Spring 会自动从扫描指定的包(base-package 属性设置)及其子包下的所有类,如果类上使用了 @Component 注解,就将该类装配到容器中。
<?xml version="1.0" encoding="UTF-8"?>
<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-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--开启组件扫描功能-->
<context:component-scan base-package="com.atguigu.spring6"></context:component-scan>
</beans>
注意:在使用 context:component-scan 元素开启自动扫描功能前,首先需要在 XML 配置的一级标签 <beans> 中添加 context 相关的约束。
情况一:最基本的扫描方式
<context:component-scan base-package="com.atguigu.spring6">
</context:component-scan>
方法一:可以扫描多个包,不同的包用逗号分隔
<context:component-scan base-package="com.songzhishu.spring6.bean,com.songzhishu.spring6.pojo,com.songzhishu.spring6.domain"/>
方法二:找到多个包的父包,然后扫描父包,但是这跟这种方式的效率会大大的降低,所以不建议!
情况二:指定要排除的组件
<context:component-scan base-package="com.atguigu.spring6">
<!-- context:exclude-filter标签:指定排除规则 -->
<!--
type:设置排除或包含的依据
type="annotation",根据注解排除,expression中设置要排除的注解的全类名
type="assignable",根据类型排除,expression中设置要排除的类型的全类名
-->
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<!--<context:exclude-filter type="assignable" expression="com.atguigu.spring6.controller.UserController"/>-->
</context:component-scan>
情况三:仅扫描指定组件
<context:component-scan base-package="com.atguigu" use-default-filters="false">
<!-- context:include-filter标签:指定在原有扫描规则的基础上追加的规则 -->
<!-- use-default-filters属性:取值false表示关闭默认扫描规则 -->
<!-- 此时必须设置use-default-filters="false",因为默认规则即扫描指定包下所有类 -->
<!--
type:设置排除或包含的依据
type="annotation",根据注解排除,expression中设置要排除的注解的全类名
type="assignable",根据类型排除,expression中设置要排除的类型的全类名
-->
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<!--<context:include-filter type="assignable" expression="com.atguigu.spring6.controller.UserController"/>-->
</context:component-scan>
2、使用注解定义 Bean
Spring 提供了以下多个注解,这些注解可以直接标注在 Java 类上,将它们定义成 Spring Bean。
注解 | 说明 |
---|---|
@Component | 该注解用于描述 Spring 中的 Bean,它是一个泛化的概念,仅仅表示容器中的一个组件(Bean),并且可以作用在应用的任何层次,例如 Service 层、Dao 层等。 使用时只需将该注解标注在相应类上即可。 |
@Repository | 该注解用于将数据访问层(Dao 层)的类标识为 Spring 中的 Bean,其功能与 @Component 相同。 |
@Service | 该注解通常作用在业务层(Service 层),用于将业务层的类标识为 Spring 中的 Bean,其功能与 @Component 相同。 |
@Controller | 该注解通常作用在控制层(如SpringMVC 的 Controller),用于将控制层的类标识为 Spring 中的 Bean,其功能与 @Component 相同。 |
实际他们的作用是一样的,只不过是我们做啦一定的规范。
测试类:
//@Component(value ="user") //相当于 <bean id="user" class=".....">
//@Repository //不写默认就是类名首字母小写
//@Service
@Controller
public class User {
}
基于不同的注解,他们实现的功能是一样的,但是为了开发规范我们一般对于普通的类使用的是component的注解。
3、使用注解完成注入:
(1)@value:(简单类型)
当属性的类型是简单类型时,可以使用@Value注解进行注入。可以用在属性上,然后不提供set方法也可以,然后value的注解可以写在属性上,也可以setter方法上,更可以写在构造方法的形参上!
goods类:
@Component
public class Goods {
@Value(value = "铅笔")
private String name;
@Value("20")
private double price;
@Override
public String toString() {
return "Goods{" +
"name='" + name + '\'' +
", price=" + price +
'}';
}
}
(2)@Autowired注入:(不简单类型)
单独使用@Autowired注解,默认根据类型装配。【默认是byType】
controller:
package com.songzhishu.autowired.controller;
import com.songzhishu.autowired.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import java.sql.SQLOutput;
/**
* @BelongsProject: Spring6
* @BelongsPackage: com.songzhishu.autowired
* @Author: 斗痘侠
* @CreateTime: 2023-10-08 16:05
* @Description: TODO
* @Version: 1.0
*/
@Controller//创建bean对象
public class UserController {
/*//第一种方式属性注入
@Autowired //根据类型找到对应的对象完成注入
private UserService userService;*/
private UserService userService;
//第二种方式 setter方法
/*@Autowired //注解写在setter方法上
public void setUserService(UserService userService) {
this.userService = userService;
}*/
//第三种方式 构造器方式
/* @Autowired //基于构造器注入
public UserController(UserService userService) {
this.userService = userService;
}*/
//第四种方式 形参上注入
/*public UserController(@Autowired UserService userService) {
this.userService = userService;
}*/
/*//第五种 特殊情况 只有一个有参构造函数 可以不加注解
public UserController(UserService userService) {
this.userService = userService;
}*/
//由@Autowired和Qualifier注解联合起来 根据名称进行匹配
public UserController(UserService userService) {
this.userService = userService;
}
public void add(){
System.out.println("controller");
userService.add();
}
}
Service:(实现类)
package com.songzhishu.autowired.service;
import com.songzhishu.User;
import com.songzhishu.autowired.dao.UserDao;
import com.songzhishu.autowired.dao.UserDaoImpl;
import com.songzhishu.autowired.dao.UserRedisDaoImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
/**
* @BelongsProject: Spring6
* @BelongsPackage: com.songzhishu.autowired.service
* @Author: 斗痘侠
* @CreateTime: 2023-10-08 16:07
* @Description: TODO
* @Version: 1.0
*/
@Service
public class UserServiceImpl implements UserService {
/*@Autowired//属性注入
private UserDao userDao;*/
@Autowired //自动注入
@Qualifier(value = "userRedisDaoImpl")//名字---id
private UserDao userDao;
/*@Autowired //setter
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}*/
/*@Autowired
public UserServiceImpl(UserDao userDao) {
this.userDao = userDao;
}*/
/*public UserServiceImpl(@Autowired UserDao userDao) {
this.userDao = userDao;
}*/
//当一个类型有多个bean的时候只依赖类型的话是完成不了注入的 使用两个是注解完成注入
/*public UserServiceImpl(UserDao userDao) {
this.userDao = userDao;
}*/
@Override
public void add() {
System.out.println("service");
userDao.add();
}
}
dao:
package com.songzhishu.autowired.dao;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;
/**
* @BelongsProject: Spring6
* @BelongsPackage: com.songzhishu.autowired.dao
* @Author: 斗痘侠
* @CreateTime: 2023-10-08 16:07
* @Description: TODO
* @Version: 1.0
*/
@Repository
public class UserDaoImpl implements UserDao{
@Override
public void add() {
System.out.println("dao");
}
}
package com.songzhishu.autowired.dao;
import org.springframework.stereotype.Repository;
/**
* @BelongsProject: Spring6
* @BelongsPackage: com.songzhishu.autowired.dao
* @Author: 斗痘侠
* @CreateTime: 2023-10-08 16:47
* @Description: TODO
* @Version: 1.0
*/
@Repository
public class UserRedisDaoImpl implements UserDao{
@Override
public void add() {
System.out.println("redis...dao");
}
}
总结
-
@Autowired注解可以出现在:属性上、构造方法上、构造方法的参数上、setter方法上。
-
当带参数的构造方法只有一个,@Autowired注解可以省略。()
-
@Autowired注解默认根据类型注入。如果要根据名称注入的话,需要配合@Qualifier注解一起使用。(为了解决一个类型有多个bean的情况)
(3)@Resource注入:
@Resource注解也可以完成属性注入。那它和@Autowired注解有什么区别?
-
@Resource注解是JDK扩展包中的,也就是说属于JDK的一部分。所以该注解是标准注解,更加具有通用性。(JSR-250标准中制定的注解类型。JSR是Java规范提案。)
-
@Autowired注解是Spring框架自己的。
-
@Resource注解默认根据名称装配byName,未指定name时,使用属性名作为name。通过name找不到的话会自动启动通过类型byType装配。
-
@Autowired注解默认根据类型装配byType,如果想根据名称装配,需要配合@Qualifier注解一起用。
-
@Resource注解用在属性上、setter方法上。
-
@Autowired注解用在属性上、setter方法上、构造方法上、构造方法参数上。
@Resource注解属于JDK扩展包,所以不在JDK当中,需要额外引入以下依赖:【如果是JDK8的话不需要额外引入依赖。高于JDK11或低于JDK8需要引入以下依赖。】
<!-- https://mvnrepository.com/artifact/jakarta.annotation/jakarta.annotation-api -->
<dependency>
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
<version>2.1.1</version>
</dependency>
@Resource注解:默认byName注入,没有指定name时把属性名当做name,根据name找不到时,才会byType注入。byType注入时,某种类型的Bean只能有一个。
(4)Spring全注解开发:
所谓的全注解开发就是不再使用spring配置文件了。写一个配置类来代替配置文件。
配置类:
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
//扫描多个包
//@ComponentScan({"com.atguigu.spring6.controller", "com.atguigu.spring6.service","com.atguigu.spring6.dao"})
@ComponentScan("com.atguigu.spring6")
public class Spring6Config {
}
不再new ClassPathXmlApplicationContext()对象了。
@Test
public void testNoXml(){
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(Spring6Configuration.class);
UserService userService = applicationContext.getBean("userService", UserService.class);
userService.save();
}
5、jdbcTemplate:
JdbcTemplate是Spring提供的一个JDBC模板类,是对JDBC的封装,简化JDBC代码。
也可以让Spring集成其它的ORM框架,例如:MyBatis、Hibernate等。
package com.songzhishu.spring6.test;
import com.songzhishu.spring6.bean.User;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* @BelongsProject: Spring6
* @BelongsPackage: com.songzhishu.spring6.test
* @Author: 斗痘侠
* @CreateTime: 2023-10-16 22:03
* @Description: TODO
* @Version: 1.0
*/
public class TestJdbcTemplate {
@Test
public void test(){
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("spring.xml");
JdbcTemplate jdbcTemplate = applicationContext.getBean("jdbcTemplate", JdbcTemplate.class);
System.out.println(jdbcTemplate);
}
//添加
@Test
public void test1(){
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("spring.xml");
JdbcTemplate jdbcTemplate = applicationContext.getBean("jdbcTemplate", JdbcTemplate.class);
//添加
String sql ="insert into t_user(real_name,age) values (?,?)";
/*只要是在jdbcTemplate中,增删改都是 调用update方法*/
int count = jdbcTemplate.update(sql, "赵六", 26);
System.out.println(count);
}
//修改
@Test
public void test2(){
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("spring.xml");
JdbcTemplate jdbcTemplate = applicationContext.getBean("jdbcTemplate", JdbcTemplate.class);
String sql="update t_user set real_name =? ,age=? where id=?";
int count = jdbcTemplate.update(sql, "张三丰", 103, 1);
System.out.println(count);
}
//删除
@Test
public void test3(){
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("spring.xml");
JdbcTemplate jdbcTemplate = applicationContext.getBean("jdbcTemplate", JdbcTemplate.class);
String sql = "delete from t_user where id=?";
int count = jdbcTemplate.update(sql, 5);
System.out.println(count);
}
//查找一个
@Test
public void test4(){
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("spring.xml");
JdbcTemplate jdbcTemplate = applicationContext.getBean("jdbcTemplate", JdbcTemplate.class);
String sql="select * from t_user where id=?";
User user = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(User.class), 1);
System.out.println(user);
}
//查找所有
@Test
public void test5(){
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("spring.xml");
JdbcTemplate jdbcTemplate = applicationContext.getBean("jdbcTemplate", JdbcTemplate.class);
String sql="select * from t_user ";
List<User> query = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(User.class));
query.forEach(System.out::println);
}
//查总数
@Test
public void test6(){
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("spring.xml");
JdbcTemplate jdbcTemplate = applicationContext.getBean("jdbcTemplate", JdbcTemplate.class);
String sql="select count(*) from t_user ";
Integer integer = jdbcTemplate.queryForObject(sql, Integer.class);
System.out.println(integer);
}
//查特定值
@Test
public void test7(){
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("spring.xml");
JdbcTemplate jdbcTemplate = applicationContext.getBean("jdbcTemplate", JdbcTemplate.class);
String sql="select real_name from t_user where id=?";
String name = jdbcTemplate.queryForObject(sql, String.class, 2);
System.out.println(name);
}
//批量添加
@Test
public void test8(){
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("spring.xml");
JdbcTemplate jdbcTemplate = applicationContext.getBean("jdbcTemplate", JdbcTemplate.class);
String sql ="insert into t_user(real_name,age) values (?,?)";
List<Object[]> userList=new ArrayList<>();
Object[] user1={"小医仙",23};
Object[] user2={"美杜莎",25};
Object[] user3={"赵灵儿",26};
Collections.addAll(userList,user1,user2,user3);
int[] ints = jdbcTemplate.batchUpdate(sql, userList);
System.out.println(Arrays.toString(ints));
}
//批量删除
@Test
public void test9(){
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("spring.xml");
JdbcTemplate jdbcTemplate = applicationContext.getBean("jdbcTemplate", JdbcTemplate.class);
String sql ="delete from t_user where id=?";
List<Object[]> userList=new ArrayList<>();
Object[] user1={12};
Object[] user2={13};
Object[] user3={14};
Object[] user4={15};
Collections.addAll(userList,user1,user2,user3,user4);
int[] ints = jdbcTemplate.batchUpdate(sql, userList);
System.out.println(Arrays.toString(ints));
}
}