Spring知识点

本文目录

  • 搭建Spring环境
  • Spring 配置多个文件加载
  • Spring Ioc 实例化 Bean 对象的三种方式
  • Spring DI 依赖注入 四种注入方式
  • 面向切面编程 AOP
  • Bean 的作用域
  • Spring 整合JDBC
  • Spring JDBC 事务

搭建Spring环境

1. 创建maven项目
2. 添加gra坐标,加入Spring框架的jar包pom文件中
3. 编写Spring的配置文件spring-config.xml
4. 创建待初始化bean对象,并在spring-config.xml配置文件中进行配置
5. 测试

Spring配置多个文件加载

有两种情况:

在Spring容器加载配置文件时加载多个配置文件

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("hello.xml""dao.xml");

在xml配置文件中,使用import标签将子配置文件导入到总的配置文件中

<import resource="dao.xml"/> 
<!--(hello.xml中导入了dao.xml),此时,使用Spring框架加载配置文件时,只需要加载总的配置文件即可-->

Spring Ioc 实例化Bean对象的三种方式

空构造方式:

配置spring-config.xml,
    <bean id="别名" class="完全限定名"></bean>
注意事项:
    待初始化的bean必须要提供空参构造器
    如果没有空参构造器就会报错

静态工厂模式:

特点:要有该工厂类(用来生成bean对象的),以及工厂方法,
     工厂方法一定要是静态方法
配置:<bean id="待初始化类的别名" class="工厂类的class" factory-method="生产待初始化类的静态方法名"></bean>
示例
<bean id="goodsService" class="cn.edu.ncu.StaticFactory" factory-method="createGoodsService"></bean>

实例化工厂模式:

第一步,利用空参构造模式,将实例化工厂创建出来
<bean id="实例化工厂类的别名" class="实例化工厂类的完全限定名" ></bean>
第二步,再利用创建出来的实例化工厂来创建待初始化的bean
<bean id="bean的别名" factory-bean="实例化工厂类的别名" factory-method="实例化工厂类的非静态工厂方法"></bean>
示例
<bean id="instanceFactory" class="cn.edu.ncu.InstanceFactory" ></bean>
<bean id="orderService" factory-bean="instanceFactory" factory-method="createOrderService"></bean>

Spring DI 依赖注入 四种注入方式

Setter注入:

格式:
<bean id="待初始化属性类的别名" class="待初始化属性类的完全限定名"></bean>
<bean id="bean的别名" class="bean的完全限定名">
    <property name="bean里面的属性名称" ref="待初始化属性类的别名"></property>
</bean>
示例:
<bean id="userDao" class="cn.edu.ncu.UserDao"></bean>
<bean id="userService" class="cn.edu.ncu.UserService">
     <property name="userDao" ref="userDao"></property>
</bean>

构造器注入:

第一步:必须提供带一个参构造器
第二步:进行配置
     格式:
         <bean id="属性的别名" class="完全限定名"></bean>
         <bean id="bean的别名" class="完全限定名">
             <constructor-arg index="0" ref="userDao001" ></constructor-arg>
             <constructor-arg index="1" value="xaioqiang"></constructor-arg>
         </bean>
示例:
    <bean id="userDao001" class="cn.edu.ncu.UserDao"></bean>
    <bean id="userService" class="cn.edu.ncu.UserService">
         <constructor-arg index="0" ref="userDao001" ></constructor-arg>
         <constructor-arg index="1" value="xaioqiang"></constructor-arg>
    </bean>
构造器注入一共有3种形式:
     (1)根据index索引来匹配的
     (2)根据name属性名称来匹配的
     (3)根据type类型进行匹配
     问题:如果构造器中有多个类型相同的参数,将如何使用type类型进行依赖注入?
     此时会按照***顺序***进行注入
     (String a, String b, String c)
     <constructor-arg type="String" value="userDao001" ></constructor-arg>
     <constructor-arg type="String" value="xaioqiang"></constructor-arg>
     <constructor-arg type="String" value="daqiang"></constructor-arg>
     a = userDao001, b = xaioqiang, c = daqiang

静态工厂注入:

实质上是 Ioc的静态工厂模式和DI的Setter注入
将需要的属性对象利用静态工厂创建出来
配置:
    第一步:利用Ioc中的静态工厂面膜是创建被引用的对象
            (需要有一个被引用对象的静态工厂,且静态工厂中的方法一定要是静态的)
    <bean id="被引用对象的别名" class="创建被引用对象的工厂" factory-method="该静态工厂中的静态方法"></bean>
    <bean id="bean的别名" class="bean的class">
        <property name="bean类中属性的名称" ref="被引用对象的别名"></property>
    </bean>

实例化工厂注入:

实质上是Ioc的实例化工厂和DI的Setter注入
           将需要的属性对象利用实例化工厂创建出来
Spring的Ioc(控制反转)和DI(依赖注入)的关系:相辅相成
    Ioc:侧重于bean的创建 (有三种方式)
       空构造模式、静态工厂模式、实例化工厂模式
    DI:侧重bean的属性注入(有四种方式)
       Setter注入、构造器注入、静态工厂注入、实例化化工厂注入

DI 产生的循环依赖问题

构造器注入时,必须先注入再创建对象,这样就会产生循环依赖问题

Setter注入,先创建对象再注入。恰好就是解决了构造器注入带来的循环依赖问题

DI注入List集合

<bean id="userService" class="cn.edu.ncu.UserService">
    <property name="foodList">
        <list>
            <value>河南烩面</value>
            <value>南方臊子面</value>
            <value>油泼面</value>
            <value>刀削面</value>
            <value>安徽板面</value>
        </list>
    </property>
</bean>

DI注入Set集合

<bean id="userService" class="cn.edu.ncu.UserServcie">
    <property name="foodSet">
        <set>
            <value>快乐小馒头</value>
            <value>北方大馒头</value>
            <value>东北大麻花</value>
        </set>
    </property>
</bean>

DI注入Map

<bean id="userService" class="cn.edu.ncu.UserServcie">
    <property name="mapSence">
        <map>
            <entry>
                <key><value>河南</value></key>
                <value>云台山风景</value>
            </entry>
            <entry>
                <key><value>北京</value></key>
                <value>紫禁城</value>
            </entry>
            <entry>
                <key><value>江西</value></key>
                <value>庐山风景</value>
            </entry>
        </map>
    </property>
</bean>

注解方式

配置命名空间和范围

xmlns:context="http://www.springframework.org/schema/context“
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
<!-- 添加注解的解析 -->
<context:annotation-config />

@Autowired和@Resource区别

相同点:都能够实现DI功能
不同点:
    1、
    @Autowired是spring提供的注解
    @Resource是jdk提供的注解,并且spring框架也对@Resource提供了注解解析器
    2、匹配模式
        @Autowired默认按照bean的类型匹配
        @Resource默认按名称进行装配。如果名称找不到,再按照类型匹配。名称可以通过name属性进行指定,如果没有指定name属性,当注解写在字段上时,默认取字段名进行匹
    配注入,如果注解写在setter方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。
    但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。

    推荐使用@Resource(jdk提供的)
        import javax.annotation.Resource;
    不推荐使用@Autowired(Spring提供的,会侵入代码)
        import org.springframework.beans.factory.annotation.Autowired;

Spring Ioc 容器自动扫描管理 bean

Spring Ioc 容器自动扫描管理 bean
<context:component-scan base-package="cn.edu.ncu"/>
不需要再配置
<context:annotation-config/>
同时对于被spring管理的bean类的定义上需要加入对应的注解定义
开发中建议(开发中的一种约定)
Dao层:@Repository
Service层:@Service
视图控制层:@Controller
如果对于开发的类实在不明确到底属于哪个层,可以使用@Component注解定义。

面向切面编程 Aop

注解方式实现

配置命名空间和范围

xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd

配置Aop代理

<aop:aspectj-autoproxy/>

编写 aop 实现类

package cn.edu.ncu.aspectj;

import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

/**
 * @Author ZhaiyiJun
 * @Create by Masters on 2020-31-13-4:31.
 * @Description: MAVEN
 * @Modified by:[描述修改人]
 * @Version: 1.0
 * @History: [描述修改信息]
 */
@Aspect
@Component
public class LogCut {
    /*
    * 定义切点,匹配方法,定义规则
    * 匹配规则表达式含义 拦截 cn.edu.ncu.service 包下 以及子包下 所有类的所有
    * */
    @Pointcut("execution (* cn.edu.ncu.service..*.*(..))")
    public void cut(){

    }
    /*
    * 声明前置通知 并将通知应用到定义的切入点上
    * 目标泪方法执行前 执行该通知
    * */
    @Before(value = "cut()")
    public void before(){
        System.out.println("开始了");
    }
    /*
     * 声明最终通知 并将通知应用到定义的切入点上
     * 目标泪方法执行完毕 执行该通知
     * */
    @After(value = "cut()")
    public void after(){
        System.out.println("结束了");
    }

    /**
     *声明最终通知 并将通知应用到切入点上
     * 目标类方法执行过程中是否发生异常 均会执行该通知 相当于异常中的 finally
     */
    @AfterReturning(value = "cut()")
    public void afterReturning(){
        System.out.println("Aop的返回通知");
    }

    /**
     * 声明异常通知 并将通知应用到切入点上 
     * 目标类方法执行时发生异常 执行该通知
     */
    @AfterThrowing(value = "cut()",throwing = "e")
    public void afterThrow(Exception e){
        System.out.println("Aop的异常抛出通知" + e);
    }
}

XML 配置方式实现

<!-- aop 相关配置 --> 
<aop:config> 
    <!-- aop 切面配置 --> 
    <aop:aspect ref="logCut"> 
        <!-- 定义 aop 切入点 --> 
        <aop:pointcut expression="execution (* com.mage.service..*.*(..))" id="cut"/> 
        <!-- 配置前置通知 指定前置通知方法名 并引用切入点定义 --> 
        <aop:before method="before" pointcut-ref="cut"/> 
        <!-- 配置返回通知 指定返回通知方法名 并引用切入点定义 --> 
        <aop:after-returning method="afterReturning" pointcut-ref="cut"/> 
        <!-- 配置异常通知 指定异常通知方法名 并引用切入点定义 --> 
        <aop:after-throwing method="afterThrowing" throwing="e" pointcut ref="cut"/> 
        <!-- 配置最终通知 指定最终通知方法名 并引用切入点定义 --> 
        <aop:after method="after" pointcut-ref="cut"/> 
        <!-- 配置环绕通知 指定环绕通知方法名 并引用切入点定义 --> 
        <aop:around method="around" pointcut-ref="cut"/> 
    </aop:aspect> 
</aop:config>

编写 aop 实现类

import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.stereotype.Component;
/**
 *
 * 声明切面组件
 * */
@Component
public class LogCut {

    public void before() {
        System.out.println("前置通知.....");
    }

    public void afterReturning() {
        System.out.println("返回通知....");
    }

    public void after() {
        System.out.println("最终通知....");
    }

    public void afterThrowing(Exception e) {
        System.out.println("异常通知....方法执行异常时执行:" + e);
    }

    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("环绕前置...");
        System.out.println("环绕通知");
        System.out.println(pjp.getTarget() + "--" + pjp.getSignature());
        Object result = pjp.proceed();
        System.out.println("环绕后置...");
        return result;
    }
}

Bean的作用域

Spring 作用域(scope 属性)
1、 singleton 作用域:
    在框架启动时创建bean初始化放入缓存池中
    scope的属性值默认是singleton 单例的
    时间:在框架启动时创建bean
    模式:单例模式,启动时将创建的bean放入到缓存池中
        每次调用时,都是调用缓存池中的对象
2、lazy-init 懒加载:
    时间:lazy-init="true", 框架启动时不初始化bean,
        等到调用时再初始化bean,存在安全隐患
         lazy-init="false"(bean标签中默认配置)
         框架启动时就将bean初始化到单例缓存池中,程序启动时速度慢,但是调用bean时,不存在安全隐患
3、prototype 作用域
    prototype 每次请求创建一个全新的bean并返回,不会缓存
    时间:在bean 对象被调用的时候创建,不会被框架启动的时候被调用
    模式:不是单例模式

Spring整合JDBC

配置坐标依赖

<!--mysql 驱动包-->
<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <version>8.0.18</version>
</dependency>
<!-- c3p0 连接池 -->
<dependency>
  <groupId>c3p0</groupId>
  <artifactId>c3p0</artifactId>
  <version>0.9.1.2</version>
</dependency>
<!-- spring jdbc -->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-jdbc</artifactId>
  <version>4.2.4.RELEASE</version>
</dependency>

配置db.properties

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/user?serverTimezone=UTC
jdbc.user=root
jdbc.password=zyj18435790158@.

配置spring-config.xml

<context:component-scan base-package="cn.edu.ncu"/>
<!-- 加载 properties 配置文件 -->
<context:property-placeholder location="db.properties" />
<!--加载数据库配置文件-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <property name="driverClass" value="${jdbc.driver}"></property>
    <property name="jdbcUrl" value="${jdbc.url}"></property>
    <property name="user" value="${jdbc.user}"></property>
    <property name="password" value="${jdbc.password}"></property>
</bean>

<!--配置jdbc数据库的操作模板-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <property name="dataSource" ref="dataSource"/>
</bean>

编写模板类

package cn.edu.ncu.dao;

import cn.edu.ncu.model.Customer;
import cn.edu.ncu.util.StringUtil;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Repository;

import javax.annotation.Resource;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

@Repository
public class CustomerDao {
    @Resource
    private JdbcTemplate jdbcTemplate;

    /**
     * 实现带参查询封装结果
     * @param name
     * @return
     */
    public Customer QueryCustomerByName(String name){
        String sql = "select id, user_name, user_balance from yg_customer where user_name= ?";
        return jdbcTemplate.queryForObject(sql, new Object[]{name}, new RowMapper<Customer>() {
            @Override
            public Customer mapRow(ResultSet resultSet, int i) throws SQLException {
                Customer customer = new Customer();
                customer.setId(resultSet.getInt(1));
                customer.setUserName(resultSet.getString(2));
                customer.setUserBalance(resultSet.getDouble(3));
                return customer;
            }
        });
    }

    /**
     * 实现多条件查询
     * @param id
     * @param name
     * @return
     */
    public List<Customer> queryCustomerByNameOrId(Integer id, String name){  // int 默认值是0,Integer 默认值是null
        String sql = "select id, user_name, user_balance from yg_customer where 1 = 1";

        //参数封装
        List<Object> params = new ArrayList<Object>();
        if(id != null){
            sql += " and id = ?";
            params.add(id);
        }
        if(!StringUtil.isBlankOrNull(name)){
            sql += " and user_name = ?";
            params.add(name);
        }
        List<Customer> customerList = jdbcTemplate.query(sql, params.toArray(), new RowMapper<Customer>() {
            @Override
            public Customer mapRow(ResultSet resultSet, int i) throws SQLException {
                Customer customer = new Customer();
                customer.setId(resultSet.getInt(1));
                customer.setUserName(resultSet.getString(2));
                customer.setUserBalance(resultSet.getDouble(3));
                return customer;
            }
        });
        return customerList;
    }
}
package cn.edu.ncu.dao;

import cn.edu.ncu.model.Goods;
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.stereotype.Repository;
import javax.annotation.Resource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;


@Repository
public class GoodsDao {
    @Resource
    private JdbcTemplate jdbcTemplate;

    /**
     * 添加商品,返回受到影响的行数
     * @param goods
     * @return
     */
    public int addGoodsHasNoKey(Goods goods){
        String sql = "INSERT INTO yg_goods(goods_name, goods_price, goods_stock) VALUES (?,?,?)";
        int res = jdbcTemplate.update(sql,new Object[]{goods.getGoodsName(),
                goods.getGoodsPrice(),
                goods.getGoodsStock()});
        return res;
    }

    /**
     * 添加商品, 返回添加记录的主键
     * @param goods
     * @return
     */
    public int addGoodsHasKey(final Goods goods){
        final String sql = "INSERT INTO yg_goods(goods_name, goods_price, goods_stock) VALUES (?,?,?)";
        // 定义 keyHolder 对象,获取记录的主键值
        KeyHolder keyHolder = new GeneratedKeyHolder();
        int res = jdbcTemplate.update(new PreparedStatementCreator() {
            @Override
            public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
                // 重新组装ps,预编译快
                PreparedStatement preparedStatement= connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
                // 组装参数
                preparedStatement.setString(1,goods.getGoodsName());
                preparedStatement.setDouble(2,goods.getGoodsPrice());
                preparedStatement.setInt(3,goods.getGoodsStock());
                return preparedStatement;
            }
        }, keyHolder);
        return keyHolder.getKey().intValue();
    }

    /**
     * 更新商品库存
     * @param id
     * @param stock
     * @return
     */
    public int updateGoodsStockById(int id, int stock){
        String sql = "UPDATE yg_goods set goods_stock = ? WHERE id = ?";
        int res = jdbcTemplate.update(sql, new Object[]{stock, id});
        return res;
    }

    /**
     * 根据 id 删除商品
     * @param id
     * @return
     */
    public int updateGoodsById(int id){
        String sql = "delete from yg_goods where id = ?";
        int res = jdbcTemplate.update(sql,new Object[]{id});
        return res;
    }

    /**
     * 根据 id 批量删除
     * @param ids
     * @return
     */
    public int deleteGoodsBatch(final Integer[] ids){
        String sql = "delete from yg_goods where id = ?";
        return jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
            @Override
            public void setValues(PreparedStatement preparedStatement, int i) throws SQLException {
                preparedStatement.setInt(1,ids[i]);
            }

            @Override
            public int getBatchSize() {
                return ids.length;
            }
        }).length;
    }

    /**
     * 批量插入商品信息
     * @param list
     * @return
     */
    public int addGoodsBatch(final List<Goods> list){
        String sql = "INSERT INTO yg_goods(goods_name, goods_price, goods_stock) VALUES (?,?,?)";
        return jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
            @Override
            public void setValues(PreparedStatement preparedStatement, int i) throws SQLException {
                // 重组参数
                preparedStatement.setString(1, list.get(i).getGoodsName());
                preparedStatement.setDouble(2,list.get(i).getGoodsPrice());
                preparedStatement.setInt(3,list.get(i).getGoodsStock());
            }

            @Override
            public int getBatchSize() {
                return list.size();
            }
        }).length;
    }
}

Spring JDBC 事务

配置命名空间和范围

xmlns:tx="http://www.springframework.org/schema/tx"
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd"

配置spring-config.xml

<!--sping事务管理 配置注解支持-->
<tx:annotation-driven transaction-manager="txManager"/>

<!-- spring 注解式事务声明 -->
    <!-- 事务管理器定义 -->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

   转载规则


《Spring知识点》 TyZhai 采用 知识共享署名 4.0 国际许可协议 进行许可。
  目录