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

模拟转账业务

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

el-tabs 默认选中第一个

1. 实际开发中el-tabs 都会设置第一个为默认值 ,这样会好看一点, 而渲染的数据经常是通过后端返回的数据 , v-model 无法写死默认值 解决办法 , 通过计算机属性 ,在data 定义一个 selectedTab watch: {defaultTab(newVal) {this.selectedTab newVal; // 设置第一个标签页…

汽车贴膜店展示服务预约小程序的作用是什么

很多家庭都有车辆&#xff0c;除了车身自带颜色或外观&#xff0c;部分消费者会选择贴车衣、改色膜以及其它装饰类服务&#xff1b;而市场高需求下也促进了商家生意增长。 但随着线上化程度加深&#xff0c;传统线下门店也面临多重困境&#xff0c;品牌需要线上发展获得生意及…

Spring面试题:(二)基于xml方式的Spring配置

xml配置Bean的常见属性 id属性 name属性 scope属性 lazy-init属性 init-method属性和destroy属性 initializingBean方法 Bean实例化方式 ApplicationContext底层调用BeanFactory创建Bean&#xff0c;BeanFactory可以利用反射机制调用构造方法实例化Bean&#xff0c;也可采用工…

【6】c++11新特性(稳定性和兼容性)—>Lambda表达式

基本用法 lambda表达式是c最重要也是最常用的特性之一&#xff0c;这是现代编程语言的一个特点&#xff0c;lambda表达式有如下的一些优点&#xff1a; &#xff08;1&#xff09;声明式的编成风格&#xff1a;就地匿名定义目标函数活着函数对象&#xff0c;不需要额外写一个命…

docker离线部署

docker离线部署 1、目的 在可以连接互联网的情况下&#xff0c;可以在线安装Docker《Linux下Docker安装部署》&#xff0c;如果遇到内网服务器就没有办法进行在线安装&#xff0c;那么需要使用离线安装的方法。 2、下载安装包 创建工作文件夹&#xff1a; mkdir /opt/dock…

【错误解决方案】Error: module ‘cv2‘ has no attribute ‘SURF‘

1. 错误提示 python-opencv高版本中&#xff0c;AttributeError: module cv2 has no attribute SURF问题&#xff1b; 错误提示&#xff1a;Error: module ‘cv2‘ has no attribute ‘SURF‘ 2. 解决方案 解决&#xff1a;将sift cv2.SIFT()替换为&#xff1a;sift cv2.x…

vue3引入并加载unity工程的两种方式

1、使用unity-webgl插件 npm i unity-webglunity打包后的build文件夹是这样的 需要手动删除.unityweb这个后缀&#xff0c;完成后放在vue3项目的根目录下的public文件夹下。 下面是引入unity的vue组件,其中实例化UnityWebgl时的参数地址直接引用上面的对应文件地址 <scri…

源码角度分析Java 循环中删除数据为什么会报异常

一、源码角度分析Java 循环中删除数据为什么会报异常 相信大家在之前或多或少都知道 Java 中在增强 for中删除数据会抛出&#xff1a;java.util.ConcurrentModificationException 异常&#xff0c;例如&#xff1a;如下所示程序&#xff1a; public class RmTest {public sta…

刷题学习记录

[RoarCTF 2019]Easy Java1 一开始是一个登陆页面&#xff0c;看着有点想用sql注入先试一遍&#xff0c;但是题目已经给出了这是关于Java的 直接查看源码&#xff0c;发现参数要用POST的方式上传 点击进入新页面 百度查了一下这是web.xml 泄露 解题先知WEB-INF WEB-INF是java…

报修软件有什么用?企业如何做好设备管理与维护?

在当今的商业环境中&#xff0c;设备设施的维护和管理已经成为企业运营的重要环节。无论是学校、酒店、物业等大型企事业单位&#xff0c;还是运维集成商、制造工厂等企业单位&#xff0c;都需要对设备设施进行有效的管理。报修软件作为一种智能化的解决方案&#xff0c;为设备…

基于springboot实现疫情防控期间外出务工人员信息管理系统项目【项目源码+论文说明】

基于springboot疫情防控期间外出务工人员信息管理系统 摘要 网络的广泛应用给生活带来了十分的便利。所以把疫情防控期间某村外出务工人员信息管理与现在网络相结合&#xff0c;利用java技术建设疫情防控期间某村外出务工人员信息管理系统&#xff0c;实现疫情防控期间某村外出…

太极培训机构展示服务预约小程序的作用如何

太极是适合男女老幼的&#xff0c;很多地方也有相关的学校或培训机构&#xff0c;由于受众广且不太受地域影响&#xff0c;因此对培训机构来说&#xff0c;除了线下经营外&#xff0c;线上宣传、学员获取和发展也不可少。 接下来让我们看下通过【雨科】平台制作太极教培服务预…

如何确认目标期刊被SCI或EI收录?

原创内容&#xff0c;仅供参考&#xff0c;欢迎大家批评指正&#xff01; 目录 通过Web of Science查询SCI期刊1. 登录Web of Science2. 查找目标期刊3. 查看期刊信息 通过Scopus查询EI期刊1. 登录Scopus2. 查找目标期刊3. 查看期刊信息 参考 通过Web of Science查询SCI期刊 1…

Netty第一部

一、select和epoll原理分析 外设设备网卡、鼠标、键盘等通过总线写到内存中&#xff0c;中间就有DMA拷贝&#xff0c;操作系统怎么知道内存中有数据了&#xff0c;这就需要操作系统通过中断机制确定&#xff0c;如果有中断信号过来&#xff0c;cpu会首先打断用户程序执行&…

加州大学提出 PromptAgent 帮忙我们高效的使用 ChatGPT

本心、输入输出、结果 文章目录 加州大学提出 PromptAgent 帮忙我们高效的使用 ChatGPT前言加州大学团队提出了可以自动优化 Prompt 的框架 —— PromptAgentPromptAgent 原理论文 实例介绍PromptAgent 框架设计PromptAgent 的策略优化过过程PromptAgent 的结果是否具备普适性弘…

文心一言4.0对比ChatGPT4.0有什么优势?

目录 总结 文心一言4.0的优势 文心一言4.0的劣势 免费分享使用工具 后话 生成式AI的困境 “不会问”“不会用”“不敢信” 为什么要出收费版本&#xff1f; 目前使用过国内的文心一言3.5和WPS AI&#xff0c;国外的ChatGPT4.0。 文心一言和其他国内产品相比&#xff0…

SpringCloud(三) Ribbon负载均衡

SpringCloud(二) Eureka注册中心的使用-CSDN博客 在SpringCloud(二)中学习了如何通过Eureka实现服务的注册和发送,从而通过RestTemplate实现不同微服务之间的调用,加上LoadBalance注解之后实现负载均衡,那负载均衡的原理是什么呢? 目录 一, 负载均衡 1.1 负载均衡原理 1.2 源…

【Unity编辑器扩展】艺术字/自定义图片字体生成工具

艺术字在游戏中很常用&#xff0c;由于普通字体样式过于平淡&#xff0c;制作花里胡哨的文字图片作为游戏字体使用&#xff0c;这就是艺术字。 不依赖第三方工具&#xff0c;仅使用Unity自带的Custom Font 一张艺术字图集就能实现这个功能&#xff0c;但是为了便于使用&#…

【AI视野·今日Robot 机器人论文速览 第六十一期】Tue, 24 Oct 2023

AI视野今日CS.Robotics 机器人学论文速览 Tue, 24 Oct 2023 Totally 50 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Robotics Papers Robot Fine-Tuning Made Easy: Pre-Training Rewards and Policies for Autonomous Real-World Reinforcement Learning Autho…

【数据结构】数组和字符串(十三):链式字符串的基本操作(串长统计、查找、复制、插入、删除、串拼接)

文章目录 4.3 字符串4.3.1 字符串的定义与存储4.3.2 字符串的基本操作&#xff08;链式存储&#xff09;1. 结构体2. 初始化3. 判空4. 串尾添加5. 打印6. 串长统计7. 查找8. 复制9. 插入10. 删除11. 串拼接12. 销毁13. 主函数14. 代码整合 4.3 字符串 字符串(String)是由零个或…