Spring介绍

  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 < FATAL
log4j.rootLogger = info ,stdout,FILE  

#Appenders  (输出源)
### 输出到控制台 ###
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.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 = WARN


log4j.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哦~都超级用心的!




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值