Spring介绍
来源:互联网 发布:三枪内裤怎么样 知乎 编辑:程序博客网 时间:2024/05/21 05:38
Spring,春天~
开玩笑,怎么可能是春天啊喂,那是弹簧?
别逗了
那是什么?
Spring,是一个企业级框架,它的出现是为了简化操作,使开发更容易,最关键的是,它是开源的。
Spring是一个轻量级的控制反转(IOC)和面向切面(AOP)的容器框架
大家都喜欢使用这张图,那我也摆一下吧
上图是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
我们看一下我们剩下的两个类,FatherUser
和BabyUser
,这个取名我们就能看出端倪。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,正如我们看到的,FatherUser
的babyUser
属性参考了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哦~都超级用心的!
- Spring 介绍
- Spring介绍
- spring 介绍
- spring介绍
- Spring介绍
- spring介绍
- spring介绍
- spring介绍
- spring介绍
- Spring介绍
- Spring介绍
- Spring 介绍
- spring介绍
- Spring 介绍
- Spring 介绍
- spring介绍
- Spring介绍
- Spring介绍
- Hibernate学习---第十三节:Hibernate之锁机制&乐观锁实现
- Hibernate学习---第十四节:hibernate过滤器和拦截器的实现
- Hibernate学习---第十五节:hibernate之session线程安全
- Java设计模式之装饰者模式
- 微服务、分布式、公有云、私有云
- Spring介绍
- 数据流图和数据流程图的区别
- 为啥看到大神级的程序员基本都是外国人的形象
- Win10操作系统下,Java环境变量的配置和Java入门小程序“hello,world”的编译
- Vue.js 计算属性
- MyBatis入门基础(一) 为什么要用Mybatis
- mybatis入门基础(二)----原始dao的开发和mapper代理开发
- ceph运维常用指令
- LeetCode 454. 4Sum II