一、什么是Spring?
1、简介
Spring框架是由于软件开发的复杂性而创建的。Spring使用的是基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅仅限于服务器端的开发。从简单性、可测试性和松耦合性角度而言,绝大部分Java应用都可以从Spring中受益。
Spring依赖导入:
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
</dependencies>
2、Spring的优点
- Spring是一个开源免费的框架(容器)!
- Spring是一个轻量级、非入侵式的框架。
- Spring的两大特点:IOC(控制翻转)和AOP(面向切面编程)!
3、Spring的七大模块
Spring有七大功能模块,分别是:
Spring Core,AOP,ORM,DAO,MVC,WEB,Content。
二、Spring IOC 推导
准备工作:写好各层的接口、实体等
pojo:User
package com.zyh.pojo;
public class User {
private String name;
public User() {
System.out.println("无参构造器");
}
public User(String name) {
this.name = name;
}
public void setName(String name) {
this.name = name;
}
public void show(){
System.out.println("name=" + this.name);
}
}
dao:UserMapper、UserMapperImp
//UserMapper
package com.zyh.dao;
public interface UserMapper {
public void getUser();
}
//UserMapperImp
package com.zyh.dao.daoimp;
import com.zyh.dao.UserMapper;
public class UserMapperImp implements UserMapper {
public void getUser() {
System.out.println("获取用户数据");
}
}
Service:UserService、UserServiceImp
//UserService
package com.zyh.service;
public interface UserService {
public void getUser();
}
//UserServiceImp
package com.zyh.service.serviceimp;
import com.zyh.dao.UserMapper;
import com.zyh.dao.daoimp.UserMapperImp;
import com.zyh.service.UserService;
public class UserServiceImp implements UserService {
private UserMapper userMapper = new UserMapperImp();
public void getUser() {
userMapper.getUser();
}
}
测试:
package com.zyh.dao;
import com.zyh.dao.daoimp.UserMapperImp;
import com.zyh.dao.daoimp.UserMapperMysqlImp;
import com.zyh.service.UserService;
import com.zyh.service.serviceimp.UserServiceImp;
import org.junit.Test;
public class TestIOC {
@Test
public void test1(){
UserService userService = new UserServiceImp();
userService.getUser();
}
}
执行结果:
这是我们的原生代码,可以获取到当前数据,但是现在如果用户的需求发生了改变,不需要获取当前的数据,而是在接口层新增了一个新的接口UserMapperMysqlIml
,那么我们就需要再去service
层再去修改代码呢,这就不符合我们现在Spring开发的思想,在Spring中我们说怎么去做呢?
- 修改Service层的代码,增加一个
setUserMapper()
方法,让用户可以自己设置要获取的数据源。
package com.zyh.service.serviceimp;
import com.zyh.dao.UserMapper;
import com.zyh.dao.daoimp.UserMapperImp;
import com.zyh.service.UserService;
public class UserServiceImp implements UserService {
private UserMapper userMapper = new UserMapperImp();
public void getUser() {
userMapper.getUser();
}
//用户自己设置要获取的数据来源
public void setUserMapper(UserMapper userMapper) {
this.userMapper = userMapper;
}
}
- 用户只需要在测试中直接调用这个
setUserMapper
的方法就可以了。
测试:
package com.zyh.dao;
import com.zyh.dao.daoimp.UserMapperMysqlImp;
import com.zyh.service.UserService;
import com.zyh.service.serviceimp.UserServiceImp;
import org.junit.Test;
public class TestIOC {
@Test
public void test1(){
UserMapperMysqlImp userMapperMysqlImp = new UserMapperMysqlImp();
UserService userService = new UserServiceImp();
//用户自己修改参数改变数据的来源,利用set实现了注入
((UserServiceImp) userService).setUserMapper(userMapperMysqlImp);
userService.getUser();
}
}
执行结果:
这两种做法的区别:
- 在最开始,程序中的是主动创建的,控制权在程序人员的手里。
- 使用set注入后,程序不在是主动的了,而是变成被动的接收。
什么是IOC?
IOC—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想。在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。 如何理解好Ioc呢?理解好Ioc的关键是要明确“谁控制谁,控制什么,为何是反转(有反转就应该有正转了),哪些方面反转了”,那我们来分析一下:
- 谁控制谁,控制什么:传统Java SE程序设计,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象;而IoC是有专门一个容器来创建这些对象,即由Ioc容器来控制对 象的创建;谁控制谁?当然是IoC 容器控制了对象;控制什么?那就是主要控制了外部资源获取(不只是对象包括比如文件等)。
- 控制反转是一种通过描述(XML配置或注解)并通过第三方去生产或获取对象的方式。在Spring中实现控制反转的是IOC容器,它的实现方法是依赖注入(Dependency Injection)DI。
三、Spring hello,world程序
Spring 中的配置:
通过Maven仓库导入Spring依赖和单元测试依赖
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
实体类Hello
程序,记得编写set
方法。
package com.zyh.pojo;
public class Hello {
private String name;
public Hello(){
Stste.out.println("无参构造器");
}
public void setName(String name) {
this.name = name;
}
public void show(){
System.out.println("hello,"+name);
}
}
编写Spring配置文件:applicationContext.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">
</beans>
编写applicationContext.xml
配置文件,实现对象注入
<!--一个bean就是一个对象
id对应的就是对象的名字
class就是要注入对象的模板类
property就是给对象的属性设值-->
<bean id="hello" class="com.zyh.pojo.Hello">
<property name="name" value="Spring"/>
</bean>
测试:
package com.zyh.pojo;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestHelloSpring {
@Test
public void test1(){
//读取Spring的配置文件
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//获取注入对象
Hello hello = (Hello) context.getBean("hello");
hello.show();
}
}
执行结果:
四、IOC创建对象的方式
1、IOC默认使用无参构造器进行创建对象。
2、IOC使用有参构造创建对象
实体类:
package com.zyh.pojo;
public class User {
private String name;
public User() {
System.out.println("User的无参构造");
}
public User(String name) {
System.out.println("User有参构造");
this.name = name;
}
public void setName(String name) {
System.out.println("使用了User setName()");
this.name = name;
}
public String getName() {
return name;
}
public void show(){
System.out.println("name=" + name);
}
}
方式一:通过下标
constructor-arg index=" "
给参数赋值
<bean id="user" class="com.zyh.pojo.User">
<!--index是参数的下标,0为第一个参数,value为给参数的赋值-->
<constructor-arg index="0" value="hang"/>
</bean>
方式二:通过参数类型type给参数赋值,但这种方式存在缺陷,若两个参数类型一样,就不能这样用了。
<bean id="user" class="com.zyh.pojo.User">
<constructor-arg type="java.lang.String" value="JoJo"/>
</bean>
方式三:使用参数名给参数赋值
<bean id="user" class="com.zyh.pojo.User">
<constructor-arg name="name" value="Fofo"/>
</bean>
总结:在Spring
的.xml
配置文件被加载的时候,bean
中的对象就已经被创建好了。
3、bean 中的 ref
若
bean
中的对象初始化创建对象的话,参数为基本类型,可以像下面这个例子一样出给参数赋值。
<bean id="Mysql" class="com.zyh.mapper.MysqlUserMapperImp">
<property name="" value=""/>
</bean>
但如果参数不是基本类型,就要通过
ref
来给参数赋值。
<bean id="SqlServer" class="com.zyh.mapper.SqlServerUserMapperImp"/>
<bean id="UserService" class="com.zyh.service.UserServiceImp">
<property name="userMapper" ref="SqlServer"/>
</bean>
五、Spring 配置
Spring
的配置参数只有四个,在上面我们已经见过了bean
,其他参数为:alias、description、import
。
1、bean
<bean id="" class="" name=""/>
id
是 bean 的唯一表示符,class
是这个对象模板的全限定名,name
可以给这个对象取一个别名。
2、alias 给bean中的对象取别名
<alias name="Mysql" alias="gg" />
name的值为bean的id值,
alias
的值就是别名。
3、import
这个参数一般用于团队开发,可以导入多个配置文件,并合并到一起。
在总的配置文件中导入.xml
配置
<import resource="beans1.xml"/>
<import resource="beans2.xml"/>
六、依赖注入(DI)
依赖:所有的
bean
对象的创建都依赖于Spring容器注入:
bean
对象中的所有属性,都通过Spring容器来注入
依赖注入的方式:
-
方式一:构造器注入
Spring 中默认使用无参构造创建对象并注入。
-
方式二:通过 set 方法注入
测试环境搭建:
两个实体类
//地址
package com.zyh.pojo;
public class Address {
private String address;
public String getAddress() {
return this.address;
}
public void setAddress(String address) {
this.address = address;
}
}
//学生
package com.zyh.pojo;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Properties;
public class Student {
private String name;
private Address address;
private String[] book;
private List<String> hobby;
private Map<String,String> card;
private Properties info;
private String scholarship;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public String[] getBook() {
return book;
}
public void setBook(String[] book) {
this.book = book;
}
public List<String> getHobby() {
return hobby;
}
public void setHobby(List<String> hobby) {
this.hobby = hobby;
}
public Map<String, String> getCard() {
return card;
}
public void setCard(Map<String, String> card) {
this.card = card;
}
public Properties getInfo() {
return info;
}
public void setInfo(Properties info) {
this.info = info;
}
public String getScholarship() {
return scholarship;
}
public void setScholarship(String scholarship) {
this.scholarship = scholarship;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", address=" + address +
", book=" + Arrays.toString(book) +
", hobby=" + hobby +
", card=" + card +
", info=" + info +
", scholarship='" + scholarship + '\'' +
'}';
}
}
在resources
目录下创建Spring配置文件applicationContext.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">
</beans>
注入信息:
<?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">
<beans>
<bean id="address" class="com.zyh.pojo.Address">
<property name="address" value="xian"/>
</bean>
<bean id="student" class="com.zyh.pojo.Student">
<!--1、普通值value注入-->
<property name="name" value="zyh"/>
<!--2、应用值ref注入-->
<property name="address" ref="address"/>
<!--3、数组注入-->
<property name="book">
<array>
<value>斗破苍穹</value>
<value>武动乾坤</value>
<value>大主宰</value>
<value>元尊</value>
</array>
</property>
<!--4、List-->
<property name="hobby">
<list>
<value>打篮球</value>
<value>跑步</value>
<value>俯卧撑</value>
</list>
</property>
<!--5、Map-->
<property name="card">
<map>
<entry key="xiaohu" value="001"/>
<entry key="xiaoming" value="002"/>
<entry key="xiaojian" value="003"/>
</map>
</property>
<!--6、Properties-->
<property name="info">
<props>
<prop key="account">20200226</prop>
<prop key="petName">jojo</prop>
</props>
</property>
<!--7、Null-->
<property name="scholarShip">
<null/>
</property>
</bean>
</beans>
</beans>
测试:
import com.zyh.pojo.Student;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
@Test
public void test1(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Student student = (Student) context.getBean("student");
// System.out.println(student.getName());
// System.out.println(student.getAddress());
System.out.println(student);
}
}
执行结果:
- 方式三:拓展方式注入:p命名空间和c命名空间注入
需要导入的xml约束:
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
实体类:User
package com.zyh.pojo;
public class User {
private int age;
private String name;
public User() {
}
public User(int age, String name) {
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
注入:
<beans>
<!--p命名空间注入-->
<bean id="user" class="com.zyh.pojo.User" p:name="zyh" p:age="20"/>
<!--c命名空间注入-->
<bean id="user2" class="com.zyh.pojo.User" c:name="hang" c:age="18"/>
</beans>
测试:
import com.zyh.pojo.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest2 {
@Test
public void test1() {
ApplicationContext context = new ClassPathXmlApplicationContext("userbeans.xml");
User user = context.getBean("user", User.class);
System.out.println(user);
User user2 = context.getBean("user2", User.class);
System.out.println(user2);
}
}
执行结果:
七、bean 的作用域
- 单例模式:singleton(默认)
<bean id="user2" class="com.zyh.pojo.User" c:name="hang" c:age="18" scope="singleton"/>
- 原型模式:prototype
<bean id="user2" class="com.zyh.pojo.User" c:name="hang" c:age="18" scope="prototype"/>
八、bean 的自动装配
- 自动装配是Spring满足bean依赖一种方式!
- Spring会在上下文中自动寻找,并自动给bean装配属性!
测试环境:
实体类:
//猫
package com.zyh.pojo;
public class Cat {
public void shut(){
System.out.println("miao ...");
}
}
//狗
package com.zyh.pojo;
public class Dog {
public void shut(){
System.out.println("wang ...");
}
}
//人
package com.zyh.pojo;
public class Person {
private String name;
private Dog dog;
private Cat cat;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Dog getDog() {
return dog;
}
public void setDog(Dog dog) {
this.dog = dog;
}
public Cat getCat() {
return cat;
}
public void setCat(Cat cat) {
this.cat = cat;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", dog=" + dog +
", cat=" + cat +
'}';
}
}
1、byName 自动装配
通过 byName 自动装配的时候,要保证装配的bean id和自动注入的set方法的值一致
<bean id="dog" class="com.zyh.pojo.Dog"/>
<bean id="cat" class="com.zyh.pojo.Cat"/>
<bean id="person" class="com.zyh.pojo.Person" autowire="byName">
<property name="name" value="hang"/>
</bean>
测试 :
import com.zyh.pojo.Person;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
@Test
public void test1() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Person person = context.getBean("person", Person.class);
person.getDog().shut();
person.getCat().shut();
}
}
执行结果:
2、 byType 自动装配
通过byType自动装配时,要保证这个被自动注入的bean的class唯一,类型要和被装配的一致。
<bean id="dog111" class="com.zyh.pojo.Dog"/>
<bean id="cat111" class="com.zyh.pojo.Cat"/>
<bean id="person" class="com.zyh.pojo.Person" autowire="byType">
<property name="name" value="hang"/>
</bean>
3、使用注解进行自动装配
使用注解进行自动装配需要导入约束和注解支持:
导入约束并开启注解支持
<?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"
xmlns:aop="http://www.springframework.org/schema/aop"
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
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--开启注解的支持-->
<context:annotation-config/>
@Autowired
在实体类中加注解:
@Autowired
private Dog dog;
@Autowired
private Cat cat;
测试:
import com.zyh.pojo.Person;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
@Test
public void test1() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Person person = context.getBean("person", Person.class);
person.getDog().shut();
person.getCat().shut();
}
}
执行结果:
如果 @Autowired 自动装配的环境较为复杂,就配合
@Qualifier(value = "dog2")
注解进行选定Bean对象。
<bean id="dog" class="com.zyh.pojo.Dog"/>
<bean id="dog3" class="com.zyh.pojo.Dog"/>
<bean id="dog2" class="com.zyh.pojo.Dog"/>
@Autowired
@Qualifier(value = "dog2")
private Dog dog;
@Resource
@Resource
private Dog dog;
@Resource
private Cat cat;
如果xml配置中没有默认名的bean对象,就会报错。
<!-- <bean id="dog" class="com.zyh.pojo.Dog"/>-->
<bean id="dog3" class="com.zyh.pojo.Dog"/>
<bean id="dog2" class="com.zyh.pojo.Dog"/>
<bean id="cat" class="com.zyh.pojo.Cat"/>
<bean id="cat2" class="com.zyh.pojo.Cat"/>
<bean id="cat3" class="com.zyh.pojo.Cat"/>
<bean id="person" class="com.zyh.pojo.Person">
</bean>
这时我们可以在 @Resource
中加一个属性name
让它去找到指定的 Bean 对象自动装配。
@Resource(name = "dog2")
private Dog dog;
小结:@Resource和@Autowired 的区别
- @Autowired 是通过byType的方式实现的,要求这个 bean 对象必须存在
- @Resource 默认是通过byName的方式实现的,如果找不到这个bean对象,就使用byType。
九、使用注解开发
在 Spring4 之后,使用注解进行开发,就需要导入 aop
的依赖,并且需要保证导入 context
支持。
<?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/>
<!--开启扫描包-->
<context:component-scan base-package="com.zyh"/>
</beans>
在 web 开发中,我们会按照MVC三层架构分层dao、service、controller
这几个层的衍生注解:
- dao :
@Repository
- service:
@Service
- controller:
@Controller
十、使用Java配置Spring
使用Java配置Spring后,我们就不再需要applicationContext.xml配合文件了。
实体类: @Component
package com.zyh.pojo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
//这个注解的意思就是这个类被Spring接管了,被注册到容器中
@Component
public class User {
private String name;
public String getName() {
return name;
}
@Value("hang")
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
配置类: @Configuration
package com.zyh.config;
import com.zyh.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
//这个注解意思也是这个类被Spring接管,注册到Spring容器中。
//代表这个类只一个配置类,就像之前之前的applicationContext.xml一样
@Configuration
@ComponentScan("com.zyh.pojo")
public class MyConfig {
//注册一个Bean,相当于我们之前在applicationContext.xml中写的<bean></bean>一样
@Bean
public User getUser(){
return new User();
}
}
测试: AnnotationConfigApplicationContext
import com.zyh.config.MyConfig;
import com.zyh.pojo.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MyTest {
@Test
public void test1(){
//通过AnnotationConfigApplicationContext去加载容器
ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
//配置类中的方法名就相当于bean的id,返回值就相当于bean的class
User user = context.getBean("getUser", User.class);
System.out.println(user.getName());
}
}
执行结果:
在以后学习SpringBoot的过程中,这种使用Java配置Spring的方式很常见。