模拟转账业务
pom.xml
<dependencies>
<!--spring-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.29</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.28</version>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.29</version>
</dependency>
<!--dbutil-->
<dependency>
<groupId>commons-dbutils</groupId>
<artifactId>commons-dbutils</artifactId>
<version>1.4</version>
</dependency>
<!--数据源c3p0-->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>
<!--junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!--spring测试环境-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.3.29</version>
</dependency>
</dependencies>
Bean:Account
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Account implements Serializable {
private int aid;
private String aname;
private int amoney;
public Account(String aname, int amoney) {
this.aname = aname;
this.amoney = amoney;
}
}
Dao层:
接口
public interface IAccountDao {
public void save(Account account);
public void update(Account account);
public Account findByName(String name);
public List<Account> findAll();
}
实现类:
public class AccountDaoImp implements IAccountDao {
QueryRunner queryRunner;
public void setQueryRunner(QueryRunner queryRunner) {
this.queryRunner = queryRunner;
}
ConnectionUtil connectionUtil;
public void setConnectionUtil(ConnectionUtil connectionUtil) {
this.connectionUtil = connectionUtil;
}
@Override
public void save(Account account) {
try {
queryRunner.update(connectionUtil.createConn(),"insert into acount(aname,amoney) values (?,?)", account.getAname(), account.getAmoney());
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
@Override
public void update(Account account) {
try {
queryRunner.update(connectionUtil.createConn(),"update acount set aname=?,amoney=? where aid=?", account.getAname(), account.getAmoney(), account.getAid());
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
@Override
public Account findByName(String name) {
try {
Account query = queryRunner.query("select * from acount where aname=?", new BeanHandler<Account>(Account.class), name);
return query;
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return null;
}
@Override
public List<Account> findAll() {
try {
List<Account> all = queryRunner.query("select * from acount", new BeanListHandler<Account>(Account.class));
return all;
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return null;
}
}
Service层:
接口:
public interface IAccountService {
public void save(Account account);
public void update(Account account);
public Account findByName(String name);
public List<Account> findAll();
public void transfer(String sourceName,String targetName,int money);
}
实现类:
public class AccountServiceImp implements IAccountService {
IAccountDao dao;
public void setDao(IAccountDao dao) {
this.dao = dao;
}
TransactionUtil transactionUtil;
public AccountServiceImp(TransactionUtil transactionUtil) {
this.transactionUtil = transactionUtil;
}
@Override
public void save(Account account) {
dao.save(account);
}
@Override
public void update(Account account) {
dao.update(account);
}
@Override
public Account findByName(String name) {
Account a = dao.findByName(name);
return a;
}
@Override
public List<Account> findAll() {
List<Account> all = dao.findAll();
return all;
}
// 转账业务
@Override
public void transfer(String sourceName, String targetName, int money) {
try {
transactionUtil.openTran();
// 查询俩个人的账户状态
Account sourceAccount = dao.findByName(sourceName);
Account targetAccount = dao.findByName(targetName);
// 转账
sourceAccount.setAmoney(sourceAccount.getAmoney()-money);
targetAccount.setAmoney(targetAccount.getAmoney()+money);
// 修改数据
dao.update(sourceAccount);
int a = 10/0;
dao.update(targetAccount);
transactionUtil.commitTran();
} catch (Exception e) {
e.printStackTrace();
transactionUtil.rollbackTran();
}finally {
transactionUtil.closeTran();
}
}
}
连接工具类ConnectionUtil:
public class ConnectionUtil {
// 装配数据源
DataSource dataSource;
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
ThreadLocal<Connection> threadLocal = new ThreadLocal<Connection>();
Connection connection =null;
// 获取连接
public Connection createConn(){
try {
connection = threadLocal.get();
if(connection ==null){
connection = dataSource.getConnection();
threadLocal.set(connection);
}
return this.connection;
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return connection;
}
// 关闭连接
public void closeConn(){
threadLocal.remove();
}
}
事务管理工具类TransactionUtil:
public class TransactionUtil {
// 装配连接
ConnectionUtil connectionUtil;
public void setConnectionUtil(ConnectionUtil connectionUtil) {
this.connectionUtil = connectionUtil;
}
// 开启事务
public void openTran(){
try {
connectionUtil.createConn().setAutoCommit(false);
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
// 提交事务
public void commitTran(){
try {
connectionUtil.createConn().commit();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
// 回滚事务
public void rollbackTran(){
try {
connectionUtil.createConn().rollback();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
// 关闭事务
public void closeTran(){
try {
connectionUtil.createConn().close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
Controller类:
接口:
public interface IAccountController {
public void save(Account account);
public void update(Account account);
public Account findByName(String name);
public List<Account> findAll();
public void transfer(String sourceName,String targetName,int money);
}
实现类:
public class AccountControllerImp implements IAccountController {
IAccountService service;
public void setService(IAccountService service) {
this.service = service;
}
@Override
public void save(Account account) {
service.save(account);
}
@Override
public void update(Account account) {
service.update(account);
}
@Override
public Account findByName(String name) {
Account account = service.findByName(name);
return account;
}
@Override
public List<Account> findAll() {
List<Account> all = service.findAll();
return all;
}
@Override
public void transfer(String sourceName, String targetName, int money) {
service.transfer(sourceName,targetName,money);
}
}
Spring主配置文件applicationContext.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--注入数据源-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.cj.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring?serverTimezone=GMT"></property>
<property name="user" value="root"></property>
<property name="password" value="123456"></property>
</bean>
<!--注入连接工具类-->
<bean id="connectionUtil" class="com.dong.util.ConnectionUtil">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--注入事务工具类-->
<bean id="transactionUtil" class="com.dong.util.TransactionUtil">
<property name="connectionUtil" ref="connectionUtil"></property>
</bean>
<!--注入queryRunner-->
<bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner">
<constructor-arg name="ds" ref="dataSource"></constructor-arg>
</bean>
<!--注入dao-->
<bean id="accountDaoImp" class="com.dong.dao.AccountDaoImp">
<property name="queryRunner" ref="queryRunner"></property>
<property name="connectionUtil" ref="connectionUtil"></property>
</bean>
<!--注入service-->
<bean id="accountServiceImp" class="com.dong.service.AccountServiceImp">
<property name="dao" ref="accountDaoImp"></property>
<constructor-arg name="transactionUtil" ref="transactionUtil"></constructor-arg>
</bean>
<!--注入controller-->
<bean id="accountControllerImp" class="com.dong.controller.AccountControllerImp">
<property name="service" ref="accountServiceImp"></property>
</bean>
</beans>
模拟转账业务中,如果直接使用spring的事务管理的话,如果业务逻辑中产生异常,eg在修改两个人的金额之间出现异常,第一个人的金额发生改变,并提交了数据,因为产生异常第二个人的数据没有修改,造成数据不一致的问题,所以就需要想办法让一个业务共用一个连接,所以自定义了连接管理类和事务管理工具类,实现业务共用一个连接对象,业务层公用的连接一起提交或回滚
Java的六大设计原则
设计原则是为了更好的设计软件的高层指导方针,它不提供具体的实现方式也不会绑定任何一种编程语言,最常用的原则是SOLID(SRP, OCP, LSP, ISP, DIP)原则
- 开闭原则(OCP) - The Open-Closed Principle
- 单一职责原则(SRP) - Single Responsibility Principle
- 里氏替换原则(LSP) - Liskov Substitution Principle
- 依赖倒置原则(DIP) - Dependency Inversion Principle
- 接口隔离原则(ISP) - Interface Segregation Principle
- 迪米特法则(DP) - Demeter Principle
设计模式
设计模式对关于面向对象问题的具体解决方案;比如说,如果你想创建一个类而且它在任何时刻只会有一个对象,那么你就应该使用单例类模式
设计模式是经过大量检测的安全的做法
单例模式
单例模式性能好,但是安全性差
- 饿汉模式:不管用不用,先创建一个对象,用的时候直接给
- 懒汉模式:用的时候在创建对象,只创建一次,第二次用直接给不在创建
-
饿汉模式演示
public class Student { /*饿汉模式*/ private static Student stu=new Student(); private Student() { } public static synchronized Student getInstance(){ return stu; } }
-
懒汉模式演示
public class Student { /*懒汉模式*/ private static Student stu=null; private Student() { } public static synchronized Student getInstance(){ if(stu == null){ stu = new Student(); } return stu; } }
工厂模式
演示:
INoodels接口
public interface INoodels {
public void NoodelsType();
}
INoodels接口实现类:
-
YouPoNoodle
public class YouPoNoodels implements INoodels { @Override public void NoodelsType() { System.out.println("油泼面"); } }
-
ReGanNoodel
public class ReGanNoodls implements INoodels{ @Override public void NoodelsType() { System.out.println("武汉热干面"); } }
-
LanZhouNoodel
public class LanZhouNoodels implements INoodels { @Override public void NoodelsType() { System.out.println("兰州牛肉面"); } }
工厂NoodleFactory
public class NoodelFactory {
public static final int YOUPO_NOODEL=1;
public static final int REGAN_NOODEL=2;
public static final int LANZHOU_NOODEL=3;
public static INoodels createNoodel(int type){
if(type ==1){
return new YouPoNoodels();
}else if(type ==2){
return new ReGanNoodls();
}else if(type ==3){
return new LanZhouNoodels();
}
return null;
}
}
Test01
public class Test01 {
public static void main(String[] args) {
NoodelFactory.createNoodel(NoodelFactory.LANZHOU_NOODEL).NoodelsType();
NoodelFactory.createNoodel(3).NoodelsType();
}
}
工厂模式是由工厂帮我们创建对象
模板模式
模板模式==spring框内
* JdbcTemplate
* JpaTemplate
模板模式首先要有一个抽象类,这个抽象类公开定义了执行它的方法的方式/模板。
它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。这种类型的设计模式属于行为型模式
“比如,在造房子一样,地基,铺线,房子户型都是一样的,由开发商决定,但是在交房之后,室内的装修风格和场景布置却是由业主决定,在这个场景中,开发商其实就是一个抽象类,地基,铺线,房子户型都是可以复用的,但是装修却是不可复用的,必须由业主决定,此时的每一个业主的房子就是一个实现的子类”
Spring中jdbcTemplate、hibernateTemplate等以Template结尾的对数据库操作的类,它们就使用到模板模式。一般情况下,我们都是使用继承的方式来实现模板模式,但是Spring并没有使用这种方式,而是使用Callback模式与模板方法配合,既达到了代码复用的效果,同时增加了灵活性
所谓模板板式,就是在父类中定义算法的主要流程,而把一些个性化的步骤延迟到子类中去实现,父类始终控制着整个流程的主动权,子类只是辅助父类实现某些可定制的步骤
实现演示:
FatherClass:
public abstract class FatherClass {
public final void life(){
study();
work();
love();
}
public void study(){
System.out.println("学C++");
}
public void work(){
System.out.println("干嵌入式");
}
public abstract void love();
}
SonClass
public class SonClass extends FatherClass {
@Override
public void study() {
System.out.println("学java");
}
@Override
public void work() {
System.out.println("干java开发");
}
@Override
public void love() {
System.out.println("美女大妹");
}
}
Test:
public class Test01 {
public static void main(String[] args) {
FatherClass fatherClass = new SonClass();
fatherClass.人生();
}
}
FatherClass类里面定义人生()方法被final修饰不能重写,规定了先学习,再工作,最后恋爱,体现了模板模式
代理模式
什么是代理模式?
代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是我们生活中常见的中介。举个例子来说明:假如说我现在想买一辆二手车,虽然我可以自己去找车源,做质量检测等一系列的车辆过户流程,但是这确实太浪费我得时间和精力了。我只是想买一辆车而已为什么我还要额外做这么多事呢?于是我就通过中介公司来买车,他们来给我找车源,帮我办理车辆过户流程,我只是负责选择自己喜欢的车,然后付钱就可以了。
为什么要用代理模式?
-
中介隔离作用:在某些情况下,一个客户类不想或者不能直接引用一个委托对象而代理类对象可以在客户类和委托对象之间起到中介的作用,其特征是代理类和委托类实现相同的接口。
-
开闭原则,增加功能:代理类除了是客户类和委托类的中介之外,我们还可以通过给代理类增加额外的功能来扩展委托类的功能,这样做我们只需要修改代理类而不需要再修改委托类,符合代码设计的开闭原则
有哪几种代理模式?
有多种不同的方式来实现代理。如果按照代理创建的时期来进行分类的话可以分为两种:
静态代理:
静态代理是由程序员创建或特定工具自动生成源代码,在对其编译在程序员运行之前,代理类.class文件就已经被创建了
动态代理:
动态代理是在程序运行时通过反射机制动态创建的
- 基于接口的动态代理(jdk自带)
- 基于子类的动态代理(第三方)
静态代理
演示:假如西门庆想找潘金莲,但不能直接找,这时候出现了“中介-王婆”
接口:IWoman
public interface IWoman {
public void makeEye();
}
实现类:PanJinLian
public class Panjinlian implements IWoman{
@Override
public void makeEye() {
System.out.println("潘金莲向你发送了一个邀请");
}
}
实现类:WangPo
public class Wangpo implements IWoman{
/*被代理对象*/
private IWoman woman;
public Wangpo(IWoman woman) {
this.woman = woman;
}
public Wangpo() {
}
@Override
public void makeEye() {
System.out.println("武大郎今天不在家");
woman.makeEye();
}
}
XiMenQing:
public class XiMenQing {
public static void main(String[] args) {
Panjinlian panjinlian = new Panjinlian();
Wangpo wangpo = new Wangpo(panjinlian);
wangpo.makeEye();
}
}
上述案例,王婆就是代理,潘金莲就是被代理对象,西门庆找潘金莲就要先找王婆,达到了中介隔离的作用,而在代理中添加别的功能,不会对被代理对象产生代码污染,就实现了增加功能
基于接口动态代理jdk
演示:假如粉丝找歌手开演唱会,我们需要找到经纪人
接口:ISinger
public interface ISinger {
public void Sing();
}
实现类:CaiXuKunImpl
public class CaiXuKunImp implements ISinger {
@Override
public void Sing() {
System.out.println("只因你太美");
}
}
测试:Fans
public class Fans {
public static void main(String[] args) {
// 被代理对象
ISinger caiXuKun = new CaiXuKunImp();
ISinger jingjiren = (ISinger) Proxy.newProxyInstance(CaiXuKunImp.class.getClassLoader(), CaiXuKunImp.class.getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object obj = method.invoke(caiXuKun, args);
System.out.println("跳舞");
System.out.println("篮球");
System.out.println("RAP");
return obj;
}
});
jingjiren.Sing();
}
}
通过调用经纪人的sing(),蔡徐坤唱了一首只因你太美,
经纪人还做了增强功能,跳舞篮球Rap
基于子类的动态代理cglib
第三方插件,需要导入坐标
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
演示:粉丝通过经纪人找歌手
接口:ISinger
public interface ISinger {
public void sing();
}
实现类:CaiXuKunImpl
public class CaiXuKunImp implements ISinger {
@Override
public void sing() {
System.out.println("oi oi oi");
}
}
测试:Fans
public class Fans {
public static void main(String[] args) {
ISinger caixukun = new CaiXuKunImp();
ISinger jingjiren = (ISinger) Enhancer.create(caixukun.getClass(), new InvocationHandler() {
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
System.out.println("跳舞篮球RAP");
Object object = method.invoke(caixukun, objects);
return object;
}
});
jingjiren.sing();
}
}
对模拟转账业务升级
对代理模式有了概念和理解之后,就可以对上面的转账业务升级
静态代理实现升级改造:
创建代理类:BeansFactory
public class BeansFactory {
// 装配被代理对象
private IAccountService toService;
public void setToService(IAccountService toService) {
this.toService = toService;
}
// 装配事务管理工具
private TransactionUtil transactionUtil;
public void setTransactionUtil(TransactionUtil transactionUtil) {
this.transactionUtil = transactionUtil;
}
public IAccountService serviceProxy(){
IAccountService serviceProxy = (IAccountService) Proxy.newProxyInstance(toService.getClass().getClassLoader(), toService.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object obj = null;
try {
transactionUtil.openTran();
obj = method.invoke(toService, args);
transactionUtil.commitTran();
} catch (Exception e) {
e.printStackTrace();
} finally {
transactionUtil.closeTran();
}
return obj;
}
});
return serviceProxy;
}
}
Service层实现类不需要再关注事务问题
public class AccountServiceImp implements IAccountService {
IAccountDao dao;
public void setDao(IAccountDao dao) {
this.dao = dao;
}
@Override
public void save(Account account) {
dao.save(account);
}
@Override
public void update(Account account) {
dao.update(account);
}
@Override
public Account findByName(String name) {
Account a = dao.findByName(name);
return a;
}
@Override
public List<Account> findAll() {
List<Account> all = dao.findAll();
return all;
}
// 转账业务
@Override
public void transfer(String sourceName, String targetName, int money) {
// 查询俩个人的账户状态
Account sourceAccount = dao.findByName(sourceName);
Account targetAccount = dao.findByName(targetName);
// 转账
sourceAccount.setAmoney(sourceAccount.getAmoney()-money);
targetAccount.setAmoney(targetAccount.getAmoney()+money);
// 修改数据
dao.update(sourceAccount);
int a = 10/0; // 模拟出现异常
dao.update(targetAccount);
}
}
配置文件中的修改:
<!--注入数据源-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.cj.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring?serverTimezone=GMT"></property>
<property name="user" value="root"></property>
<property name="password" value="123456"></property>
</bean>
<!--注入连接工具类-->
<bean id="connectionUtil" class="com.dong.util.ConnectionUtil">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--注入事务工具类-->
<bean id="transactionUtil" class="com.dong.util.TransactionUtil">
<property name="connectionUtil" ref="connectionUtil"></property>
</bean>
<!--注入queryRunner-->
<bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner">
<constructor-arg name="ds" ref="dataSource"></constructor-arg>
</bean>
<!--注入dao-->
<bean id="accountDaoImp" class="com.dong.dao.AccountDaoImp">
<property name="queryRunner" ref="queryRunner"></property>
<property name="connectionUtil" ref="connectionUtil"></property>
</bean>
<!--注入service(被代理对象)-->
<bean id="accountServiceImp" class="com.dong.service.AccountServiceImp">
<property name="dao" ref="accountDaoImp"></property>
</bean >
<!--注入service代理业务-->
<bean id="accountServiceImpProxy" class="com.dong.service.AccountServiceImp" factory-bean="factory" factory-method="serviceProxy">
</bean>
<!--注入bean工厂-->
<bean id="factory" class="com.dong.factory.BeansFactory">
<property name="toService" ref="accountServiceImp"></property>
<property name="transactionUtil" ref="transactionUtil"></property>
</bean>
<!--注入controller-->
<bean id="accountControllerImp" class="com.dong.controller.AccountControllerImp">
<property name="service" ref="accountServiceImpProxy"></property>
</bean>
原来的service成为被代理对象,代理业务通过工厂造出,注入了事务管理对象和被代理业务对象,控制器中注入的对象变成被代理业务对象
Spring AOP
AOP 为 Aspect Oriented Programming 的缩写,意思为面向切面编程,是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术
AOP 是 OOP 的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
AOP 的作用及其优势
-
作用:在程序运行期间,在不修改源码的情况下对方法进行功能增强
-
优势:减少重复代码,提高开发效率,并且便于维护
Spring AOP 基于动态代理实现
○ 如果被代理的对象,已经实现某个接口,则 Spring AOP 会使用 JDK Proxy(反射),基于接口的方式,创建代理对象(JDK动态代理的核心是InvocationHandler接口和Proxy类);
○ 如果被代理的对象,没有实现某个接口,就无法使用 JDK Proxy 去进行代理了,这时被代理对象的子类来作为代理(Cglib动态代理的核心是MethodInterceptor接口和Enhancer类);
AOP主要用于
- 权限
- 日志
- 事务
AOP通知类型
AOP将抽取出来的共性功能称为通知;通知类型:以通知在上下文中的具体位置作为划分:
- 前置通知(Before)
- 返回通知(After-returning)
- 异常通知(After-throwing)
- 后置通知(After)
- 环绕通知(Around)
AOP连接点Join point
AOP将所有的方法都视为连接点,不管是接口里面的抽象方法,还是实现类里面的重写方法,都是连接点
AOP切点Pointcut
AOP将可能被抽取共性功能的方法称为切入点。切入点是连接点的子集
切点表达式配置语法:
execution(修饰符 返回值 包名称.类名称.方法名称(参数列表))
eg:
execution(public void com.apesource.service.ServiceImp.findAll())
1.修饰符可以省略代表任意
execution(返回值 包名称.类名称.方法名称(参数列表))
2.返回值可以使用“*”代表任意
execution(* 包名称.类名称.方法名称(参数列表))
3.包名可以使用“*”代表任意名称
execution(* *.*.*.类名称.方法名称(参数列表))
4.包名可以使用“..”代表任意个数
execution(* *...类名称.方法名称(参数列表))
5.类名与方法名可以使用“*”代表任意
execution(* *...*.*(参数列表))
6.参数列表可以使用".."代表任意个数任意类型
execution(* *...*.*(..))
如果有参数
int======>int
String===>java.lang.String
AOP目标对象Target
AOP目标对象(Target): 就是挖掉功能的方法对应的类生的对象,这种对象是无法直接完成最终工作的
AOP织入Weaving
AOP织入(Weaving):就是将挖掉的功能回填的动态过程
AOP切面:切点+通知
配置文件实现AOP
- 导入坐标
- 配置
演示:模拟一个业务,实现日志
-
导入坐标
<dependencies> <!--Spring--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.3.29</version> </dependency> <!-- aspectj (AOP) --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.7</version> </dependency> </dependencies>
-
业务
接口:
public interface IService { public void save(); public void delete(); public void update(); }
实现类:
public class ServiceImp implements IService { @Override public void save() { System.out.println("新增咯"); } @Override public void delete() { System.out.println("删除咯"); } @Override public void update() { System.out.println("修改咯"); } }
-
日志:
public class Log { public void beforeLogger(){ System.out.println("这是前置通知日志哦,时间:"); } public void afterReturnningLogger(){ System.out.println("这是返回通知日志哦,时间:"); } public void exceptionLogger(){ System.out.println("这是异常通知日志哦,时间:"); } public void afterLogger(){ System.out.println("这是后置通知日志哦,时间:"); } public Object arroundLogger(ProceedingJoinPoint pjp){ Object proceed =null; try { // 前置通知 System.out.println("环绕通知----》前置通知"); Object[] args = pjp.getArgs(); proceed = pjp.proceed(args); // 返回通知 System.out.println("环绕通知----》返回通知"); } catch (Throwable throwable) { // 异常通知 System.out.println("环绕通知----》异常通知"); } finally { // 后置通知 System.out.println("环绕通知----》后置通知"); } return proceed; } }
-
配置文件:applicationContext.xml
如果使用前置,返回,异常,后置通知,配置文件如下
<!--注入Service--> <bean id="serviceImp" class="com.dong.service.ServiceImp"></bean> <!--注入log--> <bean id="log" class="com.dong.logs.Log"></bean> <aop:config> <aop:aspect id="aop" ref="log"> <!--切点--> <aop:pointcut id="dian" expression="execution(* com..dong.service.*.*(..))"/> <!--前置通知--> <aop:before method="beforeLogger" pointcut-ref="dian"></aop:before> <!--返回通知--> <aop:after-returning method="beforeLogger" pointcut-ref="dian"></aop:after-returning> <!--异常通知--> <aop:after-throwing method="exceptionLogger" pointcut-ref="dian"></aop:after-throwing> <!--后置通知--> <aop:after method="afterLogger" pointcut-ref="dian"></aop:after> </aop:aspect> </aop:config>
如果使用了环绕通知,则可以替代上面四种通知,配置文件如下
<!--注入Service--> <bean id="serviceImp" class="com.dong.service.ServiceImp"></bean> <!--注入log--> <bean id="log" class="com.dong.logs.Log"></bean> <aop:config> <aop:aspect id="aop" ref="log"> <!--切点--> <aop:pointcut id="dian" expression="execution(* com..dong.service.*.*(..))"/> <!--环绕通知--> <aop:around method="arroundLogger" pointcut-ref="dian"></aop:around> </aop:aspect>
注释实现AOP
-
导坐标,spring-context和aspectjweaver坐标
-
业务:
接口:
public interface IService { public void save(); public void delete(); public void update(); }
实现类:
@Service public class ServiceImp implements IService { @Override public void save() { System.out.println("新增咯"); } @Override public void delete() { System.out.println("删除咯"); } @Override public void update() { System.out.println("修改咯"); } }
-
日志:
@Component @Aspect public class Log { @Pointcut(value = "execution(* com.dong.service.*.*(..))") public void dian(){}; @Before("dian()") public void beforeLog(){ System.out.println("这是前置日志"); } @AfterReturning("dian()") public void afterReturnningLog(){ System.out.println("这是返回日志"); } @AfterThrowing("dian()") public void exceptionLog(){ System.out.println("这是异常日志"); } @After("dian()") public void afterLog(){ System.out.println("这是后置日志"); } @Around("dian()") public Object arroundLog(ProceedingJoinPoint pjp){ Object proceed =null; try { System.out.println("环绕日志----》前置日志"); Object[] args = pjp.getArgs(); proceed = pjp.proceed(args); System.out.println("环绕日志----》返回"); } catch (Throwable throwable) { throwable.printStackTrace(); System.out.println("环绕日志----》异常"); } finally { System.out.println("环绕日志----》后置"); } return proceed; } }
-
配置文件中,要扫包,并开启AOP
<!--扫包--> <context:component-scan base-package="com.dong"></context:component-scan> <!--注解驱动--> <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
Spring整合Mybatis
-
导坐标
<dependencies> <!--spring容器--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.3.29</version> </dependency> <!--mysql--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.29</version> </dependency> <!--mybatis--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.6</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>2.0.5</version> </dependency> <!--springJDBC--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.2.9.RELEASE</version> </dependency> <!--数据源--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.21</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.28</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.3.29</version> </dependency> </dependencies>
-
Bean实体类:Account
@NoArgsConstructor @AllArgsConstructor @Data @Component public class Account { private int aid; private String aname; private int amoney; public Account(String aname, int amoney) { this.aname = aname; this.amoney = amoney; } }
-
Dao层
public interface IAccountDao { @Insert("insert into acount(aname,amoney) values(#{aname},#{amoney})") public void save(Account account); @Update("update acount set aname=#{aname} , amoney=#{amoney} where aid = #{aid}") public void update(Account account); @Select("select * from acount") public List<Account> selectAll(); @Select("select * from acount where aname=#{v}") public Account selectByName(String name); }
-
Service层:
接口:
public interface IAccountService { public void save(Account account); public void update(Account account); public Account selectByName(String name); public List<Account> selectAll(); }
实现类:
@Service public class AccountServiceImpl implements IAccountService{ @Autowired private IAccountDao dao; @Override public void save(Account account) { dao.save(account); } @Override public void update(Account account) { dao.update(account); } @Override public Account selectByName(String name) { Account account = dao.selectByName(name); return account; } @Override public List<Account> selectAll() { List<Account> accounts = dao.selectAll(); return accounts; } }
-
Controller层:
接口:
public interface IAccountController { public void save(Account account); public void update(Account account); public Account selectByName(String name); public List<Account> selectAll(); }
实现类:
@Controller public class AccountConreollerImpl implements IAccountController { @Autowired private IAccountService service; @Override public void save(Account account) { service.save(account); } @Override public void update(Account account) { service.update(account); } @Override public Account selectByName(String name) { Account account = service.selectByName(name); return account; } @Override public List<Account> selectAll() { List<Account> accounts = service.selectAll(); return accounts; } }
-
配置文件
Mybatis的配置文件:Mybatis.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <!-- 1.数据源==交给spring容器 2.事务管理==交给spring容器 3.mapper映射器注册 --> </configuration>
Spring的配置文件:applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <!--注入数据源--> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://localhost:3306/spring?serverTimezone=GMT"></property> <property name="username" value="root"></property> <property name="password" value="123456"></property> </bean> <!--配置sqlSessionFactory--> <bean id="factory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"></property> <property name="configLocation" value="classpath:mybatisConfig.xml"></property> </bean> <!--配置mapper映射器,生成代理对象注入容器--> <bean id="configurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.dong.dao"></property> </bean> <!--扫包--> <context:component-scan base-package="com.dong"></context:component-scan> </beans>
注入mapper映射器后,dao层就不需要再注入容器了
以上步骤完成,Spring就成功整合了Mybatis
Spring任务调度(spring task)
任务调度
可以用于程序运行时设置在某些固定的时刻执行特定的操作,比如设置调度任务在凌晨的时候自动同步数据等等
实现技术:spring task
实现spring task任务调布只需要导入spring context坐标即可,不需要额外导入其他坐标,只需要在spring主配置文件中配置
配置文件实现任务调度
-
假如被调度的任务输出信息
public class MyTask { public void task1(){ System.out.println("task1:"+new Date()); } public void task2(){ System.out.println("task2:"+new Date()); } }
-
spring主配置文件
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:task="http://www.springframework.org/schema/task" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd"> <!--注入任务--> <bean id="task" class="com.dong.MyTask"></bean> <!--创建调度线程池--> <task:scheduler id="poolTaskScheduler" pool-size="10"></task:scheduler> <!--配置任务调度--> <task:scheduled-tasks scheduler="poolTaskScheduler"> <!--任务方法 cron表达式:实现触发任务的策略--> <task:scheduled ref="task" method="task1" cron="0-5 * * * * *"/> <task:scheduled ref="task" method="task2" cron="3/3 * * * * *"/> </task:scheduled-tasks> </beans>
- 需要把被调度的任务对象注入容器
- 调度线程池配不配置都可以
<task:scheduled-tasks></task:scheduled-tasks>
标签中调度线程池若没配置就不需要写属性,想要调度的方法就可以在配置标签之间写,ref写注入的任务,method写调度任务的方法,cron表达式配置触发任务的策略
-
测试(让程序程序运行)
public class Test01 { public static void main(String[] args) { ApplicationContext app = new ClassPathXmlApplicationContext("applicationcontext.xml"); } }
-
控制台输出效果
上述配置的task1任务的cron表达式:每分钟前五秒调一次
task2任务的cron表达式:第3秒开始,每隔三秒钟调一次
cron表单式
cron表达式从左到右共有七位,分别代表:
秒、分、小时、日期、月份、星期、年份
cron表达式在线工具生成器网址
注解实现任务调度
-
导入坐标spring context
-
任务调度对象
需要写@Component注解,注入容器
@Scheduled注解,表示调度任务,括号中写cron表达式
@Component public class MyJob { @Scheduled(cron = "* * * * * *") public void myjob1(){ System.out.println("Hacker Dong"); } }
-
配置文件
<?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:task="http://www.springframework.org/schema/task" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd"> <!--扫包--> <context:component-scan base-package="com.dong"></context:component-scan> <!--配置任务调度开启--> <task:annotation-driven></task:annotation-driven> </beans>
- 配置扫包
- 配置开启任务调度
-
测试
public class Test01 { public static void main(String[] args) { ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); } }
Spring注解实现事务管理
spring实现事务管理就不再需要自己写事务管理工具类,只需要在业务上添加注解@Transactional
-
导入坐标
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.dong</groupId> <artifactId>SpringAOP_transaction</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <!--spring容器--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.3.29</version> </dependency> <!--mysql--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.29</version> </dependency> <!--mybatis--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.6</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>2.0.5</version> </dependency> <!--springJDBC--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.2.9.RELEASE</version> </dependency> <!--数据源--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.21</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.28</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.3.29</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>5.0.3.RELEASE</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.7</version> </dependency> </dependencies> </project>
-
Bean实体类、Dao、Service、Controller三层
Bean:
@NoArgsConstructor @AllArgsConstructor @Data @Component public class Account implements Serializable { private int aid; private String aname; private int amoney; }
dao:
public interface IAccountDao { @Insert("insert into acount(aname,amoney) values(#{aname},#{amoney})") public void save(Account account); @Update("update acount set aname=#{aname} , amoney=#{amoney} where aid = #{aid}") public void update(Account account); @Select("select * from acount where aname=#{v}") public Account selectByName(String name); @Select("select * from acount") public List<Account> selectAll(); }
Service层:
IAccountSerivce:
public interface IAccountSerivce { public void save(Account account); public void update(Account account); public Account selectByName(String name); public List<Account> selectAll(); public void zhuanzhang(String user1,String user2,int money); }
AccountServiceImp:
@Service @Transactional // spring事务管理 public class AccountServiceImp implements IAccountSerivce { @Autowired private IAccountDao dao; @Override public void save(Account account) { dao.save(account); } @Override public void update(Account account) { dao.update(account); } @Override public Account selectByName(String name) { Account account = dao.selectByName(name); return account; } @Override public List<Account> selectAll() { List<Account> accounts = dao.selectAll(); return accounts; } @Override public void zhuanzhang(String user1, String user2, int money) { Account account1 = dao.selectByName(user1); Account account2 = dao.selectByName(user2); account1.setAmoney(account1.getAmoney()-money); account2.setAmoney(account2.getAmoney()+money); dao.update(account1); dao.update(account2); } }
Controller层:
IAccountController:
public interface IAccountController { public void save(Account account); public void update(Account account); public Account selectByName(String name); public List<Account> selectAll(); public void zhuanzhang(String user1,String user2,int money); }
AccountControllerImp:
@Controller public class AccountControllerImp implements IAccountController{ @Autowired private IAccountSerivce service; @Override public void save(Account account) { service.save(account); } @Override public void update(Account account) { service.update(account); } @Override public Account selectByName(String name) { Account account = service.selectByName(name); return account; } @Override public List<Account> selectAll() { List<Account> accounts = service.selectAll(); return accounts; } @Override public void zhuanzhang(String user1, String user2, int money) { service.zhuanzhang(user1,user2,money); } }
-
配置文件:
mybatis的配置文件
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <!-- 1.数据源==交给spring容器 2.事务管理==交给spring容器 3.mapper映射器注册 --> </configuration>
spring主配置文件:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns: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.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.alibaba.com/schema/stat http://www.alibaba.com/schema/stat.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <!--注入数据源--> <bean id="data" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://localhost:3306/spring?serverTimezone=GMT"></property> <property name="username" value="root"></property> <property name="password" value="123456"></property> </bean> <!--配置sqlSessionFactory--> <bean id="factory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="data"></property> <property name="configLocation" value="classpath:mybatisConfig.xml"></property> </bean> <!--配置mapper映射器,生成代理对象注入容器--> <bean id="configurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.dong.dao"></property> </bean> <!--扫包--> <context:component-scan base-package="com.dong"></context:component-scan> <!--配置事务管理器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!--注入DataSource--> <property name="dataSource" ref="data"></property> </bean> <!--开启spring对注解事务管理的支持--> <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven> </beans>
注意事项:
- 数据源,SqlSessionFactoryBean需要配置
- 配置mapper映射器,dao层不需要再注入容器
- 扫包
- 配置事务管理器
- 需要注入数据源
- 需要开启spring对注解事务管理的支持