先赞后看,养成习惯!!!❤️ ❤️ ❤️
码字不易,如果喜欢可以关注我哦!
如果本篇学习笔记对你有所启发,欢迎访问我的个人博客了解更多内容:链接地址
Spring是一个开源Java框架,用于快速开发Java应用程序。它通过提供一组各种功能的库(如IoC、AOP、ORM等)和API简化了Java开发过程,从而提高了开发效率。
Spring 的核心理念是通过依赖注入、面向切面编程和松散耦合等机制,来提供灵活、可扩展、易于维护的企业级应用程序开发框架,这使得Spring应用程序更加灵活、可扩展和易于测试。Spring框架还提供了许多模块,如Spring MVC、Spring Boot、Spring Data等,可以根据需要选择使用。
官网:Spring | Home
Spring框架主要的优势是在简化开发和框架整合上:
概念:分层的轻量级开源框架,以IOC和AOP为内核,在不同的业务场景中Spring都为我们提供了相应的解决方案
优势:简化开发,降低企业级开发的复杂性
框架整合,正因为企业开发中使用的框架越来越多,无疑提高了项目的复杂度,Spring的出现实现了高效整合其他框架,提高了企业级应用开发与运行效率
(1)核心层
(2)AOP层
(3)数据层
(4)Web层
(5)Test层
(1)什么是控制反转呢?
(2)Spring和IOC之间的关系是什么呢?
(3)IOC容器的作用以及内部存放的是什么?
(4)当IOC容器中创建好service和dao对象后,程序能正确执行么?
像这种在容器中建立对象与对象之间的绑定关系就要用到DI:
(1)什么是依赖注入呢?
(2)IOC容器中哪些bean之间要建立依赖关系呢?
Spring中的IoC容器就是IoC思想的一个落地产品实现。IoC容器中管理的组件也叫做bean。在创建bean之前,首先需要创建IoC容器,Spring提供了IoC容器的两种实现方式
这是IoC容器的基本实现,是Spring内部使用的接口,面向Spring本身,不提供给开发人员使用。
BeanFactory的子接口,提供了更多高级特性,面向Spring的使用者,几乎所有场合都使用 ApplicationContext,而不使用底层的BeanFactory。
在对应的bean类上面添加注解@component
ApplicationContext context = new AnnotationConfigApplicationContext("cn.tedu.spring");
User u = context.getBean(User.class);
在对应的basesPackage路径里加上包路径
创建容器
//方式一∶类路径加载配置文件
ApplicationContext ctx = new classPathXmlApplicationContext("applicationContext.xml");
//方式二∶文件路径加载配置文件
ApplicationContext ctx = new FileSystemXmlApplicationContext("D:\lapplicationContext.xml");
//加载多个配置文件
ApplicationContext ctx = new ClassPathXm1ApplicationContext ("bean1.xm1" , "bean2.xml" );
获取bean
//方式一︰使用bean名称获取
BookDao bookDao = (BookDao) ctx.getBean( "bookDao" );
//方式二︰使用bean名称获取并指定类型
BookDao bookDao = ctx.getBean("bookDao" , BookDao.class);
//方式三∶使用bean类型获取
BookDao bookDao = ctx.getBean( BookDao.class);
bean相关
容器相关
单实例(Singleton)是指某个类只能创建唯一的一个实例对象,并且该类提供一个全局的访问点(静态方法)来让外界获取这个实例,常常用在那些只需要一个实例来处理所有任务的场景下,例如数据库连接。
多实例(Multiple Instance)则是指可以在同一个类的定义下,创建多个实例对象。每个对象都是相互独立的,有自己的状态和行为;常常用于需要同时处理多个任务的场景。
在Spring中可以通过 @Scope 注解来指定bean的作用域范围
取值 |
含义 |
@Scope("singleton") 单例(默认) |
在IoC容器中,这个bean的对象为单实例 |
@Scope("prototype") 多例 |
这个bean在IoC容器中有多个实例 |
在Spring中可以通过xml配置文件来指定bean的作用域范围
配置bean为非单例
在Spring配置文件中,配置scope属性来实现bean的非单例创建
scope使用后续思考
介绍完scope属性以后,我们来思考几个问题:
调用无参构造
在讲解这三种创建方式之前,我们需要先确认一件事:
bean本质上就是对象,对象在new的时候会使用构造方法完成,那创建bean也是使用构造方法完成的。
这些方式中,重点掌握构造方法和FactoryBean即可。
需要注意的一点是,构造方法在类中默认会提供,但是如果重写了构造方法,默认的就会消失,在使用的过程中需要注意,如果需要重写构造方法,最好把默认的构造方法也重写下。
(1)关于Spring中对bean生命周期控制提供了两种方式:
(2)对于bean的生命周期控制在bean的整个生命周期中所处的位置如下:
在spring中,可以通过 @PostConstruct 和 @PreDestroy 注解实现 bean对象 生命周期的初始化和销毁时的方法。
生命周期初始化方法,在对象构建以后执行。
生命周期销毁方法,比如此对象存储到了spring容器,那这个对象在spring容器移除之前会先执行这个生命周期的销毁方法(注:prototype作用域对象不执行此方法)。
(3)关闭容器的两种方式:
//bean @Component public class Life { private String lifetime; public Life(){ System.out.println("1.实例化创建bean对象"); } @Value("青少年") public void setLifetime(String lifetime){ this.lifetime=lifetime; System.out.println("2.set方法属性赋值"); } @PostConstruct public void init() { System.out.println("3.初始化方法执行"); } @PreDestroy public void destory(){ System.out.println("5.销毁阶段"); } @Override public String toString() { return "Life{" + "lifetime='" + lifetime + '\'' + '}'; } } //test类 public class TestLife { public static void main(String[] args) { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext("spring.life"); Life bean = ctx.getBean(Life.class); System.out.println("4.使用阶段"+bean); ctx.close(); } } 打印结果: 1.实例化创建bean对象 2.set方法属性赋值 3.初始化方法执行 4.使用阶段Life{lifetime='青少年'} 5.销毁阶段 Process finished with exit code 0
DI (Dependency Injection):依赖注入,依赖注入实现了控制反转的思想;
是指Spring创建对象的过程中,将对象依赖属性通过配置进行注入。
所以 IOC 是一种控制反转的思想,而依赖注入 DI 是对IOC 的一种具体实现。
Spring就是基于上面这些知识点,为我们提供了两种注入方式,分别是:
对于引用数据类型使用的是
对于简单数据类型使用的是
标签中
依赖注入方式选择
1.强制依赖使用构造器进行,使用setter注入有概率不进行注入导致null对象出现2.可选依赖使用setter注入进行,灵活性强
3.Sprng框架倡导使用构造器,第三方框架内部大多数采用构造器注入的形式进行数据初始化,相对严谨
4.如果有必要可以两者同时使用,使用构造器注入完成强制依赖的注入,使用setter注入完成可选依赖的注入
5.实际开发过程中还要根据实际情况分析,如果受控对象没有提供setter方法就必须使用构造器注入
6.自己开发的模块推荐使用setter注入
@Value注入是将属性值直接注入到bean中,主要用于注入一些简单类型的属性(如字符串、基本类型等);
使用时需要注意属性的类型和格式,否则会导致注入失败。
属于spring框架,根据类型注入,如果有相同类型的再使用Qualifier注解根据名称注入
@Autowired注入是将对象类型或者接口类型注入到bean中,并且在注入对象时会根据依赖注入容器中 bean的类型 进行匹配。
如果容器中有多个类型匹配的bean存在,则会抛出异常。
因此,@Autowired注入常用于注入复杂对象、接口类型的属性或其他bean实例。
依赖自动装配特征
根据IOC容器中,Spring Bean对象的名称进行装配
如果一个接口有多个实现类,@Autowired一定会装配失败,会报错NoUniqueBeanDefinitionException(不是唯一的bean定义异常)
可以指定bean名称@Component("名称"),使用@Qualifier注解,通过名称指定要装配的bean
@Repository public class UserMapper { @Autowired @Qualifier(value = "名称1") private Cache cache; } //对应Bean @Component("名称1") public class MapperImpl1(){...} //对应Bean @Component("名称2") public class MapperImpl2(){...}
根据名称注入,名称不一样根据属性注入,属性不一样根据类型注入。
与@Autowired作用类似,但该注解是jkd拓展包的,也属于jdk的一部分
@Repository public class ControllerMapper { @Resource(name = "mapperImpl") private Mapper mapper; } //对应Bean @Component public class MapperImpl(){...}
@Repository public class ControllerMapper { @Resource private Mapper mapperImpl; } //对应Bean @Component public class MapperImpl(){...}
@Component ("bookDao")
public class BookDaoImpl implements BookDao {
@Component
public class BookServiceImpl implements BookService {}
加载properties文件
实际开发中,很多情况下我们需要对一些变量或属性进行动态配置,而这些配置可能不应该硬编码到我们的代码中,因为这样会降低代码的可读性和可维护性。
我们可以将这些配置放到外部属性文件中,比如database.properties文件,然后在代码中引用这些属性值,例如jdbc.url和jdbc.username等。这样,我们在需要修改这些属性值时,只需要修改属性文件,而不需要修改代码,这样修改起来更加方便和安全。
而且,通过将应用程序特定的属性值放在属性文件中,我们还可以将应用程序的配置和代码逻辑进行分离,这可以使得我们的代码更加通用、灵活。
使用流程
说明
自动扫描配置是 Spring 框架提供的一种基于注解(Annotation)的配置方式,用于自动发现和注册 Spring 容器中的组件。当我们使用自动扫描配置的时候,只需要在需要被 Spring 管理的组件(比如 Service、Controller、Repository 等)上添加对应的注解,Spring 就会自动地将这些组件注册到容器中,从而可以在其它组件中使用它们。
在 Spring 中,通过 @ComponentScan 注解来实现自动扫描配置。
@ComponentScan 注解用于指定要扫描的包或类。
Spring 会在指定的包及其子包下扫描所有添加 @Component(或 @Service、@Controller、@Repository 等)注解的类,把这些类注册为 Spring Bean,并纳入 Spring 容器进行管理。
/**
* 设置自动扫描
* Configuration注解: 标识此类为Spring的配置类,Spring在启动时会自动加载此类;
* ComponentScan注解: 自动扫描注解;
* 1.指定包路径为:cn.tedu.spring, 扫描该包及子孙包中所有的类,把所有添加相关注解的类注册为Spring Bean;
* 2.未指定包路径,则扫描该配置文件[SpringConfig.java]所在包以及子孙包中的类;
*/
@Configuration
@ComponentScan("cn.tedu.spring")
public class SpringConfig {
}
可以使用通配符描述切入点,快速描述
匹配com.itheima包下的任意包中的UserService类或接口中所有find开头的带有一个参数的方法
execution (public * com.itheima.*.UserService.find*(*))
匹配com包下的任意包中的UserService类或接口中所有名称为findByld的方法
execution (public User com..UserService.findById (..))
execution(* *..*Service+.*(..))
书写技巧
AOP通知描述了抽取的共性功能,根据共性功能抽取的位置不同,最终运行代码时要将其加入到合理的位置
AOP通知共分为5种类型
1.环绕通知必须依赖形参ProceedingloinPoint才能实现对原始方法的调用,进而实现原始方法调用前后同时添加通知2通知中如果未使用ProceedingJoinPoint对原始方法进行调用将跳过原始方法的执行
3.对原始方法的调用可以不接收返回值,通知方法设置成void即可,如果接收返回值,必须设定为Object类型
4.原始方法的返回值如果是void类型,通知方法的返回值类型可以设置成void,也可以设置成Object
5. 由于无法预知原始方法运行后是否会抛出异常,因此环绕通知方法必须抛出Throwable对象
@Around("pt()"")
public object around(ProceedingJoinPoint pjp)throws Throwable{
System.out.println("around before advice ...");
Object ret = pjp.proceed();
system.out.println("around after advice ...");
return ret;
}
==>解决方法:事务
事务作用:在数据层保障一系列的数据库操成功或失败
Spring事务作用:在数据层或业务层保障一系列的数据库操成功或失败
==>之所以加上业务层是因为业务层也不过是数据层的不同组合,一次业务层事务的提交要么全部失败要么全部成功
1.在配置类里添加事务的Bean
2.在Spring配置类里添加@EnableTransactionManagement标签
3.在需要添加事务管理的接口方法上添加@Transactional注解(该注解也可以写在接口类上,表示该接口所有方法都开启事务)
概念:
->事务管理员:一般是带有@Transactional标签的接口方法,可以将其他事务纳入自己的事务中(最终变成自己的一个事务)
->事务协调员:一般是被收纳的接口方法
我们可以通过在@Transactional注解里添加属性实现对事物的进一步管理
例: @Transactional(rollbackFor = IOException.class) //并不是遇到所有的异常事物都会回滚,所以我们要将那些特例的异常指定出来回滚
==>解决方法:让事务协调员新建一个事务,不被管理员统一管理(事务传播行为)
实现:在该事务协调员(接口方法)上添加@Transactional(propagation = Propagation.REQUIRES_NEW) ->表示协调员不参与管理员事务(默认的参数是Propagation.REQUIRES,表示管理员涵盖了该事务就自动加入)
意思就是说通过这种方式,虽然记录日志,加钱减钱的方法还是写在了同一个事务里,但是记录日志的方法在前两者发生异常时也会执行(回滚事务),不和他们同成功同失败
按照上述的三个组成部分,在我们项目开发中呢,可以将代码分为三层:
在之前的入门案例中,要把某个对象交给IOC容器管理,需要在类上添加一个注解:
- @Component (用于将类标识为Bean组件,使得Spring能够自动扫描并将其实例化为Bean对象,并进行依赖注入)
而Spring框架为了更好的标识web应用程序开发当中,bean对象到底归属于哪一层,又提供了@Component的衍生注解:
- @Controller (标注在控制层类上)
- @Service (标注在业务层类上)
- @Repository (标注在数据访问层类上)
如果在IOC容器中,存在多个相同类型的bean对象,会出现什么情况呢?
程序运行会报错
如何解决上述问题呢?Spring提供了以下几种解决方案:
- @Primary
- @Qualifier
- @Resource
面试题 :
@Autowird 与 @Resource的区别?
@Autowired 是spring框架提供的注解,而@Resource是JDK提供的注解
@Autowired 默认是按照类型注入,而@Resource是按照名称注入
->通常我们写完代码想要测试这段代码的正确性,那么必须新建一个类,然后创建一个 main() 方法,然后编写测试代码。如果需要测试的代码很多呢?
那么要么就会建很多main() 方法来测试,要么将其全部写在一个 main() 方法里面。这也会大大的增加测试的复杂度,降低程序员的测试积极性。
而JUnit能很好的解决这个问题,简化单元测试,写一点测一点,在编写以后的代码中如果发现问题可以较快的追踪到问题的原因,减小回归错误的纠错难度。
用法:在需要测试类上方添加
@RunWith(SpringJUnit4ClassRunner.class)和
@Configuration(SpringConfig.class)标签,
并在方法上面添加@Test注解,需要测试时直接右键运行即可
1.导入坐标(JUnit坐标以及Spring集成JUnit的坐标)
2.编写测试类
①测试方法上必须使用@Test进行修饰
②测试方法必须使用public void 进行修饰,不能带任何的参数
③新建一个源代码目录来存放我们的测试代码,即将测试代码和项目业务代码分开
④测试类所在的包名应该和被测试类所在的包名保持一致
⑤测试单元中的每个方法必须可以独立测试,测试方法间不能有任何的依赖