利用SpringAOP技术完成数据库连接事物实战

本篇博客知识点
利用SpringAOP技术完成事物,
事物具体需求如下:同时存储两条记录信息到两个表Person、User表当其中一个存储失败,另外一个也要回滚。

整个项目的包结构
这里写图片描述
项目的web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" 
    xmlns="http://java.sun.com/xml/ns/javaee" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
    http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
  <display-name></display-name> 

  <context-param>
    <param-name>contextConfigLocation</param-name>
    <!-- 知识点:将两个不同位置的XML文件加载到一起 -->
    <param-value>
        classpath:beans.xml,
        /WEB-INF/applicationContext.xml
    </param-value>
  </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

  <servlet>
    <servlet-name>SaveServlet</servlet-name>
    <servlet-class>cn.hncu.servlet.SaveServlet</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>SaveServlet</servlet-name>
    <url-pattern>/SaveServlet</url-pattern>
  </servlet-mapping>

  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
</web-app>

applicationContext.xml

<?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"
    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/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        ">
      <!-- 通过读取配置文件来连接数据库 -->
       <context:property-placeholder location="WEB-INF/conf/jdbc.properties"/> 
      <bean id="dataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource">
        <property name="driverClass" value="${driver}"/>
        <property name="url" value="${url}"></property>
        <!-- 
          坑: username是一个系统的内部变量名,我们的自定义变量会被它屏蔽,因此我们不要取这个变量名,否则读不出我们设的值
        <property name="username" value="${username}"></property>
         -->
        <property name="username" value="${uName}"/>
        <property name="password" value="${pwd}"/>
      </bean>  


</beans>

核心beans.xml Spring的核心容器

<?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"
    xmlns:tx="http://www.springframework.org/schema/tx"
    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/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

        <bean id="personDao" class="cn.hncu.dao.PersonDao">
            <property name="dataSource" ref="dataSource"/>
        </bean>
        <bean id="UserDao" class="cn.hncu.dao.UserDao">
                    <property name="dataSource" ref="dataSource"></property>
        </bean>  
        <bean id="service" class="cn.hncu.service.IServiceImp">
            <property name="pDao" ref="personDao"/>
            <property name="udao" ref="UserDao"/>
        </bean>
   <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"></bean>

    <bean id="advisor" class="org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor">
        <property name="expression" value="execution(void cn.hncu.service.*.*(..) )"/>
        <property name="advice">
            <bean class="cn.hncu.service.TxAdvice"/>
        </property>
    </bean>
    <bean id="advisor2" class="org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor">
        <property name="expression" value="execution(* *..*.getConnection(..) )"/>
        <property name="advice">
            <bean class="cn.hncu.service.ConAdvice"/>
        </property>
    </bean>
</beans>

用来处理事务的通知—

package cn.hncu.service;

import java.sql.Connection;

import javax.sql.DataSource;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;


public class TxAdvice implements MethodInterceptor,ApplicationContextAware{
    private ApplicationContext ctx =null;

    @Override//用来拿到Spring容器
    public void setApplicationContext(ApplicationContext ctx)
            throws BeansException {
        this.ctx=ctx;
    }

    //通知---这个里面需要拿到dataSource,所以需要先拿到Spring的容器
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        DataSource dataSource = ctx.getBean(DataSource.class);
        Connection con = dataSource.getConnection();
        Object res =null;
        try {
            con.setAutoCommit(false);
            //开启一个事务
            res = invocation.proceed();//放行
            con.commit();//提交
            System.out.println("提交一个事务...");
        } catch (Exception e) {
            con.rollback();
            System.out.println("事务回滚...");
        }finally{
            con.setAutoCommit(true);//关闭事务
            con.close();
        }
        return res;
    }


}

用来拦截getConnection的通知

package cn.hncu.service;

import java.lang.reflect.Method;
import java.sql.Connection;

import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodProxy;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;


public class ConAdvice implements MethodInterceptor{
    private ThreadLocal<Connection> t = new ThreadLocal<Connection>();
    @Override
    public Object invoke(MethodInvocation inc) throws Throwable {
        Connection con = t.get();
        if(con==null){
            final Connection  con0 = (Connection) inc.proceed();
            Callback callback = new net.sf.cglib.proxy.MethodInterceptor(){
                @Override
                public Object intercept(Object proxiedObj, Method method,
                        Object[] args, MethodProxy proxy) throws Throwable {
                    if(method.getName().equals("close")){
                        return null;
                    }
                    return method.invoke(con0, args);//放行
                }
            };
            //生成代理后的对象
            con = (Connection) Enhancer.create(Connection.class, callback);
            t.set(con);
            System.out.println("本地线程管理对象t中不存在,放入一个连接"+con);
        }
        System.out.println("从本地线程管理对象t中取出一个连接"+con);
        return con;
    }

}

Servlet

package cn.hncu.servlet;

import java.io.IOException;
import java.sql.SQLException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import cn.hncu.domain.Person;
import cn.hncu.domain.User;
import cn.hncu.service.IService;

public class SaveServlet extends HttpServlet {
    private IService service = null;

    @Override
    public void init() throws ServletException {
        ApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(getServletContext());

    //获取web项目中的spring容器--- 再通过容器拿到Service
        service = ctx.getBean("service", IService.class);
    }

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doPost(request, response);
    }

    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        Person person = new Person("p001","Person1");
        User user = new User("hncu", "1234");
        try {
            System.out.println(service);
            service.save(person, user);
        } catch (SQLException e) {

            System.out.println("出錯--------");
            e.printStackTrace();
        }
    }

}

总结:想要完成JDBC的事物,应该要在切点在service层,事物要成功的核心在于connection是要同一个,因此就在需要拦截getConnection方法。所以利用SpringAOP切面技术控制好这两个点就可以了,其他小知识点包括,通过读取配置文件链接数据库,EL表达式~ ,Servlet是由Tomcat服务器创建,我们就不能把他放到Bean 通过ref注入service,解决方法是Servlet初始化时 去拿Spring容器里面的service.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值