Spring两大核心之一:AOP(面向切面编程)含设计模式讲解,通知类型切点;附有案例,实现spring事务管理

news2024/9/25 23:21:55

模拟转账业务

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

设计模式

设计模式对关于面向对象问题的具体解决方案;比如说,如果你想创建一个类而且它在任何时刻只会有一个对象,那么你就应该使用单例类模式

设计模式是经过大量检测的安全的做法

单例模式

单例模式性能好,但是安全性差

  • 饿汉模式:不管用不用,先创建一个对象,用的时候直接给
  • 懒汉模式:用的时候在创建对象,只创建一次,第二次用直接给不在创建
  1. 饿汉模式演示

    public class Student {
        /*饿汉模式*/
        private static Student stu=new Student();
    
        private Student() {
    
        }
    
        public static synchronized Student getInstance(){
            return stu;
        }
    }
    
  2. 懒汉模式演示

    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接口实现类:

  1. YouPoNoodle

    public class YouPoNoodels implements INoodels {
        @Override
        public void NoodelsType() {
            System.out.println("油泼面");
        }
    }
    
  2. ReGanNoodel

    public class ReGanNoodls  implements INoodels{
        @Override
        public void NoodelsType() {
            System.out.println("武汉热干面");
        }
    }
    
  3. 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文件就已经被创建了

动态代理:

​ 动态代理是在程序运行时通过反射机制动态创建的

  1. 基于接口的动态代理(jdk自带)
  2. 基于子类的动态代理(第三方)

静态代理

演示:假如西门庆想找潘金莲,但不能直接找,这时候出现了“中介-王婆”

接口: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主要用于

  1. 权限
  2. 日志
  3. 事务

AOP通知类型

AOP将抽取出来的共性功能称为通知;通知类型:以通知在上下文中的具体位置作为划分:

  1. 前置通知(Before)
  2. 返回通知(After-returning)
  3. 异常通知(After-throwing)
  4. 后置通知(After)
  5. 环绕通知(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

  1. 导入坐标
  2. 配置

演示:模拟一个业务,实现日志

  1. 导入坐标

    <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>
    
  2. 业务

    接口:

    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("修改咯");
        }
    }
    
  3. 日志:

    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;
        }
    }
    
  4. 配置文件: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

  1. 导坐标,spring-context和aspectjweaver坐标

  2. 业务:

    接口:

    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("修改咯");
        }
    }
    
  3. 日志:

    @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;
        }
    }
    
  4. 配置文件中,要扫包,并开启AOP

    <!--扫包-->
    <context:component-scan base-package="com.dong"></context:component-scan>
    <!--注解驱动-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    

Spring整合Mybatis

  1. 导坐标

    <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>
    
  2. 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;
        }
    }
    
  3. 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);
    
    }
    
  4. 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;
        }
    }
    
  5. 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;
        }
    }
    
  6. 配置文件

    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主配置文件中配置

配置文件实现任务调度

  1. 假如被调度的任务输出信息

    public class MyTask {
        public void task1(){
            System.out.println("task1:"+new Date());
        }
    
        public void task2(){
            System.out.println("task2:"+new Date());
        }
    }
    
  2. 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表达式配置触发任务的策略
  3. 测试(让程序程序运行)

    public class Test01 {
        public static void main(String[] args) {
            ApplicationContext app = new ClassPathXmlApplicationContext("applicationcontext.xml");
        }
    }
    
  4. 控制台输出效果

    在这里插入图片描述

    上述配置的task1任务的cron表达式:每分钟前五秒调一次

    ​ task2任务的cron表达式:第3秒开始,每隔三秒钟调一次

cron表单式

在这里插入图片描述

cron表达式从左到右共有七位,分别代表:

秒、分、小时、日期、月份、星期、年份

cron表达式在线工具生成器网址

注解实现任务调度

  1. 导入坐标spring context

  2. 任务调度对象

    需要写@Component注解,注入容器

    ​ @Scheduled注解,表示调度任务,括号中写cron表达式

    @Component
    public class MyJob {
        @Scheduled(cron = "* * * * * *")
        public void myjob1(){
            System.out.println("Hacker Dong");
        }
    }
    
  3. 配置文件

    <?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>
    
    • 配置扫包
    • 配置开启任务调度
  4. 测试

    public class Test01 {
        public static void main(String[] args) {
            ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        }
    }
    

Spring注解实现事务管理

spring实现事务管理就不再需要自己写事务管理工具类,只需要在业务上添加注解@Transactional

  1. 导入坐标

    <?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>
    
  2. 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);
        }
    }
    
  3. 配置文件:

    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对注解事务管理的支持

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1151671.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Jmeter 测试 MQ 接口怎么做?跟我学秒变大神!

MQ(message queue)消息队列&#xff0c;是基础数据结构 先进先出 的一种典型数据结构。一般用来解决应用解耦&#xff0c;异步消息&#xff0c;流量削锋等问题&#xff0c;实现高性能&#xff0c;高可用&#xff0c;可伸缩和最终一致性架构。 MQ 主要产品包括&#xff1a;Rabb…

ChatGLM3 模型学习与实战

文章目录 一&#xff0c;说明二、前言1. 背景2. 系统说明3. 相比于ChatGLM2-6B进行的 性能升级4. 模型列表 三、环境搭建1. 下载代码2 构建环境3 安装依赖4. 大模型下载方式4.1 安装 lfs 方便本地下载 ChatGLM2-6B 大模型4.2 Hmodelscop 上手动下载模型及其文件 【推荐】4.3 Hu…

大中小企业自招人力及劳务派遣全行业招聘抖音直播招聘效果佳

在抖音平台上&#xff0c;企业或者人力资源公司可以通过直播的形式&#xff0c;将职位以视频直播的方式展现出来。通过抖音直播招聘报白&#xff0c;企业或者人力资源公司可以利用抖音的短视频流量红利&#xff0c;触达到每天超过8亿的活跃用户群体。这样可以提高岗位信息的曝光…

python爬取百度图片上的图像

from fake_useragent import UserAgent import requests import re headers {"User-agent": UserAgent().random, # 随机生成一个代理请求"Accept-Language": "zh-CN,zh;q0.9,en;q0.8,en-GB;q0.7,en-US;q0.6","Connection": "k…

大厂面试题-JVM如何判断一个对象可以被回收

在JVM里面&#xff0c;要判断一个对象是否可以被回收&#xff0c;最重要的是判断这个对象是否还在被使用&#xff0c;只有没被使用的对象才能回收。 1. (如图)引用计数器&#xff0c;也就是为每一个对象添加一个引用计数器&#xff0c;用来统计指向当前对象的引用次数&…

精美UI强大娱乐功能组合微信小程序源码

这是一个多娱乐功能的小程序&#xff0c;功能炒鸡多&#xff0c;自行去体验把。 具体由以下功能组合: 网易云在线音乐(在线播放音乐和网易云功能界面一样) 外卖CPS(外卖平台优惠劵) 打车CPS(打车平台优惠劵) 头像功能(多分类头像,另外还支持姓氏头像制作) 图片加水印 表…

办公软件有哪些,办公软件哪个好

办公软件是指为办公和生产工作而设计的软件&#xff0c;包括文字处理、表格处理、演示文稿、电子邮件、日历、计划等各种应用软件。办公软件可以提高工作效率&#xff0c;让人们更加便捷地完成各种工作任务。随着科技不断发展&#xff0c;办公软件也在不断更新和完善&#xff0…

Instagram网红经济发展迅速!该如何紧抓这个流量

根据数据显示&#xff0c;网红营销市场规模在短短五年时间内从2016年的17亿美元增长至2022年的164亿美元&#xff0c;累计增速超过了712%。未来&#xff0c;有专家预计该市场预计将进一步增长&#xff0c;将在2023年突破210亿美元。这种惊人的增长趋势源于社交媒体的快速发展以…

[迁移学习]UniDAformer域自适应全景分割网络

原文的标题为&#xff1a;UniDAformer: Unified Domain Adaptive Panoptic Segmentation Transformer via Hierarchical Mask Calibration&#xff0c;发表于CVPR2023。 一、概述 域自适应全景分割是指利用一个或多个相关域中的现成标注数据来缓解语义分割数据标注复杂的问题。…

python环境部署

一、源码安装 1.安装依赖软件包 安装开发工具包 [rootlocalhost ~]# yum groupinstall "Development Tools" -y [rootlocalhost ~]# [rootlocalhost ~]# yum -y install zlib-devel bzip2-devel openssl-devel sqlite-devel readline-devel libffi-devel官网下…

pytorch打印模型结构和参数

两种方式 当我们使用pytorch进行模型训练或测试时&#xff0c;有时候希望能知道模型每一层分别是什么&#xff0c;具有怎样的参数。此时我们可以将模型打印出来&#xff0c;输出每一层的名字、类型、参数等。 常用的命令行打印模型结构的方法有两种&#xff1a; 一是直接prin…

[已解决]大数据集群CPU告警问题解决

大数据集群CPU告警问题解决 问题 6台机器的 CPU总是连续超过90% 思路 调整yarn资源 常见的是调整容器虚拟 CPU 内核 yarn.nodemanager.resource.cpu-vcores 根据集群具体的CPU核数规划 我另外调整了两个参数 最小容器虚拟 CPU 内核数量 yarn.scheduler.minimum-allocati…

中科院上高院,协鑫,和数“能源数字化智能管控”合作项目开启

10月27日&#xff0c;上海和数软件有限公司与协鑫综合能源服务有限公司、中国科学院上海高等研究院签署了《关于“能源数字化智能管控”开发与应用框架合作协议》。 这也标志着新疆协鑫智慧能源有限公司数字化智能提升项目——数字孪生项目正式启动。 根据协议&#xff0c;三方…

Flash Attention 的优点以及Softmax 归一化系数解释

文章&#xff1a;FLASHATTENTION: Fast and Memory-Efficient Exact Attention with IO-Awareness 原始Attention 计算使用gpu存储标准流程 涉及两个gpu存储器&#xff1a; 1&#xff09;SRAM&#xff08;static Random Access Memory&#xff09;:静态随机存取存储器 2&…

Python Django 之全局配置 settings 详解

文章目录 1 概述1.1 Django 目录结构 2 常用配置&#xff1a;settings.py2.1 注册 APP&#xff1a;INSTALLED_APPS2.2 模板路径&#xff1a;TEMPLATES2.3 静态文件&#xff1a;STATICFILES_DIRS2.4 数据库&#xff1a;DATABASES2.5 允许访问的主机&#xff1a;ALLOWED_HOSTS 1 …

【C】柔性数组

柔性数组 也许你从来没有听说过柔性数组&#xff08;flexible array)这个概念&#xff0c;但是它确实是存在的。 C99 中&#xff0c;结构中的最后一个元素允许是未知大小的数组&#xff0c;这就叫做『柔性数组』成员。 例如&#xff1a; 柔性数组的特点 结构中的柔性数组成员前…

tab切换样式

一、样式一 <view class"tabs"><view class"tab" :class"{active: active index}" v-for"(item, index) in tabs" click"active index">{{item.label}}</view></view> data(){return {active: 0…

Codeforces Round 905 (Div. 3)

目录 A. Morning B. Chemistry C. Raspberries D. In Love E. Look Back F. You Are So Beautiful G1. Dances (Easy version) G2. Dances (Hard Version) A. Morning time limit per test 2 seconds memory limit per test 256 megabytes input standard input …

重磅新闻-国内首家八类网线认证分析仪上市了

伴随USA对国内某些敏感企业的非常不友好&#xff0c;设置层层障碍&#xff0c;技术堡垒。使得一些网线基础制造研发、线缆线束厂、汽车生产生产厂、军工用途的线缆品质的认证、以及相关高校的研发受到了不同的程度的阻碍。重磅消息&#xff0c;国内首家八类网线认证测仪-维信仪…