文章目录
1. 简介
Spring就是一个轻量级的控制反转(IOC) 和面向切面编程(AOP)的框架!
Spring目的:解决企业应用开发的复杂性
2. IOC理论推导
传统new对象的逻辑开发:
dao—>daoImpl—>service—>serviceImpl
如果用户想要切换数据库实现,所有的主动权在程序员的手上,需要修改程序。
所以要控制反转:原来程序员的主动权要交给用户;需要对外暴露接口(set方法)
控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法
(1)在pom.xml 中导入junit 测试依赖
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
(2)在dao层创建接口,以及接口的实现类
- UserMapper接口
package com.zz.dao;
//关于用户的接口
public interface UserMapper {
public void getUser();
}
- 接口的实现类UserMapperImpl
package com.zz.dao.impl;
import com.zz.dao.UserMapper;
public class UserMapperImpl implements UserMapper {
public void getUser() {
System.out.println("获取用户数据");
}
}
- 接口的实现类UserMapperMysqlImpl
package com.zz.dao.impl;
import com.zz.dao.UserMapper;
public class UserMapperMysqlImpl implements UserMapper {
public void getUser() {
System.out.println("Myaql获取用户数据");
}
}
(3)同样在service层(调用dao层)创建接口,以及接口的实现类
- UserService接口
package com.zz.service;
//用户业务层,本质调用dao层(获取数据),然后在业务层执行操作
public interface UserService {
public void getUser();
}
- 接口的实现类UserServiceImpl
package com.zz.service.impl;
import com.zz.dao.UserMapper;
import com.zz.service.UserService;
public class UserServiceImpl implements UserService {
//调用dao层
private UserMapper userMapper;
//set方法 只需要对外提供接口 程序不用管理实现
public void setUserMapper(UserMapper userMapper) {
this.userMapper = userMapper;
}
public void getUser() {
userMapper.getUser();
}
}
(4)在test层,创建测试类
用户根据不同的需求进行修改测试类代码
- 调用UserMapperImpl实现类的方法
注释掉UserMapperMysqlImpl userMapper = new UserMapperMysqlImpl();这行代码即可
import com.zz.dao.impl.UserMapperImpl;
import com.zz.dao.impl.UserMapperMysqlImpl;
import com.zz.pojo.Hello;
import com.zz.pojo.Student;
import com.zz.service.impl.UserServiceImpl;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
//客户端
public class MyTest {
@Test
public void test1(){
//主动权交给用户 想用就去调用接口
UserMapperImpl userMapper = new UserMapperImpl();
//UserMapperMysqlImpl userMapper = new UserMapperMysqlImpl();
UserServiceImpl userService = new UserServiceImpl();
userService.setUserMapper(userMapper);
userService.getUser();
}
}
测试结果:
- 调用UserMapperMysqlImpl实现类的方法
注释掉UserMapperImpl userMapper = new UserMapperImpl();这行代码即可
import com.zz.dao.impl.UserMapperImpl;
import com.zz.dao.impl.UserMapperMysqlImpl;
import com.zz.pojo.Hello;
import com.zz.pojo.Student;
import com.zz.service.impl.UserServiceImpl;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
//客户端
public class MyTest {
@Test
public void test1(){
//主动权交给用户 想用就去调用接口
//UserMapperImpl userMapper = new UserMapperImpl();
UserMapperMysqlImpl userMapper = new UserMapperMysqlImpl();
UserServiceImpl userService = new UserServiceImpl();
userService.setUserMapper(userMapper);
userService.getUser();
}
}
测试结果:
3. HelloSpring
项目目录:
(1)在pom.xml 中导入spring-webmvc依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
(2)创建实体类 Hello
package com.zz.pojo;
public class Hello {
private String name;
public void setName(String name) {
this.name = name;
}
public void show(){
System.out.println("Hello,"+name);
}
}
注意:set方法必须要有
(3)在resources目录下创建配置文件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">
<!--注册到Spring Bean就是我们的对象
Hello hello = new Hello();
hello.setName();
id=对象
name=属性
value=属性值
-->
<bean id="hello" class="com.zz.pojo.Hello">
<property name="name" value="Spring"/>
</bean>
</beans>
(4)编写测试类测试
@Test
public void test2(){
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Hello hello = (Hello) context.getBean("hello");
hello.show();
}
运行结果:
- hello 对象是由Spring创建的
- hello 对象的属性是由Spring容器设置的
4. IOC创建对象的方式
4.1 使用无参构造创建对象(默认)
实体类中需要set方法,配置文件中通过property标签注入
(1)实体类
package com.zz.pojo;
public class User {
private String name;
public User() {
System.out.println("对象被创建了");
}
public void setName(String name) {
this.name = name;
}
public void show(){
System.out.println("name="+name);
}
}
(2)配置文件
<bean id="user" class="com.zz.pojo.User">
<property name="name" value="大佬"/>
</bean>
(3)测试类
@Test
public void test3(){
//在加载容器时,所有bean就已经被创建了
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
User user = (User) context.getBean("user");
user.show();
}
测试结果:
注意:在加载容器时,所有bean就已经被创建了
4.2 使用有参构造创建对象
实体类中需要有参构造方法,配置文件中通过constructor-arg标签注入
(1)实体类
package com.zz.pojo;
public class User {
private String name;
public User(String name) {
this.name = name;
}
public void show(){
System.out.println("name="+name);
}
}
(2)配置文件
<bean id="user" class="com.zz.pojo.User">
<!--有参构造注入-->
<constructor-arg name="name" value="大佬2"/>
</bean>
(3)测试类
与上面无参测试类相同
@Test
public void test3(){
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
User user = (User) context.getBean("user");
user.show();
}
测试结果:
5. Spring配置
5.1 别名
<!--别名,如果添加了别名,我们也可以使用别名获取到这个对象-->
<alias name="user" alias="userNew"/>
5.2 bean的配置
id : bean 的唯一标识符,也就是相当于我们学的对象名
class : bean 对象所对应的全限定名 : 包名 + 类型
name :也是别名,而且name 可以同时取多个别名
<bean id="user" name="user3 user4 user5" class="com.zz.pojo.User">
5.3 import标签
import一般用于团队开发使用,他可以将多个配置文件,导入合并为一个。
在applicationContext.xml文件中有如下代码,而没有Bean的配置
<import resource="spring-dao.xml"/>
Bean的配置写在spring-dao.xml文件中
<bean id="user" class="com.zz.pojo.User">
<constructor-arg name="name" value="大佬2"/>
</bean>
依然可以获取user对象,测试成功。
6. 属性注入
(1)创建实体类 Address 和Student
package com.zz.pojo;
public class Address {
private String address;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "Address{" +
"address='" + address + '\'' +
'}';
}
}
package com.zz.pojo;
import java.util.*;
public class Student {
private String name;
private Address address;
private String[] books;
private List<String> list;
private Map<String,String> map;
private Set<String> set;
private String wife; //null
private Properties info;
public void setName(String name) {
this.name = name;
}
public void setAddress(Address address) {
this.address = address;
}
public void setBooks(String[] books) {
this.books = books;
}
public void setList(List<String> list) {
this.list = list;
}
public void setMap(Map<String, String> map) {
this.map = map;
}
public void setSet(Set<String> set) {
this.set = set;
}
public void setWife(String wife) {
this.wife = wife;
}
public void setInfo(Properties info) {
this.info = info;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", address=" + address +
", books=" + Arrays.toString(books) +
", list=" + list +
", map=" + map +
", set=" + set +
", wife='" + wife + '\'' +
", info=" + info +
'}';
}
}
(2)配置文件student.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">
<bean id="addr" class="com.zz.pojo.Address">
<property name="address" value="安徽"/>
</bean>
<bean id="student" class="com.zz.pojo.Student">
<!--常量注入-->
<property name="name" value="大佬"/>
<!--引用类型 是对象用ref-->
<property name="address" ref="addr"/>
<!--数组类型-->
<property name="books">
<array>
<value>语文</value>
<value>数学</value>
<value>英语</value>
</array>
</property>
<!--list类型-->
<property name="list">
<list>
<value>list1</value>
<value>list2</value>
</list>
</property>
<!--map类型-->
<property name="map">
<map>
<entry key="k1" value="v1"/>
<entry key="k2" value="v2"/>
</map>
</property>
<!--set类型-->
<property name="set">
<set>
<value>set1</value>
<value>set2</value>
</set>
</property>
<!--null注入-->
<property name="wife">
<null/>
</property>
<!--properties类型-->
<property name="info">
<props>
<prop key="id">001</prop>
<prop key="name">zz</prop>
</props>
</property>
</bean>
</beans>
(3)测试类 MyTest
@Test
public void test4(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Student student = (Student) context.getBean("student");
System.out.println(student.toString());
}
测试结果:
bean的作用域:scope属性
singleton:单例,容器中只存心一个对象(默认)
prototype:非单例,每次创建新对象
<bean id="student" class="com.zz.pojo.Student" scope="prototype">
其余的 request、session、application这些只能在web开发中使用到。
7. 自动装配
手动装配:一个个赋值
自动装配:自动化赋值,不需要一个个赋值
7.1 测试环境搭建
一个人有两个宠物
(1)在java目录下,创建entity包,包下创建三个实体类User、Cat、Dog
package com.zz.entity;
public class Cat {
public void shout(){
System.out.println("miao...");
}
}
package com.zz.entity;
public class Dog {
public void shout(){
System.out.println("wang...");
}
}
package com.zz.entity;
public class User {
private String name;
private Cat cat;
private Dog dog;
public void setName(String name) {
this.name = name;
}
public void setCat(Cat cat) {
this.cat = cat;
}
public void setDog(Dog dog) {
this.dog = dog;
}
public String getName() {
return name;
}
public Cat getCat() {
return cat;
}
public Dog getDog() {
return dog;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", cat=" + cat +
", dog=" + dog +
'}';
}
}
7.2 byName自动装配
autowire="byName"会自动在容器上下文中查找,和自己对象set方法后面的值对应的 bean id
所以下列代码中,手动配置的那两行可以注释掉
user.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">
<!--cat和dog都是引用类型,且没有属性-->
<bean id="cat" class="com.zz.entity.Cat"/>
<bean id="dog" class="com.zz.entity.Dog"/>
<!--自动配置-->
<bean id="user" class="com.zz.entity.User" autowire="byName">
<property name="name" value="大佬"/>
<!--<property name="cat" ref="cat"/>-->
<!--<property name="dog" ref="dog"/>-->
</bean>
</beans>
MyTest测试类:
@Test
public void test5(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
User user = (User) context.getBean("user");
System.out.println(user);
user.getCat().shout();
user.getDog().shout();
}
测试结果:
7.3 byType自动装配
autowire="byType"会自动在容器上下文中查找,和自己对象属性类型相同的bean
只将上述代码中autowire="byName"改为autowire=“byType”,其余代码均与上面相同,测试结果也相同
但是如果这个类型有多个bean,就会报错
小结:
- byname的时候,需要保证所有bean的id唯一,并且这个bean需要和自动注入的属性的set方法的值一致!
- bytype的时候,需要保证所有bean的class唯一,并且这个bean需要和自动注入的属性的类型一致!
- 在真实的开发中,我们不这么用,而是使用注解实现自动配置
7.4 使用注解实现自动装配
要使用注解须知:
在applicationContext.xml中
-
导入约束 : context约束
xmlns:context=“http://www.springframework.org/schema/context”
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd"> -
配置注解的支持 : context:annotation-config/
添加后,完整的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"
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/>
<import resource="user.xml"/>
</beans>
(1)@Autowired注解
- 直接在属性上使用即可!也可以在set方式上使用!
- 使用Autowired 我们可以不用编写Set方法了,前提是你这个自动装配的属性在 IOC(Spring)容器中存在,且符合名字byname!
User类中删除了setCat和setDog方法,添加了@Autowired注解:
package com.zz.entity;
import org.springframework.beans.factory.annotation.Autowired;
public class User {
private String name;
@Autowired //自动装配
private Cat cat;
@Autowired
private Dog dog;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public Cat getCat() {
return cat;
}
public Dog getDog() {
return dog;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", cat=" + cat +
", dog=" + dog +
'}';
}
}
user.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">
<!--cat和dog都是引用类型,且没有属性-->
<bean id="cat" class="com.zz.entity.Cat"/>
<bean id="dog" class="com.zz.entity.Dog"/>
<!--自动配置-->
<bean id="user" class="com.zz.entity.User">
<property name="name" value="大佬"/>
</bean>
</beans>
测试类不变,依然能测出相同的结果
科普:@Nullable 字段标记了这个注解,说明这个字段可以为null;
(2)@Resource注解
public class People {
@Resource(name = "cat2")
private Cat cat;
@Resource
private Dog dog;
}
小结:
@Resource 和@ Autowired 的区别:
- 都是用来自动装配的,都可以放在属性字段上
- @ Autowired 通过byType的方式实现,而且必须要求这个对象存在! 【常用】
- @ Resource 默认通过byname的方式实现,如果找不到名字,则通过byType实现!如果两个都找不到的情况下,就报错! 【常用】
- 执行顺序不同:@ Autowired 通过byType的方式实现。@ Resource 默认通过byname的方式实现
8. 使用注解开发
(1)在Spring4之后,要使用注解开发,必须要保证 aop的包导入了
(2)使用注解需要导入context约束,增加注解的支持!
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"
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.zz.pojo"/>
</beans>
不用再编写people.xml 文件,不用进行bean标签之类的配置
(3)实体类People:
@Component(“people”) 相当于 : < bean id=“people” …/>
@Value("大哥”)相当于: < property name=“name” value=“大哥”/>
@Scope(“prototype”) 表示作用域,也分为原型模式prototype和单例模式singleton
package com.zz.pojo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component("people")
@Scope("prototype")
public class People {
@Value("大哥")
public String name;
//也可以放在set方法上
// @Value("大哥2")
public void setName(String name) {
this.name = name;
}
}
(4)测试类:
@Test
public void test6(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
People people = (People) context.getBean("people");
System.out.println(people.name);
}
测试结果:
衍生的注解:
@Component 有几个衍生注解,我们在web开发中,会按照mvc三层架构分层!
-
dao 【@Repository】
-
service 【@Service】
-
controller 【@Controller】
这四个注解功能都是一样的,都是代表将某个类注册到Spring中,装配Bean
小结:
xml 与 注解:
- xml 更加万能,适用于任何场合!维护简单方便
- 注解 不是自己类使用不了,维护相对复杂!
xml 与 注解最佳实践:
- xml 用来管理bean;
如: < bean id=“student” class=“com.zz.pojo.Student”/> - 注解只负责完成属性的注入;
如:@Value(“小明”)
private String name; - 我们在使用的过程中,只需要注意一个问题:必须让注解生效,就需要开启注解的支持