【框架】[Spring] 基于Spring框架的Web应用演示(附带cglib工具进行动态代理)

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS PostgreSQL,集群系列 2核4GB
简介: 转载请注明出处:http://blog.csdn.net/qq_26525215本文源自【大学之旅_谙忆的博客】前言:Spring也差不多学了Ioc控制反转和实现AOP技术的两种方式了,分享一个学习Spring,用来入门挺好的例子。

转载请注明出处:http://blog.csdn.net/qq_26525215

本文源自大学之旅_谙忆的博客

前言:

Spring也差不多学了Ioc控制反转和实现AOP技术的两种方式了,分享一个学习Spring,用来入门挺好的例子。

如果你是刚刚学习Spring,那么此实例应该可以很好的帮助你应用Spring到Web项目中。

里面的DAO层-提交数据库的事务我并没有使用Spring 的注解功能,而是用spring的AOP来实现的。这样更灵活,其实,框架为我们做的事越多,我们就越受框架的约束。想把功能做灵活,就越难实现。

只要我们把底层学好,框架的功能我们都能自己写出来的,而且自己写出来的东西,肯定会更熟悉。
框架是为了降低程序之间的依赖性和耦合性,使重用性达到最高。

学习框架,我更多的希望自己能学会框架的思想,理解为什么!

首先准备数据库:

create database mydb charset=utf8;

create table stud(
  s_id varchar(32) primary key,
  s_name varchar(40)
);

create table book(
  b_id int primary key  auto_increment,
  b_name varchar(40)
);

准备好这2个表:

Jar包少不了:

相信学到这一步的朋友应该有了自己的一个配套包了吧,在这里我就不去一 一将包链接写出了。
如果需要这些包的,在本博客最后我会给出整个项目的链接,请到里面的WEB-INF/lib目录下去下载。

配好web.xml:

配置web.xml-以使用Spring。

  <context-param>
        <!-- param的name必须为contextConfigLocation,Spring内部会解析的 -->
        <param-name>contextConfigLocation</param-name>
        <!-- contextConfigLocation参数的值,课配置多个,用英文逗号隔开 -->
        <param-value>
            classpath:beans.xml,
            /WEB-INF/conf/applicationContext.xml
        </param-value>
  </context-param>
  <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>

org.springframework.web.context.ContextLoaderListener监听器的作用就是启动Web容器时,自动装配ApplicationContext的配置信息。因为它实现了ServletContextListener这个接口,在web.xml配置这个监听器,启动容器时,就会默认执行Spring实现的方法。

小知识点:
容器对于web.xml的加载过程是context-param >> listener >> fileter >> servlet.

接下来就是写:
classpath:beans.xml和/WEB-INF/conf/applicationContext.xml这2个xml。
classpath:代表beans.xml的位置在src(bin)目录下。

既然需要连接数据库,我们还需要一个配置文件jdbc.properties,声明一些数据库的协议(其实可以在applicationContext.xml中直接配置的,可以不用这个文件)

jdbc.properties:

#如果是utf-8编码,第一行必须空一行.因为utf-8的文件开头有一个符号
#在本例中,我用这个配置文件会出现账号密码错误,无法连接数据库,原因未知
#所以,我在本例是直接配置dataSource的,未用到本文件
#数据库驱动包
driver=com.mysql.jdbc.Driver
#连接数据库的协议--三个"/"代表通过数据库默认端口连接本机的数据库,也可以写成//localhost:3306/
url=jdbc:mysql:///mydb?characterEncoding=utf-8
username=root
pwd=1234
#其实前面的4个变量名都是自己随便可以取的,因为真正的读取不是在这里
#真正的读取在applicationContext.xml中

在applicationContext.xml配置如下就可以拿到数据库连接了。

    <!-- 使用jdbc.properties配置文件,就要写下面这句 -->
<!--    <context:property-placeholder location="WEB-INF/conf/jdbc.properties"/> -->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource">
        <!-- 使用jdbc.properties配置文件,类似如下配置 -->
<!--        <property name="driverClass" value="${driver}"></property> -->
        <property name="driverClass" value="com.mysql.jdbc.Driver"></property> 
        <property name="url" value="jdbc:mysql:///mydb?characterEncoding=UTF-8"></property> 
        <property name="username" value="root"></property> 
        <property name="password" value="1234"></property> 
    </bean>

beans.xml

写好DAO,service,servlet层的架构-方法和变量:
在beans.xml中配置好DAO,service的初始化bean,初始化属性。
而由于我们在web.xml配置了servlet,是Tomcat帮我们new-servlet的,所以,但是我们需要在servlet中需要访问service的对象,这个时候,我们就可以利用servlet的生命周期,在init方法中,给service对象赋值.

<?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:tx="http://www.springframework.org/schema/tx"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
                http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
                http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
    <bean id="studDao" class="cn.hncu.stud.dao.StudDaoJdbc">
       <!--ref="dataSource",引用applicationContext.xml中的dataSource  -->
       <property name="dataSource" ref="dataSource"></property>
    </bean>
    <bean id="bookDao" class="cn.hncu.stud.dao.BookDaoJdbc">
       <!--ref="dataSource",引用applicationContext.xml中的dataSource  -->
       <property name="dataSource" ref="dataSource"></property>
    </bean>

    <bean id="saveService" class="cn.hncu.stud.service.StudServiceImpl">
        <property name="studDao" ref="studDao"></property>
        <property name="bookDao" ref="bookDao"></property>
    </bean>
</beans>

servlet中加入此方法,实现service的初始化:

@Override
    public void init() throws ServletException {
        //在这里,我们可以直接获取Web中的Spring容器-不能重新去new,因为那样就不是同一个容器的了
        ApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
        service=ctx.getBean(ISaveService.class);
    }

TxAdvice-AOP通知

package cn.hncu.utils;

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;

//另外一种方法获取Web中的spring容器--实现ApplicationContextAware接口
public class TxAdvice implements MethodInterceptor,ApplicationContextAware{
    private ApplicationContext ctx =null;

    @Override
    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;
    }


}

事务

如果只代理到上面这里,写con.close方法其实会出问题的。
当然,本例很简单,servlet只请求了一个service中的一个方法,这样写没什么问题,
但是,假如我有多个service和一个service有多个方法,需要被一个用户请求servlet时同时调用时,这个连接就不能被关闭了。
因为Spring容器的事务机制的实质是对传统JDBC的封装,也即是Spring事务管理无论是对单数据库实例还是分布式数据库实例,要实现事务管理,那么必须保证在一个事务过程获得Connetion对象是同一个。

假如是servlet调用多个service或service中多个方法,需要实现的是同一个事务,我们可以:在service中写一个综合方法,在其中调用其它方法,然后给综合方法设置代理,因为这个综合方法在这里就是一个业务
,多个service,原理一样。

AOP拦截getConnection()方法,cglib工具进行动态代理Connection

然后再拦截Connection的close方法!

package cn.hncu.utils;

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 CloseAdvice implements MethodInterceptor{
    private ThreadLocal<Object> t = new ThreadLocal<Object>();

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {

        Object obj = t.get();
        if(obj==null){

            final Object con = invocation.proceed();//返回原型对象Connection

            //通过cglib工具进行动态代理
            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;
                    }
                    //con为原型Connection对象
                    return method.invoke(con, args);
                }
            };

            //obj为cglib工具代理后的Connection对象
            obj=Enhancer.create(Connection.class, callback);
            t.set(obj);
        }
        return obj;
    }





}

在applicationContext.xml中配置拦截getConnection()

<bean id="conClose" class="org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor">
       <property name="expression" value="execution( * *..*.*.getConnection() )"></property>
       <property name="advice">
            <bean id="advice" class="cn.hncu.utils.CloseAdvice"></bean>
       </property>
    </bean>

接下来就是要用到AOP了,拦截事务。
拦截service层的。

拦截事务的切面配置:

<!-- 自动代理 -->
    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"></bean>
    <!-- 事务  切面=切点+通知 -->
    <bean id="tx" class="org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor">
        <!-- 拦截cn.hncu包下的,方法名最后为Service的任意返回值任意参数的方法 -->
        <property name="expression" value="execution (* cn.hncu..*Service.*(..) )">
        </property>
        <property name="advice">
            <bean class="cn.hncu.utils.TxAdvice"></bean>
        </property>
    </bean>

DAO层的实现类代码:

stud的实现类:

package cn.hncu.stud.dao;

import java.sql.SQLException;
import java.util.UUID;

import javax.sql.DataSource;

import org.apache.commons.dbutils.QueryRunner;

import cn.hncu.stud.domain.Book;
import cn.hncu.stud.domain.Stud;

public class StudDaoJdbc implements StudDAO{
    private DataSource dataSource = null;//依赖注入
    public DataSource getDataSource() {
        return dataSource;
    }

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    @Override
    public void saveStud(Stud stud) throws SQLException {
        String uuid = UUID.randomUUID().toString().replaceAll("-", "");
        stud.setS_id(uuid);
        QueryRunner run = new QueryRunner(dataSource);
        run.update("insert into stud(s_id,s_name) values(?,?)", stud.getS_id(),stud.getS_name());
    }
}

book的实现类

package cn.hncu.stud.dao;

import java.sql.SQLException;

import javax.sql.DataSource;

import org.apache.commons.dbutils.QueryRunner;

import cn.hncu.stud.domain.Book;

public class BookDaoJdbc implements BookDAO{
    private DataSource dataSource = null;//依赖注入

    public DataSource getDataSource() {
        return dataSource;
    }

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    @Override
    public void saveBook(Book book) throws SQLException {
        QueryRunner run = new QueryRunner(dataSource);
        run.update("insert into book(b_name) values(?)", book.getB_name());
    }

}

测试:

打开页面输入:

点按钮提交:


service:cn.hncu.stud.service.SaveServiceImpl@4adeee3d这个输出是我在servlet中测试一个错误的时候的输出。

再看数据库的数据:

然后,我们来测试下,事务回滚情况:

因为service层是:

@Override
    public void saveStudAndBook(Stud stud, Book book) throws SQLException {
        studDao.saveStud(stud);
        bookDao.saveBook(book);
    }

后调用bookDao的,所以,我们让saveBook挂了,改一下saveBook的方法中sql语句为:

这样,后面Book的存储肯定是出问题的。

再来测试:

点添加。

可以看到事务回滚了,但是看这里没用,我们去看下stud和book表有没有存储。当然book表肯定是不会被存储的,去看stud表就可以了:

可以看到,李四这个用户并没有被保存,证明事务起作用了。

完整项目源码链接:

–>点击访问本系列源码–

本文章由[谙忆]编写, 所有权利保留。

转载请注明出处:http://blog.csdn.net/qq_26525215

本文源自大学之旅_谙忆的博客

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
11天前
|
XML 安全 Java
|
14天前
|
缓存 NoSQL Java
什么是缓存?如何在 Spring Boot 中使用缓存框架
什么是缓存?如何在 Spring Boot 中使用缓存框架
20 0
|
2天前
|
Kubernetes 安全 Devops
有效抵御网络应用及API威胁,聊聊F5 BIG-IP Next Web应用防火墙
有效抵御网络应用及API威胁,聊聊F5 BIG-IP Next Web应用防火墙
21 10
有效抵御网络应用及API威胁,聊聊F5 BIG-IP Next Web应用防火墙
|
12天前
|
Java 开发者 微服务
Spring Boot 入门:简化 Java Web 开发的强大工具
Spring Boot 是一个开源的 Java 基础框架,用于创建独立、生产级别的基于Spring框架的应用程序。它旨在简化Spring应用的初始搭建以及开发过程。
28 6
Spring Boot 入门:简化 Java Web 开发的强大工具
|
11天前
|
XML Java 数据格式
Spring Core核心类库的功能与应用实践分析
【12月更文挑战第1天】大家好,今天我们来聊聊Spring Core这个强大的核心类库。Spring Core作为Spring框架的基础,提供了控制反转(IOC)和依赖注入(DI)等核心功能,以及企业级功能,如JNDI和定时任务等。通过本文,我们将从概述、功能点、背景、业务点、底层原理等多个方面深入剖析Spring Core,并通过多个Java示例展示其应用实践,同时指出对应实践的优缺点。
35 14
|
7天前
|
缓存 Java 数据库连接
Spring框架中的事件机制:深入理解与实践
Spring框架是一个广泛使用的Java企业级应用框架,提供了依赖注入、面向切面编程(AOP)、事务管理、Web应用程序开发等一系列功能。在Spring框架中,事件机制是一种重要的通信方式,它允许不同组件之间进行松耦合的通信,提高了应用程序的可维护性和可扩展性。本文将深入探讨Spring框架中的事件机制,包括不同类型的事件、底层原理、应用实践以及优缺点。
39 8
|
9天前
|
XML 前端开发 安全
Spring MVC:深入理解与应用实践
Spring MVC是Spring框架提供的一个用于构建Web应用程序的Model-View-Controller(MVC)实现。它通过分离业务逻辑、数据、显示来组织代码,使得Web应用程序的开发变得更加简洁和高效。本文将从概述、功能点、背景、业务点、底层原理等多个方面深入剖析Spring MVC,并通过多个Java示例展示其应用实践,同时指出对应实践的优缺点。
32 2
|
15天前
|
前端开发 JavaScript UED
在数字化时代,Web 应用性能优化尤为重要。本文探讨了CSS与HTML在提升Web性能中的关键作用及未来趋势
在数字化时代,Web 应用性能优化尤为重要。本文探讨了CSS与HTML在提升Web性能中的关键作用及未来趋势,包括样式表优化、DOM操作减少、图像优化等技术,并分析了电商网站的具体案例,强调了技术演进对Web性能的深远影响。
22 5
|
17天前
|
存储 Java 关系型数据库
在Spring Boot中整合Seata框架实现分布式事务
可以在 Spring Boot 中成功整合 Seata 框架,实现分布式事务的管理和处理。在实际应用中,还需要根据具体的业务需求和技术架构进行进一步的优化和调整。同时,要注意处理各种可能出现的问题,以保障分布式事务的顺利执行。
32 6
|
15天前
|
消息中间件 Java Kafka
Spring Boot 与 Apache Kafka 集成详解:构建高效消息驱动应用
Spring Boot 与 Apache Kafka 集成详解:构建高效消息驱动应用
23 1