一、Spring简介
Spring是什么
分层的Java SE/EE应用full-stack轻量级开源框架,以IOC(控制反转)和AOP(面向切面编程)为内核。
Spring优势
- 方便解耦,简化开发 将对象的创建权交给 Spring 容器,不用new一个对象了
- AOP编程支持 增强、动态代理啥的
- 声明式事务的支持 解放编程式事务,配置进行事务 控制
- 方便测试 集成了Junit
- 方便集成各种优秀框架 dao层的mybatis…..
- 降低API使用难度 像是封装 工具类,JDBC,JavaMail等创建了薄薄的封装层,降低了使用难度
- Java源码学习典范
Spring的体系结构
Spring快速入门
Spring开发步骤
导入坐标(pom.xml)
<dependencies> <!--导入spring的坐标--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.0.5.RELEASE</version> </dependency> <!--导入junit单元测试的坐标--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> </dependencies>编写Dao和实现类
//接口 package com.itheima; public interface UserDao { void save(); } //实现类 package com.itheima.impl; import com.itheima.UserDao; public class UserDaoImpl implements UserDao { @Override public void save() { System.out.println("save running"); } }创建Spring核心配置文件,在resource下,约定俗成:applicationContext.xml
在Spring配置文件中配置UserDaoImpl
<?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 class="com.itheima.impl.UserDaoImpl" id="userDao"></bean> </beans>使用Spring的API获得userDao对象
package com.itheima.test; import com.itheima.UserDao; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class UserDaoTest { @Test public void test01(){ ApplicationContext applicationContext=new ClassPathXmlApplicationContext("ApplicationContext.xml"); UserDao userDao = (UserDao) applicationContext.getBean("userDao"); userDao.save(); } }
Spring配置文件详解
Bean标签基本配置
<bean class="com.itheima.impl.UserDaoImpl" id="userDao"></bean>id:唯一标识
class:全限定名
Bean标签范围配置
Bean生命周期配置
init-method:初始化方法
destory-method:销毁方法
<bean class="com.itheima.impl.UserDaoImpl" id="userDao" scope="singleton" init-method="init" destroy-method="destory"></bean>
Bean实例化的三种方式
无参构造(默认)
<bean class="com.itheima.impl.UserDaoImpl" id="userDao" scope="singleton" init-method="init" destroy-method="destory"></bean>工厂静态方法 不需要有工厂对象,直接调用静态方法( factory-method=”la”)
<bean class="com.itheima.factory.StaticFactory" id="factory" factory-method="la"></bean>package com.itheima.factory; import com.itheima.UserDao; import com.itheima.impl.UserDaoImpl; public class StaticFactory { public static UserDao la(){ return new UserDaoImpl(); } }工厂实例方法 需要有工厂对象,
<bean class="com.itheima.factory.DynamicFactory" id="dynamicFactory" ></bean> <bean id="dao" factory-bean="dynamicFactory" factory-method="la"></bean>
Bean的依赖注入
依赖注入分析 直接在配置文件中将 userDao的对象设置给UserServiceImpl
给对象设置值的两种方式: 构造方法和set方法,再这里用set
依赖注入DI概念 spring核心IOC的具体表现
降低代码中的依赖关系。例如:service需要dao,可以把dao的创建交给spring来完成。坐等框架把持久层传入业务层,不需要自己去获取
依赖注入的两种方式
构造方法(有参)
serviceImpl类中
package com.itheima.service.impl; import com.itheima.dao.impl.UserDaoImpl; import com.itheima.service.UserService; public class UserServiceImpl implements UserService { private UserDaoImpl userDao; public UserServiceImpl(UserDaoImpl userDao) { this.userDao = userDao; } public UserServiceImpl() { } @Override public void save() { userDao.save(); } }配置文件中 name指的是UserServiceImpl里面的有参构造方法方法的名字
<bean class="com.itheima.dao.impl.UserDaoImpl" id="userDao"></bean> <bean class="com.itheima.service.impl.UserServiceImpl" id="userService"> <constructor-arg name="userDao" ref="userDao"/> </bean>
set方法
Tips:直接在Controller里面获取 UserService userService=new UserServiceImpl();执行会报空指针,因为只有spring里面的UserServiceImpl才被注入了userDao
serviceImpl类中
private UserDaoImpl userDao; public void setUserDao(UserDaoImpl userDao) { this.userDao = userDao; }配置文件中 name指的是UserServiceImpl里面的set方法的名字,即属性名称
<bean class="com.itheima.dao.impl.UserDaoImpl" id="userDao"></bean> <bean class="com.itheima.service.impl.UserServiceImpl" id="userService"> <property name="userDao" ref="userDao"></property> <!-- name指的是UserServiceImpl里面的set方法的名字,即属性名称 --> </bean>
Bean依赖注入的数据类型
普通数据类型
引用
集合
userDaoImpl
package com.itheima.dao.impl; import com.itheima.dao.UserDao; import com.itheima.user.User; import java.util.List; import java.util.Map; import java.util.Properties; public class UserDaoImpl implements UserDao { private String username; private int age; private List<String> stringList; private Map<String, User> userMap; private Properties properties; public UserDaoImpl(String username, int age, List<String> stringList, Map<String, User> userMap, Properties properties) { this.username = username; this.age = age; this.stringList = stringList; this.userMap = userMap; this.properties = properties; } public UserDaoImpl() { } @Override public void save() { System.out.println("我叫"+username+"今年"+age+"岁了"+properties+userMap+stringList); } }applicationContext.xml中
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:p="http://www.springframework.org/schema/p" 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 class="com.itheima.user.User" id="user"> <property name="name" value="郭硕"/> <property name="addr" value="山西省长治市武乡县"/> </bean> <bean class="com.itheima.user.User" id="user2"> <property name="name" value="gss"/> <property name="addr" value="迪拜"/> </bean> <bean class="com.itheima.dao.impl.UserDaoImpl" id="userDao"> <constructor-arg name="username" value="guoshuo"/> <constructor-arg name="age" value="21"/> <constructor-arg name="stringList"> <list> <value>"gs"</value> <value>"hr"</value> <value>"zjt"</value> </list> </constructor-arg> <constructor-arg name="userMap"> <map> <entry key="1" value-ref="user"></entry> <entry key="2" value-ref="user2"></entry> </map> </constructor-arg> <constructor-arg name="properties"> <props> <prop key="1">"ppp1"</prop> <prop key="2">"ppp2"</prop> <prop key="3">"ppp3"</prop> </props> </constructor-arg> </bean> <bean class="com.itheima.service.impl.UserServiceImpl" id="userService"> <constructor-arg name="userDao" ref="userDao"/> </bean> </beans>
引入其他配置文件 ——–分模块开发
<import resource="applicationContext-product.xml"/>小结
Spring相关的API
二、Spring配置数据源(连接池)
2.1 连接池的作用
- 提高程序性能
- 事先实例化数据源,初始化部分连接资源
- 使用连接资源时从数据源获取
- 使用完毕归还数据源
2.2 数据源开发步骤&&抽取配置文件
导入坐标(数据源和数据库驱动)
<dependencies>
<!--spring数据源-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<!-- 数据驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.12</version>
</dependency>
<!-- 数据源-->
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.1</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
<!-- junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
创建对象
设置基本参数信息
使用和归还
//手动创建c3p0数据源
@Test
public void test01() throws PropertyVetoException, SQLException {
//设置数据源信息
ComboPooledDataSource dataSource=new ComboPooledDataSource();
dataSource.setDriverClass("com.mysql.cj.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8&useSSL=false&allowPublicKeyRetrieval=true");
dataSource.setUser("root");
dataSource.setPassword("111111");
//获取资源
Connection connection = dataSource.getConnection();
System.out.println(connection);
connection.close();
}
//手动创建druid
@Test
public void test02() throws PropertyVetoException, SQLException {
//设置数据源信息
DruidDataSource dataSource=new DruidDataSource();
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8&useSSL=false&allowPublicKeyRetrieval=true");
dataSource.setUsername("root");
dataSource.setPassword("111111");
//获取资源
Connection connection = dataSource.getConnection();
System.out.println(connection);
connection.close();
}
//手动创建c3p0数据源(加载properties配置文件)
@Test
public void test03() throws PropertyVetoException, SQLException {
//读取配置文件getBundle("基名专门读取properties文件,不用后缀")
ResourceBundle properties = ResourceBundle.getBundle("jdbc");
String driver = properties.getString("jdbc.driver");
String url = properties.getString("jdbc.url");
String username = properties.getString("jdbc.username");
String password = properties.getString("jdbc.password");
ComboPooledDataSource dataSource=new ComboPooledDataSource();
dataSource.setDriverClass(driver);
dataSource.setJdbcUrl(url);
dataSource.setUser(username);
dataSource.setPassword(password);
//获取资源
Connection connection = dataSource.getConnection();
System.out.println(connection);
connection.close();
}
//手动创建druid数据源(加载properties配置文件)
@Test
public void test04() throws PropertyVetoException, SQLException {
//读取配置文件getBundle("基名专门读取properties文件,不用后缀")
ResourceBundle properties = ResourceBundle.getBundle("jdbc");
String driver = properties.getString("jdbc.driver");
String url = properties.getString("jdbc.url");
String username = properties.getString("jdbc.username");
String password = properties.getString("jdbc.password");
DruidDataSource dataSource=new DruidDataSource();
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
//获取资源
Connection connection = dataSource.getConnection();
System.out.println(connection);
connection.close();
}
1.3 📝Spring配置数据源—在applicationContext.xml中加载jdbc.properties配置文件
在applicationContext.xml中引入context命名空间和约束路径
加载properties的标签不在bean的命名空间下,在context空间下
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
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
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:property-placeholder location="classpath:jdbc.properties"/>
<bean class="com.mchange.v2.c3p0.ComboPooledDataSource" id="dataSource">
<property name="driverClass" value="${jdbc.driver}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
</beans>
//在applicationContext.xml中加载jdbc.properties配置文件
@Test
public void test05() throws PropertyVetoException, SQLException {
ApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml");
ComboPooledDataSource dataSource = app.getBean(ComboPooledDataSource.class);
Connection connection = dataSource.getConnection();
System.out.println(connection);
connection.close();
}
三、Spring注解开发
3.1📝 Spring原始注解
注解开发的时候,使用@Autowired+@Qualifier(“dao”),后面的变量不用给其设置set方法
@Autowired//按照数据类型从Spring容器中进行匹配,只有一个同类型的对象好用
@Qualifier(“dao”)//按照id名称从容器匹配, @Qualifier(“dao”)要结合@Autowired一起使用
@Resource(name = “dao”)//按照id名称,相当于@Qualifier(“dao”)+@Autowired
@Scope:控制单例还是多例
@PostConstruct:标注初始化的方法
@PreDestroy:标注销毁方法
package com.itheima.service.impl;
import com.itheima.dao.UserDao;
import com.itheima.service.UserService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
//<!-- <bean name="service" class="com.itheima.service.impl.UserServiceImpl" p:userDao-ref="dao"/>-->
///>
//@Component("service")
@Service("service")
public class UserServiceImpl implements UserService {
@Value("${jdbc.driver}")
private String driver;
// p:userDao-ref="dao"
// @Autowired
// @Qualifier("dao")
@Resource(name = "dao")
private UserDao userDao;
// public void setUserDao(UserDaoImpl userDao) {
// this.userDao = userDao;
// }
@Override
public void save() {
System.out.println(driver);
userDao.save();
}
@PostConstruct
public void init(){
System.out.println("service对象的初始化方法");
}
@PreDestroy
public void destory(){
System.out.println("sevice对象销毁");
}
}
3.2 Spring新注解
旧的注解不能全部的替代掉xml配置,对于那些非自定义的bean****、加载properties文件、组件扫描,引入其他xml文件****束手无策,
//<context:property-placeholder location=”classpath:jdbc.properties”/>
@PropertySource(“classpath:jdbc.properties”)
@Bean(“dataSource”)//spring会将当前方法返回值以指定名称存储到容器
@Configuration//标志该类是spring的核心配置类
@Import({DataSourceConfiguration.class})//加载分配置文件
//<context:component-scan base-package=”com.itheima”/>
**@ComponentScan(basePackages = “com.itheima”)**组件扫描(注解开发的必备)
核心配置类
package com.itheima.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration//标志该类是spring的核心配置类
//<context:component-scan base-package="com.itheima"/>
@ComponentScan(basePackages = "com.itheima")
@Import({DataSourceConfiguration.class})//加载分配置文件
public class SpringConfigure {
}
数据源配置类(分)
package com.itheima.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
import javax.sql.DataSource;
//<context:property-placeholder location="classpath:jdbc.properties"/>
@PropertySource("classpath:jdbc.properties")
public class DataSourceConfiguration {
//在@Value里面可以像在xml配置文件里一样使用el表达式获取Spring容器里面的的文件内容
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Bean("dataSource")//spring会将当前方法返回值以指定名称存储到容器
public DataSource getDatasource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
}
四、Spring整合junit
4.1 原始Junit测试的问题
这俩行代码不可以轻易的删掉
ApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml");
ComboPooledDataSource dataSource = app.getBean(ComboPooledDataSource.class);
4.2 解决方案
- 让SpringJunit负责创建 Spring容器,将配置文件名称告诉他
- 将需要测试的Bean直接注入
4.3开发步骤
导入坐标(spring集成junit)spring-test+junit
<!-- 导入spring集成Junit的包--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.0.5.RELEASE</version> </dependency> <!-- junit--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency>使用@Runwith注解去替代原来的运行期
@RunWith(SpringJUnit4ClassRunner.class)使用@ContextConfiguration指定配置文件或者配置类
使用@Autowired注入需要测试的对象
创建测试方法进行测试
指定配置文件
//需要在原来的配置文件里面加上这些
//<bean class="com.itheima.dao.impl.UserDaoImpl" id="userDao"/>
// <bean name="userService" class="com.itheima.service.impl.UserServiceImpl" //p:userDao-ref="userDao">
// </bean>
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
//@ContextConfiguration(classes = SpringConfigure.class)
public class SpringJunitTest {
@Autowired
private UserService userService;
@Autowired
private DruidDataSource dataSource;
public void setUserService(UserService userService) {
this.userService = userService;
}
@Test
public void test01() throws SQLException {
userService.save();
System.out.println(dataSource.getConnection());
}
}
指定配置类
@RunWith(SpringJUnit4ClassRunner.class)
//@ContextConfiguration("classpath:applicationContext.xml")
@ContextConfiguration(classes = SpringConfigure.class)
public class SpringJunitTest {
@Autowired
private UserService userService;
@Autowired
private DataSource dataSource;
@Test
public void test01() throws SQLException {
userService.save();
System.out.println(dataSource.getConnection());
}
}
五、Spring集成WEB环境
5.1环境搭建
JavaEE项目+servlet支持+三层架构+在web.xml里面配置servlet
<servlet>
<servlet-name>userServlet</servlet-name>
<servlet-class>com.itheima.controller.UserServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>userServlet</servlet-name>
<url-pattern>/userServlet</url-pattern>
</servlet-mapping>
5.2 弊端和解决(白学,,仅仅是个例子,有封装)
ApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = app.getBean(UserService.class);
用一次创建一次,配置文件加载多次,ApplicationContext的对象创建多次
解决:在Web项目中,使用ServletContextListener监听Web应用的启动,一启动就加载Spring的配置文件,创建ApplicationContext的对象,将其存储到最大的域servletContext域中,这样就可以在任意位置从域中获取ApplicationContext的对象了
编写监听器 一个继承了ServletContextListener的类
package com.itheima.listener; import com.itheima.service.UserService; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; public class ContextLoaderListener implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent sce) { //获取应用上下文对象 ApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml"); //将Spring的应用上下文对象存储到servletContext中 ServletContext servletContext = sce.getServletContext(); servletContext.setAttribute("app",app); } @Override public void contextDestroyed(ServletContextEvent sce) { ServletContextListener.super.contextDestroyed(sce); } }配置监听器 web.xml中
<!-- 配置监听器--> <listener> <listener-class>com.itheima.listener.ContextLoaderListener</listener-class> </listener>在servlet里面获取对象即可
package com.itheima.controller; import com.itheima.service.UserService; import org.springframework.context.ApplicationContext; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; public class UserServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //ServletContext servletContext1 = this.getServletContext(); ServletContext servletContext = req.getServletContext(); ApplicationContext app = (ApplicationContext) servletContext.getAttribute("app"); UserService userService = app.getBean(UserService.class); userService.save(); } }优化
将监听器中获取配置文件 的文件名字解耦 在web.xml里面配置
//获取应用上下文对象
ApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml");
<!-- 全局初始化参数--> <context-param> <param-name>contextConfigureLocation</param-name> <param-value>applicationContext.xml</param-value> </context-param>- 在web层获取对象需要知道属性名字 servletContext.getAttribute(“app”);
创建一个工具类,直接返回ApplicationContext对象
package com.itheima.listener; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import javax.servlet.ServletContext; public class WebApplicationUtils { public static ApplicationContext getWebApplicationContext(ServletContext servletContext){ return (ApplicationContext) servletContext.getAttribute("app"); } }
5.3 Spring提供的获取应用上下文的工具
导入坐标 pom.xml
<!-- 监听器--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>5.0.5.RELEASE</version> </dependency>配置监听器 web.xml
<!-- 全局初始化参数--> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param> <!-- 配置监听器--> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>使用












