目录
一.Spring简介
什么是框架:半成品软件
框架的作用:
- 提高开发效率
- 增强可重用性
- 提供编写规范
- 节约维护成本
- 解耦底层实现原理
Spring是分层的JavaSE/EE应用full-stack轻量级开源框架
1.1Spring体系结构
底层是核心容器:
- Beans
- Core
- Context
- SpringEI表达式
中间层技术:
- AOP
- Aspects
应用层技术:
- 数据访问与数据基础
- Web集成
- Web实现
基于Test测试
二.IoC简介
耦合:代码书写过程中所使用技术的结合紧密度,也就是各模块间的互联程度
内聚:代码书写过程中单个模块内部各组成部分间的联系,也就是各功能模块内部的功能联系
目标:高内聚,低耦合
IoC:控制反转,Spring反向控制应用程序所需要使用的外部资源
Spring控制的资源全部放置在Spring容器中,该容器称为IoC容器
2.1实现案例(xml配置)
1.现在pom.xml中导入Spring核心依赖包
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.14</version>
</dependency>
2.编写业务层接口
public interface UserService {
void save();
}
3.编写实现类
public class UserServiceImpl implements UserService {
@Override
public void save() {
System.out.println("save");
}
}
4.建立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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd>
<!-- 配置spring控制的资源-->
<!-- 将userService注册到Spring容器中,交给Spring容器管理-->
<bean id="userService" class="com.seehope.service.impl.UserServiceImpl"/>
</beans>
5.创建测试类
public class Test {
public static void main(String[] args) {
//加载配置文件
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
//获取资源
UserService userService=(UserService)applicationContext.getBean("userService");
userService.save();
}
2.2 应用案例
Tomcat作为一个容器管理Servlet,Spring是作为一个容器管理所有的组件
1.首先在ioc文件下建立mocktomcat文件,再建立MockHttpServlet类,mock是伪造的意思
表示创建一个伪造的mockServlet标准
public class MockHttpServlet {
public void init(){
}
public void doService(){
}
public void destory(){
}
}
2.创建MockTomcat类,为主程序
public class MockTomcat {
public static void main(String[] args) {
}
}
3.创建两个MockServlet,并继承MockHttpServlet
public class MockServlet1 extends MockHttpServlet {
@Override
public void init() {
System.out.println("init MockServlet1");
}
@Override
public void doService() {
System.out.println("doService MockServlet1");
}
@Override
public void destory() {
System.out.println("destory MockServlet1");
}
}
public class MockServlet2 extends MockHttpServlet{
@Override
public void init() {
System.out.println("init MockServlet2");
}
@Override
public void doService() {
System.out.println("doService MockServlet2");
}
@Override
public void destory() {
System.out.println("destory MockServlet2");
}
}
4.在resources里创建applicationContext.xml配置文件,类似于web.xml文件
bean的生命周期:
init-method:定义bean对象在初始化
destroy-method:定义bean对象销毁
取值为bean对应的类中对应的具体方法名
bean的scope属性:
< bean scope=“singleton”></ bean>
取值:
singleton:设定创建出的对象保存在spring容器中,是一个单例对象,为scope的默认值
prototype:设定创建出的对象保存在spring容器中,是一个非单例的对象
request、session、application、 websocket :设定创建出的对象放置在web容器对应的位
置
<?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">
<!--将mockServlet1组件注册到Spring容器进行管理-->
<bean init-method="init" destroy-method="destory"
scope="prototype" <!--非单例的-->
id="mockServlet1" class="net.milkytea.ioc.mocktomcat.MockServlet1">
</bean>
<bean init-method="init" destroy-method="destory"
id="mockServlet2" class="net.milkytea.ioc.mocktomcat.MockServlet2">
</bean>
</beans>
5.在main方法中读取
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MockTomcat {
public static void main(String[] args) {
//读取xml文件
//spring容器的创建,读取类路径下的xml文件
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
//通过id获取后通过多态接回来
MockHttpServlet httpServlet1 = (MockHttpServlet) applicationContext.getBean("mockServlet1");
MockHttpServlet httpServlet2 = (MockHttpServlet) applicationContext.getBean("mockServlet2");
MockHttpServlet httpServlet11 = (MockHttpServlet) applicationContext.getBean("mockServlet1");
MockHttpServlet httpServlet22 = (MockHttpServlet) applicationContext.getBean("mockServlet2");
httpServlet1.doService();
httpServlet2.doService();
System.out.println(httpServlet1);
System.out.println(httpServlet11);
//httpServlet2为单例的,所以输出的地址相同
System.out.println(httpServlet2);
System.out.println(httpServlet22);
}
}
spring相当于一个大型工厂,自己的组件可以交给spring管理,同时可以把别人的组件注册到spring中,在工程中使用别人的组件实现目的,就不用再new了,所有组件在工厂里注册和装配。
2.3 依赖注入案例
dependency inject 依赖注入
可以给成员变量赋予初始化的值
1.先创建MockServlet3,并继承MockHttpServlet,同时创建成员变量,并提供getset方法
public class MockServlet3 extends MockHttpServlet{
private String username;
private int age;
private List<Integer> ids;
private Map<String,MockHttpServlet> map;
private MockHttpServlet mockHttpServlet;
@Override
public void init() {}
@Override
public void doService() {}
@Override
public void destory() {}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public List<Integer> getIds() {
return ids;
}
public void setIds(List<Integer> ids) {
this.ids = ids;
}
public Map<String, MockHttpServlet> getMap() {
return map;
}
public void setMap(Map<String, MockHttpServlet> map) {
this.map = map;
}
public MockHttpServlet getMockHttpServlet() {
return mockHttpServlet;
}
public void setMockHttpServlet(MockHttpServlet mockHttpServlet) {
this.mockHttpServlet = mockHttpServlet;
}
}
2.在xml中注册
通过配置文件完成装配,只要修改配置文件就可以更改装配的关系,实现解耦
<bean init-method="init" destroy-method="destory"
id="mockServlet3" class="net.milkytea.ioc.mocktomcat.MockServlet3">
<!--通过getset方法赋值-->
<!--property在init方法之后调用getset方法-->
<property name="username" value="123"/><!--八大基本类型赋值-->
<property name="age" value="12"/>
<property name="ids">
<list>
<value>1</value>
<value>2</value>
<value>3</value>
</list>
</property>
<!--一个组件给另一个组件的成员变量-->
<property name="mockHttpServlet" ref="mockServlet1">
<!--插入引用类型,该引用类型必须已经在容器中注册好,通过ref引用过来,这里也可以修改为mockServlet2-->
</property>
<property name="map">
<map>
<entry key="key1" value-ref="mockServlet1"></entry> <!--八大基本类型用value,引用类型用ref,key是string类型-->
</map>
</property>
</bean>
法二:
通过构造器给值
< constructor-arg name="" value=""/>
此时要在User类给出Constructor
3.在main中输出
//spring容器的创建,读取类路径下的xml文件
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
//要调用getset方法所以要用子类
MockServlet3 mockServlet3 = (MockServlet3) applicationContext.getBean("mockServlet3");
System.out.println(mockServlet3.getUsername());
System.out.println(mockServlet3.getAge());
mockServlet3.getIds().forEach(key->{
System.out.println(key);
});
mockServlet3.getMockHttpServlet().doService();
mockServlet3.getMap().keySet().forEach(key->{
System.out.println("key:"+key+",value:"+mockServlet3.getMap().get(key));
});
2.4 模拟工程案例
1.创建mapper文件,并创建UserMapper类
public class UserMapper {
public void sayHello(){
System.out.println("hello");
}
}
2.创建pojo文件,并创建User类
import java.io.Serializable;
public class User implements Serializable {
private String username;
private String password;
public User() {
}
public User(String username, String password) {
this.username = username;
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
3.创建service文件,并创建接口UserService
public interface UserService {
public void doService();
}
4.在service文件下创建impl文件,并创建UserServiceImpl类
import net.milkytea.ioc.mapper.UserMapper;
import net.milkytea.ioc.service.UserService;
public class UserServiceImpl implements UserService {
UserMapper userMapper;
@Override
public void doService() {
userMapper.sayHello();
}
//通过get set方法装配UserMapper
public UserMapper getUserMapper() {
return userMapper;
}
public void setUserMapper(UserMapper userMapper) {
this.userMapper = userMapper;
}
}
5.创建web.Controller文件,并创建UserController类
import net.milkytea.ioc.service.UserService;
public class UserController {
private UserService userService;
//处理请求
public void handleRequest(){
userService.doService();
}
//通过get set方法装配UserService
public UserService getUserService() {
return userService;
}
public void setUserService(UserService userService) {
this.userService = userService;
}
}
6.在applicationContext.xml下装配
<!--把每个层级放到容器中,装配-->
<bean id="userController" class="net.milkytea.ioc.web.controller.UserController">
<property name="userService" ref="userService">
<!--完成装配-->
</property>
</bean>
<bean id="userService" class="net.milkytea.ioc.service.impl.UserServiceImpl">
<property name="userMapper" ref="userMapper"></property>
</bean>
<bean id="userMapper" class="net.milkytea.ioc.mapper.UserMapper">
</bean>
7.Test类
import net.milkytea.ioc.web.controller.UserController;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
UserController userController=(UserController)applicationContext.getBean("userController");
userController.handleRequest();
}
}
2.5 模拟工程案例2(2.4升华版)
使用注解
基于注解的装配,不用写getset方法
1.在applicationContext.xml文件中开启注解扫描
<!--用注解完成注册和装配-->
<!--开启注解扫描范围
可以使用通配符 例如 net.milkytea
也空以同时对多个包开启扫描 例如 net.milkytea,org.spring-->
<context:component-scan base-package="net.milkytea"/>
2.在UserController类前添加注解,并装配,完成自动注入
import net.milkytea.ioc.service.UserService;
import org.springframework.stereotype.Controller;
//把该类交给spring控制,完成注册,注册时默认以该类名字首字母小写作为bean的id
@Controller("userController")
public class UserController {
@Autowired
private UserService userService;
//处理请求
public void handleRequest(){
userService.doService();
}
//通过get set方法装配UserService
public UserService getUserService() {
return userService;
}
public void setUserService(UserService userService) {
this.userService = userService;
}
}
3.在UserServiceImpl类前添加注解,并添加装配@Autowired
@Service
public class UserServiceImpl implements UserService {
@Override
public void save() {
System.out.println("save");
}
@Autowired//自动注入
UserMapper userMapper;
@Override
public void doService() {
userMapper.sayHello();
}
//通过get set方法装配UserMapper
public UserMapper getUserMapper() {
return userMapper;
}
public void setUserMapper(UserMapper userMapper) {
this.userMapper = userMapper;
}
}
4.在UserMapper类前添加注解
@Repository
public class UserMapper {
public void sayHello(){
System.out.println("hello");
}
}
2.6 如何完成自动装配
完成自动装配注入,必须加上@Autowired,否则会报bug
案例:
在impl文件下创建UserServiceImpl2
通过在类前添加注解,也把该类添加到spring容器中
所以此时容器中有两个UserService的实现
@Service
public class UserServiceImpl2 extends UserService {
@Override
public void save() {
}
@Override
public void doService() {
}
}
此时就会出现异常
如何实现自动装配:
先按照类型装配
调用getBeanOfType接口
applicationContext.getBeanOfType(UserService.class)
若该查询结果只有一个,则直接注入
若多个结果,则进一步匹配变量名字
若变量名和bean名字是一致的,则注入
当有多个结果时
法一:
修改UserController 类中代码:
将UserService该成userServiceImpl
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller("userController") //把该类交给spring控制,完成注册,注册时默认以该类名字首字母小写作为bean的id
public class UserController {
@Autowired
private UserService userServiceImpl;
//处理请求
public void handleRequest(){
userServiceImpl.doService();
}
//通过get set方法装配UserService
public UserService getUserService() {
return userServiceImpl;
}
public void setUserService(UserService userService) {
this.userServiceImpl = userService;
}
}
法二:
不修改名字
直接加入注解@Qualifier,指定装配的名字
@Controller("userController") //把该类交给spring控制,完成注册,注册时默认以该类名字首字母小写作为bean的id
public class UserController {
@Autowired
@Qualifier("userServiceImpl")
private UserService userService;
//处理请求
public void handleRequest(){
userService.doService();
}
//通过get set方法装配UserService
public UserService getUserService() {
return userService;
}
public void setUserService(UserService userService) {
this.userService = userService;
}
}
2.7 注解解析
@Controller:控制器
@Service:业务层
@Repository:数据模型层 仓库 相当于Dao类
@configuration:配置文件
@Component:除此之外、前提,除了控制器,业务器,仓库之外,其他组件用改注解,他是所有注解的父类
以上注解功能相同,都是将被标注的类注册到容器中
@Autowired:首先通过类型匹配,如果有且只有一个,那就直接注入,如果找到多个匹配的bean,那么判断bean的id和成员变量的名字是否一致,如果一致则注入,否则报bug
@Qualifier:如果同类型有多个匹配,指定具体bean的id