框架学习-Spring

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()时
      • init-method:指定类中的初始化方法名称
      • destroy-method:指定类中的销毁方法名称
  • 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创建方法进行测试
//使用@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:事务状态
      随着事件的进行,内部的信息会跟着相应的改变 (不需要通过配置的方式改变)

声明式事务控制

作用:不侵入开发的组件,类似于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

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 AOP 面向切面编程
    • 主要思想:原理是使用动态代理的思想,增强目标方法,弄清楚 切面类(增强) 和 目标类(方法),配置织入关系
    • 步骤:
      • 先使用 IOC 让 Spring 托管对象的创建
      • 然后使用 注解 / xml切面类 / 配置文件 中完成 织入(切点 + 通知 的结合过程)
    • 作用:编译时期动态的对方法进行增强,降低耦合
  • Spring集成Junit
    • 简化利用 Spring 开发时测试代码的重复编写,提高效率,降低耦合
  • Spring jdbcTemplate
    • 配合注入数据源来使用,简化原来 jdbc 繁琐的操作
  • Spring 事务控制
    • 使用spring进行事务控制,主要就是利用 AOP 思想,不侵入开发的组件,通过在系统层面配置事务控制来控制业务逻辑,使 业务逻辑 和 事务控制 分开,降低耦合
  • Spring提供的获取上下文的工具
    • 就是为了减少 ApplicationContext 对象的重复创建,优化程序,同时将 spring配置文件的名字抽取到 web.xml 中,降低了耦合

参考

2020年IDEA版黑马Java :BV1WZ4y1H7du

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇