前言
Spring的学习还是很简单的,到SpringMVC的时候则会比较复杂了,因为要创建Web项目以及一些Web因素等等。
Spring的简介
Spring入门案例
导入依赖
<packaging>jar</packaging>
<dependencies>
<!-- 基于Maven依赖传递性,导入spring-context依赖即可导入当前所需所有的jar包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.1</version>
</dependency>
<!-- junit测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
对象在Spring中被称为Bean,现在先基于XML来管理Bean。
创建Spring的XML文件
<?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="hello" class="com.atguigu.spring.pojo.HelloWorld"/>
</beans>
@Test
public void testSpring(){
//获取IOC容器
ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
//取出Bean对象实例
HelloWorld hello = ioc.getBean("hello", HelloWorld.class);
//调用对象实例方法
hello.sayHello();
}
第一种是2个形参,传入了泛型参数,如果不传入就像②所示一样,需要进行强制类型转换,第三种方式是只传入了Bean的实际类型。
IOC容器创建对象的方式
IOC通过底层类型反射来默认使用无参构造器创建的对象,所以POJO对象一定要有无参构造。
获取bean的三种方式和注意事项
@Test
public void test(){
ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
//通过bean的id获取的对象是Object,需要进行强制类型转换
//Student studentOne = (Student) ioc.getBean("studentOne");
//通过类型匹配则一定是获取的对应类型,如果有两个相同类型不同id的bean,则只能id+class来获取
//Student student = ioc.getBean(Student.class);
//若没有任何一个类型匹配的bean,则抛出异常:NoSuchBeanDefinitionException
//若有多个类型匹配的bean,则抛出异常:NoUniqueBeanDefinitionException
Student student = ioc.getBean("studentOne",Student.class);
System.out.println(student);
//在IOC中一个类型的bean配置一次就行了,因为默认是单列的
//IOC中的bean不能是接口,普遍用法都是id是接口名,class是接口实现类
}
依赖注入
setter注入是指Spring利用setter方法来将A依赖的B对象赋值。
构造器注入是指Spring利用构造器来注入依赖,在保存到核心容器前,依赖就已经完成了。
该操作是用于基本类型或其包装类类型的注入,不过依赖注入大部分时候还是应用于实体类依赖的注入。
构造器注入更符合高内聚的规范。
null值
如果想要给一个属性设置null,是不能直接通过value="null"设置的,因为这样会被当成一个字面量,相当于是一个字符串null。
使用property的子元素标签null元素来完成赋值
<null />
value也算是一个元素,不过普遍还是将value作为属性来进行赋值。
依赖注入为类类型赋值
<bean id = "studentOne" class = "com.atguigu.spring.pojo.Student">
<property name="class" ref="class"></property>
</bean>
<bean id = "class" class="Class"/>
这些详细的内容,在Spring基础使用的文章有很详细的描述。
级联方式赋值
该作用并没有什么实际意义,因为只有clazz先初始化才能这样操作,所以相当于覆盖了已经依赖注入好了的clazz的属性值。
内部bean
为数组类型赋值
如果数组里是类对象,则用ref即可。
为集合类型的属性赋值
map集合赋值
p命名空间
如果引用的属性是字面量则直接使用无ref的后缀属性即可。
Spring管理数据源和引入外部属性文件
bean的作用域
bean的声明周期
FactoryBean
是否单例是一个默认方法,默认返回true。
获取时直接传入user的class类型即可。
基于XML的自动装配
比如三层架构,Controller、Service、DAO,它们之间都是依次向下依赖的,控制层有服务层的实例,服务层有DAO的实例,它们在类中是硬编码的形式。
我们将三个组件交给Spring的IOC容器,添加依赖注入的setter和getter方法:
不使用自动装配的依赖注入
控制层调用服务层的方法,然后服务层的方法调用DAO层的方法。
所以在运行时只需要调用控制层即可。
基于xml的自动装配之byType
autowirte表示自动装配属性,no表示不装配,default就表示no,byType表示根据类型自动装配。
如果类型匹配搜索到两个同样的类型,但是id不同的情况,则编译就会直接报错。
一个类型的bean一般在IOC里只设置一次,因为一般都是单例。
基于xml的自动装配之byName
该设置用于处理如果有两个相同但id不同的bean对象时设置。
基于注解管理bean
基于注解管理bean之注解和扫描
四个注解功能一模一样,名称是给程序员看的。
这两个都是接口,要把注解写到实现类上,控制层是一个正常的类。
完成上面三层架构的创建以及注解的标记后,在spring配置文件中添加扫描设置:
<!-- 扫描组件 context:上下文 component:组件 scan:扫描 base-package:包路径 -->
<!-- 想扫描其他的包可以在后面直接加逗号,不过三层构建都在spring包下,所以直接指定spring包就行了 -->
<context:component-scan base-package="com.atguigu.spring"></context:component-scan>
下面可以测试一下,不过此时如果有依赖对象,则是没有的,需要用注解的自动装配来标记一下,让Spring去扫描并自动装配依赖关系。
基于注解管理bean之扫描组件
当Spring和SpringMVC在一起的时候,可能会出现同一个组件扫描了两次的情况,为了避免这种情况,我们可以设置Spring扫描组件的配置:
排除扫描
<context:exclude-filter type="" expression=""/><!-- 排除 -->
在type里有这两个比较常用的属性。
经过如下配置,Spring会排除对全类名所标识的类的扫描:
<context:component-scan base-package="com.atguigu.spring">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/><!-- 排除 -->
</context:component-scan>
经过扫描排除后,再次执行,框架会抛出没有找到组件的异常,虽然添加了控制层注解,但是因为排除扫描,所以最终并没有扫描到控制层组件。
扫描层的排除结果并不会影响其他两个组件的运行。
下面是类类型排除扫描的写法。
<context:exclude-filter type="assignable" expression="com.atguigu.spring.controller.UserController"/>
其实和注解排除差不多效果(第一个是排除了属于控制层注解的组件,第二个则只是单个类,此处肯定效果差不多,因为只有一个控制层)。这里的type的assignable表示根据类的类型排除,上面是根据注解排除,用的比较多的是第一种,在SSM整合时的场景,SpringMVC扫描控制层,Spring扫描控制层以外的组件。
包含扫描
<context:component-scan base-package="com.atguigu.spring" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
上面设置表示包含扫描,默认不全部扫描,只扫描include(包含)标签设置的组件。
因为设置了只扫描Controller组件,所以发生了其他组件的未找到异常。
因为排除本身就只是排除,所以全部扫描的设置是无需必须设置的。
第一种排除扫描是用的最多的,也是最实用的。
我们一般就是一句:
<context:component-scan base-package="com.atguigu.spring"></context:component-scan>
扫描全部组件,直接就完事了。
基于注解管理bean之bean的id
扫描组件后,Spring容器为扫描的bean组件自动设置了一个id,注解+扫描得到的bean组件的id默认为类的小驼峰:比如控制层的UserController,那么默认的id就是userController。
自定义组件id
我们可以看到,不论是哪个注解(上面的四个组件),它们都有一个String value的注解属性。
这个是让我们用来指定自定义的组件id的,默认是类名的小驼峰,如下程序示例了自定义注解扫描的id。
可以看到,我们在对@Controller传入了一个String参数时,原来的类名小驼峰就失效了,此时我们只有传入我们刚刚自定义的String参数才能再次获取到扫描组件实例对象。
一般获取bean对象都是通过类型获取,id获取比较少,所以一般都是默认id->类名小驼峰。
基于注解的自动装配之@Autowired注解能够标识的地方
前面只是说到了各个组件的bean扫描,还没有提到依赖注入的自动装配,下面通过@Autowired来完成依赖注入,不过先来看看该注解都可以标识在哪些地方。
setter方法上
成员变量上
成员变量的有参构造上
基于@AutoWirte自动装配的原理
该注解是指定某个bean的id,将这个bean为属性赋值。
自动装配@Autowired的注意事项
一般默认的byType就行了,我们根本不会设置两个同样类型的bean并且id还不和属性同名。
所以直接@Autowired就行了。
必须自动装配
如果没有bean进行装配就会报错,可以设置为false,那么如果没有可装配就不装配了,值是null。