SSM2---spring

news2024/11/18 7:32:32

Spring

spring环境搭建

  • 创建一个空白模块,目录结构如下
    在这里插入图片描述

  • 在pom.xml文件中引入相关依赖

<?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">
    <parent>
        <artifactId>SSM</artifactId>
        <groupId>com.itzhh.ssm</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.itzhh.spring</groupId>
    <artifactId>spring_ioc_xml</artifactId>
    <packaging>jar</packaging>

    <dependencies>
        <!-- 基于Maven依赖传递性,导入spring-context依赖即可导入当前所需所有jar包 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.1</version>
        </dependency>

        <!-- junit测试 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

</project>
  • 创建对应测试实体类
package com.itzhh.spring.pojo;

/**
 * Author: zhh
 * Date: 2022-12-27 14:36
 * Description: <描述>
 */
public class Student {
    private Integer sid;
    private String sname;
    private String gender;
    private Integer age;

    public Student(Integer sid, String sname, String gender, Integer age) {
        this.sid = sid;
        this.sname = sname;
        this.gender = gender;
        this.age = age;
    }

    public Student() {
    }

    public Integer getSid() {
        return sid;
    }

    public void setSid(Integer sid) {
        this.sid = sid;
    }

    public String getSname() {
        return sname;
    }

    public void setSname(String sname) {
        this.sname = sname;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "sid=" + sid +
                ", sname='" + sname + '\'' +
                ", gender='" + gender + '\'' +
                ", age=" + age +
                '}';
    }
}

  • 在resources下创建spring配置文件,这边目前可以任意命名,但后面SSM整合时命名为applicationContext.xml,我这边命名为spring-ioc.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="studentOne" class="com.itzhh.spring.pojo.Student">
        <property name="sid" value="1"></property>
        <property name="sname" value="张三"></property>
        <property name="age" value="23"></property>
        <property name="gender" value=""></property>
    </bean>

    <bean id ="studentTwo" class="com.itzhh.spring.pojo.Student">
        <constructor-arg value="2" name="sid"></constructor-arg>
        <constructor-arg value="李四" name="sname"></constructor-arg>
        <constructor-arg value="45" name="age"></constructor-arg>
        <constructor-arg value="" name="gender"></constructor-arg>
    </bean>
</beans>
  • 创建测试类调用
package com.itzhh.spring.test;

import com.itzhh.spring.pojo.Student;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * Author: zhh
 * Date: 2022-12-27 14:38
 * Description: <描述>
 */
public class StudentTest {

    @Test
    public void testStudent(){

        ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc.xml");
        //Student studentOne = (Student) ioc.getBean("studentOne");
        Student studentOne = (Student) ioc.getBean("studentOne",Student.class);
        Student studentTwo = (Student) ioc.getBean("studentTwo",Student.class);
        System.out.println(studentOne);
        System.out.println(studentTwo);
    }
}

  • 结果
Student{sid=1, sname='张三', gender='男', age=23}
Student{sid=2, sname='李四', gender='女', age=45}

Process finished with exit code 0

ioc

常见类型的依赖注入

  • 在spring-ioc.xml中配置bean,实现依赖注入
<?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:util="http://www.springframework.org/schema/util"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd">

    <!--setter方式注入-->
    <bean id="studentOne" class="com.itzhh.spring.pojo.Student">
        <property name="sid" value="1"></property>
<!--        CDATA节中的内容会原样解析-->
        <property name="sname" >
            <value><![CDATA[<张三>]]></value>
        </property>
        <property name="age" value="23"></property>
        <property name="gender" value=""></property>
<!--        array类型注入-->
        <property name="hobbies">
            <array>
                <value>看电影</value>
                <value>打游戏</value>
                <value>睡觉</value>
            </array>
        </property>
    </bean>

<!--    构造器注入-->
    <bean id ="studentTwo" class="com.itzhh.spring.pojo.Student">
        <constructor-arg value="2" name="sid"></constructor-arg>
        <constructor-arg value="李四" name="sname"></constructor-arg>
        <constructor-arg value="45" name="age"></constructor-arg>
        <constructor-arg value="" name="gender"></constructor-arg>
    </bean>

    <bean id="clazzOne" class="com.itzhh.spring.pojo.Clazz">
        <property name="cid" value="1"></property>
        <property name="cname" value="最强王者班"></property>
        <property name="students" ref="students"></property>
<!--        map类型注入方式一-->
        <property name="teachers">
            <map>
                <entry>
                    <key>
                        <value>10086</value>
                    </key>
                    <ref bean="teacherOne"></ref>
                </entry>
                <entry>
                    <key>
                        <value>10001</value>
                    </key>
                    <ref bean="teacherTwo"></ref>
                </entry>
            </map>
        </property>
    </bean>

    <bean id="clazzTwo" class="com.itzhh.spring.pojo.Clazz">
        <property name="cid" value="2"></property>
        <property name="cname" value="菜鸡班"></property>
        <property name="teachers" ref="teachers"></property>
<!--        list类型注入方式一-->
        <property name="students">
            <list>
                <ref bean="studentOne"></ref>
                <ref bean="studentTwo"></ref>
            </list>
        </property>
    </bean>

    <bean id="teacherOne" class="com.itzhh.spring.pojo.Teacher">
        <property name="tid" value="1"></property>
        <property name="tname" value="大宝"></property>
    </bean>

    <bean id="teacherTwo" class="com.itzhh.spring.pojo.Teacher">
        <property name="tid" value="2"></property>
        <property name="tname" value="小宝"></property>
    </bean>
<!--list类型注入方式二-->
    <util:list id="students">
        <ref bean="studentOne"></ref>
        <ref bean="studentTwo"></ref>
    </util:list>
<!--map类型注入方式二-->
    <util:map id="teachers">
        <entry >
            <key>
                <value>10086</value>
            </key>
            <ref bean="teacherOne"></ref>
        </entry>
        <entry>
            <key>
                <value>10001</value>
            </key>
            <ref bean="teacherTwo"></ref>
        </entry>
    </util:map>

<!--    通过p注入,需要引入命名空间-->
    <bean id="studentThree" class="com.itzhh.spring.pojo.Student"
          p:sid="3" p:age="23" p:gender="">
    </bean>


</beans>
  • 测试代码
package com.itzhh.spring.test;

import com.itzhh.spring.pojo.Clazz;
import com.itzhh.spring.pojo.Student;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import sun.util.resources.cldr.az.CalendarData_az_Latn_AZ;

/**
 * Author: zhh
 * Date: 2022-12-27 14:38
 * Description: <描述>
 */
public class StudentTest {

    @Test
    public void testStudent(){

        ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc.xml");
        //Student studentOne = (Student) ioc.getBean("studentOne");
        // Student studentOne = (Student) ioc.getBean("studentOne",Student.class);
         Student studentThree= (Student) ioc.getBean("studentThree",Student.class);
        // Student studentTwo = (Student) ioc.getBean("studentTwo",Student.class);
        // System.out.println(studentOne);
        // System.out.println(studentTwo);
         System.out.println(studentThree);

        Clazz clazzOne = (Clazz) ioc.getBean("clazzOne");
        Clazz clazzTwo = (Clazz) ioc.getBean("clazzTwo");
        System.out.println(clazzOne);
        System.out.println(clazzTwo);
    }
}

引入外部属性文件

  • 添加相关依赖
  <!-- MySQL驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.16</version>
        </dependency>
        <!-- 数据源 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.0.31</version>
        </dependency>
  • 创建spring-dataSource.xml并通过context引入外部属性文件jdbc.properties
<?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">

    <!--引入外部属性文件-->
<context:property-placeholder location="jdbc.properties"></context:property-placeholder>

    <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driver}"></property>
        <property name="url" value="${jdbc.url}"></property>
        <property name="username" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>
</beans>
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC
jdbc.username=root
jdbc.password=123456
  • 测试代码
package com.itzhh.spring.test;

import com.alibaba.druid.pool.DruidDataSource;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.sql.SQLException;

/**
 * Author: zhh
 * Date: 2022-12-28 10:21
 * Description: <描述>
 */
public class DataSourceTest {

    @Test
    public void testDataSource() throws SQLException {
        ApplicationContext ioc=new ClassPathXmlApplicationContext("spring-dataSource.xml");
        DruidDataSource druidDataSource = ioc.getBean(DruidDataSource.class);
        System.out.println(druidDataSource.getConnection());
    }
}

bean生命周期

  • 创建测试对象
package com.itzhh.spring.pojo;

/**
 * Author: zhh
 * Date: 2022-12-28 13:48
 * Description: <描述>
 */
public class User {
    private Integer id;
    private String username;
    private String password;
    private String gender;

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", gender='" + gender + '\'' +
                '}';
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
        System.out.println("生命周期2----依赖注入");
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public User() {
        System.out.println("生命周期1--创建对象");
    }

    public User(Integer id, String username, String password, String gender) {
        this.id = id;
        this.username = username;
        this.password = password;
        this.gender = gender;
    }

    public void initMethod(){
        System.out.println("生命周期3---初始化");
    }

    public void destroyMethod(){
        System.out.println("生命周期5---销毁");
    }
}

  • 配置后处理器
package com.itzhh.spring.pojo;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

/**
 * Author: zhh
 * Date: 2022-12-28 14:07
 * Description: <描述>
 */
public class MyBeanProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("后置处理器-postProcessBeforeInitialization+beanName:"+beanName);
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("后置处理器-postProcessAfterInitialization+beanName:"+beanName);
        return bean;
    }
}

  • 创建配置文件
<?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="user" class="com.itzhh.spring.pojo.User" init-method="initMethod" destroy-method="destroyMethod">
        <property name="id" value="1"></property>
        <property name="username" value="admin" ></property>
        <property name="password" value="123456"></property>
        <property name="gender" value="male" ></property>
    </bean>

    <bean class="com.itzhh.spring.pojo.MyBeanProcessor"></bean>
</beans>
  • 测试代码,使用 ConfigurableApplicationContext来管理ioc
package com.itzhh.spring.test;

import com.itzhh.spring.pojo.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * Author: zhh
 * Date: 2022-12-28 13:57
 * Description: <描述>
 */
public class LifeCycleTest {

    @Test
    public void testLifeCycle(){
        // ApplicationContext ioc=new ClassPathXmlApplicationContext("spring-lifeCycle.xml");
        ConfigurableApplicationContext ioc=new ClassPathXmlApplicationContext("spring-lifeCycle.xml");
        User bean = ioc.getBean(User.class);
        System.out.println(bean);
        ioc.close();

    }
}

  • 结果
生命周期1--创建对象
生命周期2----依赖注入
后置处理器-postProcessBeforeInitialization+beanName:user
生命周期3---初始化
后置处理器-postProcessAfterInitialization+beanName:user
User{id=1, username='admin', password='123456', gender='male'}
生命周期5---销毁

Process finished with exit code 0

FactoryBean的使用

  • 创建一个类实现FactoryBean接口
package com.itzhh.spring.pojo;

import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIConversion;
import org.springframework.beans.factory.FactoryBean;

/**
 * Author: zhh
 * Date: 2022-12-28 13:42
 * Description: <描述>
 */
public class StudentFactoryBean implements FactoryBean<Student> {
    @Override
    public Student getObject() throws Exception {
        return new Student();
    }

    @Override
    public Class<?> getObjectType() {
        return Student.class;
    }
}

  • 在配置文件中配置
<?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 class="com.itzhh.spring.pojo.StudentFactoryBean"></bean>
</beans>
  • 测试代码:通过FactoryBean直接获取Student对象
package com.itzhh.spring.test;

import com.itzhh.spring.pojo.Student;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * Author:zhh
 * Date: 2022-12-28 13:45
 * Description: <描述>
 */
public class FactoryBeanTest {

    @Test
    public void testFactoryBean(){
        ApplicationContext ioc=new ClassPathXmlApplicationContext("spring-factoryBean.xml");
        Student bean = ioc.getBean(Student.class);
        System.out.println(bean);
    }
}

  • 结果
Student{sid=null, sname='null', gender='null', age=null, hobbies=null}

Process finished with exit code 0

基于注解的自动装配

  • 基于xml的自动装配用的不多,就不整理了。
  • 模拟一个三层结构通过注解来实现自动装配
  • 目录结构
    在这里插入图片描述
  • 通过@Component或者@Controller、@Service、@Repository将类纳入ioc容器管理
package com.itzhh.spring.controller;

import com.itzhh.spring.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;

/**
 * Author: zhh
 * Date: 2022-12-29 9:04
 * Description: <描述>
 */
@Controller
// @Component
public class UserController {

    @Autowired
    // @Qualifier("userServiceImpl")
    //可以通过@Qualifier("userServiceImpl")注解来指定具体的bean
    private UserService userService;

    public void saveUser(){
        userService.saveUser();
    }
}

package com.itzhh.spring.service;

/**
 * Author: zhh
 * Date: 2022-12-29 9:05
 * Description: <描述>
 */
public interface UserService {
    void saveUser();
}

package com.itzhh.spring.service.impl;

import com.itzhh.spring.dao.UserDao;
import com.itzhh.spring.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * Author: zhh
 * Date: 2022-12-29 9:05
 * Description: <描述>
 */
@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserDao userDao;
    @Override
    public void saveUser() {
        userDao.saveUser();
    }
}

package com.itzhh.spring.dao;

/**
 * Author: zhh
 * Date: 2022-12-29 9:05
 * Description: <描述>
 */
public interface UserDao {
    void saveUser();
}

package com.itzhh.spring.dao.impl;

import com.itzhh.spring.dao.UserDao;
import org.springframework.stereotype.Repository;

/**
 * Author: zhh
 * Date: 2022-12-29 9:06
 * Description: <描述>
 */
@Repository
public class UserDaoImpl implements UserDao {


    @Override
    public void saveUser() {
        System.out.println("saveUser...");
    }
}

  • 通过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">
<!-- context:include-filter标签:指定在原有扫描规则的基础上追加的规则 -->
<!-- use-default-filters属性:取值false表示关闭默认扫描规则 -->
<!-- 此时必须设置use-default-filters="false",因为默认规则即扫描指定包下所有类 -->
<!--
type:设置排除或包含的依据
type="annotation",根据注解排除,expression中设置要排除的注解的全类名
type="assignable",根据类型排除,expression中设置要排除的类型的全类名
-->
    
    <context:component-scan base-package="com.itzhh.spring">
<!--        根据注解设置需要排除的-->
<!--        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>-->
    </context:component-scan>

</beans>
  • 测试代码
package com.itzhh.spring.test;

import com.itzhh.spring.controller.UserController;
import com.itzhh.spring.dao.impl.UserDaoImpl;
import com.itzhh.spring.service.impl.UserServiceImpl;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * Author: zhh
 * Date: 2022-12-29 9:10
 * Description: <描述>
 */
public class annotationTest {

    @Test
    public void test(){
        ApplicationContext ioc=new  ClassPathXmlApplicationContext("applicationContext.xml");
        UserServiceImpl service = ioc.getBean(UserServiceImpl.class);
        UserDaoImpl dao = ioc.getBean(UserDaoImpl.class);
        UserController controller = ioc.getBean(UserController.class);
        System.out.println(service);
        System.out.println(dao);
        controller.saveUser();
       // System.out.println(controller);
    }
}

  • 结果
com.itzhh.spring.service.impl.UserServiceImpl@79ad8b2f
com.itzhh.spring.dao.impl.UserDaoImpl@1df82230
saveUser...

Process finished with exit code 0

aop

动态代理

  • 使用动态代理为加减乘除添加日志
package com.izhh.spring.pojo;

/**
 * Author: zhh
 * Date: 2022-12-29 14:05
 * Description: <描述>
 */
public interface Calculator {
    int add(int i, int j);
    int sub(int i, int j);
    int mul(int i, int j);
    int div(int i, int j);
}

package com.izhh.spring.pojo;

/**
 * Author: zhh
 * Date: 2022-12-29 14:05
 * Description: <描述>
 */
public class CalculatorImpl implements Calculator{

    @Override
    public int add(int i, int j) {
        int result = i + j;
        System.out.println("方法内部 result = " + result);
        return result;
    }
    @Override
    public int sub(int i, int j) {
        int result = i - j;
        System.out.println("方法内部 result = " + result);
        return result;
    }
    @Override
    public int mul(int i, int j) {
        int result = i * j;
        System.out.println("方法内部 result = " + result);
        return result;
    }
    @Override
    public int div(int i, int j) {
        int result = i / j;
        System.out.println("方法内部 result = " + result);
        return result;
    }
}


package com.izhh.spring.pojo;

import java.lang.reflect.*;
import java.util.Arrays;

/**
 * Author: zhh
 * Date: 2022-12-29 14:09
 * Description: <描述>
 */
public class ProxyFactory {
    private Object target;

    public ProxyFactory(Object target) {
        this.target = target;
    }

    public Object getProxy(){
        /**
         * newProxyInstance():创建一个代理实例
         * 其中有三个参数:
         * 1、classLoader:加载动态生成的代理类的类加载器
         * 2、interfaces:目标对象实现的所有接口的class对象所组成的数组
         * 3、invocationHandler:设置代理对象实现目标对象方法的过程,即代理类中如何重写接
         口中的抽象方法
         */
        ClassLoader classLoader = target.getClass().getClassLoader();
        Class<?>[] interfaces = target.getClass().getInterfaces();
        InvocationHandler invocationHandler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            /**
             * proxy:代理对象
             * method:代理对象需要实现的方法,即其中需要重写的方法
             * args:method所对应方法的参数
             */
                Object result = null;
                try {
                    System.out.println("动态代理-日志:"+method.getName()+",参数:"+ Arrays.toString(args));
                    result = method.invoke(target, args);
                    System.out.println("动态代理-日志:"+method.getName()+",结果:"+result);
                } catch (Exception e) {
                    e.printStackTrace();
                    System.out.println("动态代理-日志:"+method.getName()+",异常:"+e.getMessage());
                } finally {
                    System.out.println("动态代理-日志:"+method.getName()+",方法执行完毕");
                }

                return result;
            }
        };
        return Proxy.newProxyInstance(classLoader,interfaces,invocationHandler);
    }
}

  • 测试代码
package com.itzhh.spring.test;

import com.izhh.spring.pojo.Calculator;
import com.izhh.spring.pojo.CalculatorImpl;
import com.izhh.spring.pojo.ProxyFactory;
import org.junit.Test;

/**
 * Author: zhh
 * Date: 2022-12-29 14:20
 * Description: <描述>
 */
public class DynamicProxyTest {


    @Test
    public  void testDynamicProxy(){
        ProxyFactory factory=new ProxyFactory(new CalculatorImpl());
        Calculator proxy = (Calculator) factory.getProxy();
        proxy.div(3,4);
        //proxy.div(3,0);
    }


}

  • 结果
动态代理-日志:div,参数:[3, 4]
方法内部 result = 0
动态代理-日志:div,结果:0
动态代理-日志:div,方法执行完毕

相关术语

  • AOP(Aspect Oriented Programming)是一种设计思想,是软件设计领域中的面向切面编程,它是面向对象编程的一种补充和完善,它以通过预编译方式和运行期动态代理方式实现在不修改源代码的情况下给程序动态统一添加额外功能的一种技术。
  • 横切关注点 从每个方法中抽取出来的同一类非核心业务。在同一个项目中,我们可以使用多个横切关注点对相关方法进行多个不同方面的增强
  • 通知 每一个横切关注点上要做的事情都需要写一个方法来实现,这样的方法就叫通知方法。
    前置通知:在被代理的目标方法前执行
    返回通知:在被代理的目标方法成功结束后执行(寿终正寝)
    异常通知:在被代理的目标方法异常结束后执行(死于非命)
    后置通知:在被代理的目标方法最终结束后执行(盖棺定论)
    环绕通知:使用try…catch…finally结构围绕整个被代理的目标方法,包括上面四种通知对应的所有位置
  • 切面 封装通知方法的类。
  • 目标 被代理的目标对象。
  • 代理 向目标对象应用通知之后创建的代理对象。
  • 连接点 这也是一个纯逻辑概念,不是语法定义的。
  • 切入点 定位连接点的方式。

基于注解的AOP

  • 在pom.xml中引入相关依赖,不止需要aop的,还需要ioc的,因为是基于ioc实现的
<?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">
    <parent>
        <artifactId>SSM</artifactId>
        <groupId>com.itzhh.ssm</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.itzhh.spring</groupId>
    <artifactId>spring-aop</artifactId>
    <packaging>jar</packaging>

    <dependencies>
        <!-- 基于Maven依赖传递性,导入spring-context依赖即可导入当前所需所有jar包 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.1</version>
        </dependency>

        <!-- junit测试 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>

        <!-- spring-aspects会帮我们传递过来aspectjweaver -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.3.1</version>
        </dependency>
    </dependencies>


</project>
  • 创建目标接口和目标实现类并将其纳入ioc容器的管理
package com.itzhh.spring.annotation;

/**
 * Author: zhh
 * Date: 2022-12-29 14:05
 * Description: <描述>
 */
public interface Calculator {
    int add(int i, int j);
    int sub(int i, int j);
    int mul(int i, int j);
    int div(int i, int j);
}

package com.itzhh.spring.annotation;

import org.springframework.stereotype.Component;

/**
 * Author: zhh
 * Date: 2022-12-29 14:05
 * Description: <描述>
 */
@Component
public class CalculatorImpl implements Calculator{

    @Override
    public int add(int i, int j) {
        int result = i + j;
        System.out.println("方法内部 result = " + result);
        return result;
    }
    @Override
    public int sub(int i, int j) {
        int result = i - j;
        System.out.println("方法内部 result = " + result);
        return result;
    }
    @Override
    public int mul(int i, int j) {
        int result = i * j;
        System.out.println("方法内部 result = " + result);
        return result;
    }
    @Override
    public int div(int i, int j) {
        int result = i / j;
        System.out.println("方法内部 result = " + result);
        return result;
    }
}


  • 创建切面和各种通知,通过@Aspect注解来表示其为一个切面,通过@Order来设置切面的优先级,通过Pointcut来设置重用的切点表达式
package com.itzhh.spring.annotation;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.NavigableMap;

/**
 * Author: zhh
 * Date: 2022-12-30 9:02
 * Description: <描述>
 */
//@Component注解保证这个切面类能够放入IOC容器
@Component
//@Aspect表示这个类是一个切面类
@Aspect

public class LogAspect {
    //设重用切点表达式
    @Pointcut("execution(* com.itzhh.spring.annotation.*.*(..))")
    public void pointCut(){
    }

    @Before("pointCut()")
    public void beforeMethod(JoinPoint joinPoint){
        String methodName = joinPoint.getSignature().getName();
        String args = Arrays.toString(joinPoint.getArgs());
        System.out.println("Logger->前置通知,方法名:"+methodName+"参数:"+args);
    }

    @After("pointCut()")
    public void afterMethod(JoinPoint joinPoint){
        String methodName = joinPoint.getSignature().getName();
        System.out.println("Logger->后置通知,方法名:"+methodName);
    }

    @AfterReturning(value = "pointCut(),",returning = "result")
    public void afterReturningMethod(JoinPoint joinPoint,Object result){
        String name = joinPoint.getSignature().getName();
        System.out.println("Logger->返回通知,方法名:"+ name+",结果:"+result);
    }

    @AfterThrowing(value = "pointCut()",throwing = "ex")
    public void afterThrowingMethod(JoinPoint joinPoint,Throwable ex){
        String name = joinPoint.getSignature().getName();
        System.out.println("Logger->异常通知,方法名:"+name+",异常:"+ex);
    }

	 @Around("pointCut()")
	 public Object aroundMethod(ProceedingJoinPoint joinPoint){
        String name = joinPoint.getSignature().getName();
        String args=Arrays.toString(joinPoint.getArgs());
        Object result = null;
        System.out.println("环绕通知-->目标对象方法执行之前");
        //目标对象(连接点)方法的执行
        try {
            result=joinPoint.proceed();
            System.out.println("环绕通知-->目标对象方法返回值之后");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            System.out.println("环绕通知-->目标对象方出现异常时");
        }finally {
            System.out.println("环绕通知-->目标对象方法执行完毕");
        }
        return result;
    }

}

package com.itzhh.spring.annotation;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

/**
 * Author: zhh
 * Date: 2022-12-30 9:37
 * Description: <描述>
 */
@Component
@Aspect
//通过Order来设置优先级,数字越小,优先级越高
@Order(1)
public class ValidateAspect {

    @Before("com.itzhh.spring.annotation.LogAspect.pointCut()")
    public  void beforeMethod(){
        System.out.println("ValidateAspect.beforeMethod");
    }
}

  • 创建xml完成扫包和开启基于注解的aop
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       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/aop https://www.springframework.org/schema/aop/spring-aop.xsd">


    <context:component-scan base-package="com.itzhh.spring.annotation">

    </context:component-scan>
    <!--开启基于注解的AOP-->
    <aop:aspectj-autoproxy/>
</beans>
  • 测试代码
package com.itzhh.spring.annotation;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * Author: zhh
 * Date: 2022-12-30 9:41
 * Description: <描述>
 */
public class AopAnnotationTest {

    @Test
    public void testAopAnnotation(){
        ApplicationContext ioc=new ClassPathXmlApplicationContext("spring-aop-annotation.xml");
        Calculator calculator = ioc.getBean(Calculator.class);
        calculator.add(1,1);
    }
}

  • 结果
ValidateAspect.beforeMethod
环绕通知-->目标对象方法执行之前
Logger->前置通知,方法名:add参数:[1, 1]
方法内部 result = 2
Logger->返回通知,方法名:add,结果:2
Logger->后置通知,方法名:add
环绕通知-->目标对象方法返回值之后
环绕通知-->目标对象方法执行完毕

Process finished with exit code 0

基于xml的AOP

  • 环境参考基于注解的配置
package com.itzhh.spring.xml;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;

/**
 * Author: zhh
 * Date: 2022-12-30 9:02
 * Description: <描述>
 */
//@Component注解保证这个切面类能够放入IOC容器
@Component
public class LogAspect {


    public void beforeMethod(JoinPoint joinPoint){
        String methodName = joinPoint.getSignature().getName();
        String args = Arrays.toString(joinPoint.getArgs());
        System.out.println("Logger->前置通知,方法名:"+methodName+"参数:"+args);
    }


    public void afterMethod(JoinPoint joinPoint){
        String methodName = joinPoint.getSignature().getName();
        System.out.println("Logger->后置通知,方法名:"+methodName);
    }


    public void afterReturningMethod(JoinPoint joinPoint,Object result){
        String name = joinPoint.getSignature().getName();
        System.out.println("Logger->返回通知,方法名:"+ name+",结果:"+result);
    }


    public void afterThrowingMethod(JoinPoint joinPoint,Throwable ex){
        String name = joinPoint.getSignature().getName();
        System.out.println("Logger->异常通知,方法名:"+name+",异常:"+ex);
    }

    public Object aroundMethod(ProceedingJoinPoint joinPoint){
        String name = joinPoint.getSignature().getName();
        String args=Arrays.toString(joinPoint.getArgs());
        Object result = null;
        System.out.println("环绕通知-->目标对象方法执行之前");
        //目标对象(连接点)方法的执行
        try {
            result=joinPoint.proceed();
            System.out.println("环绕通知-->目标对象方法返回值之后");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            System.out.println("环绕通知-->目标对象方出现异常时");
        }finally {
            System.out.println("环绕通知-->目标对象方法执行完毕");
        }
        return result;
    }

}

package com.itzhh.spring.xml;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

/**
 * Author: zhh
 * Date: 2022-12-30 9:37
 * Description: <描述>
 */
@Component
public class ValidateAspect {

    @Before("com.itzhh.spring.xml.LogAspect.pointCut()")
    public  void beforeMethod(){
        System.out.println("ValidateAspect.beforeMethod");
    }
}

  • 创建xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       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/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

    <context:component-scan base-package="com.itzhh.spring.xml">

    </context:component-scan>

    <aop:config>
<!--        配置切面类-->
        <aop:aspect ref="logAspect">
            <aop:pointcut id="pointcut" expression="execution(* com.itzhh.spring.xml.CalculatorImpl.*(..))"/>
            <aop:before method="beforeMethod" pointcut-ref="pointcut"></aop:before>
            <aop:after method="afterMethod" pointcut-ref="pointcut"></aop:after>
            <aop:after-returning method="afterReturningMethod" pointcut-ref="pointcut" returning="result"></aop:after-returning>
            <aop:after-throwing method="afterThrowingMethod" throwing="ex" pointcut-ref="pointcut"></aop:after-throwing>
            <aop:around method="aroundMethod" pointcut-ref="pointcut"></aop:around>
        </aop:aspect>
        <aop:aspect ref="validateAspect" order="1">
            <aop:before method="beforeMethod" pointcut-ref="pointcut"></aop:before>
        </aop:aspect>
    </aop:config>
</beans>
  • 测试代码
package com.itzhh.spring.annotation;

import com.itzhh.spring.xml.Calculator;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * Author: zhh
 * Date: 2022-12-30 10:11
 * Description: <描述>
 */
public class AopXmlTest {

    @Test
    public void testAopXml(){
        ApplicationContext ioc=new ClassPathXmlApplicationContext("spring-aop-xml.xml");
        com.itzhh.spring.xml.Calculator calculator = ioc.getBean(Calculator.class);
        calculator.add(1,1);
    }

}

  • 结果
ValidateAspect.beforeMethod
Logger->前置通知,方法名:add参数:[1, 1]
环绕通知-->目标对象方法执行之前
方法内部 result = 2
环绕通知-->目标对象方法返回值之后
环绕通知-->目标对象方法执行完毕
Logger->返回通知,方法名:add,结果:2
Logger->后置通知,方法名:add

Process finished with exit code 0

JdbcTemplate

  • 在pom.xml中加入依赖
<?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">
    <parent>
        <artifactId>SSM</artifactId>
        <groupId>com.itzhh.ssm</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.itzhh.spring</groupId>
    <artifactId>spring-jdbc</artifactId>
    <packaging>jar</packaging>

    <dependencies>
        <!-- 基于Maven依赖传递性,导入spring-context依赖即可导入当前所需所有jar包 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.1</version>
        </dependency>
        <!-- Spring 持久化层支持jar包 -->
        <!-- Spring 在执行持久化层操作、与持久化层技术进行整合过程中,需要使用orm、jdbc、tx三个
        jar包 -->
        <!-- 导入 orm 包就可以通过 Maven 的依赖传递性把其他两个也导入 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>5.3.1</version>
        </dependency>
        <!-- Spring 测试相关 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.3.1</version>
        </dependency>
        <!-- junit测试 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <!-- MySQL驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.16</version>
        </dependency>
        <!-- 数据源 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.0.31</version>
        </dependency>
    </dependencies>

</project>
  • 创建jdbc.properties
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC
jdbc.username=root
jdbc.password=123456
  • 配置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:aop="http://www.springframework.org/schema/aop"
       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/aop https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

    <!--导入外部属性文件-->
    <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>

<!--    配置数据源-->
    <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driver}"></property>
        <property name="url" value="${jdbc.url}"></property>
        <property name="username" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!--        装配数据源-->
        <property name="dataSource" ref="druidDataSource"></property>
    </bean>
</beans>
  • 在测试类装配 JdbcTemplate,并测试增删改查功能
package com.itzhh.spring;

import com.itzhh.spring.entity.User;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.util.List;

/**
 * Author: zhh
 * Date: 2022-12-30 10:56
 * Description: <描述>
 */
//指定当前测试类在spring的测试环境中执行,此时就可以通过注入的方式直接获取IOC容器中的bean
@RunWith(SpringJUnit4ClassRunner.class)
//设置spring测试环境的配置文件
@ContextConfiguration("classpath:spring-jdbc.xml")
public class JdbcTemTest {
    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Test
    public void testUpdate(){
        String sql="insert into t_user values (null,?,?,?,?,?)";
        int result = jdbcTemplate.update(sql,"po","12367",34,"女","3456@qq.com");
        System.out.println(result);
    }

    @Test
    public void testSelecById(){
        String sql="select * from t_user where id=?";
        User user  = jdbcTemplate.queryForObject(sql,new BeanPropertyRowMapper<>(User.class),8);
        System.out.println(user);
    }

    @Test
    public void testSelectList(){
        String sql= "select * from t_user";
        List<User> list = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(User.class));
        list.forEach(user -> System.out.println(user));

    }

    @Test
    public void testSelectCount(){
        String sql="select count(*) from t_user";
        Integer count = jdbcTemplate.queryForObject(sql, Integer.class);
        System.out.println(count);
    }
}

基于注解的声明式事务

  • pom.xml中添加所需依赖
<?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">
    <parent>
        <artifactId>SSM</artifactId>
        <groupId>com.itzhh.ssm</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.itzhh.spring</groupId>
    <artifactId>spring-jdbc</artifactId>
    <packaging>jar</packaging>

    <dependencies>
        <!-- 基于Maven依赖传递性,导入spring-context依赖即可导入当前所需所有jar包 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.1</version>
        </dependency>
        <!-- Spring 持久化层支持jar包 -->
        <!-- Spring 在执行持久化层操作、与持久化层技术进行整合过程中,需要使用orm、jdbc、tx三个
        jar包 -->
        <!-- 导入 orm 包就可以通过 Maven 的依赖传递性把其他两个也导入 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>5.3.1</version>
        </dependency>
        <!-- Spring 测试相关 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.3.1</version>
        </dependency>
        <!-- junit测试 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <!-- MySQL驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.16</version>
        </dependency>
        <!-- 数据源 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.0.31</version>
        </dependency>
    </dependencies>

</project>
  • 在数据库中添加相应表以及创建三层结构用来测试
package com.itzhh.spring.controller;

import com.itzhh.spring.service.BookService;
import com.itzhh.spring.service.CheckoutService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;

/**
 * Author: zhh
 * Date: 2022-12-30 16:08
 * Description: <描述>
 */
@Controller
public class BookController {

    @Autowired
    private BookService bookService;

    @Autowired
    private CheckoutService checkoutService;

    public void buyBook(Integer bookId,Integer userId){
        bookService.buyBook(bookId,userId);
    }

    public void checkout(Integer[] bookIds, Integer userId){
        checkoutService.checkout(bookIds, userId);
    }
}

package com.itzhh.spring.service;

/**
 * Author: zhh
 * Date: 2022-12-30 16:08
 * Description: <描述>
 */
public interface BookService {
    void buyBook(Integer bookId, Integer userId);
}

package com.itzhh.spring.service.impl;

import com.itzhh.spring.dao.BookDao;
import com.itzhh.spring.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.concurrent.TimeUnit;

/**
 * Author: zhh
 * Date: 2022-12-30 16:09
 * Description: <描述>
 */
@Service

public class BookServiceImpl implements BookService {

    @Autowired
    private BookDao bookDao;

    // @Transactional(isolation = Isolation.DEFAULT)//使用数据库默认的隔离级别
    // @Transactional(isolation = Isolation.READ_UNCOMMITTED)//读未提交
    // @Transactional(isolation = Isolation.READ_COMMITTED)//读已提交
    // @Transactional(isolation = Isolation.REPEATABLE_READ)//可重复读
    // @Transactional(isolation = Isolation.SERIALIZABLE)//串行化
    @Transactional(
            // readOnly = true,
            // timeout = 3,
            // noRollbackFor = ArithmeticException.class,
            // noRollbackForClassName = "java.lang.ArithmeticException"
            //isolation = Isolation.DEFAULT
            //propagation = Propagation.REQUIRES_NEW
            //可以通过@Transactional中的propagation属性设置事务传播行为
    )
    @Override
    public void buyBook(Integer bookId, Integer userId) {

        // try {
        //     TimeUnit.SECONDS.sleep(5);
        // } catch (InterruptedException e) {
        //     e.printStackTrace();
        // }

        //查询图书的价格
        Integer price = bookDao.getPriceByBookId(bookId);
        //更新图书的库存
        bookDao.updateStock(bookId);
        //更新用户的余额
        bookDao.updateBalance(userId, price);
    }
}

package com.itzhh.spring.service;

/**
 * Author: zhh
 * Date: 2022-12-30 16:37
 * Description: <描述>
 */
public interface CheckoutService {
    void checkout(Integer[] bookIds, Integer userId);
}

package com.itzhh.spring.service.impl;

import com.itzhh.spring.service.BookService;
import com.itzhh.spring.service.CheckoutService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * Author: zhh
 * Date: 2022-12-30 16:38
 * Description: <描述>
 */
@Service
public class CheckoutServiceImpl implements CheckoutService {
    @Autowired
    private BookService bookService;
    @Override
    @Transactional
    //一次购买多本图书
    public void checkout(Integer[] bookIds, Integer userId) {
        for (Integer bookId : bookIds) {
            bookService.buyBook(bookId, userId);
        }
    }
}

package com.itzhh.spring.dao;

/**
 * Author: zhh
 * Date: 2022-12-30 16:09
 * Description: <描述>
 */
public interface BookDao {
    Integer getPriceByBookId(Integer bookId);

    void updateStock(Integer bookId);

    void updateBalance(Integer userId, Integer price);
}

package com.itzhh.spring.dao.impl;

import com.itzhh.spring.dao.BookDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;

/**
 * Author: zhh
 * Date: 2022-12-30 16:09
 * Description: <描述>
 */
@Repository
public class BookDaoImpl implements BookDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public Integer getPriceByBookId(Integer bookId) {
        String sql = "select price from t_book where book_id = ?";
        return jdbcTemplate.queryForObject(sql, Integer.class, bookId);
    }

    @Override
    public void updateStock(Integer bookId) {
        String sql = "update t_book set stock = stock - 1 where book_id = ?";
        jdbcTemplate.update(sql, bookId);
    }

    @Override
    public void updateBalance(Integer userId, Integer price) {
        String sql = "update t_user set balance = balance - ? where user_id = ?";
        jdbcTemplate.update(sql, price, userId);
    }
}

  • 创建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:aop="http://www.springframework.org/schema/aop"
       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/aop https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

    <context:component-scan base-package="com.itzhh.spring"></context:component-scan>

    <!--导入外部属性文件-->
    <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>

    <!--    配置数据源-->
    <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driver}"></property>
        <property name="url" value="${jdbc.url}"></property>
        <property name="username" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <!--        装配数据源-->
        <property name="dataSource" ref="druidDataSource"></property>
    </bean>

<!--    配置事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="druidDataSource"></property>
    </bean>
<!--    开启事务的注解驱动-->
<!--    通过注解@Transactional所标识的方法或标识的类中所有的方法,都会被事务管理器管理事务-->

    <!-- transaction-manager属性的默认值是transactionManager,如果事务管理器bean的id正好就
    是这个默认值,则可以省略这个属性 -->
    <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
</beans>
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC
jdbc.username=root
jdbc.password=123456
  • 测试代码
package com.itzhh.spring;

import com.itzhh.spring.controller.BookController;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

/**
 * Author: zhh
 * Date: 2022-12-30 16:15
 * Description: <描述>
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:tx-annotation.xml")
public class TxByAnnotationTest {

    @Autowired
    private BookController bookController;

    @Test
    public void testBuyBook(){
        bookController.buyBook(1,1);
    }
}

  • 总结
声明式事务的配置步骤:
 1、在Spring的配置文件中配置事务管理器
 2、开启事务的注解驱动
 3、在需要被事务管理的方法上,添加@Transactional注解,该方法就会被事务管理
@Transactional注解标识的位置:
 1、标识在方法上
 2、标识在类上,则类中所有的方法都会被事务管理

基于xml的声明式事务(用的不多,了解一下)

  • 基于xml实现的声明式事务,必须引入aspectJ的依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.1</version>
</dependency>
  • 将Spring配置文件中去掉tx:annotation-driven 标签,并添加配置:

    <aop:config>
        <!-- 配置事务通知和切入点表达式 -->
        <aop:advisor advice-ref="txAdvice" pointcut="execution(*
    com.itzhh.spring.tx.xml.service.impl.*.*(..))"></aop:advisor>
    </aop:config>
    <!-- tx:advice标签:配置事务通知 -->
    <!-- id属性:给事务通知标签设置唯一标识,便于引用 -->
    <!-- transaction-manager属性:关联事务管理器 -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <!-- tx:method标签:配置具体的事务方法 -->
            <!-- name属性:指定方法名,可以使用星号代表多个字符 -->
            <tx:method name="get*" read-only="true"/>
            <tx:method name="query*" read-only="true"/>
            <tx:method name="find*" read-only="true"/>
            <!-- read-only属性:设置只读属性 -->
            <!-- rollback-for属性:设置回滚的异常 -->
            <!-- no-rollback-for属性:设置不回滚的异常 -->
            <!-- isolation属性:设置事务的隔离级别 -->
            <!-- timeout属性:设置事务的超时属性 -->
            <!-- propagation属性:设置事务的传播行为 -->
            <tx:method name="save*" read-only="false" rollbackfor="java.lang.Exception" propagation="REQUIRES_NEW"/>
            <tx:method name="update*" read-only="false" rollbackfor="java.lang.Exception" propagation="REQUIRES_NEW"/>
            <tx:method name="delete*" read-only="false" rollbackfor="java.lang.Exception" propagation="REQUIRES_NEW"/>
        </tx:attributes>
    </>

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

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

相关文章

基于SSM(Spring+SpringMVC+Mybatis)实现的个人博客系统,含数据库文件及详细说明

关于项目 该博客是基于SSM实现的个人博客系统&#xff0c;适合初学SSM和个人博客制作的同学学习。 最新版本支持用户注册&#xff0c;包含用户和管理员两个角色 。 博主已写了一篇该项目的毕业论文和录制了2个小时的代码讲解可以供大家学习&#xff0c;需要的可以联系博主&…

RFID在模块管理中的应用

应用背景 模具是工业生产的基础工艺装备&#xff0c;被称为“工业之母”。作为国民经济的基础行业&#xff0c;模具涉及机械、汽车、轻工、电子、化工、冶金、建材等各个行业&#xff0c;应用范围十分广泛。模具资产管理采用传统的人工纸质记录的方式已经无法及时有效的进行&am…

还在用 XShell - 试试 IntelliJ IDEA 的 SSH

SSH 是很多人用得不多&#xff0c;但是又不得不用的工具。 如果你不是搞运维&#xff0c;没有必要搞个 CRT&#xff0c;XShell 也够用了&#xff0c;但是这 2 个都是收费软件&#xff0c;同时还不太便宜。 试试 IDEA 的 SSH 其实 IntelliJ IDEA 已经提供了 SSH 的功能。 如…

053-线程的状态改变及线程同步详细介绍

【上一讲】051-java线程的2种实现方法详解_CSDN专家-赖老师(软件之家)的博客-CSDN博客 线程可以处于以下四个状态之一1.新建(new):线程对象已经建立,但还没有启动,所以他不能运行。2.就绪(runnable):这种状态下,只要调度程序把时间片分配给线程就可以执行,也就是说…

10分钟带你了解什么是ArrayBuffer?

前言 很多时候我们前端开发是用不到 ArrayBuffer 的&#xff0c;但是用不到 ArrayBuffer 不代表我们不需要了解这个东西。本文就围绕 ArrayBuffer 来讲一下相关知识&#xff0c;大概需要10分钟左右就可以读完本文。 什么是ArrayBuffer&#xff1f; 描述 ArrayBuffer 对象用…

Paramiko库讲解

目录 基本概念 Paramiko组件架构 Key handing类 Transport类 SFTPClient类 SSHClient类—主要使用的类 Python编写完整例子 基本概念 Paramiko是Python实现SSHv2协议的模块&#xff0c;支持口令认证和公钥认证两种方式 通过Paramiko可以实现通过Python进行安全的远程命…

Html5网页播放器的同层播放功能

Html5网页播放器的同层播放功能&#xff1a; 在Android手机上使用H5播放视频时&#xff0c;大多数的国内浏览器厂商都会在视频播放时劫持<video>标签&#xff0c;使用浏览器自带的播放器播放视频&#xff0c;而且播放器会处于最高层级&#xff0c;视频上面无法显示其它h…

数影周报:字节跳动员工违规获取TikTok用户数据,阿里组织调整

本周看点&#xff1a;字节跳动员工违规获取TikTok用户数据&#xff1b;钉钉宣布用户数破6 亿&#xff1b;阿里组织调整&#xff1b;星尘数据完成 5000 万元 A 轮融资...... 数据安全那些事 字节跳动员工违规获取TikTok用户数据 字节跳动旗下热门应用TikTok日前曝出严重风波。字…

郭德纲落选,冯巩、赵炎上榜,国家非物质文化遗产传承人评选落幕

根据国家广电局26日消息&#xff0c;经过激烈的竞争&#xff0c;国家非物质文化遗产传承人评选工作&#xff0c;已经顺利落下帷幕。 在此次评选活动当中&#xff0c;评委会一致审议通过&#xff0c;著名相声演员冯巩和赵炎&#xff0c;被评为了非物质文化遗产传承人。而呼声很高…

Linux | 进程理解 | 进程的终止,等待与替换 | 环境变量的介绍与使用

文章目录进程终止进程终止的方法操作系统是怎么终止进程的&#xff1f;进程等待为何需要等待进程&#xff1f;怎么等待一个进程&#xff1f;非阻塞式等待进程替换什么是进程替换&#xff1f;为什么要进程替换&#xff1f;怎样替换一个进程&#xff1f;exec系列函数环境变量用命…

企业微信开发(一)常见问题收集及解决方案

持续收集企业微信开发中遇到疑难杂症&#xff0c;并给出相应的解决方案 一、好友上限&#xff08;84061&#xff09; 背景&#xff1a;达到添加好友数上限的员工&#xff0c;新增自动通过的好友&#xff0c;无法拉取到客户信息。 根因&#xff1a;企业微信业务限制 员工添加的…

启辰全面转型新能源,能否创造风日产第二增长曲线?

随着新能源汽车市占率的不断增长&#xff0c;传统汽车大厂加大了在新能源汽车领域的布局。这其中决心最大的当属东风日产&#xff0c;第二品牌——“启辰”全面转型新能源&#xff0c;告别纯然有车开发。 12月30日&#xff0c;以“新科技 新生活”为主题的第二十届广州车展盛大…

Linux系统定时信号SIGALRM的触发与alarm函数的使用

1、 定时信号SIGALRM的用途 在编程的过程中&#xff0c;很多时候我们需要为程序设置一个闹钟&#xff0c;然后到了闹钟设定的时刻然后再去采取相关的操作。比如进行socket编程时&#xff0c;如果客户端长时间没有与服务器进行交互&#xff0c;需要服务器在一定时间之后主动关闭…

Linux | 权限管理

啊我摔倒了..有没有人扶我起来学习.... &#x1f471;个人主页&#xff1a;《CGod的个人主页》\color{Darkorange}{《CGod的个人主页》}《CGod的个人主页》交个朋友叭~ &#x1f492;个人社区&#xff1a;《编程成神技术交流社区》\color{Darkorange}{《编程成神技术交流社区》…

Vue(二)

1. 模板语法 1.1 实现效果 1.2 模板的理解 不加v-bind就相当于直接把双引号里的东西当成字符串执行&#xff0c;加了bind双引号里的东西就当成js表达式执行 v-bind:可以简写成&#xff1a; 起始标签和结束标签中间夹着的就是标签体 <!DOCTYPE html> <html><hea…

【数据结构】插入排序、希尔排序、冒泡排序、选择排序

文章目录 一、直接插入排序 思想 程序代码 时间复杂度 二、希尔排序 思想 程序代码 时间复杂度 三、冒泡排序 思想 程序代码 时间复杂度 四、选择排序 思想 程序代码 时间复杂度 一、直接插入排序 思想 直接插入排序有些类似于我们玩扑克牌时的整理牌序动作&a…

目标检测中常见的神经网络组成层------Pytorch

物体检测中常见的神经网络组成层 文章目录物体检测中常见的神经网络组成层卷积层激活函数层池化层Dropout层全连接层常见的物体检测算法常用卷积层、池化层、全连接层、激活函数层、Dropout层。 卷积层 CNN–各层的介绍_Miracle Fan的博客-CSDN博客_cnn各层介绍 在pytorch中…

Python3入门基础(08)一个函数

Python3 函数 函数是组织好的&#xff0c;可重复使用的&#xff0c;用来实现单一&#xff0c;或相关联功能的代码段。 函数能提高应用的模块性&#xff0c;和代码的重复利用率。你已经知道Python提供了许多内建函数&#xff0c;比如print()。但你也可以自己创建函数&#xff0…

Windows 11 连接 hercules zOS Websphere MQ 配置

基本配置 zOS v1.10 基本配置可以先参考下面教程 ubuntu 编译 hercules 主机安装 z/OS 教程 zOS MQ 版本为 CSQ700 Windows 11 IBM MQ 版本为最新 9.x IBM MQ 官网有开发者版本可以免费下载&#xff0c;档名如下 mqadv_dev931_windows.zip (全名为 IBM MQ Advanced for Devel…

kafka — 2、基础环境搭建

前述 kafka的运行依赖于zooKeeper&#xff0c;所以在搭建kafka的环境之前需要搭建zookeeper环境。 zooKeeper&#xff1a; ZooKeeper是一个分布式协调服务&#xff0c;它的主要作用是为分布式系统提供一致性服务&#xff0c;可以保证数据在集群间的事务一致性&#xff0c;提供…