Spring详解,代码事例,IOC,AOP,事务。整合MyBatis,JUnit

news2024/11/19 20:34:09

在这里插入图片描述

Spring核心

核心概念

  • 代码书写现状
    • 耦合度偏高
  • 解决方案
    • 使用对象时,在程序中不要主动使用new产生对象,转换为由外部提供对象
  • IOC ( Inversion of Control )控制反转
    • 对象的创建控制权由程序转移到外部,这种思想称为控制反转
    • 使用对象时,由主动new产生对象转换为由外部提供对象,此过程中对象创建控制权由程序转移到外部,此思想称为控制反转
  • Spring技术对IoC思想进行了实现
    • Spring提供了一个容器,称为IoC容器,用来充当IoC思想中的外部)
    • IOC容器负责对象的创建、初始化等一系列工作,被创建或被管理的对象在IoC容器中统称为Bean
  • DI ( Dependency Injection)依赖注入
    • 在容器中建立 bean 与 bean 之间的依赖关系的整个过程,称为依赖注入

IOC 入门

思路

  1. 管理什么? ( Service与Dao )
  2. 如何将被管理的对象告知IoC容器?(配置)
  3. 被管理的对象交给IoC容器,如何获取到Ioc容器?(接口)
  4. IOC容器得到后,如何从容器中获取bean ?(接口方法)
  5. 使用Spring导入哪些坐标?( pom.xml )
public class BookDaoImpl implements BookDao {
    @Override
    public void save() {
        System.out.println("BookDaoImpl.......");
    }
}
import com.blm.dao.BookDao;
import com.blm.service.BookService;

public class BookServiceImpl implements BookService {
    BookDao bookDao = new BookDao;

    public void setBookDao(BookDao bookDao) {
        this.bookDao = bookDao;
    }

    @Override
    public void save() {
        System.out.println("BookServiceImpl........");
        bookDao.save();
    }
}
<?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="bookService" class="com.blm.service.impl.BookServiceImpl"/>
</beans>
public class App {
    public static void main(String[] args) {
        // 获取IOC容器
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        // 获取bean
        BookService bookService = (BookService) ctx.getBean("bookService");
        bookService.save();
    }
}

DI 入门

  1. 基于IoC管理bean
  2. Service中使用new形式创建的Dao对象是否保留 ?(否)
  3. Service中需要的Dao对象如何进入到Service中 ?(提供方法)
  4. Service与Dao间的关系如何描述 ?(配置)
public class BookDaoImpl implements BookDao {
    @Override
    public void save() {
        System.out.println("BookDaoImpl.......");
    }
}
public class BookServiceImpl implements BookService {
    // 删除业务层中使用new 的方式创建的对象
    private BookDao bookDao;

    public void setBookDao(BookDao bookDao) {
        this.bookDao = bookDao;
    }

    @Override
    public void save() {
        System.out.println("BookServiceImpl........");
        bookDao.save();
    }
}
<?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="bookDao" class="com.blm.dao.impl.BookDaoImpl"/>
    <bean id="bookService" class="com.blm.service.impl.BookServiceImpl">
        <!--配置service 和 dao 的关系-->
        <!--property:当前bean 的属性
            name:设置哪个一个bean的属性
            ref 参照哪个bean-->
        <property name="bookDao" ref="bookDao"/>
    </bean>
</beans>
public class App {
    public static void main(String[] args) {
        // 获取IOC容器
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        // 获取bean

        BookService bookService = (BookService) ctx.getBean("bookService");
        bookService.save();
    }
}

基础配置

Bean 别名

<bean id="bookService" name="service bookService2 bookEbi" class="com.blm.service.impl.BookServiceImpl">
       <property name="bookDao" ref="bookDao"/>
 </bean>

在这里插入图片描述

bean 的作用范围

在这里插入图片描述

为什么bean默认为单例?
  • 适合交给容器进行管理的bean
    • 表现层对象
    • 业务层对象
    • 数据层对象
    • 工具对象
  • 不适合交给容器进行管理的bean
    • 封装实体的域对象

bean 实例化过程

构造方法

spring调的是无参构造方法,public ,private都可以掉到(反射)

无参构造方法如果不存在,将抛出异常BeanCreationException

静态工厂

package com.blm.factory;

import com.blm.dao.OrderDao;
import com.blm.dao.impl.OrderDaoImpl;

public class OrderDaoFactory {
    public static OrderDao getOrderDao() {
        return new OrderDaoImpl();
    }
}
<bean id="orderDao" class="com.blm.factory.OrderDaoFactory" factory-method="getOrderDao"></bean>
public class App {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        OrderDao orderDao = (OrderDao) ctx.getBean("orderDao");
        orderDao.test();
    }
}

实例工厂

public class OrderDaoFactory {
    public OrderDao getOrderDao() {
        return new OrderDaoImpl();
    }
}
<bean id="orderDaoFactory" class="com.blm.factory.OrderDaoFactory"/>
<bean id="orderDao" factory-method="getOrderDao" factory-bean="orderDaoFactory"></bean>

使用 FactoryBean 实例化

public class OrderDaoFactoryBean implements FactoryBean<OrderDao> {
    @Override
    public OrderDao getObject() throws Exception {
        return new OrderDaoImpl();
    }

    @Override
    public Class<?> getObjectType() {
        return OrderDao.class;
    }
    
    @Override
    public boolean isSingleton() {
        return true; // true: 单例,false: 非单例
    }
}
<bean id="orderDao" class="com.blm.factory.OrderDaoFactoryBean"/>
public class App {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        OrderDao orderDao = (OrderDao) ctx.getBean("orderDao");
        orderDao.test();
    }
}

bean 生命周期

  • 生命周期:从创建到消亡的完整过程
  • bean生命周期:bean从创建到销毁的整体过程
  • bean生命周期控制:在bean创建后到销毁前做一些事情
public class BookDaoImpl implements BookDao {
    @Override
    public void save() {
        System.out.println("BookDaoImpl.......");
    }

    // 表示 bean 初始化对应的操作
    public void init() {
        System.out.println("init....");
    }

    // 表示 bean 销毁前对应的操作
    public void destory () {
        System.out.println("destory....");
    }
}
<bean id="bookDao" class="com.blm.dao.impl.BookDaoImpl" init-method="init" destroy-method="destory"/>
public class App {
    public static void main(String[] args) {
         // 获取IOC容器
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        ctx.registerShutdownHook(); // 设置  关闭虚拟机前,销毁容器 (可以在任何位置)

        BookService bookService = (BookService) ctx.getBean("bookService");
        bookService.save();
        // ctx.close(); // 必须要关闭容器,才调用 销毁前对应的操作生命周期
    }
}

方法二:

public class BookDaoImpl implements BookDao, InitializingBean, DisposableBean {
    @Override
    public void save() {
        System.out.println("BookDaoImpl.......");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("BookDao destroy");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("BookDao init");
    }
}
<bean id="bookDao" class="com.blm.dao.impl.BookDaoImpl"/>

依赖注入

思考: 依赖注入描述了在容器中建立bean与bean之间依赖关系的过程,如果bean运行需要的是数字或字符串呢?

  • 引用类型
  • 简单类型( 基本数据类型与string )

普通方法注入(setter注入)

引用类型 & 简单类型
public class BookServiceImpl implements BookService {
    // 删除业务层中使用new 的方式创建的对象
    private BookDao bookDao;
    private OrderDao orderDao;
    private String name;
    private int num;

    public void setBookDao(BookDao bookDao) {
        this.bookDao = bookDao;
    }

    public void setOrderDao(OrderDao orderDao) {
        this.orderDao = orderDao;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setNum(int num) {
        this.num = num;
    }

    @Override
    public void save() {
        System.out.println("BookServiceImpl........" + name + "," + num);
        bookDao.save();
        orderDao.save();
    }
}
	<bean id="bookDao" class="com.blm.dao.impl.BookDaoImpl"/>
    <bean id="orderDao" class="com.blm.factory.OrderDaoFactoryBean"/>
    <bean id="bookService" name="service bookService2 bookEbi" class="com.blm.service.impl.BookServiceImpl">
        <property name="bookDao" ref="bookDao"/> <!-- 引用类型 -->
        <property name="orderDao" ref="orderDao"/>  <!-- 引用类型 -->
        <property name="name" value="hello"/>  <!-- 简单类型 -->
        <property name="num" value="100000000"/>  <!-- 简单类型 -->
    </bean>

构造器注入

引用类型 & 简单类型
public class BookServiceImpl implements BookService {
    // 删除业务层中使用new 的方式创建的对象
    private BookDao bookDao;
    private OrderDao orderDao;
    private String name;
    private int num;

    public BookServiceImpl(BookDao bookDao, OrderDao orderDao, String name, int num) {
        this.bookDao = bookDao;
        this.orderDao = orderDao;
        this.name = name;
        this.num = num;
    }

    @Override
    public void save() {
        System.out.println("BookServiceImpl........" + name + "," + num);
        bookDao.save();
        orderDao.save();
    }
}

	<bean id="bookDao" class="com.blm.dao.impl.BookDaoImpl"/>
    <bean id="orderDao" class="com.blm.factory.OrderDaoFactoryBean"/>
    <bean id="bookService" name="bookService" class="com.blm.service.impl.BookServiceImpl">
        <constructor-arg index="0" ref="bookDao"/>
        <constructor-arg index="1" ref="orderDao"/>
        <constructor-arg index="2" value="Hello"/>
        <constructor-arg index="3" value="99999999"/>
    </bean>

or

<bean id="bookService" name="bookService" class="com.blm.service.impl.BookServiceImpl">
<!--        <constructor-arg index="0" ref="bookDao"/>-->
<!--        <constructor-arg index="1" ref="orderDao"/>-->
<!--        <constructor-arg index="2" value="Hello"/>-->
<!--        <constructor-arg index="3" value="99999999"/>-->
        <constructor-arg name="bookDao" ref="bookDao"/>
        <constructor-arg name="orderDao" ref="orderDao"/>
        <constructor-arg name="name" value="Hello"/>
        <constructor-arg name="num" value="99999999"/>
    </bean>

依赖注入方式的选择

  1. 强制依赖使用构造器进行,使用setter注入有概率不进行注入导致nu11对象出现
  2. 可选依赖使用setter注入进行,灵活性强
  3. Spring框架倡导使用构造器,第三方框架内部大多数采用构造器注入的形式进行数据初始化,相对严谨
  4. 如果有必要可以两者同时使用,使用构造器注入完成强制依赖的注入,使用setter注入完成可选依赖的注入
  5. 实际开发过程中还要根据实际情况分析,如果受控对象没有提供setter方法就必须使用构造器注入
  6. 自己开发的模块推荐使用setter注入

依赖自动装配

public class BookServiceImpl implements BookService {
    private BookDao bookDao;
    private OrderDao orderDao;

    public void setBookDao(BookDao bookDao) {
        this.bookDao = bookDao;
    }

    public void setOrderDao(OrderDao orderDao) {
        this.orderDao = orderDao;
    }

    @Override
    public void save() {
        System.out.println("BookServiceImpl........");
        bookDao.save();
        orderDao.save();
    }
}
<bean id="bookService" name="bookService" class="com.blm.service.impl.BookServiceImpl" autowire="byType"/>
<!--byType: 按类型装配-->
特征
  • 自动装配用于引用类型依赖注入,不能对简单类型进行操作
  • 使用按类型装配时( byType ) 必须保障容器中相同类型的bean唯一,推荐使用
  • 使用按名称装配时 ( byName )必须保障容器中具有指定名称的bean,因变量名与配置耦合,不推荐使用
  • 自动装配优先级低于setter注入与构造器注入,同时出现时自动装配配置失效

集合注入

public class OrderDaoImpl implements OrderDao {
    private int[] array;
    private List<String> list;
    private Set<String> set;
    private Map<String, String> map;
    private Properties properties;

    public void setArray(int[] array) {
        this.array = array;
    }

    public void setList(List<String> list) {
        this.list = list;
    }

    public void setSet(Set<String> set) {
        this.set = set;
    }

    public void setMap(Map<String, String> map) {
        this.map = map;
    }

    public void setProperties(Properties properties) {
        this.properties = properties;
    }

    @Override
    public void save() {
        System.out.println("OrderDaoImpl.........");
        System.out.println("array:" + array);
        System.out.println("list:" + list);
        System.out.println("set:" + set);
        System.out.println("map:" + map);
        System.out.println("properties:" + properties);
    }
}
	<bean id="orderDao" class="com.blm.dao.impl.OrderDaoImpl">
        <property name="array">
            <array>
                <value>100</value>
                <value>200</value>
                <value>300</value>
            </array>
        </property>
        <property name="list">
            <list>
                <value>100</value>
                <value>200</value>
                <value>300</value>
            </list>
        </property>
        <property name="set">
            <set>
                <value>100</value>
                <value>200</value>
                <value>300</value>
            </set>
        </property>
        <property name="map">
            <map>
                <entry key="country" value="china"/>
                <entry key="province" value="yunnan"/>
                <entry key="city" value="dali"/>
            </map>
        </property>
        <property name="properties">
            <props>
                <prop key="country">china</prop>
                <prop key="province">yunnan</prop>
                <prop key="city">dali</prop>
            </props>
        </property>
    </bean>

数据源对象管理

<!-- 管理DruidDataSource -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://127.0.0.1:3306/spring_db"/>
    <property name="username" value="root"/>
    <property name="password" value="blm123"/>
</bean>
import com.blm.dao.impl.OrderDaoImpl;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import javax.sql.DataSource;

public class App2 {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        DataSource dataSource = (DataSource)ctx.getBean("dataSource");
        System.out.println(dataSource);
    }
}

加载 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
       http://www.springframework.org/schema/context/spring-context.xsd">
    
    <!-- 1. 开启 context 命名空间 -->
    <!--
		xmlns:context="http://www.springframework.org/schema/context"
		http://www.springframework.org/schema/context
		 http://www.springframework.org/schema/context/spring-context.xsd
	-->
    <!-- 2. 使用 context 加载 properties 文件 -->
    <!-- system-properties-mode="NEVER" 不加载系统属性 -->
    <context:property-placeholder location="jdbc.properties" system-properties-mode="NEVER"/>
    <!-- 3. ${} 读取properties 文件中的属性 -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
</beans>

jdbc.properties 文件

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/spring_db
jdbc.username=root
jdbc.password=blm123

多个 properties 文件加载

  1. 不加载系统属性
<context: property-placeholder location="jdbc. properties" system-properties-mode="NEVER"/>
  1. 加载所有properties文件
<context:property-placeholder location="jdbc.properties,msg.properties" />
  1. 加载所有properties文件
<context:property-placeholder location="*.properties" />
  1. 加载properties文件标准格式
<context:property-placeholder location="classpath:*.properties" />
  1. 从类路径或jar包中搜索并加载properties文件
<context:property-placeholder location="classpath*:*.properties" />

容器

加载

// 1. 从类路径下加载容器
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
// 2. 从文件系统下加载容器
ApplicationContext ctx2 = new FileSystemXmlApplicationContext("D:\\java\\20230515_ss\\ddup\\spring\\src\\main\\resources\\applicationContext.xml");
OrderDaoImpl orderDaoImpl = (OrderDaoImpl)ctx.getBean("orderDao");
OrderDaoImpl orderDaoImpl = ctx.getBean("orderDao", OrderDaoImpl.class);
OrderDaoImpl orderDaoImpl = ctx.getBean(OrderDaoImpl.class); // 按类型获取

创建容器

// 方式一︰类路径加载配置文件
ApplicationContext ctx1 = new classPathXmlApplicationContext( "applicationContext.xml" );
// 方式二︰文件路径加载配置文件
ApplicationContext ctx2 = new FileSystemXmIApplicationContext("D:\\applicationContext.xml" );
// 加载多个配置文件
ApplicationContext ctx3 = new ClassPathXmlApplicationContext("bean1.xml", "bean2.xm1");

bean 相关

在这里插入图片描述

注解开发定义 bean

@Component // 省略名称,按类型获取并
public class Com implements BookDao {}

@Component("com")
public class Com implements BookDao {
    @Override
    public void save() {
        System.out.println("com-----------------");
    }
}

// 和 @Component 功能样
@Service // 业务层
@Repository // 数据程
@Controller // 表现层
<?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
       http://www.springframework.org/schema/context/spring-context.xsd">

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

</beans>

纯注解开发

  • Spring3.0开启了纯注解开发模式,使用Java类替代配置文件,开启了Spring快速开发赛道
  • Java类代替Spring核心配置文件,

@Configuration注解用于设定当前类为配置类121212

  • @ComponentScan注解用于设定扫描路径,此注解只能添加一次,多个数据请用数组格式
@Component
@Scope("singleton")
// @Scope("prototype") // 非单例
public class Com implements BookDao {
    @Override
    public void save() {
        System.out.println("com-----------------");
    }
}
@ComponentScan({com.itheima.service" , " com.itheima.dao"})
package com.blm.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan("com.blm")
public class SpringConfig {
}
public class App3 {
    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
        Com com = (Com)ctx.getBean("com");
        com.save();
    }
}

bean作用范围 和 生命周期

package com.blm.dao.impl;

import com.blm.dao.BookDao;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Repository;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;


@Repository
@Scope("singleton")
// @Scope("prototype") // 非单例
public class Com implements BookDao {
    @Override
    public void save() {
        System.out.println("com-----------------");
    }
    @PostConstruct // 构造方法后
    public void init () {
        System.out.println("init.....");
    }

    @PreDestroy // 销毁前
    public void destroy () {
        System.out.println("destroy.....");
    }
}
public class App3 {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
        Com com = ctx.getBean(Com.class);
        com.save();
        ctx.close();
    }
}

自动装配

按类型装配

package com.blm.dao.impl;

import com.blm.dao.UserDao;
import org.springframework.stereotype.Repository;

@Repository
public class UserDaoImpl implements UserDao {
    @Override
    public void save() {
        System.out.println("UserDaoImpl..........");
    }
}
package com.blm.service.impl;

import com.blm.dao.UserDao;
import com.blm.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDao userDao;
    @Override
    public void save() {
        System.out.println("UserServiceImpl....");
        userDao.save();
    }
}
public class App3 {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
        UserServiceImpl userService = ctx.getBean(UserServiceImpl.class);
        userService.save();
    }
}

按名字装配

userDao

有多个相同类型时,需要 按名字装配

@Repository("userDao")
public class UserDaoImpl implements UserDao {
    @Override
    public void save() {
        System.out.println("UserDaoImpl..........");
    }
}
@Service
public class UserServiceImpl implements UserService {
    
    @Autowired
    @Qualifier("userDao")
    private UserDao userDao;

    @Override
    public void save() {
        System.out.println("UserServiceImpl....");
        userDao.save();
    }
}
  • 注意:自动装配基于反射设计创建对象并暴力反射对应属性为私有属性初始化数据,因此无需提供setter方法
  • 注意:自动装配建议使用无参构造方法创建对象(默认),如果不提供对应构造方法,请提供唯一的构造方法

简单类型装配

@Repository("userDao")
public class UserDaoImpl implements UserDao {
    @Value("Hello")
    private String name;
}

加载 properties

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/spring_db
jdbc.username=root
jdbc.password=blm123
@Configuration
@ComponentScan("com.blm")
@PropertySource("classpath:jdbc.properties")
public class SpringConfig { }
@Repository("userDao")
public class UserDaoImpl implements UserDao {
    @Value("${jdbc.username}")
    private String name;
}

第三方 bean 管理

package com.blm.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.Bean;
import javax.sql.DataSource;

public class JdbcConfig {
    // 1. 定义一个方法获得要管理的对象
    // 2. @Bean, 表示当前方法的返回值是一个bean
    @Bean
    public DataSource dataSource () {
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName("com.mysql.jdbc.Driver");
        ds.setUrl("jdbc:mysql://127.0.0.1:3306/spring_db");
        ds.setUsername("root");
        ds.setPassword("123456");
        return ds;
    }
}
@Configuration
@Import(JdbcConfig.class)
public class SpringConfig { }
public class App3 {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
        DataSource dataSource = ctx.getBean(DataSource.class);
    }
}

第三方 Bean 注入资源

public class JdbcConfig {
    @Value("com.mysql.jdbc.Driver")
    private String className;
    @Value("jdbc:mysql://127.0.0.1:3306/spring_db")
    private String username;
    @Value("root")
    private String url;
    @Value("123456")
    private String password;
    @Bean
    public DataSource dataSource (UserDao userDao) { //
        System.out.println(">>userDao<<" + userDao);
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName(className);
        ds.setUrl(url);
        ds.setUsername(username);
        ds.setPassword(password);
        return ds;
    }
}

XML 配置对比注解配置

在这里插入图片描述

Spring 整合 MyBatis

导包

		<dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.5</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.46</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.2.10.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>1.3.1</version>
        </dependency>

com.blm.config

package com.blm.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.PropertySource;

@Configuration
@ComponentScan("com.blm")
@PropertySource("classpath:jdbc.properties")
@Import({JdbcConfig.class, MybatisConfig.class})
public class SpringConfig {
}
package com.blm.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;

import javax.sql.DataSource;

public class JdbcConfig {
    @Value("${jdbc.driver}")
    private String driver;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String userName;
    @Value("${jdbc.password}")
    private String password;
    @Bean
    public DataSource dataSource(){
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName(driver);
        ds.setUrl(url);
        ds.setUsername(userName);
        ds.setPassword(password);
        return ds;
    }
}
package com.blm.config;

import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.context.annotation.Bean;

import javax.sql.DataSource;

public class MybatisConfig {
    @Bean
    public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){
        SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();
        ssfb.setTypeAliasesPackage("com.blm.domain"); // 类型别名
        ssfb.setDataSource(dataSource);
        return ssfb;
    }
    //定义bean,返回MapperScannerConfigurer对象
    @Bean
    public MapperScannerConfigurer mapperScannerConfigurer(){
        MapperScannerConfigurer msc = new MapperScannerConfigurer();
        msc.setBasePackage("com.blm.dao"); // Mapper 映射
        return msc;
    }
}

com.blm.domain

package com.blm.domain;

import java.io.Serializable;

public class User implements Serializable {
    private Integer id;
    private String username;
    private String password;
    private String gender;
    private String addr;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    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 String getAddr() {
        return addr;
    }

    public void setAddr(String addr) {
        this.addr = addr;
    }

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

com.blm.dao

package com.blm.dao;

import com.blm.domain.User;
import org.apache.ibatis.annotations.Select;

import java.util.List;

public interface UserDao {
    @Select("select * from tb_user where id = #{id}")
    User getUserById(Integer id);

    @Select("select * from tb_user")
    List<User> getUserAll();
}

com.blm.service

package com.blm.service;

import com.blm.domain.User;

import java.util.List;

public interface UserService {
    User getUserById(Integer id);

    List<User> getUserAll();
}
package com.blm.service.impl;

import com.blm.dao.UserDao;
import com.blm.domain.User;
import com.blm.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDao userDao;

    @Override
    public User getUserById(Integer id) {
        return userDao.getUserById(id);
    }

    @Override
    public List<User> getUserAll() {
        return userDao.getUserAll();
    }
}

app

package com.blm;

import com.blm.config.SpringConfig;
import com.blm.domain.User;
import com.blm.service.UserService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import java.util.List;

public class app {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext annt = new AnnotationConfigApplicationContext(SpringConfig.class);
        UserService userService = annt.getBean(UserService.class);
        User user = userService.getUserById(1);
        System.out.println(user);
        List<User> users = userService.getUserAll();
        for (User u : users) {
            System.out.println(u);
        }
    }
}

jdbc.properties

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/db
jdbc.username=root
jdbc.password=123456

Spring 整合 JUnit

<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.2.10.RELEASE</version>
</dependency>
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class) // Spring 配置类
public class UserServiceTest {

    @Autowired
    private UserService userService;

    @Test
    public void testGetUserById() {
        System.out.println(userService.getUserById(2));
    }
}

AOP

简介

  • AOP(Aspect Oriented Programming)面向切面编程,一种编程范式,指导开发者如何组织程序结构
    • OOP(Object Oriented Programming)面向对象编程
    • 作用:在不惊动原始设计的基础上为其进行功能增强
    • Spring理念:无入侵式 / 无侵入式

在这里插入图片描述

  • 连接点(JoinPoint ):程序执行过程中的任意位置,粒度为执行方法、抛出异常、设置变量等
    • 在SpringAOP中,理解为方法的执行
  • 切入点 (Pointcut ):匹配连接点的式子
    • 在SpringAOP中,一个切入点可以只描述一个具体方法,也可以匹配多个方法
      • 一个具体方法:com.blm.dao包下的BookDao接口中的无形参无返回值的save方法
      • 匹配多个方法:所有的save方法,所有的get开头的方法,所有以Dao结尾的接口中的任意方法,所有带有一个参数的方法
  • 通知 (Advice):在切入点处执行的操作,也就是共性功能
    • 在springAOP中,功能最终以方法的形式呈现
  • 通知类: 定义通知的类
  • 切面( Aspect):描述通知与切入点的对应关系

入门

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.4</version>
</dependency>
package com.blm.config;
import org.springframework.context.annotation.*;

@Configuration
@ComponentScan("com.blm")
@EnableAspectJAutoProxy  // 声明注解开发 AOP
public class SpringConfig {
}
package com.blm.aop;

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

@Component // Spring Bean
@Aspect // AOP 注解
public class MyAdvice {
    /**
     * 切入点
     */
    @Pointcut("execution(* com.blm.dao.UserDao.getUserById(..)))")
    private void pt() {}
	// 通知
    @Before("pt()")
    public void method () {
        System.out.println("time" + System.currentTimeMillis());
        System.out.println("time + System.currentTimeMillis()");
    }
}

AOP 工作流程

  1. Spring容器启动
  2. 读取所有切面配置中的切入点
  3. 初始化bean,判定bean对应的类中的方法是否匹配到任意切入点3.
    • 匹配失败,创建对象
    • 匹配成功,创建原始对象( 目标对象)的代理对象
  4. 获取bean执行方法
    • 获取bean,调用方法并执行,完成操作
    • 获取的bean是代理对象时,根据代理对象的运行模式运行原始方法与增强的内容,完成操作
    • 目标对象(Target): 原始功能去掉共性功能对应的类产生的对象,这种对象是无法直接完成最终工作的
    • 代理(Proxy ):目标对象无法直接完成工作,需要对其进行功能回填,通过原始对象的代理对象实现

AOP 切入点表示式

  • 切入点:要进行增强的方法切入点

  • 表达式:要进行增强的方法的描述方式

  • 描述方式一: 执行com.blm.dao包下的BookDao接口中的无参数update方法
    execution(void com.blm.dao.BookDao.update()

  • 描述方式二: 执行com.itheima.dao.impl包下的BookDaoImpl类中的无参数update方法
    execution(void com.itheima.dao.impl.BookDaoImpl.update())

  • 切入点表达式标准格式:动作关键字( 访问修饰符 返回值包名类/接名方法名(参数异常名)

    @execution (public User com.itheima.service.UserService.findById (int))
    
    • 动作关键字: 描述切入点的行为动作,例如execution表示执行到指定切入点

    • 访问修饰符 : public,private等,可以省略

    • 返回值

    • 包名

    • 类/接口名

    • 方法名

    • 参数

    • 异常名: 方法定义中抛出指定异常,可以省略

    • 可以使用通配符描述切入点,快速描述

      • *:单个独立的任意符号,可以独立出现,也可以作为前缀或者后缀的匹配符出现

        execution (public * com.itheima.*.UserService.find* (*) )
        

        匹配comitheima包下的任意包中的UserService类或接口中所有find开头的带有一个参数的方法

      • …:多个连续的任意符号,可以独立出现,常用于简化包名与参数的书写

        execution (public User com..UserService.findById (..) )
        
      • 匹配com包下的任意包中的UserService类或接口中所有名称为findByld的方法+:专用于匹配子类类型

        execution(**..*Service+.*(..))
        

书写技巧

  • 所有代码按照标准规范开发,否则以下技巧全部失效
  • 描述切入点通常描述接口,而不描述实现类
  • 访问控制修饰符针对接口开发均采用public描述( 可省略访问控制修饰符描述
  • 返回值类型对于增删改类使用精准类型加速匹配,对于查询类使用通配快速描述
  • 包名书写尽量不使用…匹配,效率过低,常用*做单个包描述匹配,或精准匹配
  • 接口名/类名书写名称与模块相关的采用匹配,例如UserService书写成Service,绑定业务层接口名
  • 方法名书写以动词进行精准匹配,名词采用*匹配,例如getByld书写成getBy,selectAll书写成selectAll
  • 参数规则较为复杂,根据业务方法灵活调整
  • 通常不使用异常作为匹配规则

AOP通知类型

  • AOP通知描述了抽取的共性功能,根据共性功能抽取的位置不同,最终运行代码时要将其加入到合理的位置
  • AOP通知共分为5种类型
    • 前置通知
    • 后置通知
    • 环绕通知(重点)
    • 返回后通知(了解)
    • 抛出异常后通知(了解 )
package com.blm.aop;

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

@Component
@Aspect // AOP 注解
public class MyAdvice {
    /**
     * 切入点
     */
    @Pointcut("execution(* com.blm.dao.UserDao.getUserById(..)))")
    private void pt() {}

    @Before("pt()")
    public void  before() {
        System.out.println("前置通知");
    }

    @After("pt()")
    public void  after() {
        System.out.println("后置通知");
    }

    @Around("pt()")
    public void around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("环绕通知-前置");
        // 表示对原始方法的调用
        pjp.proceed();
        System.out.println("环绕通知-后置");
    }

    @AfterReturning("pt()")
    public void afterReturning() {
        System.out.println("返回后通知");
    }

    @AfterThrowing("pt()")
    public void afterThrowing() {
        System.out.println("抛出异常后通知");
    }
}

环绕通知,可以对返回结果做一些处理

@Around("pt()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("环绕通知发-前置");
        // 表示对原始方法的调用
        Object ret = pjp.proceed();
        System.out.println("环绕通知-后置" + ret);
        return ret;
    }

@Around 注意事项

  1. 环绕通知必须依赖形参ProceedingloinPoint才能实现对原始方法的调用,进而实现原始方法调用前后同时添加通知
  2. 通知中如果未使用ProceedingJoinPoint对原始方法进行调用将跳过原始方法的执行
  3. 对原始方法的调用可以不接收返回值,通知方法设置成void即可,如果接收返回值,必须设定为Object类型
  4. 原始方法的返回值如果是void类型,通知方法的返回值类型可以设置成void,也可以设置成Object
  5. 由于无法预知原始方法运行后是否会抛出异常,因此环绕通知方法必须抛出Throwable对象

案例(业务接口10000次接口执行时间)

package com.blm.aop;

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

@Component
@Aspect // AOP 注解
public class ProjectAdvice {
    /**
     * 切入点
     */
    @Pointcut("execution(* com.blm.service.*Service.*(..)))")
    private void servicePt() {}

    @Around("servicePt()")
    public Object runSpeed(ProceedingJoinPoint pjp) throws Throwable {
        long starTime = System.currentTimeMillis();
        Object ret = null;
        for (int i = 0; i < 10000; i ++) {
            ret = pjp.proceed();
        }
        long endTime = System.currentTimeMillis();
        System.out.println(pjp.getSignature() + "万次执行时间" + (endTime - starTime) + "ms");
        return ret;
    }
}

AOP 通知获取数据

  • 获取切入点方法的参数
    • JoinPoint :适用于前置、后置、返回后、抛出异常后通知
    • proceedJointPoint :适用于环绕通知
  • 获取切入点方法返回值
    • 返回后通知
    • 环绕通知
  • 获取切入点方法运行异常信息
    • 抛出异常后通知
    • 环绕通知
package com.blm.aop;

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

@Component
@Aspect // AOP 注解
public class MyAdvice {
    /**
     * 切入点
     */
    @Pointcut("execution(* com.blm.dao.UserDao.getUserById(..)))")
    private void pt() {}

//    @Before("pt()")
    public void  before(JoinPoint jp) {
        Object[] args = jp.getArgs();
        System.out.println(args);
        System.out.println("前置通知");
    }

//    @After("pt()")
    public void after(ProceedingJoinPoint pjp) {

        System.out.println("后置通知");
    }

//    @Around("pt()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        Object[] args = pjp.getArgs();
        args[0] = 4; // 修改参数
        System.out.println(args);
        Object obj = pjp.proceed(args); // 调用原始方法
        System.out.println("环绕通知-后置");
        return obj;
    }

//    @AfterReturning(value = "pt()", returning = "ret")
    public void afterReturning(Object ret) {
        System.out.println(ret); // 返回值
        System.out.println("返回后通知");
    }

    @AfterThrowing(value = "pt()", throwing = "t")
    public void afterThrowing(Throwable t) {
        System.out.println("抛出异常后通知" + t); // Throwable 异常对象
    }
}

AOP总结

  • 切入点表达式标准格式:动作关键字( 访问修饰符 返回值包名/接名方法名(参数)异常名)

    • execution(* com.itheima.service.Service.(…))
      切入点表达式描述通配符
    • 作用:用于快速描述,范围描述
      • : 配任意符号( 常用 )
    • … : 匹配多个连续的任意符号( 常用 )
    • +:匹配子类类型
  • 切入点表达式书写技巧

    1. 按标准规范开发
    2. 查询操作的返回值建议使用*匹配
    3. 减少使用…的形式描述包
    4. 对接口进行描述,使用表示模块名,例如UserService的匹配描述为Service
    5. 方法名书写保留动词,例如get,使用表示名词,例如getById匹配描述为getBy
    6. 参数根据实际情况灵活调整
  • 通知类型

    • 前置通知

    • 后置通知

      • 环绕通知( 重点)
      • 环绕通知依赖形参ProceedingJoinPoint才能实现对原始方法的调用
      • 环绕通知可以隔离原始方法的调用执行
      • 环绕通知返回值设置为object类型
      • 环绕通知中可以对原始方法调用过程中出现的异常进行处理
    • 返回后通知

    • 抛出异常后通知

  • 获取切入点方法的参数

    • JoinPoint : 适用于前置、后置、返回后、抛出异常后通知,设置为方法的第一个形参
    • ProceedJointPoint:适用于环绕通知
  • 获取切入点方法返回值

    • 返回后通知
    • 环绕通知
  • 获取切入点方法运行异常信息

    • 抛出异常后通知
    • 环绕通知

Spring 事物

  • 事务作用: 在数据层保障一系列的数据库操作同成功同失败
  • Spring事务作用:在数据层或业务层保障一系列的数据库操作 同成功 同失败

转账案例

需求:实现任意两个账户间转账操作

需求微缩: A账户减钱,B账户加钱

分析:

  1. 数据层提供基础操作,指定账户减钱 ( outMoney )指定账户加钱(inMoney )
  2. 业务层提供转账操作 ( transfer ),i调用减钱与加钱的操作
  3. 提供2个账号和操作金额执行转账操作
  4. 基于Spring整合MyBatis环境搭建上述操作

实现

  1. config
public class JdbcConfig {
    @Value("${jdbc.driver}")
    private String driver;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String userName;
    @Value("${jdbc.password}")
    private String password;
    @Bean
    public DataSource dataSource(){
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName(driver);
        ds.setUrl(url);
        ds.setUsername(userName);
        ds.setPassword(password);
        return ds;
    }

    // 事物管理器
    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
        transactionManager.setDataSource(dataSource);
        return transactionManager;
    }
}
public class MybatisConfig {
    @Bean
    public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){
        SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();
        ssfb.setTypeAliasesPackage("com.blm.domain");
        ssfb.setDataSource(dataSource);
        return ssfb;
    }
    //定义bean,返回MapperScannerConfigurer对象
    @Bean
    public MapperScannerConfigurer mapperScannerConfigurer(){
        MapperScannerConfigurer msc = new MapperScannerConfigurer();
        msc.setBasePackage("com.blm.dao");
        return msc;
    }
}
@Configuration
@ComponentScan("com.blm")
@PropertySource("classpath:jdbc.properties")
@Import({JdbcConfig.class, MybatisConfig.class}) 
@EnableTransactionManagement // 注解事物驱动
public class SpringConfig { }
public interface AccountDao {
    @Update("update bill set fee = fee - #{fee} where id = #{id}")
    void outMoney(@Param("id") Integer id, @Param("fee") Double fee);

    @Update("update bill set fee = fee + #{fee} where id = #{id}")
    void inMoney(@Param("id") Integer id, @Param("fee") Double fee);
}
public interface AccountService {
    @Transactional // 注解开启事务管理
    void transfer(Integer outId, Integer inId, Double fee);
}
@Service
public class AccountServiceImpl implements AccountService {
    @Override
    public void transfer(Integer outId, Integer inId, Double fee) {
        accountDao.outMoney(outId, fee);
        accountDao.inMoney(inId, fee);
    }
}
  1. Test
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class AccountServiceTest {
    @Autowired
    AccountService accountService;

    @Test
    public void updateTest() {
        accountService.transfer(1,2, 200.0);
    }
}

注意

  1. Spring注解式事务通常添加在业务层接口中而不会添加到业务层实现类中,降低耦合
  2. 注解式事务可以添加到业务方法上表示当前方法开启事务,也可以添加到接口上表示当前接口所有方法开启事务

事务角色

  • 事务管理员发起事务方在Spring中通常指代业务层开启事务的方法.
  • 加入事务方事务协调员在Spring中通常指代数据层方法,也可以是业务层方法.

转账日志记录

CREATE TABLE log (
	id INT PRIMARY KEY auto_increment,
	info VARCHAR(100) NOT NULL,
	create_date datetime DEFAULT CURRENT_TIMESTAMP
);
public interface LogDao {

    @Insert("insert into log(info, status) values(#{info}, #{status})")
    void log(@Param("info") String info, @Param("status") Integer status);
}
public interface LogService {	
	@Transactional(propagation = Propagation.REQUIRES_NEW) // 事物传播行为
 	void log(String info, Integer status);
}
public class LogServiceImpl implements LogService {
    @Autowired
    private LogDao logDao;
    @Override
    public void log(String info, Integer status) {
        logDao.log(info, status);
    }
}
@Service
public class AccountServiceImpl implements AccountService {
    @Autowired
    private LogService logService;

    @Override
    public void transfer(Integer outId, Integer inId, Double fee) {
        try {
            accountDao.outMoney(outId, fee);
            int i = 1/0;
            accountDao.inMoney(inId, fee);
        } finally {
            String info = outId + "-->" + inId + ":" + fee;
            logService.log(info, 1);
        }
    }
}
 	@Autowired
    LogService logService;
    @Test
    public void updateTest() {
        accountService.transfer(1,2, 200D);
    }

事务相关配置

在这里插入图片描述

@Transactional(readOnly = true, timeout = -1, rollbackFor = {IOException.class})
void transfer(Integer outId, Integer inId, Double fee);

事物传播行为配置

在这里插入图片描述

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

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

相关文章

2024年C语言最新经典面试题汇总(1-10)

C语言文章更新目录 C语言学习资源汇总&#xff0c;史上最全面总结&#xff0c;没有之一 C/C学习资源&#xff08;百度云盘链接&#xff09; 计算机二级资料&#xff08;过级专用&#xff09; C语言学习路线&#xff08;从入门到实战&#xff09; 编写C语言程序的7个步骤和编程…

Structured Knowledge Distillation for Accurate and Efficient Object Detection

摘要 许多之前的知识蒸馏方法是为图像分类而设计的&#xff0c;在具有挑战性的任务&#xff08;如目标检测&#xff09;中失败。本文首先提出了知识蒸馏在目标检测中失败的主要原因是&#xff1a;&#xff08;1&#xff09;前景和背景之间不平衡&#xff1a;(2)缺乏对不同像素…

NLP 笔记:Latent Dirichlet Allocation (介绍篇)

1 问题介绍 假设我们有一堆新闻&#xff0c;每个新闻都有≥1个主题 我们现在只知道新闻的内容&#xff0c;我们希望一个算法&#xff0c;帮我们把这些新闻分类成主题人类可以根据每个每个文章里面的单词判断主题&#xff0c;那计算机怎么做呢&#xff1f; ——>LDA(Latent D…

一文详解Rust中的字符串

有人可能会说&#xff0c;字符串这么简单还用介绍&#xff1f;但是很多人学习rust受到的第一个暴击就来自这浓眉大眼、看似毫无难度的字符串。 请看下面的例子。 fn main() {let my_name "World!";greet(my_name); }fn greet(name: String) {println!("Hello…

HTML元素语义化补充之css函数(三)

文章目录 CSS中的函数css函数–varcss函数–calccss函数–blurcss函数–gradientlinear-gradient的使用 CSS中的函数 ◼ 在前面我们有使用过很多个CSS函数: 比如rgb/rgba/translate/rotate/scale等; CSS函数通常可以帮助我们更加灵活的来编写样式的值&#xff1b; ◼ 下面有几…

亚稳态及其解决办法

异步电路 亚稳态 亚稳态亚稳态的产生原因什么是同步异步信号怎么消除亚稳态 亚稳态 在数字电路中&#xff0c;每一位数据不是1&#xff08;高电平&#xff09;就是0&#xff08;低电平&#xff09;。当然对于具体的电路来说&#xff0c;并非1&#xff08;高电平&#xff09;就是…

学习Python的第一天(下载Python环境和工具Pycharm)

下载工具 PyCharm Community Edition 2023.3.4 下载环境 Python3.10.4 目录 1.环境Python下载 1.1 Python环境变量下载网址: 1.2 下载步骤 2.工具Pycharm下载 2.1 pycharm下载网址 2.2 pycharm下载步骤 1.环境Python下载 1.1 Python环境变量下载网址: Python Releas…

在离线的arm架构kylin v10服务器上使用Kuboard-Spray搭建K8S集群

在离线的arm架构kylin v10服务器上使用Kuboard-Spray搭建K8S集群 在内网项目中需要安装K8S集群&#xff0c;经过调研&#xff0c;选择使用Kuboard-Spray工具搭建K8S集群&#xff0c;降低学习成本&#xff0c;提高安装效率。 为了简化安装使用集群的过程&#xff0c;搭建了私有…

01.家目录及桌面的认识(Linux基本概念)

知识引入&#xff1a; 我们在打开我们的电脑后&#xff0c;直接就可以看到图形化界面&#xff0c;这个图形化的界面就是我们常说的桌面。同时我们的电脑还可以多用户使用&#xff0c;就是在电脑开机的时候&#xff0c;选择用户&#xff0c;输入密码。这就说明&#xff0c;我们的…

java数据结构与算法基础-----字符串------正则表达式的练习案例---持续补充中

java数据结构与算法刷题目录&#xff08;剑指Offer、LeetCode、ACM&#xff09;-----主目录-----持续更新(进不去说明我没写完)&#xff1a;https://blog.csdn.net/grd_java/article/details/123063846 正则表达式基础&#xff1a;https://blog.csdn.net/grd_java/article/det…

零基础-MySQL数据库的基本操作

①MYSQL数据库基本操作-DDL 1.DDL的解释 DDL&#xff08;Data Definition Language&#xff09;&#xff0c;数据定义语言&#xff0c;包括以下内容&#xff1a; 对数据库的常用操作 对表结构的常用操作 修改表结构 2.对数据库的常用操作 功能SQL查看所有的数据库show d…

阿猪写作能用吗 #媒体#微信

阿猪写作是一个非常实用的论文写作工具&#xff0c;它不仅能帮助用户快速完成论文写作&#xff0c;还可以提供查重降重的功能&#xff0c;帮助用户确保论文的原创性。在当今社会&#xff0c;论文写作是学术界和科研领域最重要的工作之一&#xff0c;而阿猪写作的出现无疑是给这…

使用阿里云服务器搭建网站教程,超简单10分钟网站上线

使用阿里云服务器快速搭建网站教程&#xff0c;先为云服务器安装宝塔面板&#xff0c;然后在宝塔面板上新建站点&#xff0c;阿里云服务器网aliyunfuwuqi.com以搭建WordPress网站博客为例&#xff0c;来详细说下从阿里云服务器CPU内存配置选择、Web环境、域名解析到网站上线全流…

JVM——运行时数据区

前言 由于JAVA程序是交由JVM执行的&#xff0c;所以我们所说的JAVA内存区域划分也是指的JVM内存区域划分&#xff0c;JAVA程序具体执行的过程如下图所示。首先Java源代码文件会被Java编译器编译为字节码文件&#xff0c;然后由JVM中的类加载器加载各个类的字节码文件&#xff0…

RabbitMQ之Plugins插件----AMQP对接MQTT

1.启用插件 rabbitmq-plugins enable rabbitmq_mqtt 2.检查是否启动成功&#xff0c;打开rabbitmq后台 3.概念&#xff1a; AMQP是由交换器和queue队列组成的消息队列机制&#xff0c;MQTT是由订阅主题组成的消息机制 1.MQTT创建连接时会向rabbitmq创建一个自己的queue&…

信号处理之快速傅里叶变换(FFT)

信号处理之快速傅里叶变换FFT 历史溯源欧拉公式傅里叶级数(FS)傅里叶变换(FT)离散傅里叶级数(DFS)离散时间傅里叶变换(DTFT)离散傅里叶变换(DFT)快速傅里叶变换(FFT)MATLAB中常用的FFT工具FFT中常见的问题 历史溯源 相信很多人知道傅里叶变换&#xff0c;但是很多人对傅里叶变…

【Monero】Onion Monero Blockchain Explorer | 洋葱门罗币区块链浏览器

github&#xff1a;onion-monero-blockchain-explorer Onion Monero Blockchain Explorer特点: 没有cookie&#xff0c;没有网络分析跟踪器&#xff0c;没有image&#xff0c; 开源&#xff0c; 完全用C编写&#xff0c; 显示加密的付款 ID&#xff0c; 显示环签名&#xff0c;…

C# WPF编程-控件

C# WPF编程-控件 概述WPF控件类别包括以下控件&#xff1a;背景画刷和前景画刷字体文本装饰和排版字体继承字体替换字体嵌入文本格式化模式鼠标光标 内容控件Label&#xff08;标签&#xff09;Button&#xff08;按钮&#xff09; 概述 在WPF领域&#xff0c;控件通常被描述为…

阿里云原生:如何熟悉一个系统

原文地址:https://mp.weixin.qq.com/s/J8eK-qRMkmHEQZ_dVts9aQ?poc_tokenHMA-_mWjfcDmGVW6hXX1xEDDvuJPE3pL9-8uSlyY 导读&#xff1a;本文总结了熟悉系统主要分三部分&#xff1a;业务学习、技术学习、实战。每部分会梳理一些在学习过程中需要解答的问题&#xff0c;这些问题…

一笔画--PTA

文章目录 题目描述思路AC代码 题目描述 输入样例1 3 2 1 2 2 3 输出样例1 Y输入样例2 4 3 1 2 1 3 1 4 输出样例2 N输入样例3 1 0 输出样例3 Y思路 dfs 、欧拉通路、欧拉回路的判定 前导知识 欧拉通路、欧拉回路、欧拉图 无向图&#xff1a; ①设G是连通无向图&#xff0c;则称…