Spring介绍

来源:互联网 发布:三枪内裤怎么样 知乎 编辑:程序博客网 时间:2024/05/21 05:38

  Spring,春天~
  开玩笑,怎么可能是春天啊喂,那是弹簧?
  别逗了

  那是什么?
  Spring,是一个企业级框架,它的出现是为了简化操作,使开发更容易,最关键的是,它是开源的。

Spring是一个轻量级的控制反转(IOC)和面向切面(AOP)的容器框架

  大家都喜欢使用这张图,那我也摆一下吧
  
  Spring框架图
  
  上图是Spring七大模块明确定义,虽然我还没有完全使用过,鉴于作者能力有限,这篇权当做科普文章,简单介绍Spring最重要的两个概念——IOC和AOP。

IOC(控制反转)

  使用IOC容器来管理对象的生命周期、依赖关系等,从而使应用程序的配置和依赖性规范与实际的应用代码分开。简单来说,使用IOC可以让我们解耦我们的程序代码。
  
  何为解耦呢?
  耦合,是两个事物之间存在一种相互作用和相互影响的关系,解耦就是解除这种关系。
  我们举个例子,在java的代码中,当我们使用new新建对象的时候,我们就不知不觉中产生了一种耦合关系,是在当前使用模块和对象所在类的一种耦合。我们消除这个耦合的过程(或者说降低耦合)就是解耦。

IOC的实现是基于java技术的反射机制,通俗的说,反射就是根据给出的类名(字符串)来生成对象。

  这里我么不管java的反射机制如何实现,那要另开一篇专题。我们只关心Spring如何实现IOC。

  用过Spring的朋友都知道,我们通过配置xml文件中的beans来管理我们的Spring容器中的内容。我们使用Spring实现IOC也是在配置beans中体现的。

  啰里啰嗦一大堆,可能会晕晕的,直接实操,配合讲解,我们剥茧抽丝,巴拉巴拉~
  
  先看个例子
  限于篇幅,这里只讲解通过beans配置文件来获取对象。
  我们先看一下使用Spring时需要使用的jar包,这里我们使用maven依赖,所以直接给出的是配置文件中的依赖关系。
  

    <dependency>        <groupId>junit</groupId>        <artifactId>junit</artifactId>        <version>4.12</version>        <scope>test</scope>    </dependency>    <!-- https://mvnrepository.com/artifact/org.springframework/spring-beans -->    <dependency>        <groupId>org.springframework</groupId>        <artifactId>spring-beans</artifactId>        <version>4.3.9.RELEASE</version>    </dependency>    <dependency>        <groupId>org.springframework</groupId>        <artifactId>spring-core</artifactId>        <version>4.3.9.RELEASE</version>    </dependency>    <dependency>        <groupId>org.springframework</groupId>        <artifactId>spring-context</artifactId>        <version>4.3.9.RELEASE</version>    </dependency>    <dependency>        <groupId>org.springframework</groupId>        <artifactId>spring-test</artifactId>        <version>4.3.9.RELEASE</version>    </dependency></dependencies>

  这里共使用了四个Spring依赖包,具体什么作用不再详述,建议百度或者参考官方文档。
  
  然后看一下我的项目结构,以作参考
  这里写图片描述

package entity;/** * Created by yubotao on 2017/09/24. */public class User {    private String firstName;    private String lastName;    //getter , setter and toString.}
package entity;/** * Created by yubotao on 2017/09/24. */public class AnotherUser {    private String firstName;    private String lastName;    //getter , setter and toString.}
package entity;/** * Created by yubotao on 2017/09/24. */public class FatherUser {    private BabyUser babyUser;    private String power;    private Integer age;    //getter , setter and toString.}
package entity;/** * Created by yubotao on 2017/09/24. */public class BabyUser {    private String firstName;    private String lastName;    //getter , setter and toString.}
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"       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">    <bean name="user" class="entity.User"/>    <bean name="anotherUser" class="entity.AnotherUser"/>    <bean name="baby" class="entity.BabyUser">        <property name="firstName" value="YU"/>        <property name="lastName" value="Botao"/>    </bean>    <bean name="father" class="entity.FatherUser">        <property name="babyUser" ref="baby"/>    </bean></beans>
import entity.AnotherUser;import entity.FatherUser;import entity.User;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;import org.springframework.test.context.ContextConfiguration;import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;/** * Created by yubotao on 2017/09/24. */@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration({"classpath:beans.xml"})public class BeansTest {    @Test    public void method1(){        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");        User user1 =(User) applicationContext.getBean("user");        user1.setFirstName("YU");        user1.setLastName("Botao");        System.out.println(user1);    }    @Autowired    AnotherUser anotherUser;    @Test    public void method2(){        anotherUser.setFirstName("YU");        anotherUser.setLastName("Botao");        System.out.println(anotherUser);    }    @Autowired    FatherUser fatherUser;    @Test    public void ref(){        fatherUser.setAge(18);        fatherUser.setPower("*");        System.out.println(fatherUser);    }}

  为什么把代码全贴出来了?
  先去跑起来吧,这样你才会有一个直观的感受,这样做是行得通的,如果跑不通,请耐心一点,排除错误,这是必经之路。
  

代码剖析

  接下来我们就聊聊吧,这些代码是什么意思,为什么这么写啊?
  我们先看配置文件
  

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"       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">

  文件头:简单的理解为规定,就像法律一样,你不可以越线。这里没有声明的一些标签是无法开启使用的。
  
  第一种方式

<bean name="user" class="entity.User"/>

  Spring的beans管理,这里就是Spring实现IOC的具体体现,我们将这些实体类交由Spring容器统一管理,需要取用对象时,由Spring在相应的工厂类中为我们生成相关对象,我们只需通过使用配置文件的方式,即可达到解耦的效果。
  可以看到我们在测试类中的相关代码
  

@Test    public void method1(){        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");        User user1 =(User) applicationContext.getBean("user");        user1.setFirstName("YU");        user1.setLastName("Botao");        System.out.println(user1);    }

  这里我们使用ClassPathXmlApplicationContext("beans.xml")来读取配置文件,并返回一个ApplicationContext对象;这个对象继承BeanFactory工厂类,这也是我们看到的它拥有getBean()方法。
  此时我们成功从工厂中获取了一个User对象,然后使用它的方法,最后打印,成功实现对象的创建。
  这里写图片描述

  这就是我们说的不使用new新建对象,通过SpringIOC的注入方式进行创建,实现解耦。

  第二种方式
  接下来我们看下一个,另外一种方式来新建对象,通过注解的方式注入,也是我们最常用的方式。
  

    @Autowired    AnotherUser anotherUser;    @Test    public void method2(){        anotherUser.setFirstName("YU");        anotherUser.setLastName("Botao");        System.out.println(anotherUser);    }

  这里需要注意的是,在使用这个注解的时候,我们需要对我们的测试类添加一些额外的注解
  

@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration({"classpath:beans.xml"})

  否则会报空指针异常。至于为什么,这里不表,感兴趣的朋友可以自己去查。
  这里我们相当于使用
  

    @Autowired    AnotherUser anotherUser;

来代替

        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");        AnotherUser anotherUser=(AnotherUser) applicationContext.getBean("anotherUser");

当然那些额外的注解也要算上。
  
  通常在使用注解的时候,我们会开启一个自动扫描包注解的标签<context:component-scan base-package="packagePath"/>,这里不做讲解。可以关注后续文章~

  bean的依赖关系ref
  我们看一下我们剩下的两个类,FatherUserBabyUser,这个取名我们就能看出端倪。FatherUser中包含了一个BabyUser的私有对象变量;这时,我们看一下我们在配置文件中的相关配置:
  

    <bean name="baby" class="entity.BabyUser">        <property name="firstName" value="YU"/>        <property name="lastName" value="Botao"/>    </bean>    <bean name="father" class="entity.FatherUser">        <property name="babyUser" ref="baby"/>    </bean>

  我们看到,我们首先注册了一个BabyUser的bean,并且通过<property>标签将它的相关变量进行了定义,感兴趣的朋友可以去查一下<property>标签,还有另外一个标签<constructor-arg>,也很有趣。
  <property>标签有三个属性,分别为必须的(required)name,以及对应的value或者ref,且二者只能选择其一。value正如它的字面意义,赋值;ref是”reference”的简写,“参考”意,即该属性参考其他的bean,正如我们看到的,FatherUserbabyUser属性参考了BabyUser,而我又对BabyUser进行了赋值,所以,当我们运行相应的测试代码时
  

    @Autowired    FatherUser fatherUser;    @Test    public void ref(){        fatherUser.setAge(18);        fatherUser.setPower("*");        System.out.println(fatherUser);    }

不要忘记之前说的特别注解哦~
  可发现结果如下:
这里写图片描述
  我们看到,在配置文件中对于BabyUser的赋值,同样应用到FatherUser中,因为我们配置了一个参考属性。这种参考属性最常用在我们使用数据库连接时参考dataSource

  同时我们看到我们在测试的时候打印出了很多日志信息,这得益于Spring的日志机制,通过这些日志信息,也让我们可以对IOC创建对象的过程有一个大致的了解。

  
  到了这里,我就讲完Spring的IOC内容了,但是Spring的广阔绝不仅仅这些,希望你在“春天”的海洋里遨游的时候可以沐浴春风!

AOP

  首先强调一下,如下这篇教程将绝大部分的aop内容讲解的差不多了,为了节省资源,建议首先移步该教程,然后在回来看我狗尾续貂。
  就是这,点吧,不是什么奇怪的网站。

  好,那我就开始了。

什么是AOP?

面向切面编程(Aspect Oriented Programing,AOP)采用横向抽取机制,是面向对象编程(Object Oriented Programing,OOP)的补充和完善。OOP引入封装、继承、多态等概念来建立一种对象层次结构,OOP允许开发者定义纵向的关系,但并不适合定义横向的关系,例如日志功能、权限管理、异常处理等,该类功能往往横向地散布在核心代码当中,这种散布在各处的无关代码被称为横切。AOP恰是一种横切技术,解剖开封装对象的内部,将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为Aspect(切面),所谓切面,简单的说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。

  AOP最常用的几种使用便是:声明式事务管理、权限校验和日志记录。本文放弃权限校验,仅介绍事务管理以及日志记录。

声明式事务管理

  先看事务是什么?

事务是一系列动作,这一系列动作综合在一起组成一个完整的工作单元,如果有任何一个动作执行失败,那么事务就将回到最开始的状态,仿佛一切都没发生过。

  接下来的介绍我就不赘述了,之前提到的教程中有详细讲解,其他想详细了解的地方,请移步:点吧,不会怀孕。

  接下来就介绍如何使用SpringAOP来管理事务,当然,只有AOP是不够的,还需要其它的东东~
  那我就先把需要的jar包提供一下吧。
  

<dependencies>        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->        <dependency>            <groupId>mysql</groupId>            <artifactId>mysql-connector-java</artifactId>            <version>5.1.42</version>        </dependency>        <!-- https://mvnrepository.com/artifact/commons-dbcp/commons-dbcp -->        <dependency>            <groupId>commons-dbcp</groupId>            <artifactId>commons-dbcp</artifactId>            <version>1.4</version>        </dependency>        <!-- https://mvnrepository.com/artifact/commons-pool/commons-pool -->        <dependency>            <groupId>commons-pool</groupId>            <artifactId>commons-pool</artifactId>            <version>1.6</version>        </dependency>        <!-- https://mvnrepository.com/artifact/org.springframework/spring-beans -->        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-beans</artifactId>            <version>4.3.9.RELEASE</version>        </dependency>        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-core</artifactId>            <version>4.3.9.RELEASE</version>        </dependency>        <!-- https://mvnrepository.com/artifact/org.springframework/spring-aop -->        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-aop</artifactId>            <version>4.3.9.RELEASE</version>        </dependency>        <!-- https://mvnrepository.com/artifact/aopalliance/aopalliance -->        <dependency>            <groupId>aopalliance</groupId>            <artifactId>aopalliance</artifactId>            <version>1.0</version>        </dependency>        <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->        <dependency>            <groupId>org.aspectj</groupId>            <artifactId>aspectjweaver</artifactId>            <version>1.8.10</version>        </dependency>        <!-- https://mvnrepository.com/artifact/org.springframework/spring-test -->        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-test</artifactId>            <version>4.3.9.RELEASE</version>            <scope>test</scope>        </dependency>        <!-- https://mvnrepository.com/artifact/org.springframework/spring-tx -->        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-tx</artifactId>            <version>4.3.9.RELEASE</version>        </dependency>        <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-context</artifactId>            <version>4.3.9.RELEASE</version>        </dependency>        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-jdbc</artifactId>            <version>4.3.9.RELEASE</version>        </dependency>        <!-- https://mvnrepository.com/artifact/c3p0/c3p0 -->        <dependency>            <groupId>c3p0</groupId>            <artifactId>c3p0</artifactId>            <version>0.9.1.2</version>        </dependency>        <!-- https://mvnrepository.com/artifact/log4j/log4j -->        <dependency>            <groupId>log4j</groupId>            <artifactId>log4j</artifactId>            <version>1.2.17</version>        </dependency>        <dependency>            <groupId>junit</groupId>            <artifactId>junit</artifactId>            <version>4.12</version>        </dependency>    </dependencies>

  然后看一下我的项目结构图
  这里写图片描述
  那个羞羞的马赛克是一会要讲的东西,我们先放一下。
  还是按惯例,先贴代码跑起来,然后在讲解。
  

package POJO;/** * Created by yubotao on 2017/09/25. */public class User {    private int userID;  //用户ID    private String userName;  //用户名    private String password;    //用户密码    //getter,setter and toString.}
package POJO;/** * Created by yubotao on 2017/09/25. */public class UserFactory {        public User createUser(String name, int id, String password){        User user = new User();        user.setUserName(name);        user.setUserID(id);        user.setPassword(password);        return user;    }}
package Dao;import POJO.User;import java.util.List;/** * Created by yubotao on 2017/09/25. */public interface UserDao {    public int addUser(User user);    public int updateUser(User user);    public int deleteUser(User user);    public User findUserByID(int id);    public List<User> findAllUser();}
package Daoimpl;import Dao.UserDao;import POJO.User;import org.springframework.jdbc.core.BeanPropertyRowMapper;import org.springframework.jdbc.core.JdbcTemplate;import org.springframework.jdbc.core.RowMapper;import java.util.List;/** * Created by yubotao on 2017/09/25. */public class UserDaoImpl implements UserDao {    private JdbcTemplate jdbcTemplate;    public void setJdbcTemplate(JdbcTemplate jdbc){        this.jdbcTemplate = jdbc;    }    public int addUser(User user) {        // TODO Auto-generated method stub        String sql = "insert into t_user(userid,username,password)values(?,?,?)";        Object[] obj = new Object[]{                user.getUserID(),                user.getUserName(),                user.getPassword()        };        return this.execute(sql, obj);    }    public int updateUser(User user) {        // TODO Auto-generated method stub        String sql = "update t_user set username=?,password=? where userid=?";        Object[] obj = new Object[]{                user.getUserName(),                user.getPassword(),                user.getUserID()        };        return this.execute(sql, obj);    }    public int deleteUser(User user) {        // TODO Auto-generated method stub        String sql = "delete from t_user where userid=?";        Object[] obj = new Object[]{                user.getUserID()        };        return this.execute(sql, obj);    }    private int execute(String sql, Object[] obj){        return this.jdbcTemplate.update(sql, obj);    }    public User findUserByID(int id) {        // TODO Auto-generated method stub        String sql = "select * from t_user where userid=?";        RowMapper<User> rowMapper = new BeanPropertyRowMapper(User.class);        return this.jdbcTemplate.queryForObject(sql, rowMapper, id);    }    public List<User> findAllUser() {        // TODO Auto-generated method stub        String sql = "select * from t_user";        RowMapper<User> rowMapper = new BeanPropertyRowMapper(User.class);        return this.jdbcTemplate.query(sql, rowMapper);    }}

  这里沿用了原教程里的JDBCTemplate,使用Mybatis也是可以的,这些在我之前的blog里都有相关讲解,这里不赘述。
  

package Dao;/** * Created by yubotao on 2017/09/25. */public interface AccountDao {    public void addAccount(int id, double account);    public void inAccount(int id, double account);    public void outAccount(int id, double account);}
package Daoimpl;import Dao.AccountDao;import org.springframework.jdbc.core.JdbcTemplate;/** * Created by yubotao on 2017/09/25. */public class AccountDaoImpl implements AccountDao {    private JdbcTemplate jdbcTemplate;    public void setJdbcTemplate(JdbcTemplate jdbc){        this.jdbcTemplate = jdbc;    }    public void addAccount(int id, double account) {        // TODO Auto-generated method stub        String sql = "insert into account values(" + id + "," + account + ")";        this.jdbcTemplate.execute(sql);    }    public void inAccount(int id, double account) {        // TODO Auto-generated method stub        String sql = "update account set account=account+? where userid=?";        this.jdbcTemplate.update(sql, account,id);    }    public void outAccount(int id, double account) {        // TODO Auto-generated method stub        String sql = "update account set account=account-? where userid=?";        this.jdbcTemplate.update(sql, account,id);    }}
package Service;import POJO.User;/** * Created by yubotao on 2017/09/25. */public interface AccountService {    /*    * 转账,实现从outUser转出account金额的钱到inUser    */    public void transfer(User outUser, User inUser, double account);}
package ServiceImpl;import Dao.AccountDao;import POJO.User;import Service.AccountService;/** * Created by yubotao on 2017/09/25. */public class AccountServiceImpl implements AccountService {    private AccountDao accountDao;    public void setAccountDao(AccountDao accountDao) {        this.accountDao = accountDao;    }    public void transfer(User outUser, User inUser, double account){        // TODO Auto-generated method stub        this.accountDao.outAccount(outUser.getUserID(), account);        //模拟程序异常,无法执行inAccount方法        int i = 1 / 0;        this.accountDao.inAccount(inUser.getUserID(), account);    }}
package util;import org.springframework.jdbc.core.JdbcTemplate;/** * Created by yubotao on 2017/09/25. */public class CreateTables {    //通过JdbcTemplate对象创建表    private JdbcTemplate jdbcTemplate;    public void setJdbcTemplate(JdbcTemplate jdbc){        jdbcTemplate = jdbc;    }    public void createTable(String sql){        jdbcTemplate.execute(sql);    }}
package Client;import Dao.AccountDao;import Dao.UserDao;import POJO.User;import POJO.UserFactory;import Service.AccountService;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;import util.CreateTables;/** * Created by yubotao on 2017/09/25. */public class Client {    public static void main(String[] args) {        //定义配置文件路径        String path = "classpath:Transaction.xml";        //加载配置文件        ApplicationContext applicationContext = new ClassPathXmlApplicationContext(path);        //获取CreateTables实例        CreateTables tables = (CreateTables) applicationContext.getBean("createTables");        //创建t_user表        String create_user = "create table t_user(userid int primary key auto_increment, username varchar(20), password varchar(32))";        tables.createTable(create_user);        //创建工资表,工资表的userid关联t_user表的userid        String create_account = "create table account(userid int primary key auto_increment, account double, foreign key(userid) references t_user(userid) on delete cascade on update cascade)";        tables.createTable(create_account);        //创建用户        User user1 = new UserFactory().createUser("张三", 1, "zhangsan");        User user2 = new UserFactory().createUser("李四", 2, "lisi");        User user3 = new UserFactory().createUser("王五", 3, "wangwu");        User user4 = new UserFactory().createUser("赵六", 4, "zhaoliu");        //获取用户数据访问对象        UserDao userDao = (UserDao) applicationContext.getBean("userDao");        System.out.println(userDao.addUser(user1));        System.out.println(userDao.addUser(user2));        System.out.println(userDao.addUser(user3));        System.out.println(userDao.addUser(user4));        //获取存款数据访问对象        AccountDao account = (AccountDao) applicationContext.getBean("accountDao");        account.addAccount(1, 100);        account.addAccount(2, 290.5);        account.addAccount(3, 30.5);        account.addAccount(4, 50);        AccountService accountService = (AccountService) applicationContext.getBean("accountService");        accountService.transfer(user1, user3, 10);}}

  上面的所有代码都是没有难度的,所以就不浪费时间讲解了,如果有某些地方看不懂,就请花点时间找找资料或者再认真看看。
  我们将重点放在最关键的配置文件的讲解上
  不过我先把日志的配置文件贴出来,再讲重要的,hhhhhhh~
  

#Loggers(记录器)#log4j.rootLogger = [ level ] , appenderName1, appenderName2, …#DEBUG < INFO < WARN < ERROR < FATALlog4j.rootLogger = info ,stdout,FILE  #Appenders  (输出源)### 输出到控制台 ###log4j.appender.stdout = org.apache.log4j.ConsoleAppenderlog4j.appender.stdout.Target = System.out#Layouts(布局)#org.apache.log4j.PatternLayout(可以灵活地指定布局模式)log4j.appender.stdout.layout = org.apache.log4j.PatternLayout  log4j.appender.stdout.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n  ### 输出到日志文件 ####org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件)log4j.appender.FILE = org.apache.log4j.DailyRollingFileAppender#直接在项目路径下生成,也可写绝对路径,但是有中文乱码问题,设置utf-8并没有卵用log4j.appender.FILE.File =logs/log.log#Append=false:true表示消息增加到指定文件中,false则将消息覆盖指定的文件内容,默认值是true。log4j.appender.FILE.Append = true  ## 输出WARN级别以上的日志log4j.appender.FILE.Threshold = WARNlog4j.appender.FILE.layout = org.apache.log4j.PatternLayout  log4j.appender.FILE.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n

重要配置文件Transaction.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"       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.xsd        http://www.springframework.org/schema/tx        http://www.springframework.org/schema/tx/spring-tx.xsd        http://www.springframework.org/schema/aop        http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">    <!-- 配置数据源 -->    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">        <!-- 数据库驱动 -->        <property name="driverClass" value="com.mysql.jdbc.Driver"/>        <!-- 连接数据库的URL -->        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test?characterEncoding=utf-8"/>        <!-- 连接数据库的用户名 -->        <property name="user" value="root"/>        <!-- 连接数据的密码 -->        <property name="password" value="yubotao9527"/>    </bean>    <!-- 配置JDBC模板 -->    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">        <!-- 默认必须使用数据源 -->        <property name="dataSource" ref="dataSource"/>    </bean>    <bean id="createTables" class="util.CreateTables">        <!-- 通过setter方法实现JdbcTemplate对象的注入 -->        <property name="jdbcTemplate" ref="jdbcTemplate"/>    </bean>    <bean id="userDao" class="Daoimpl.UserDaoImpl">        <property name="jdbcTemplate" ref="jdbcTemplate"/>    </bean>    <bean id="accountDao" class="Daoimpl.AccountDaoImpl">        <property name="jdbcTemplate" ref="jdbcTemplate"/>    </bean>    <bean id="accountService" class="ServiceImpl.AccountServiceImpl">        <property name="accountDao" ref="accountDao"/>    </bean>    <!-- 事务管理器,依赖于数据源 -->    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">        <property name="dataSource" ref="dataSource"/>    </bean>    <!-- 编写通知:对事务进行增强,需要对切入点和具体执行事务细节 -->    <tx:advice id="txAdvice" transaction-manager="txManager">        <tx:attributes>            <!-- <tx:method> 给切入点添加事务详情                 name:方法名称, *表示任意方法, do* 表示以do开头的方法                 propagation:设置传播行为                 isolation:隔离级别                 read-only:是否只读 -->            <tx:method name="*" propagation="REQUIRED" isolation="DEFAULT" read-only="false"/>            <!--ISOLATION_DEFAULT 默认级别(对大多数数据库来说就是ISOLATION_READ_COMMITTED)-->            <!--ISOLATION_READ_UNCOMMITTED 最低的隔离级别。事实上我们不应该隔离级别,因为在事务完成前,其他事务可以看到该事务所修改的数据。            而在其他事务提交前,该事务也可以看到其他事务所做的修改。-->            <!--ISOLATION_READ_COMMITTED 大多数数据库的默认级别。在事务完成前,其他事务无法看到该事务所修改的数据。            遗憾的是,在该事务提交后,你就可以查看其他事务插入活更新的数据。这意味着在事务的不同点上,如果其他事务修改数据,你会看到不同的数据。-->            <!--ISOLATION_REPEATABLE_READ 该隔离级别确保如果在事务中查询了某个数据集,你至少还能再次查询到相同的数据集,即使其他事务修改了所查询的数据。            然而如果其他事务插入了新数据,你就可以查询到该新插入的数据。-->            <!--ISOLATION_SERIALIZABLE 代价最大、可靠性最高的隔离级别,所有的事务都是俺顺序一个接一个的执行。-->        </tx:attributes>    </tx:advice>    <!-- aop编写,让Spring自动对目标进行代理,需要使用AspectJ的表达式 -->    <aop:config>        <!-- 切入点 -->        <aop:pointcut expression="execution(* ServiceImpl.AccountServiceImpl.*(..))" id="txPointCut"/>        <!-- 切面:将切入点和通知整合 -->        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>    </aop:config></beans>

  前面的bean配置我们已经讲过了,这里我们只需看两个新的东西,这两个标签<tx:advice><aop:config>
  不过我感觉注解都写的这么详细了,还让我讲就有点过分了。不过我还是勉为其难的贴点东西,应付一下吧~
  

你需要在<tx:advice>标签内设置id和transaction-manager属性。 id是该advice bean的标识,而transaction-manager则必须引用一个PlatformTransactionManager bean。
除了这两个属性以外,你还可以通过<tx:attributes>标签定制<tx:advice>标签所创建的通知的行为。这可以让你对transactionAttributes属性表达式所支持的属性以更加结构化的方式进行配置。

详见这篇blog:我不想写了,点吧

  而<aop:pointcut>配置切点,其中expression定义了我们要将这个通知方法放到哪个类的哪个方法,有几种写法,这里支持正则表达式,可以看到我们就使用了正则表达式,如果你不知道正则表达式是什么,请移步:点吧,不会怀孕。

  看下回滚效果
  这里写图片描述
  报错后,这个Service的方法被回滚了,数据库中的数据没有发生任何改变。
  这里写图片描述



  最后,我们捋一下逻辑,我们使用声明式事务管理的过程是怎样的?
  首先,我们将我们的Service定义为一种事务过程,并且使用<tx:advice>对事务进行装配,将这个事务通知通过切面的形式(<aop:config>进行装配)连接到我们定义的Service上,这样就将我们的Service进行了事务管理。

日志记录

  我们之前说到,日志管理有时候也是一种横向的切面流程,所以同样可以使用AOP进行日志记录;但是,这个记录的颗粒度就比较粗化了,所以,细颗粒度的日志记录还是要放到代码块中;而我们使用AOP管理的范围最小的颗粒度也是方法。

  接下来我们就看一下日志记录。
  揭开刚刚羞羞的马赛克面纱
  

package util;import org.apache.log4j.Logger;import org.aspectj.lang.ProceedingJoinPoint;import java.text.SimpleDateFormat;import java.util.Date;/** * Created by yubotao on 2017/09/25. */public class LogHandler {    private static Logger loggerAdvice = Logger.getLogger(LogHandler.class);    //切入点执行之前需要执行的方法    public void LogBefore(){        System.out.println("转账开始时间:" + System.currentTimeMillis());    }    //切入点执行结束执行该方法    public void LogAfter(){        System.out.println("转账结束时间:" + System.currentTimeMillis());    }    //环绕方法使用    public Object around(ProceedingJoinPoint pip)throws Throwable{        //获取组件类名        String className = pip.getTarget().getClass().getName();        //获取调用方法名        String method = pip.getSignature().getName();        //取得数据库连接前时间        long begin = System.currentTimeMillis();        //当前系统时间        String date = new SimpleDateFormat("yyyy-MM-dd:mm:ss").format(new Date());        Object obj = pip.proceed();        //取得数据库连接后时间        long end = System.currentTimeMillis();        //数据库响应时间        int sqlTime = (int) (end-begin);        String msg = date + ",执行了" + className + "." + method + "()";        loggerAdvice.warn(msg + "\t数据库响应时间: " + sqlTime);        return obj;    }}

还是在刚刚的Transaction.xml配置文件中添加相关配置

<!-- 配置日志打印类 -->    <bean id="logHandler" class="util.LogHandler"/>    <aop:config>        <!-- order属性表示横切关注点的顺序,当有多个时,序号依次增加 -->        <aop:aspect id="log" ref="logHandler" order="1">            <!--  切入点为AccountServiceImpl类下的transfer方法 -->            <aop:pointcut id="logTime" expression="execution(* ServiceImpl.AccountServiceImpl.transfer(..))"/>            <aop:before method="LogBefore" pointcut-ref="logTime"/>            <aop:after method="LogAfter" pointcut-ref="logTime"/>            <aop:around method="around" pointcut-ref="logTime"/>        </aop:aspect>    </aop:config>

  <aop:before><aop:after><aop:around>这几个标签就是为了日志管理而生的,也正如它们的字面意思,分别是方法执行前,执行后和环绕方法运行的,注解也比较详细了,这里就不详细解释了。
  看下运行成果吧
  这里写图片描述

  当然,你可能觉得我讲的东西太少了。
 “不过瘾啊,老师可不可再给点力啊“”。
  
  那就看看这个吧:真的都是干货哦~
  还有这个:两者可能会有重复,不过风格不同。

  


呼~终于讲完了,花了我这么多的时间和精力,希望这篇blog可以为你带来帮助,也可以关注一下我的其他blog哦~都超级用心的!




原创粉丝点击