Spring框架(三)

本文介绍Spring中AspectJ简化AOP配置的方法,包括POJO实现AOP的细节,以及Spring与MyBatis的整合流程,涵盖配置、代码编写等方面,同时深入解析声明式事务管理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

第一节 AspectJ简化AOP配置

  1. 通过ProxyFactoryBean配置AOP存在的问题
    a) 这种配置方式是一个过渡方式, 为了让大家理解AOP就是对动态代理的具体应用.
    b) 这种配置方式需要一个切点一个切点的配置, 不灵活, 配置信息越写越多.
    c) 使用AspectJ方式进行配置, 可以解决上述问题.
  2. AspectJ方式配置AOP
    a) AspectJ是一个插件, 需要额外导包: aspectjweaver.jar
    b) 特点是可以支持通配符配置, *作为通配符, 可以代表包, 类, 方法…
    c) 只需要配置一次, 后续所有匹配的位置都将变成切点.
    d) 需要遵循特定的规范.
  3. AspectJ配置步骤
    a) 导包: aspectjweaver.jar
    b) 在spring配置文件中引入aop命名空间
    c) 通过指定的标签进行aop配置
<?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:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--管理service对象-->
    <bean id="userService" class="com.bjsxt.service.impl.UserServiceImpl" />
    <!--管理通知对象-->
    <bean id="txAdvice" class="com.bjsxt.advice.TxAdvice" />
    <!--配置aop-->
    <aop:config>
        <!--配置切点-->
        <!--
            id: 唯一标识切点
            expression: 切点表达式, 让spring通过这个表达式定位切点
                execution(), 固定语法
                * 任意类型的方法返回值
                包名.*.* 指定包下的任意类的任意方法
                (..) 任意数量,任意类型的参数列表
        -->
        <aop:pointcut id="pc" expression="execution(* com.bjsxt.service.*.*(..))"/>
        <!--配置通知-->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pc" />
    </aop:config>
</beans>

第二节 POJO实现AOP(了解)
POJO指的是除了jdk之外, 没有其他任何依赖的java类. 之前实现AOP通知, 要求必须实现指定的接口(MethodBeforeAdvice, AfterReturningAdvice, ThrowsAdvice, MethodInterceptor). POJO实现AOP指的是通知不实现任何接口, 就是一个普通的java类. 好处: 通知类简单; 缺点: 配置复杂, 需要通过指定的标签将POJO模拟成对应的通知.

public class DemoAdvice {
    public void myBefore() {
        System.out.println("[前置通知]");
    }

    public void myAfter(String result) {
        System.out.println("[后置通知], 返回结果是: " + result);
    }

    public void myThrows(Exception e) {
        System.out.println("[异常通知], 异常信息是: " + e.getMessage());
    }
}
<?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:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--管理service对象-->
    <bean id="demoService" class="com.bjsxt.service.DemoService" />
    <!--管理通知对象-->
    <bean id="demoAdvice" class="com.bjsxt.advice.DemoAdvice" />
    <!--配置AOP-->
    <aop:config>
        <!--配置切点-->
        <aop:pointcut id="pc" expression="execution(* com.bjsxt.service.*.*(..))"/>
        <!--配置切面, 将POJO模拟成指定的通知-->
        <aop:aspect ref="demoAdvice">
            <aop:before method="myBefore" pointcut-ref="pc" />
            <aop:after-returning method="myAfter" pointcut-ref="pc" returning="result" />
            <aop:after-throwing method="myThrows" pointcut-ref="pc" throwing="e" />
        </aop:aspect>
    </aop:config>
</beans>

第三节 Spring整合MyBatis

  1. 如何进行整合
    Spring可以将已有技术变得更好用, MyBatis是可以简化JDBC操作. 前提:
    a) 导包, 要注意Spring默认没有提供整合MyBatis的包, 由MyBatis提供, 需要将整合包导进来.
    b) 责任划分, MyBatis负责数据库的操作(映射文件xxxMapper.xml); Spring负责整体的管理工作(管理数据源, 管理事务, 管理MyBatis, 管理对象, 管理AOP…)
  2. 整合流程
    a) 创建项目并导包:
     spring相关:
     spring-core, spring-beans, spring-context, spring-expression
     spring-aop, aopalliance, aspectjweaver
     commons-logging
     spring-jdbc, spring-tx: 管理数据源和事务
     spring-web: 提供web相关的资源(监听器, 过滤器)
     MyBatis相关:
     mybatis, mysql-connector-java
     log4j
     整合包
     mybatis-spring, 注意版信息(http://mybatis.org/spring/zh/index.html)

 其他包
 jstl, junit, hamcrest
b) 提供配置(重点)
 db.properties – 数据库连接信息
 log4j.properties – 日志配置信息
 spring相关配置信息(拆分)
 applicationContext-service.xml – 负责管理service对象


<?xml version="1.0" encoding="UTF-8"?>








	applicationContext-mybatis.xml – 负责管理MyBatis(整合)
<?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
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
    <!--spring整合MyBatis的配置信息-->
    <!--加载资源文件, 需要使用context命名空间-->
    <context:property-placeholder location="classpath:config/commons/*.properties" />
    <!--配置数据源, 在spring-jdbc.jar中提供了一个测试用的数据源, DriverManagerDataSource-->
    <!--常见的数据源: dbcp, c3p0, druid, hikariCP-->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="${db.driver}" />
        <property name="url" value="${db.url}" />
        <property name="username" value="${db.username}" />
        <property name="password" value="${db.password}" />
    </bean>
    <!--mybatis-spring.jar, SqlSessionFactoryBean用于构建工厂对象-->
    <bean id="factory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!--数据源-->
        <property name="dataSource" ref="dataSource" />
        <!--配置别名-->
        <property name="typeAliasesPackage" value="com.bjsxt.pojo" />
    </bean>
    <!--配置映射扫描, mybatis-spring.jar, MapperScannerConfigurer-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!--扫描位置-->
        <property name="basePackage" value="com.bjsxt.mapper" />
        <!--注入工厂-->
        <property name="sqlSessionFactoryBeanName" value="factory" />
    </bean>
</beans>

 applicationContext-tx.xml – 负责事务管理(见第四节)
 MyBatis配置信息
 映射文件 – 定义SQL语句
 web.xml – 配置监听器, 过滤器

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
            http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <!--全局参数, 配置spring配置文件的位置-->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:config/spring/*.xml</param-value>
    </context-param>
    <!--监听器-->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <!--字符编码过滤器, post乱码问题-->
    <filter>
        <filter-name>characterEncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>characterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>
  1. 代码编写
    a) pojo(略)
    b) jsp
<table>
  <tr>
    <td>编号</td>
    <td>用户名</td>
    <td>姓名</td>
    <td>年龄</td>
    <td>生日</td>
    <td>注册时间</td>
    <td>操作</td>
  </tr>
  <c:forEach items="${list}" var="u">
    <tr>
      <td>${u.id}</td>
      <td>${u.username}</td>
      <td>${u.realname}</td>
      <td>${u.age}</td>
      <td>${u.birthday}</td>
      <td>${u.regTime}</td>
      <td>
        <a href="delUser?id=${u.id}" onclick="return confirm('确定要删除吗?');">删除</a>
      </td>
    </tr>
  </c:forEach>
</table>

c) servlet

@WebServlet("/userList")
public class UserListServlet extends HttpServlet {
    private UserService userService;

    @Override
    public void init() throws ServletException {
        // 从application作用域中拿到spring容器
        WebApplicationContext context =
                WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
        // 为userService赋值
        this.userService = context.getBean("userService", UserService.class);
    }

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 调用服务层方法获取用户列表集合
        List<User> list = this.userService.userList();
        // 保存到作用域
        req.setAttribute("list", list);
        // 页面跳转
        req.getRequestDispatcher("/index.jsp").forward(req, resp);
    }
}
@WebServlet("/delUser")
public class UserDelServlet extends HttpServlet {
    private UserService userService;

    @Override
    public void init() throws ServletException {
        WebApplicationContext context =
                WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
        this.userService = context.getBean("userService", UserService.class);
    }

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 接收参数
        int id = Integer.parseInt(req.getParameter("id"));
        // 调用服务方法进行删除操作
        userService.delUser(id);
        // 重新查询用户列表
        resp.sendRedirect(req.getContextPath() + "/userList");
    }
}

d) service

public class UserServiceImpl implements UserService {
    // 需要在spring创建userService对象时将userMapper进行注入
    private UserMapper userMapper;

    public UserMapper getUserMapper() {
        return userMapper;
    }

    @Override
    public void delUser(int id) {
        userMapper.delUser(id);
    }

    public void setUserMapper(UserMapper userMapper) {
        this.userMapper = userMapper;
    }

    @Override
    public List<User> userList() {
        return userMapper.selAll();
    }
}

e) mapper

public interface UserMapper {
    List<User> selAll();

    void delUser(int id);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bjsxt.mapper.UserMapper">
    <select id="selAll" resultType="user">
        select
            id, username, password, realname, age, birthday, reg_time regTime
        from
            tb_user
    </select>

    <delete id="delUser">
        delete from tb_user where id=#{id}
    </delete>
</mapper>

第四节 声明式事务

  1. 什么是声明式事务
    事务的管理方式可以分为两种:
    a) 编程式事务管理: 通过代码编写的形式进行事务管理的方式
    b) 声明式事务管理: 通过配置的形式进行事务管理的方式
    Spring中的声明式事务是借助AOP实现的. 声明事务还提供了对事务更细致的控制.
  2. 完成声明式事务的配置
    必须依赖spring-tx.jar包和tx及aop命名空间

<?xml version="1.0" encoding="UTF-8"?>








<tx:advice id=“txAdvice” transaction-manager=“txManager”>
tx:attributes


            <tx:method name="sel*" read-only="true" />
            <tx:method name="query*" read-only="true" />
            <tx:method name="find*" read-only="true" />
            <tx:method name="*List" read-only="true" />
            <tx:method name="*" />
         </tx:attributes>
    </tx:advice>
    <!--配置AOP-->
    <aop:config>
        <aop:pointcut id="pc" expression="execution(* com.bjsxt.service.*.*(..))" />
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pc" />
    </aop:config>
</beans>
  1. 声明式事务中常见的属性介绍
    3.1 name属性
    通过通配符定义方法名称, 用于区分查询操作和增删改操作
    3.2 read-only属性
    布尔值, 默认为false, 表示增删改操作, spring会进行事务管理; 如果设置为true, 表示只读操作(查询), spring不对其进行事务管理, 能够提升查询的效率.
    3.3 rollback-for属性
    用于指定发生什么异常时要进行事务的回滚. 默认不需要配置, spring会在发生运行时异常时进行事务的回滚.
    3.4 no-rollback-for属性
    用于指定当发生什么异常时不进行事务的回滚. 基本不会配置.
    3.5 isolation属性
    用于指定事务的隔离级别. 取值有:
     DEFAULT, 表示采用数据库默认的隔离级别
     READ_UNCOMMITTED, 表示可以读取到未提交的数据. 可能发生脏读, 不可重复读和幻读. 安全性最低, 效率最高.
     READ_COMMITTED, 表示只能读取到已经提交的数据, 可以防止脏读, 无法防止不可重复读和幻读.
     REPEATABLE_READ, 表示可以重复读取. 可以防止脏读和不可重复读. 可能发生幻读. 实现方式是给行加锁.
     SERIALIZABLE, 表示序列化(排队). 级别最高, 防止发生脏读, 不可重复读和幻读. 给整张表加锁. 安全性最高, 效率最差.
    需要先明白几个专业词汇:
    脏读: 读取脏数据(未提交的数据)的过程
    不可重复读: 在同一次事务中, 两次读取的数据不一致.
    幻读: 针对整张表格查询, 同一次事务中, 两次查询整张表格结果不一致
    3.6 propagation属性
    事务的传播行为. 常用的属性:
     REQUIRED, 默认值. 必须在事务环境下执行, 如果当前有事务, 则使用当前事务执行; 如果当前没有事务, 创建事务执行.
     REQUIRES_NEW. 必须在事务环境下执行, 如果当前没有事务, 创建事务执行; 如果当前有事务, 将当前事务挂起, 创建新事务执行, 结束后原来的事务继续执行.
     SUPPORTS. 支持事务环境. 如果当前有事务, 使用当前事务环境执行; 如果当前没有事务, 可以在非事务环境执行.
     NOT_SUPPORTED. 不支持事务. 如果当前没有事务, 则正常执行; 如果当前有事务, 将事务挂起, 在非事务环境下执行, 之后原来的事务继续执行.
     MANDATORY. 必须在事务环境执行. 如果当前有事务, 则在当前事务环境执行; 如果当前没有事务, 直接抛出异常.
     NEVER. 必须没有事务. 如果当前没有事务, 正常执行; 如果当前有事务, 直接抛出异常.
     NESTED. 嵌套的. 如果当前没有事务, 创建事务执行; 如果当前已有事务, 再创建一个嵌套的事务继续执行(不会挂起原有的事务).
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值