Spring简介
- 分层的Java SE/EE应用full-stack轻量级开源框架,以IOC(反转控制)和AOP(面向切面编程)为内核
- 能整合开源世界众多第三方框架和类库,逐渐成为使用最多的Java EE企业应用开源框架
- 优势:
- 方便解耦:通过IOC容器,将对象间的依赖关系交给Spring控制,避免编码的过度耦合
- AOP编程的支持
- 声明式事务支持:声明式灵活管理事务,减少不必要的事务管理的代码编写
- 方便测试
- 方便集成优秀框架
- 降低对Java EE API的使用难度
- 源码是学习典范
- Spring基本开发步骤:
- 导入jar包坐标
- 创建Javabean
- 创建配置文件applicationContext.xml
- 配置文件种进行配置
- 创建ApplicationContext对象调用getBean()方法
Spring的IOC开发
Spring配置文件
- Bean标签基本配置
- 用于配置的对象交给spring创建,默认情况下调用类中的无参构造,如果没有则创建失败
- 基本属性:
- id:Bean实例在Spring容器中的唯一标识
- class:Bean的全限定名称
- 其他属性:
- scope:对象的作用范围,singleton(单例的,默认值),prototype(多例的),request,session,global session
- 当scope属性为singleton时:
- 实例化时机:Spring核心文件被加载时(配置文件被加载时)
- 当scope属性为prototype时:
- 实例化时机:调用getBean()时
- 当scope属性为singleton时:
- init-method:指定类中的初始化方法名称
- destroy-method:指定类中的销毁方法名称
- scope:对象的作用范围,singleton(单例的,默认值),prototype(多例的),request,session,global session
- Bean实例化三种方式:
- 无参构造方法实例化(掌握):
<bean id="userDao" class="com.itheima.domain.User"/>
- 工厂静态方法实例化(了解):
public class StaticFactoryBean{ public static UserDao createUserDao(){ return new UserDaoImpl(); } }
<bean id="userDao" class="com.itheima.factory.StaticFactoryBean" factory-method="createUserDao" />
- 工厂实例化方法实例化(了解):
public class DynamicFactoryBean{ public UserDao createUserDao(){ return new UserDaoImpl(); } }
<bean id="factoryBean" class="com.itheima.factory.DynamicFactoryBean"/> <bean id="userDao" factory-bean="factoryBean" factory-method="createUserDao"/>
- 无参构造方法实例化(掌握):
- Bean的依赖注入入门:
- 创建UserService,UserService内部调用UserDao的save()方法;
- 将UserServiceImpl的创建全交给Spring
- 从Spring容器中获得UserService进行操作
- 分析:因为UserService和UserDao都在Spring容器中,而最终程序直接使用的是UserService,所以可以在Spring容器中,将UserDao设置到UserService内部。
- Bean的依赖注入方式:
1.set方法注入:
UserServiceImpl
//在UserServiceImpl中添加setUserDao方法
pubic class UserServiceImpl implements UserService{
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}
applicationContext.xml
property 的 name 属性代表的是 userService 中 set 方法的后半段也就是 userDao
<!--在配置文件中配置-->
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"/>
<!-- name是set方法后半段的对应名字,ref是配置文件中的其他引用id -->
<bean id="userService" class="com.itheima.service.impl.UserServiceImpl">
<property name="userDao" ref="userDao"/>
</bean>
Controller
public class UserController {
public static void main(String[] args) {
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) app.getBean("userService");
userService.save();
}
}
注意:此时将UserDao的创建权交给了Spring,如果Controller层没有通过Spring配置文件创建UserService,而是通过自己new 一个 UserService,此时自己new 的UserService内部将没有通过set方法创建和注入DaoUser对象,因此使用UserDao.save方法会产生空指针异常
p命名空间注入(了解,属于set方式)
扩展:引入p命名空间写法,一般不常用
<!-- 引入p命名空间-->
xmlns:p="http://springframework.org/schema/p"
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl">
<!-- 修改注入方式,p:userDao-ref=引用的 Bean id-->
<bean id="userService" class="com.itheima.service.impl.UserServiceImpl" p:userDao-ref="userDao"/>
2.构造方法注入:
创建有参构造
<!-- 配置spring容器调用有参构造时进行注入-->
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"/>
<bean id="userService" class="com.itheima.service.impl.UserServiceImpl">
<constuctor-arg name="userDao" ref="userDao"/>
</bean>
- Bean的依赖注入的数据类型(这里都是使用set方式)
1.普通数据类型
例如String,在UserDaoImpl创建私有对象name和age,创建set方法
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl">
<property name="name" value="zhangsan"/>
<property name="age" value="18"/>
</bean>
2.引用数据类型(上面已有)
3.集合数据类型
例如list<String>
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl">
<property name="strList" value="zhangsan">
<list>
<value>123</value>
<value>123</value>
<value>123</value>
</list>
</property>
</bean>
例如map
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl">
<property name="userMap">
<map>
<entry key="u1" value-ref="user1"></entry>
<entry key="u2" value-ref="user2"></entry>
</map>
</property>
</bean>
<bean id="user1" class="com.itheima.domain.User">
<property name="name" value="zhangsan"></property>
<property name="addr" value="changsha"></property>
</bean>
<bean id="user2" class="com.itheima.domain.User">
<property name="name" value="lisi"></property>
<property name="addr" value="sichuan"></property>
</bean>
例如properties 类型的键值对
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl">
<property name="props">
<props>
<prop key="p1">aaa</prop>
<prop key="p2">bbb</prop>
</props>
</property>
</bean>
- 引入其他配置文件,分模块开发:
<import resource="applicationContext-xxx.xml"/>
可以在主配置文件中通过import引入其他配置文件
如果配置发生冲突,按照配置引入的先后顺序进行覆盖
简单记忆
- 注意:配置文件中property的name 用set方法注入,name:set方法后半段的属性值 (setUserDao => userDao) 用构造方法,name:要传入的参数名 (传入userDao => userDao)
- 依赖注入引用对象:配置 ref 属性
依赖注入普通对象:配置 value 属性 (String类型也是直接配value)
依赖注入普通对象如果是集合类型,要根据集合类型配置不同属性 - 如:List类型的注入 要配置
<list>
属性,list属性中再配置相应对象;Map类型的注入 要配置<Map>
属性,其中由键值对组成,所以再配<entry>
属性
Spring相关API
- ApplicationContext:接口类型,代表应用上下文,可以通过实例获得Spring容器中的Bean对象
- ApplicatioContext实现类:
- ClassPathXmlApplicationContext:从类的根路径下加载配置文件,推荐使用
- FileSystemXmlApplicationContext:从磁盘路径上加载配置文件,配置文件可以在磁盘任意位置,不常用
- AnnotationConfigApplicationContext:使用注解配置容器对象时,使用此类来创建Spring容器,同来读取注解
- getBean()方法:
- 参数类型为String时,标识根据Bean的id从容器中获得Bean实例,返回Object
- 参数类型为Class类型时,根据类型从容器中获取Bean实例,不能用于容器中有多个相同类型的Bean
Spring配置数据源(连接池)
- 开发步骤:
- 导入数据源坐标和数据库驱动坐标
- 创建数据源对象
- 设置数据源的基本连接数据
- 使用数据源获取连接资源和归还连接资源
原始方式配置数据源(未解耦)
//不使用配置文件
@Test
public void testDruid() throw Exception{
//创建数据源
DruidDataSource dataSource = new DruidDataSource();
//设置数据库连接参数
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/test");
dataSource.setUsername("root");
dataSource.setPassword("root");
//获得连接对象
DruidPooledConnection connection = dataSource.getConnection();
System.out.println(connection);
connection.close();
}
读取jdbc.properties配置文件创建连接池(解耦,但没有让spring帮我们创建数据源对象)
//读取jdbc.properties配置文件创建连接池
@Test
public void test2() throws SQLException {
//加载类路径下的jdbc.properties配置文件
ResourceBundle rb = ResourceBundle.getBundle("jdbc");
//创建druid数据源对象
DruidDataSource dataSource = new DruidDataSource();
//设置驱动,url,username,password
dataSource.setDriverClassName(rb.getString("driver"));
dataSource.setUrl(rb.getString("url"));
dataSource.setUsername(rb.getString("username"));
dataSource.setPassword(rb.getString("password"));
//获取连接对象
DruidPooledConnection connection = dataSource.getConnection();
System.out.println(connection);
connection.close();
}
使用Spring配置数据源
将DataSource的创建权交给Spring容器去完成(解耦合,但同时配置文件分工不够明确)
- DataSource有无参构造方法,Spring时默认通过无参构造方法实例化对象
- DataSource还需要通过set方法设置数据库连接信息,Spring可以通过set方法注入
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/test"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
//测试从容器中获取数据源
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
DataSource dataSource = (DataSource)app.getBean("dataSource");
Connection connection = dataSource.getConnection();
System.out.println(connection);
抽取jdbc配置文件配置数据源
(解耦合,同时每个配置文件分工明确,数据库配置 和 spring配置分开了)
applicationContext.xml加载jdbc.properties配置文件获得连接信息
location=”类加载路径:xxx.properties”
<!-- 在beans引入context命名空间和约束路径-->
xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
<!-- 加载jdbc.properties配置文件-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 使用SpEL将配置文件中的value赋进去-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
Spring注解开发(主流 重要)
注解代替xml配置
- 步骤:
- 先在类中加上注解
- 开启组件扫描
spring原始注解
- 使用注解开发时:需要在applicationContext.xml中配置组件扫描,作用时指定那个包及其子包下的Bean需要进行扫描以便试别需要使用注解配置的类,字段和方法
<!-- 注解组件扫描-->
<context:component-scan base-package="com.itheima"/>
- 使用@Component或者@Repository标识UserDaoImpl进行Spring实例化
//<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"></bean>
//@Component("userDao")
@Repository("userDao")
public class UserDaoImpl implements UserDao {
public void save() {
System.out.println("save running...");
}
}
- 使用@Component或者@Service标识UserServiceImpl进行Spring实例化
- 使用@Value进行字符串的注入
- 使用@Autowired或者@Autowired+@Qualifier或者@Resource进行userDao的注入
(使用注解注入可以不需要set方法,直接通过反射给目标赋值)
//<bean id="userService" class="com.itheima.service.impl.UserServiceImpl">
//@Component("userService")
@Service("userService")
public class UserServiceImpl implements UserService {
//使用@Value进行字符串的注入
@Value("${jdbc.driver}")
private String driver;
private UserDao userDao;
//<property name="userDao" ref="userDao"></property>
//@Autowired //只写Autowired时,是按照数据类型从Spring容器中匹配的
//@Qualifier("userDao") //Autowired+Qualified是按照id值从Spring容器中匹配的
@Resource(name = "userDao")
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void save() {
System.out.println(driver);
userDao.save();
}
}
- 使用Scope标注Bean的范围
@Scope("singleton")
public class UserDaoImpl implements UserDao{
}
注意
- 在要依赖注入属性前只写Autowired时,是Spring按照该数据类型从Spring容器中匹配的,
例如:当前需要一个userDao对象,然后Spring中正好将UserDaoImpl默认singleton注入了,所以就可以使用,但如果有多个对象,就会出问题
即:按照类型注入:@Autowired
;按照 id 注入:@Autowired + @Qualifier("xxx")
- 使用注解注入对象可以不需要set方法,直接通过反射找到目标对象,给目标赋值
@Value("${jdbc.driver}")
Value一般配合SpEL使用,来注入Spring配置文件中的一些普通变量
Spring新注解
使用原始注解还不能全部代替xml文件,还需要新注解
- 非自定义的Bean配置:
<bean>
- 加载properties文件的配置:
<context:property-placeholder>
- 扫描组件的配置:
<context:component-scan>
- 引入其他文件:
<import>
@Configuration //指定该类是Spring配置类,加载该类的注解
//<context:component-scan base-package="com.itheima"/>
@ComponentScan("com.itheima") //指定扫描组件的配置
@Import({DataSourceConfiguration.class}) //导入其他配置类,是个数组
public class SpringConfiguration {
}
@Configuration
//<context:property-placeholder location="classpath:jdbc.properties"/>
@PropertySource("classpath:jdbc.properties") //加载其他配置文件
public class DataSourceConfiguration {
@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(name = "dataSource") //将改方法的返回值存放到Spring容器中
public DataSource getDataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
}
测试类要使用AnnotationConfigApplicationContext类指定配置类名称,获得ApplicationContext对象
public class controller {
public static void main(String[] args) {
ApplicationContext app = new AnnotationConfigApplicationContext(SpringConfiguration.class);
UserService userService = app.getBean(UserService.class);
userService.save();
}
}
Spring集成Junit
每个测试类中,每个测试方法都会有下面两行代码
ApplicationContext app = new ClassPathXmlApplicationContext("bean.xml");
Object o = app.getBean(xxx.class);
这两行的代码作用是获取容器,不写的话,提示空指针异常,所以不能轻易删掉
- 解决思路:让SpringJunit负责创建Spring容器,但是需要将配置文件名字告诉它,将需要进行测试的bean直接在测试类中注入
- Spring继承Junit步骤:
- 导入Spring集成Junit的坐标:
<artifactId>spring-test</artifactId>
- 使用@Runwith注解替换原来的运行期
- 使用@ContextConfiguration指定配置文件或配置类
- 使用@Autowired注入需要测试的对象
- 使用@Test创建方法进行测试
- 导入Spring集成Junit的坐标:
//使用@Runwith注解替换原来的运行期
@RunWith(SpringJUnit4ClassRunner.class)
//使用@ContextConfiguration指定配置文件或配置类
@ContextConfiguration("classpath:applicationContext.xml")
//@ContextConfiguration(classes = {SpringConfiguration.class})
public class SpringJunitTest {
@Autowired
private UserService userService;
@Autowired
private DataSource dataSource;
@Test
public void test1() throws SQLException {
System.out.println(dataSource.getConnection());
userService.save();
}
}
Spring的AOP开发
面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的技术
- 优点:在程序运行期间不修改源码的情况下进行功能增强,减少重复代码,提高效率便于维护
- 实现:底层通过Spring的动态代理技术实现
- 常用动态代理技术:
- JDK代理:基于接口的动态代理技术
- cglib代理:基于父类的动态代理技术
JDK动态代理
//目标类接口
public interface TargetInterface {
public void save();
}
//目标类
public class Target implements TargetInterface{
public void save() {
System.out.println("save running....");
}
}
//增强类的增强方法
public class Advice {
public void before(){
System.out.println("前置增强");
}
public void after(){
System.out.println("后置增强");
}
}
//动态代理代码
public class ProxyTest {
public static void main(String[] args) {
//目标对象
final Target target = new Target();
//增强对象
final Advice advice = new Advice();
TargetInterface proxyInstance = (TargetInterface) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
advice.before();
Object invoke = method.invoke(target, args);
advice.after();
return invoke;
}
}
);
//代理对象的方法测试
proxyInstance.save();
}
}
cglib动态代理
//目标类
public class Target {
public void save() {
System.out.println("save running....");
}
}
//增强类的增强方法
public class Advice {
public void before(){
System.out.println("前置增强");
}
public void after(){
System.out.println("后置增强");
}
}
//动态代理代码
public class ProxyTest {
public static void main(String[] args) {
final Target target = new Target();
final Advice advice = new Advice();
//返回值就是动态生成的代理对象,基于cglib
//创建增强器
Enhancer enhancer = new Enhancer();
//设置父类(目标)
enhancer.setSuperclass(Target.class);
//设置回调
enhancer.setCallback(new MethodInterceptor() {
public Object intercept(Object o, Method method, Object[] obj, MethodProxy methodProxy) throws Throwable {
advice.before();
Object invoke = method.invoke(target, obj);
advice.after();
return invoke;
}
});
//创建代理对象
Target proxy = (Target) enhancer.create();
//测试代理方法
proxy.save();
}
}
AOP相关概念
Spring的AOP实现底层就是对上面的动态代理代码进行了封装
AOP相关术语:
- Target(目标对象):要代理的目标对象
- Proxy(代理):一个类被AOP织入增强后,就产生一个结果代理类
- Joinpoint(连接点):被拦截到的方法,可以被增强的方法
- Pointcut(切入点):要被增强的方法,实际被增强的方法
- Advice(通知、增强):增强方法
- Aspect(切面):Pointcut+Advice
- Weaving(织入):动态代理的过程
AOP底层使用哪种代理方式:Spring中,会根据目标类是否实现了接口来决定采用哪种方式
开发明确:
- 谁是切点(切点表达式的配置)
- 谁是通知(切面类中的增强方法)
- 将切点和通知进行织入配置
基于XML的AOP开发快速入门
- 步骤:
- 导入AOP相关坐标(aspectj)
- 创建目标接口和目标类(内部有切点)
- 创建切面类(内部有增强方法)
- 将目标类和切面类的创建权交给Spring
- 在applicationContext.xml中配置织入关系
- 测试
导入AOP相关坐标
<!--cglib等在context中-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<!--使用AspectJ的织入-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.4</version>
</dependency>
创建目标接口和目标类
public interface TargetInterface {
public void save();
}
public class Target implements TargetInterface {
public void save() {
System.out.println("save running....");
}
}
创建切面类(内部有增强方法)
public class MyAspect {
public void before(){
System.out.println("前置增强....");
}
public void afterReturning(){
System.out.println("前置增强....");
}
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("环绕前增强....");
Object proceed = pjp.proceed();
System.out.println("环绕后增强....");
return proceed;
}
public void afterThrowing(){
System.out.println("异常抛出增强....");
}
public void after(){
System.out.println("最终增强....");
}
}
将目标对象和切面对象的创建权交给Spring
<!--目标对象-->
<bean id="target" class="com.itheima.aop.Target"></bean>
<!--切面对象-->
<bean id="myAspect" class="com.itheima.aop.MyAspect"></bean>
在applicationContext.xml配置织入关系
<!--先配置aop命名空间-->
xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
<!--配置切点表达式和和增强的织入关系-->
<!--配置织入:告诉spring框架 哪些方法(切点)需要进行增强(前置,后置....)-->
<aop:config>
<!--引用myAspect的Bean作为切面对象-->
<aop:aspect ref="myAspect">
<!--配置Target的save方法执行时要被myAspect的before方法前值增强-->
<aop:before method="before" pointcut="execution(public void com.itheima.aop.Target.save())"/>
</aop:aspect>
</aop:config>
XML配置AOP详解
- 切点表达式写法:
execution([修饰符] 返回值类型 包名.类名.方法名(参数))
- 注意:
- 修饰符可以省略
- 除开修饰符都可以用星号
*
表示任意 - 包名与类名之间一个点
.
表示当前包下的类,两个点..
表示当前包及其子包下的类 - 参数列表可以用两个点
..
表示任意个数,参数类型的参数列表 - 常用:
execution(* com.itheima.aop .*.*(..))
- 通知的配置语法:
<aop:通知类型 method="切面类中方法名" pointcut="切点表达式" />
切点表达式的抽取
当多个切点表达式相同时,配置pointcut-ref属性进行抽取
<aop:pointcut id="myPointcut" expression="execution(* com.itheima.aop.*.*(..))"/>
<aop:before method="before" pointcut-ref="myPointcut"/>
基于注解的AOP配置
通知的配置语法:@通知注解(“切点表达式”)
- 步骤:
- 前置步骤:用
@Component
将 切面类 和 目标类 交给Spring创建,同时开启组件扫描 - 使用
@Aspect
标注切面类 - 使用
@增强类型
注解标注通知方法 - 在配置文件中配置aop自动代理
<aop:aspectj-autoproxy>
- 前置步骤:用
将目标类和切面类的对象创建权交给Spring(使用注解)
在切面类中使用注解配置织入关系
@Component("target")
public class Target implements TargetInterface {
public void save() {
System.out.println("save running....");
}
}
@Component("myAspect")
//告诉Spring这是切面类
@Aspect
public class MyAspect {
//配置织入关系
@Before("execution(* com.itheima.anno.*.*(..))")
public void before(){
System.out.println("前置增强....");
}
}
在配置文件中开启组件扫描和AOP自动代理
<!-- 组件扫描-->
<context:component-scan base-package="com.itheima.anno"/>
<!-- aop自动代理-->
<aop:aspectj-autoproxy/>
测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext-anno.xml")
public class AnnoTest {
@Autowired
private TargetInterface targetInterface;
@Test
public void test1(){
targetInterface.save();
}
}
解耦:切面类中,切点表达式的抽取
@Component("myAspect")
@Aspect
public class MyAspect {
@Before("pointcut()")
public void before(){
System.out.println("前置增强....");
}
@Pointcut("execution(* com.itheima.anno.*.*(..))")
public void pointcut(){}
}
Spring JdbcTemplate基本使用
要导入 spring-jdbc 和 spring-tx 坐标依赖
使用Spring配置创建jdbcTemplate对象 模板
applicationContext.xml
<?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命名空间,引入jdbc.properties-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--创建数据源对象-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!--依赖注入数据源对象-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
</beans>
jdbc.properties
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test
jdbc.username=root
jdbc.password=root
jdbcTemplate 常用方法
int row = update(sql, ...args)
常用于 add delete update 操作,返回影响的行数List<?> list= query(sql, new BeanRowMapper<?>())
常用于查询多条记录,返回 list 集合Object o = queryForObject(sql, new BeanRowMapper<Object>())
常用于查询单行,返回一个指定对象Long count = queryForObject(sql, Long.class)
用于查询记录的数量,返回数量(可以是Integer或者Long)
Spring的事务控制
编程式事务控制
- 三大对象
- PlatformTransactionManager:平台事务管理接口
实现是根据不同的Dao层的技术而实现的,不同的平台事务管理器内部封装的控制事务的方式,API 都不一样,(需要通过配置的方式指定) - TransactionDefinition:事务定义对象,封装一些事务的参数
隔离级别,传播行为 (需要通过配置的方式指定) - TransactionStatus:事务状态
随着事件的进行,内部的信息会跟着相应的改变 (不需要通过配置的方式改变)
- PlatformTransactionManager:平台事务管理接口
声明式事务控制
作用:不侵入开发的组件,类似于AOP思想,通过在系统层面配置事务控制来控制业务逻辑,使 业务逻辑 和 事务控制 分开,降低耦合,改变事务时,也只需要在配置文件中更改即可。
Spring中声明式事务控制底层就是AOP
基于xml的声明式事务控制
步骤:
- 前置步骤:配置dataSource和jdbcTemplate,还有 AccountDaoImpl 以及 AccountServiceImpl
- 配置平台事务管理器 (要依赖于dataSource)
- 配置增强 (指定事务管理器,指定每个被事务控制的方法事务属性 如:隔离级别、传播行为等)
- 配置织入 (使用aop:advisor,因为事务控制只有一种增强)
<!--配置平台事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--配置增强-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="transfer" isolation="DEFAULT" propagation="REQUIRED" timeout="-1" read-only="false" />
</tx:attributes>
</tx:advice>
<!--配置织入-->
<aop:config>
<aop:pointcut id="txPointcut" expression="execution(* com.itheima.service.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>
基于注解的声明式事务控制
步骤:
- 前置步骤:将 AccountDaoImpl 和 AccountServiceImpl 使用注解配置
- 将配置增强的xm配置 使用
@Transactional
代替即可 可以定义在类上(控制类中所有方法),或者方法上,就近原则
applicationContext.xml
<!--开启组件扫描-->
<context:component-scan base-package="com.itheima"/>
<!--配置平台事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--事务注解驱动-->
<tx:annotation-driven transaction-manager="transactionManager"/>
AccountServiceImpl
@Service("accountService")
@Transactional(isolation = Isolation.READ_COMMITTED)
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao;
@Transactional(isolation = Isolation.READ_COMMITTED)
public void transfer(String outMan, String inMan, double money) {
accountDao.out(outMan,money);
int i = 1/0;
accountDao.in(inMan,money);
}
}
注意:使用注解配置时一定要记得配置事务注解驱动:tx:annotation-driven
小结
- 使用spring进行事务控制,主要就是利用 AOP 思想,不侵入开发的组件,通过在系统层面配置事务控制来控制业务逻辑,使 业务逻辑 和 事务控制 分开,降低耦合
Spring 集成 web 环境
ApplicationContext获取方式的优化思路
- 在 web 项目中,可以使用 ServletContextListener 监听 web 应用的启动,在 web 应用启动时 就加载 Spring 配置文件,创建应用上下文对象,将其存储到最大的域 ServletContext 域当中 ,就可以从为任意位置从任意域中获取 ApplicationContext 对象了。
减少了不必要的多次创建ApplicaionContext对象 - 还可以在 web.xml 中进行优化,降低耦合,使用 context-param 全局初始化参数,将 ”applicationContext.xml“抽取出来,使用时只需要用 ServletContext 对象 getInitParameter 即可
使用Spring提供的获取上下文的工具
- Spring提供了一个监听器,
ContextLoaderListener
就是对上述功能的封装,该监听器内部加载Spring 配置文件,创建应用上下文对象,并存储到ServletContext
域中,提供一个客户端工具WebApplicationContextUtils
供使用者获得应用上下文对象 - 使用步骤:
- 导入spring-web坐标,在 web.xml 中配置
ContextLoaderListener
监听器 - 使用
WebApplicationContextUtils
传入一个ServletContext
对象,获得应用上下文对象ApplicationContext
- 导入spring-web坐标,在 web.xml 中配置
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>
UserServlet中
public class UserServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext servletContext = req.getServletContext();
WebApplicationContext app = WebApplicationContextUtils.getWebApplicationContext(servletContext);
UserService userService = app.getBean(UserService.class);
userService.save();
}
}
总结
- Spring IOC 控制反转:
- 主要思想:主要是将对象的创建权交给Spring来完成,降低耦合
在一个对象的创建需要依赖另一个对象时,就需要使用依赖注入,让Spring来自动创建需要依赖的对象,从而完成原始对象的创建 - 使用案例:使用通过spring帮我们创建数据源对象,同时引入 context 命名空间,使spring配置文件加载jdbc.properties,既解了耦合,使之后项目部署了之后可以修改配置文件来修改项目;又jdbc和spring配置文件分开,使配置文件分工更加明确
- 简化:使用注解配置
- 主要思想:主要是将对象的创建权交给Spring来完成,降低耦合
- Spring AOP 面向切面编程
- 主要思想:原理是使用动态代理的思想,增强目标方法,弄清楚 切面类(增强) 和 目标类(方法),配置织入关系
- 步骤:
- 先使用 IOC 让 Spring 托管对象的创建
- 然后使用 注解 / xml 在 切面类 / 配置文件 中完成 织入(切点 + 通知 的结合过程)
- 作用:编译时期动态的对方法进行增强,降低耦合
- Spring集成Junit
- 简化利用 Spring 开发时测试代码的重复编写,提高效率,降低耦合
- Spring jdbcTemplate
- 配合注入数据源来使用,简化原来 jdbc 繁琐的操作
- Spring 事务控制
- 使用spring进行事务控制,主要就是利用 AOP 思想,不侵入开发的组件,通过在系统层面配置事务控制来控制业务逻辑,使 业务逻辑 和 事务控制 分开,降低耦合
- Spring提供的获取上下文的工具
- 就是为了减少 ApplicationContext 对象的重复创建,优化程序,同时将 spring配置文件的名字抽取到 web.xml 中,降低了耦合
参考
2020年IDEA版黑马Java :BV1WZ4y1H7du