1.Spring简介
1.1官方文档
官方文档:https://spring.io/
官方下载地址: http://repo.spring.io/release/org/springframework/spring
构建一切--------->协调一切-------->连接一切:
- Spring Boot
- 一个快速开发的脚手架
- 基于SpringBoot可以快速开发单个微服务
- 约定大于配置
- 学习Spring Boot前提是要完全掌握Spring和SpringMVC
- Spring Cloud
- 基于Spring Boot实现的
依赖:
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.13</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.13</version>
</dependency>
1.2历史,优点,组成
历史:
- 2002年,首次推出Spring框架的雏形—>interface21框架
- Spring框架以interface21框架为基础经过重新设计,并不断丰富其内涵,于2004年3.24发布了1.0正式版本
- Rod Johnson,SpringFramework创始人
- Spring理念:是现有的技术更加容易使用,本身是一个大杂烩,整合了现有的技术框架
- 弊端:发展太久,违背了原来的理念,配置变得十分繁琐(“配置地狱”)
- SSH:Struct2+Spring+Hibernate(全自动持久化框架)
- SSM:SpringMvc+Spring+Mybatis(半自动持久化框架)
优点:
- 开源的免费的框架(容器)
- 轻量级的,非入侵式的框架
- 控制反转(IOC),面向切面编程(AOP)
- 支持事务的处理,对框架整合的支持
组成:
七大模块
2.IOC
2.1IOC理论
1.UserDao里定义一个getUser接口,然后三个实现类,区别只有输出不同:
public class UserDaoImpl implements UserDao {
@Override
public void getUser() {
System.out.println("user");
}
}
2.业务层接口也是getUser,实现类为:
public class UserServiceImpl implements UserService {
private UserDao userDao ;
//用set进行实现动态值的注入
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void getUser() {
userDao.getUser();
}
}
3.测试(用set就可以在测试中修改代码,调用不同的实现类)
public class MyTest {
public static void main(String[] args) {
//用户实际接触的是业务层,根本不需要接触dao层
UserServiceImpl userService = new UserServiceImpl();
userService.setUserDao(new UserDaoMysqlImpl());
userService.getUser();
}
}
-
之前的业务中,用户的需求会影响我们原来的代码,我们需要按照用户的需求去修改原代码,一旦代码量十分大,修改一次的成本就会十分昂贵。
-
后来我们使用一个Set接口实现,已经发生了革命性的变化。之前程序是主动创建对象,控制权在程序员手上;使用set注入后,程序不再具有主动性,而是被动接收对象,这就达到了控制反转的效果。
-
这种思想,从本质上解决了问题,我们程序员不用再去管理对象的创建了。系统的耦合性大大降低,可以更专注的在业务实现上,这就是IOC的原型。
2.2IOC原理
控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法,也有人认为DI只是IoC的另一种说法。没有IoC的程序中 , 我们使用面向对象编程 , 对象的创建与对象间的依赖关系完全靠编码在程序中,对象的创建由程序自己控制;控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式反转了。
IOC是Spring框架的核心内容,使用多种方式完美实现了IOC,可以使用XML配置,也可以使用注解,新版本的Spring也可以零配置实现IOC。
Spring容器在初始化时先读取配置文件,根据配置文件或元数据创建与组织对象存入容器中,程序使用时再从Ioc容器中取出需要的对象。
采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。
控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependency Injection)。
3.HelloSpring
3.1项目
-
首先建立一个空的maven
-
实体类pojo
package moli.pojo; public class Hello { private String str; public String getStr() { return str; } public void setStr(String str) { this.str = str; } @Override public String toString() { return "Hello{" + "str='" + str + '\'' + '}'; } }
-
配置文件beans.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 https://www.springframework.org/schema/beans/spring-beans.xsd"> <!--使用Spring来创建对象,在Spring中这些都被叫做bean,bean=对象 Hello hello = new Hello() 类型 变量名 = new 类型() id=变量名,class=new的对象,property:给对象中的属性设置一个值 此过程就是一个控制反转的过程! --> <bean id="hello" class="moli.pojo.Hello"> <property name="str" value="Spring"/> </bean> </beans>
-
测试,实例化容器
import moli.pojo.Hello; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MyTest { public static void main(String[] args) { //获取spring的上下文对象 ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); //我们的对象都放到了Spring中了,我们需要就到beans里取即可 Hello hello = (Hello) context.getBean("hello"); System.out.println(hello.toString()); } }
-
结果
3.2解决问题
3.2.1jar包不存在
报错:
解决方法:
-
修改maven本地仓库里的setting.xml
-
刷新并重启
-
重启后再次刷新工程
3.2.2执行main函数报错
报错:
Failed to execute goal org.codehaus.mojo:exec-maven-plugin:3.0.0:exec (default-cli) on project helloSpring: Command execution failed.
解决办法:
-
idea降版本(2017)
-
用单元测试,不去使用main方法。因为maven项目执行main函数的时候需要两个插件:
-
maven-compiler-plugin:用来编译Java文件,指定JDK版本等(可以不导入)
-
exec-maven-plugin:用来执行class文件
<build> <plugins> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>exec-maven-plugin</artifactId> <version>1.6.0</version> <executions> <execution> <goals> <goal>java</goal> </goals> </execution> </executions> <configuration> <classpathScope>test</classpathScope> </configuration> </plugin> </plugins> </build>
-
3.2.3乱码
<properties>
<!-- 文件拷贝时的编码 -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<!-- 编译时的编码 -->
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
</properties>
3.3修改IOC理论中的例子
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="Impl" class="moli.dao.UserDaoImpl"/>
<bean id="MysqlImpl" class="moli.dao.UserDaoMysqlImpl"/>
<bean id="OracleImpl" class="moli.dao.UserDaoOracleImpl"/>
<bean id="ServiceImpl" class="moli.service.UserServiceImpl">
<!--ref:引用spring容器中创建好的对象
value:一个具体的值,基本数据类型-->
<property name="userDao" ref="MysqlImpl"/>
</bean>
</beans>
import moli.service.UserServiceImpl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
//获取ApplicationContext:拿到spring的容器
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
//容器在手,需要什么就get什么
UserServiceImpl serviceImpl = (UserServiceImpl) context.getBean("ServiceImpl");
serviceImpl.getUser();
}
}
3.4小总结
-
spring的这个过程就叫控制反转 :
- 控制 : 谁来控制对象的创建 , 传统应用程序的对象是由程序本身控制创建的new ;使用Spring后 , 对象是由Spring来创建的
- 反转 : 程序本身不创建对象 , 而变成被动的接收对象
-
依赖注入 : 就是利用set方法来进行注入的,所以实体类中的set方法必须存在,否则spring失效
-
IOC是一种编程思想,由主动的编程变成被动的接收
-
可以通过ClassPathXmlApplicationContext去浏览一下底层源码
-
到了现在 , 我们彻底不用再程序中去改动了 , 要实现不同的操作 , 只需要在xml配置文件中进行修改
-
所谓的IOC,一句话搞定 : 对象由Spring 来创建 , 管理 , 装配 !
4.IOC创建对象的方式
在配置文件加载的时候,容器中的对象就已将初始化了。
package moli.pojo;
public class User {
private String name;
public User(){
System.out.println("wucan");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void show(){
System.out.println("name="+name);
}
}
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="user" class="moli.pojo.User">
<property name="name" value="ming"/>
</bean>
</beans>
import moli.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
User user = (User) context.getBean("user");
user.show();
}
}
-
使用无参构造创建对象,默认
-
如果使用有参构造创建对象
方式一:下标赋值
<bean id="user" class="moli.pojo.User"> <constructor-arg index="0" value="ming"/> </bean>
方式二:通过类型创建,不建议使用,因为类型可重复
<bean id="user" class="moli.pojo.User"> <constructor-arg type="java.lang.String" value="ming"/> </bean>
方式三:直接通过参数名来设置,推荐使用
<bean id="user" class="moli.pojo.User"> <constructor-arg name="name" value="ming"/> </bean>
5.Spring的配置
5.1别名
如果添加了别名,我们也可以用别名来获取对象
<alias name="user" alias="o"/>
5.2bean的配置
bean中的name也是别名,而且分隔符很灵活
<bean id="user" class="moli.pojo.User" name="user1,user2 user3;user4"></bean>
5.3import
一般用于团队开发使用,可以将多个配置文件导入合并为一个总的,直接使用总的配置就可以。
6.DI
三类:构造器注入,set方式注入,拓展方式注入
6.1set
- 依赖注入:set注入!
- 依赖:bean对象的创建依赖于容器
- 注入:bean对象中的所有属性,由容器来注入
【环境搭建】
-
复杂类型
package moli.pojo; public class Address { private String address; public String getAddress() {return address;} public void setAddress(String address) {this.address = address;} }
-
真实测试对象
package moli.pojo; import java.util.*; public class Student { private String name; private Address address; private String[] books; private List<String> hobbies; private Map<String,String> card; private Set<String> games; private String wife; private Properties info; //然后敲出这些实体类的get和set
-
beans.xml
<bean id="address" class="moli.pojo.Address"> <property name="address" value="黑龙江"/> </bean> <bean id="student" class="moli.pojo.Student"> <!--第一种:普通值注入,value--> <property name="name" value="小明"/> <!--第二种:bean注入,ref--> <property name="address" ref="address"/> <!--第三种:数组--> <property name="books"> <array> <value>西游记</value> <value>水浒传</value> <value>红楼梦</value> </array> </property> <!--第四种:列表--> <property name="hobbies"> <list> <value>睡觉</value> <value>敲代码</value> <value>打球</value> </list> </property> <!--第五种:map--> <property name="card"> <map> <entry key="身份证" value="1234567"/> <entry key="银行卡" value="1239870"/> </map> </property> <!--第六种:set--> <property name="games"> <set> <value>LOL</value> <value>COC</value> <value>BOB</value> </set> </property> <!--第七种:null--> <property name="wife"> <null/> </property> <!--第八种:自定义信息--> <property name="info"> <props> <prop key="性别">男</prop> <prop key="年龄">19</prop> <prop key="成绩">99</prop> </props> </property> </bean>
-
测试
public class MyTest { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); Student student = (Student) context.getBean("student"); System.out.println(student.toString()); } } //Student{name='小明', address=Address{address='黑龙江'}, books=[西游记, 水浒传, 红楼梦], hobbies=[睡觉, 敲代码, 打球], card={身份证=1234567, 银行卡=1239870}, games=[LOL, COC, BOB], wife='null', info={性别=男, 成绩=99, 年龄=19}}
6.2p、c
官方文档:
public class User {
private String name;
private int age;
}
//依旧get、set、toString
<?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:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--p命名空间注入,可以直接注入属性的值,无参构造,property-->
<bean id="user" class="moli.pojo.User" p:name="小明" p:age="19"/>
<!--c命名空间注入,有参构造,construct-args-->
<bean id="user2" class="moli.pojo.User" c:name="小红" c:age="1"/>
</beans>
6.3bean的作用域
-
单例模式(spring的默认模式),永久都是一个
<bean id="user" class="moli.pojo.User" p:name="小明" p:age="19" scope="singleton"/>
-
原型模式,每次从容器中get的时候都会产生一个新的对象
<bean id="user" class="moli.pojo.User" p:name="小明" p:age="19" scope="prototype"/>
-
request、session、application在web开发中应用
7.Bean的自动装配
- 自动装配是spring满足bean依赖的一种方式
- spring会在上下文中自动寻找,并自动给bean装配属性
在spring中有三种装配方式
- 在xml中显示的配置
- 在java中显示的配置
- 隐式的自动装配bean(重要)
7.1测试
一个人有两个宠物
7.2byName、byType
<bean id="cat" class="moli.pojo.Cat"/>
<bean id="dog1" class="moli.pojo.Dog"/>
<!--byName:会自动在容器上下文中查找,和自己set对象后面名字相同的beanid,如果id不同则报错-->
<!--byType:会自动在容器上下文中查找,和自己对象属性类型相同的beanclass,id可以不同s可以,但是属性类型要唯一,否则报错-->
<bean id="people" class="moli.pojo.People" autowire="byType">
<property name="name" value="小明"/>
</bean>
7.3使用注解自动装配
jdk1.5支持注解,spring2.5支持注解
使用注解:
-
导入约束:context约束
-
配置注解支持:context:annotation-config/
<?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 https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <context:annotation-config/> </beans>
7.3.1@Autowired
-
直接在属性上使用即可,也可以在set方式上使用
-
而且使用Autowired我们就可以不再编写set方法了,前提是你这个自动装配的属性在ioc(spring)容器中存在,且符合byType
-
先通过byType,再通过byName寻找
-
如果@Autowired自动装配环境比较复杂,自动装配无法通过一个注解( @Autowired)完成的时候,我们可以使用**@Qualifier(value = “xxx”)**去配置@Autowired的使用,指定一个唯一的bean对象注入!
拓展:@Nullable
@Nullable字段标记了这个注解,说明这个字段可以为null。
//@Autowired的一段源码
public @interface Autowired {
boolean required() default true;
}
public class People {
//如果显示的定义required属性为false,说明这个对象可以为null,否则不允许为空
@Autowired(required = false)
private Cat cat;
@Autowired
private Dog dog;
private String name;
}
7.3.2@Resource
java原生自动装配注解,比较强大,先通过byName再通过byType寻找
import javax.annotation.Resource;
public class People {
@Resource
private Cat cat;
@Resource(name = "dog1")
private Dog dog;
private String name;
}
8.使用注解开发
-
在spring4之后使用注解开发必须导入aop的jar包
-
导入context约束,增加注解支持
-
bean:@Component
-
属性注入:@Value(“xxx”)
//组件,等价于<bean id="user" class="moli.pojo.User"/> @Component @Scope("singleton") public class User { //相当于<property name="name" value="小明"/> @Value("小明") public String name; }
<?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 https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <!--指定要扫描的包,这个包下的注解就会生效--> <context:component-scan base-package="moli.pojo"/> <context:annotation-config/> </beans>
-
衍生的注解:
@Component的衍生注解
- dao:@Repository
- service:@Service
- controller:@Controller
-
自动装配:@Autowired
-
作用域:@Scope(“singleton”)
-
小结
xml与注解:
-
xml适用于任何场合,比较万能,维护简单
-
注解不是自己的类使用不了,维护比较复杂
xml与注解的最佳实现: -
xml负责管理bean
-
注解负责实现属性的注入
9.使用java的方式配置spring
我们现在要完全不使用Spring的xml配置了,全权交给Java来做。JavaConfig是Spring的一个子项目,在Spring4之后,它成为了一个核心功能。
-
实体类
package moli.pojo; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @Component public class User { @Value("小明") private String name; public String getName() {return name;} public void setName(String name) {this.name = name;} @Override public String toString() {return "User{" +"name='" + name + '\'' +'}';} }
-
配置类
package moli.config; import moli.pojo.User; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @Configuration //@Configuration代表这是一个配置类,就相当于beans.xml //这个也会被spring管理注册到容器中,因为相当于@Component @ComponentScan("moli.pojo") //扫描包 @Import(UserConfig1.class) public class UserConfig { // 注册一个bean,就相当于我们之前写的一个bean标签 // 这个方法的名字,就相当于bean标签中id属性 // 这个方法的返回值,就相当于bean标签中的class属性 @Bean public User getUser(){ return new User(); // 就是返回要注入到bean的对象! } }
-
测试
import moli.config.UserConfig; import moli.pojo.User; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class MyTest { public static void main(String[] args) { //如果完全使用了配置类方式去做,我们就只能通过 AnnotationConfig 上下文来获取容器,通过配置类的class对象加载! ApplicationContext context = new AnnotationConfigApplicationContext(UserConfig.class); //方法名就是bean的名字 User getUser = (User) context.getBean("getUser"); System.out.println(getUser.getName()); } }