Sping全面复习

news2024/11/18 23:11:54

Spring框架是一个功能强大且广泛使用的Java平台,它通过提供全面的基础设施支持,使得开发人员能够轻松构建高效、可移植、易于测试的代码。Spring的核心特性包括依赖注入(DI)、面向切面编程(AOP)和事件驱动模型,这些特性共同构成了一个灵活的编程范式,简化了数据访问层、业务逻辑层和表示层的开发。Spring还提供了对多种数据存储方案的支持,包括关系型数据库和NoSQL数据库,以及对消息传递系统的集成。此外,Spring Boot进一步简化了基于Spring的应用开发,通过自动配置和无需XML配置的方式,使得开发者可以快速启动和运行Spring应用程序。Spring生态系统的扩展性使其成为微服务架构、云计算和企业级应用开发的理想选择。

在这里插入图片描述
1、导入spring坐标
在这里插入图片描述
2.定义接口和实现类
在这里插入图片描述
3.编写xml文件
在这里插入图片描述
4.创建工厂测试类
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
beanfactory和Applicationcontext关系
在这里插入图片描述beanfactory的继承体系
在这里插入图片描述
ApplicationContext的继承体系
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
Bean的配置详解
在这里插入图片描述
bean的范围配置
在这里插入图片描述
bean的延迟加载
在这里插入图片描述
bean的初始化方法一

在这里插入图片描述
bean的初始化方法一
在这里插入图片描述
bean的实例化方式
有两种:工厂方式和构造方式
在这里插入图片描述
构造方式实例化
在这里插入图片描述
工厂方式实例化
在这里插入图片描述
静态工厂实例化
在这里插入图片描述
找工厂方法的返回值作为对象作为bean的id值进行返回
在这里插入图片描述
实例工厂方法(如整合第三方bean的时候)
在这里插入图片描述
需要先配置工厂对象才能创建bean和静态实例化方法区分开
在这里插入图片描述
实例化factory的规范实例化Bean
在这里插入图片描述
在这里插入图片描述
bean的依赖注入配置
人为注入
在这里插入图片描述
在这里插入图片描述
自动注入
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
spring获取bean的三种方式
在这里插入图片描述
sping配置非定义的bean
在这里插入图片描述

需要考虑两个问题
1.bean的实例化方式是什么?2.bean是否需要注入必要属性?

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
bean实例化的基本流程
Spring Bean的基本流程
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
spring的后处理器
Spring的后处理器是Spring对外开发的重要扩展点,允许我们介入到Bean的整个实例化流程中来,以达到动态注册BeanDefinition,动态修改BeanDefinition,以及动态修改Bean的作用。Spring主要有两种后处理器:
1.BeanFactoryPostProcessor: Bean工厂后处理器,在BeanDefinitionMap填充完毕,Bean实例化之前执行;
2.BeanPostProcessor: Bean后处理器,一般在Bean实例化之后,填充到单例池sinqletonObiects之前执行。
Bean工厂后处理器-BeanFactoryPostProcessor
在这里插入图片描述
其中ConfigurableListableBeanFactory继承了ListableBeanFactory,ListableBeanFactory继承了BeanFactory,所以使用ConfigurableListableBeanFactory就可以操作BeanDefinition。
修改某个BeanDefinition:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
Spring的后处理器
在这里插入图片描述
BeanPostProcessor的接口定义如下:

public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
       System.out.println(beanName + ":postProcessBeforeInitialization");
       return bean;
    }
    
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
       System.out.println(beanName + ":postProcessAfterInitialization");
       return bean;
    }
}

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    if (bean instanceof UserDaoImpl) {
       UserDaoImpl userDao = (UserDaoImpl) bean;
       userDao.setUsername("zkt");
    }
    System.out.println(beanName + ":postProcessBeforeInitialization");
    return bean;
}

在这里插入图片描述

由此可见,postProcessBeforeInitialization和postProcessAfterInitialization中间还会执行afterPropertiesSet和init方法。
有了BeanPostProcessor后Bean实例化流程:
在这里插入图片描述
Bean的生命周期
在这里插入图片描述
Spring Bean的初始化过程涉及如下几个过程:
Bean实例的属性填充
Aware接口属性注入
BeanPostProcessor的before()方法回调
InitializingBean接口的初始化方法回调
自定义初始化方法init回调
BeanPostProcessor的after()方法回调

在这里插入图片描述
Bean实例的属性填充
Spring在进行属性注入时,会分为如下几种情况:
注入普通属性,String、int或存储基本类型的集合时,直接通过set方法的反射设置进去;
注入单向对象引用属性时,从容器中getBean获取后通过set方法反射设置进去,如果容器中没有,则先创建被注入对象Bean实例(完成整个生命周期)后,在进行注入操作;
注入双向对象引用属性时,就比较复杂了,涉及了**循环引用(循环依赖)**问题,下面会详细阐述解决方案。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
当userService注入userDao时发现没有userDao,会到三级缓存中去找被封装为ObjectFactory的userDao,找到后会把userDao注入给userService,并把userDao在三级缓存中去掉,在二级缓存中添加userDao。
UserService和UserDao循环依赖的过程结合上述三级缓存描述一下:
UserService 实例化对象,但尚未初始化,将UserService存储到三级缓存;
UserService 属性注入,需要UserDao,从缓存中获取,没有UserDao;
UserDao 实例化对象,但尚未初始化,将UserDao存储到到三级缓存;
UserDao 属性注入,需要UserService,从三级缓存获取UserService,UserService从三级缓存移入二级缓存;
UserDao 执行其他生命周期过程,最终成为一个完成Bean,存储到一级缓存,删除二三级缓存;
UserService 注入UserDao:
UserService 执行其他生命周期过程,最终成为一个完成Bean,存储到一级缓存,删除二三级缓存。
在这里插入图片描述

常用的Aware接口
在这里插入图片描述
在这里插入图片描述
基于xml整合第三方框架
在这里插入图片描述
在这里插入图片描述
原始操作

package JDBC_Template;

public class Userbean
{
    private int id;
    private String name;
    private String password;

    public Userbean() {
    }

    public Userbean(int id, String name, String password) {
        this.id = id;
        this.name = name;
        this.password = password;
    }

    public int getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    public String getPassword() {
        return password;
    }

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

    @Override
    public String toString() {
        return "Userbean{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}
package JDBC_Template;

import java.util.List;

public interface UserMapper
{
    List<Userbean> findUser();
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="JDBC_Template.UserMapper">
    <select id="findUser" resultType="JDBC_Template.Userbean">
        select * from dsuser
    </select>
</mapper>

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://127.0.0.1/mybatis"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="/JDBC_Template/UserMapper.xml"/>
    </mappers>
</configuration>

package JDBC_Template;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class MybatisTest
{
    @Test
    public void Test1() throws IOException {
        String resource = "Mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<Userbean> user = mapper.findUser();
        System.out.println(user);

    }
}

Spring整合mybatis的目的:将下列的冗余的代码放入bean中方便管理,并可重复使用
导入Mybatis整合Spring的相关坐标

<dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>1.3.2</version>
</dependency>

package JDBC_Template;

import java.util.List;

public interface UserMapper
{
    List<Userbean> findUser();
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="JDBC_Template.UserMapper">
    <select id="findUser" resultType="JDBC_Template.Userbean">
        select * from dsuser
    </select>
</mapper>

<?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"/>
<!--    配置数据源-->
    <bean class="com.mchange.v2.c3p0.ComboPooledDataSource" id="dataSource">
        <property name="user" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
        <property name="driverClass" value="${jdbc.driver}"/>
        <property name="jdbcUrl" value="${jdbc.url}"/>
    </bean>
<!--    作用将SqlSessionfactory存储到Spring容器中-->
    <bean id="bean" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource"></property>
</bean>
<!--    作用:扫描指定的包,产生Mapper对象存储到spring容器中-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="JdbcTemplate.UserMapper"></property>
    </bean>
</beans>

注:SqlSessionFactoryBean的作用是向Spring提供SqlSessionFactory(SqlSessionFactory一旦被创建就在应用的运行期间一直存在)
我们来看看SqlSessionFactoryBean的定义是怎样的:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
 
}

当实现了这个接口的 Bean 在配置为被 Spring 接管时,存入IoC容器中的实例类型将会是实例化泛型的那个类型,从IoC容器中获取时也是实例化泛型的那个类型,这种情况下,Spring 将会在应用启动时为你创建 SqlSessionFactory 对象,然后将它以 SqlSessionFactory 为名来存储。当把这个bean注入到Spring中去了以后,IoC容器中的其他类型就可以拿到 SqlSession 实例了,就可以进行相关的SQL执行任务了。
注:MapperScannerConfigurer的作用是将Mapper的对象存储到Spring容器中

例如:

   <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!--注入mapper接口所在的包-->
        <property name="basePackage" value="com.bus.mapper,com.sys.mapper,com.stat.mapper">
        </property>
        <!--注入sqlsessionfactory-->
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
    </bean>

在这里插入图片描述

package JDBC_Template;

public class UserDao
{
    private UserMapper userMapper;

    public void setUserMapper(UserMapper userMapper) {
        this.userMapper = userMapper;
    }
    public void show()
    {
        System.out.println(userMapper.findUser());
    }
}
 @Test
    public void Test2()
    {
        ApplicationContext ioc=new ClassPathXmlApplicationContext("bean03.xml");
//        List<Userbean> user = userMapper.findUser();
        UserDao userDao = ioc.getBean(UserDao.class);
        userDao.show();
        System.out.println("通过spring整合之后获取");
    }

需要引入第三方框架命名空间,需要使用Spring的配置文件配置第三方框架的本身内容,例如:Dubbo
为了将数据库的信息与bean文件中的信息加以区分,也为了更好操作、查找对应的数据库的驱动,地址,密码,账号,所以将这些数据放入properties文件中保存,加载外部propreties文件的标签为
location表示properties文件的位置

 <context:property-placeholder location="jdbc.properties"/>

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
基于bean的注解开发
在这里插入图片描述
使用注解代替xml文件
也就是说告诉spring扫描哪些包,spring会扫描基本包及其下面子包

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

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
@Autowired根据类型进行注入,如果同一类型有多个名字不同的bean那就按照名字进行二次匹配
【重温SSM框架系列】3 - Spring注解开发(注解代替xml文件配置)

    <bean id="userService" class="com.wang.service.impl.UserServiceImpl">
        <property name="userDao" ref="userDao"></property>
    </bean>

@Autowired结合@Qualifier根据名字进行注入
在这里插入图片描述
非自定义bean的配置
在这里插入图片描述

@Configuration      //标志该类为Spring的核心配置类
@ComponentScan("com.wang")
@PropertySource("jdbc.properties")
public class SpringConfig {

    @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("dataSource")
    public DataSource getDataSource(){
        DruidDataSource dataSource = new DruidDataSource();
//        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
//        dataSource.setUrl("jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai");
//        dataSource.setUsername("root");
//        dataSource.setPassword("123456");
        dataSource.setDriverClassName(driver);
        dataSource.setUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        return dataSource;
    }
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
注解方式整合第三方框架
在这里插入图片描述
使用@Bean将DataSource和SqlSessionFactoryBean存储到Spring容器中,而MapperScannerConfigurer使用注解@MapperScan进行指明需要扫描的Mapper在哪个包下,使用注解整合MyBatis配置方式如下:

@Configuration
@ComponentScan("com.itheima")
@MapperScan("com.itheima.mapper")
public class ApplicationContextConfig {

	@Bean
    public DataSource dataSource(
            @Value("${jdbc.driver}") String driver,
            @Value("${jdbc.url}") String url,
            @Value("${jdbc.username}") String username,
            @Value("${jdbc.password}") String password
    ){
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(driver);
        dataSource.setUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        return dataSource;
    }
    
	@Bean
	public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){
		SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
		sqlSessionFactoryBean.setDataSource(dataSource);
		return sqlSessionFactoryBean;
	}
}

注解方式,Spring整合MyBatis的原理,关键在于@MapperScan,@MapperScan不是Spring提供的注解,而是MyBatis为了整合Spring,在整合包org.mybatis.spring.annotation中提供的注解,源码如下:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({MapperScannerRegistrar.class})
@Repeatable(MapperScans.class)
public @interface MapperScan {
	String[] value() default {};
	String[] basePackages() default {};
	Class<?>[] basePackageClasses() default {};
	Class<? extends Annotation> annotationClass() default Annotation.class;
	// ... ...
}

在这里插入图片描述
@Import整合第三方框架原理
在这里插入图片描述
@Import导入实现了ImportSelector接口的类

@Configuration
@ComponentScan("com.itheima")
@Import({MyImportSelector.class})
public class ApplicationContextConfig {
}
public class MyImportSelector implements ImportSelector {
	@Override
	public String[] selectImports(AnnotationMetadata annotationMetadata) {
		// 返回要进行注册的Bean的全限定名数组
		return new String[]{User2.class.getName()};
	} 
}

ImportSelector接口selectImports方法的参数AnnotationMetadata代表注解的媒体数据,可以获得 当前注解(@Import({MyImportSelector.class})) 修饰的类(ApplicationContextConfig ) 的 其他注解的元信息,例如:@Configuration、@ComponentScan(“com.itheima”)注解的元信息

public class MyImportSelector implements ImportSelector {
	@Override
	public String[] selectImports(AnnotationMetadata annotationMetadata) {
		// 获得指定类型注解的全部信息
		Map<String, Object> annotationAttributes = annotationMetadata.getAnnotationAttributes(ComponentScan.class.getName());
		// 获得全部信息中basePackages信息
		String[] basePackages = (String[]) annotationAttributes.get("basePackages");
		// 打印结果是com.itheima
		System.out.println(basePackages[0]);
		return new String[]{User2.class.getName()};
	} 
}

@Import导入实现ImportBeanDefinitionRegistrar接口的类,实现了该接口的类的registerBeanDefinitions方法会被自动调用,在该方法内可以注册BeanDefinition

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
	@Override
	public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
		// 使用给定的BeanDefinitionRegistry参数,手动注册BeanDefinition
		BeanDefinition beanDefinition = new RootBeanDefinition();
		beanDefinition.setBeanClassName("com.itheima.pojo.User2");
		registry.registerBeanDefinition("user2", beanDefinition);
	} 
}

Aop

AOP,Aspect Oriented Programming,面向切面编程,是对面向对象编程OOP的升华。OOP是纵向对一个事物的抽象,一个对象包括静态的属性信息,包括动态的方法信息等。而AOP是横向的对不同事物的抽象,属性与属性、方法与方法、对象与对象都可以组成一个切面,而用这种思维去设计编程的方式叫做面向切面编程

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
引入坐标
编写目标对象userService

public interface UserService {
    void show1();
    void show2();
}

public class UserServiceImpl implements UserService {
    @Override
    public void show1() {
        System.out.println("show1 ...");
    }

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

编写增强(通知)对象myAdvice

// 增强类,内部提供增强方法
public class MyAdvice {
    public void beforeAdvice(){
        System.out.println("前置增强....");
    }

    public void afterReturningAdvice(){
        System.out.println("后置增强....");
    }
}

编写bean后处理器模拟AOP实现

public class MockAopBeanPostProcessor implements BeanPostProcessor , ApplicationContextAware {
    private ApplicationContext applicationContext = null;
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        // 目的:对UserServiceImpl中的show1,show2方法进行增强,增强方法存在与MyAdvice中
        // 问题1:筛选service.impl包下所有类和所有方法多可以进行增强,解决方案:if-else
        // 问题2:MyAdvice怎么获取? 解决方案:从spring容器中获取
        if (bean.getClass().getPackage().getName().equals("com.mem.service.impl")){
            //生成当前Bean的代理对象
            Object proxyInstance = Proxy.newProxyInstance(
                    bean.getClass().getClassLoader(),
                    bean.getClass().getInterfaces(),
                    (Object proxy, Method method, Object[] args)->{
                        MyAdvice myAdvice = (MyAdvice) applicationContext.getBean("myAdvice");
                        //执行增强对象的前置方法
                        myAdvice.beforeAdvice();
                        //执行目标对象的目标方法
                        Object result = method.invoke(bean, args);
                        //执行增强对象的后置方法
                        myAdvice.afterReturningAdvice();
                        return result;
                    }
            );
            return proxyInstance;
        }else{
            return bean;
        }
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

配置文件:载入容器

<bean id="myAdvice" class="com.mem.advice.MyAdvice"/>
<bean id="userService" class="com.mem.service.impl.UserServiceImpl"/>
<bean id="mockAopBeanPostProcessor" class="com.mem.processor.MockAopBeanPostProcessor"/>

测试

public class ApplicationContextTest {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService userService = (UserService) applicationContext.getBean("userService");
        userService.show1();
    }
}
// 输出
前置增强....
show1 ...
后置增强....

** AOP相关概念**
在这里插入图片描述
在这里插入图片描述
基于xml配置的AOP
通过配置文件的方式去解决上述问题:

  • 配置哪些包、哪些类、哪些方法需要被增强 (涉及切点表达式的配置)
  • 配置目标方法要被哪些通知方法所增强,在目标方法执行之前还是之后执行增强(涉及织入的配置)
    配置方式的设计、配置文件(注解)的解析工作,Spring已经帮我们封装好了
    在这里插入图片描述
    1.导入AOP相关坐标;aspectj是实现AOP的一种实现方式
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.6</version>
</dependency>

Spring-context坐标下已经包含spring-aop的包了,所以就不用额外导入了
在这里插入图片描述
2.准备目标类、准备增强(通知)类,并配置给Spring管理
目标类

public interface UserService {
    void show1();
    void show2();
}

public class UserServiceImpl implements UserService {
    @Override
    public void show1() {
        System.out.println("show1 ...");
    }

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

通知类

// 增强类,内部提供增强方法
public class MyAdvice {
    public void beforeAdvice(){
        System.out.println("前置增强....");
    }

    public void afterReturningAdvice(){
        System.out.println("后置增强....");
    }
}

配置文件(需引入aop的命名空间)

<bean id="myAdvice" class="com.mem.advice.MyAdvice"/>
<bean id="userService" class="com.mem.service.impl.UserServiceImpl"/>

3.配置切点表达式(哪些方法被增强);
4.配置织入(切点被哪些通知方法增强,是前置增强还是后置增强)。

<!--配置aop-->
<aop:config>
    <!--配置切点表达式,目的:指定哪些方法要被增强-->
    <aop:pointcut id="myPointcut" expression="execution(void com.mem.service.impl.UserServiceImpl.show*())"/>
    <!--配置织入,目的:指定哪些切点与哪些通知进行结合-->
    <aop:aspect id="" ref="myAdvice">
        <aop:before method="beforeAdvice" pointcut-ref="myPointcut"/>
        <aop:after-returning method="afterReturningAdvice" pointcut-ref="myPointcut"/>
    </aop:aspect>
</aop:config>

在这里插入图片描述
切点表达式的配置方式:
直接将切点表达式配置在通知上
可以将切点表达式抽取到外面,在通知上进行引用
在这里插入图片描述
切点表达式的配置语法
在这里插入图片描述
在这里插入图片描述
通知类型
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
AOP的配置的两种方式
在这里插入图片描述
前置通知和后置通知接口
通知类实现了前置通知和后置通知接口

public class MyAdvice2 implements MethodBeforeAdvice, AfterReturningAdvice {
    @Override
    public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
        System.out.println("后置通知*****");
    }

    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println("前置通知*****");
    }
}
<!--配置aop-->
<aop:config>
    <!--配置切点表达式,目的:指定哪些方法要被增强-->
    <aop:pointcut id="myPointcut" expression="execution(void com.mem.service.impl.UserServiceImpl.show*())"/>
    <!--配置织入,目的:指定哪些切点与哪些通知进行结合-->
    <aop:advisor advice-ref="myAdvice2" pointcut-ref="myPointcut"/>
</aop:config>

环绕通知
通知类实现了方法拦截器接口

public class MyAdvice3 implements MethodInterceptor {

    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        System.out.println("环绕前*****");
        // 执行目标方法
        Object res = methodInvocation.getMethod().invoke(methodInvocation.getThis(), methodInvocation.getArguments());
        System.out.println("环绕后*****");
        return res;
    }
}

<!--配置aop-->
<aop:config>
    <!--配置切点表达式,目的:指定哪些方法要被增强-->
    <aop:pointcut id="myPointcut" expression="execution(void com.mem.service.impl.UserServiceImpl.show*())"/>
    <!--配置织入,目的:指定哪些切点与哪些通知进行结合-->
    <aop:advisor advice-ref="myAdvice3" pointcut-ref="myPointcut"/>
</aop:config>

在这里插入图片描述
基于注解配置的AOP
Spring的AOP也提供了注解方式配置,使用相应的注解替代之前的xml配置,xml配置AOP时,我们主要配置了三部分:目标类被Spring容器管理、通知类被Spring管理、通知与切点的织入(切面),如下:
在这里插入图片描述

<!--配置通知 -->
<bean id="myAdvice" class="com.mem.advice.MyAdvice"/>
<!--配置目标 -->
<bean id="userService" class="com.mem.service.impl.UserServiceImpl"/>
<!--配置aop-->
<aop:config>
    <!--配置织入,目的:指定哪些切点与哪些通知进行结合-->
    <aop:aspect id="" ref="myAdvice">
        <aop:before method="beforeAdvice" pointcut="execution(void com.mem.service.impl.UserServiceImpl.show*())"/>
        <aop:after-returning method="afterReturningAdvice" pointcut="execution(void com.mem.service.impl.UserServiceImpl.show*())"/>
    </aop:aspect>
</aop:config>

通知

@Service  // 第一步
public interface UserService {
    void show1();
}

目标

@Component  // 第二步
public class MyAdvice {

    public void beforeAdvice(){
        System.out.println("前置增强....");
    }

    public void afterReturningAdvice(){
        System.out.println("后置增强....");
    }
}

用注解替代配置aop
配置aop,其实配置aop主要就是配置通知类中的哪个方法(通知类型)对应的切点表达式是什么

@Component
@Aspect  // 第三步
public class MyAdvice {

    // <aop:before method="beforeAdvice" pointcut="execution(void com.mem.service.impl.UserServiceImpl.show*())"/>
    @Before("execution(void com.mem.service.impl.UserServiceImpl.show*())")  // 第四步
    public void beforeAdvice(){
        System.out.println("前置增强....");
    }
    // <aop:after-returning method="afterReturningAdvice" pointcut="execution(void com.mem.service.impl.UserServiceImpl.show*())"/>
    @AfterReturning("execution(void com.mem.service.impl.UserServiceImpl.show*())")  // 第四步
    public void afterReturningAdvice(){
        System.out.println("后置增强....");
    }
}

配置文件或配置类的修改
配置文件: 注解@Aspect、@Around需要被Spring解析,所以在Spring核心配置文件中需要配置aspectj的自动代理 aop:aspectj-autoproxy/

<!-- aspectj 自动代理-->
<aop:aspectj-autoproxy/>
<!-- 容器包扫描 -->
<context:component-scan base-package="com.mem"/>

配置类:需要加上@EnableAspectJAutoProxy

public class ApplicationContextTest {
    public static void main(String[] args) {
        // 配置文件
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext2.xml");
        UserService userService = (UserService) applicationContext.getBean("userService");
        userService.show1();
        // 配置类
//        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(ApplicationConfig.class);
//        UserService userService = (UserService) applicationContext.getBean("userService");
//        userService.show1();
    }
}
// 两种方式的打印结果
前置增强....
show1 ...
后置增强....

基于AOP的声明式事务控制
事务是开发中必不可少的东西,使用JDBC开发时,我们使用connnection对事务进行控制,使用MyBatis时,我们使用SqlSession对事务进行控制,缺点显而易见,当我们切换数据库访问技术时,事务控制的方式总会变化, Spring 就将这些技术基础上,提供了统一的控制事务的接口。Spring的事务分为:编程式事务控制 和 声明式事务控制
在这里插入图片描述
在这里插入图片描述
搭建测试环境
搭建一个转账的环境,dao层一个转出钱的方法,一个转入钱的方法,service层一个转账业务方法,内部分别调 用dao层转出钱和转入钱的方法,准备工作如下:

数据库准备一个账户表tb_account;

DROP TABLE IF EXISTS `account`;

CREATE TABLE `account` (
  `id` int NOT NULL AUTO_INCREMENT,
  `account_name` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL,
  `money` int DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

insert  into `account`(`id`,`account_name`,`money`) values (1,'tom',5000),(2,'lucy',5000);

dao层准备一个AccountMapper,包括incrMoney和decrMoney两个方法;

public interface AccountMapper {
    // 加钱
    @Update("update account set money=money+#{money} where account_name=#{accountName}")
    void incrMoney(@Param("accountName") String accountName, @Param("money") Integer money);
    // 减钱
    @Update("update account set money=money-#{money} where account_name=#{accountName}")
    void decrMoney(@Param("accountName") String accountName, @Param("money") Integer money);
}

service层准备一个transferMoney方法,分别调用incrMoney和decrMoney方法

public interface AccountService {

    void transferMoney(String outAccount,String inAccount,Integer money);
}

@Service("accountService")
public class AccountServiceImpl implements AccountService {
    @Autowired
    private AccountMapper accountMapper;
    @Override
    public void transferMoney(String outAccount, String inAccount, Integer money) {
        accountMapper.decrMoney(outAccount,money);
//        int i = 1/0;
        accountMapper.incrMoney(inAccount,money);
    }
}

在applicationContext文件中进行Bean的管理配置;

<!--组件扫描-->
<context:component-scan base-package="com.mem"/>
<!--加载properties文件-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 配置数据源信息 -->
<bean id="dataSource" 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>

<!-- 配置SqlSessionFactoryBean,作用将SqlSessionFactory存储到Spring容器中-->
<bean class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
</bean>

<!--MapperScannerConfigurer,作用扫描指定的包,产生Mapper对象,存储到Spring容器中-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.mem.mapper"/>
</bean>

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://192.168.91.135:3306/mybatis?useSSL=false
jdbc.username=root
jdbc.password=123456

测试正常转账与异常转账。

public class AccountTest {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        AccountService accountService = (AccountService) applicationContext.getBean("accountService");
        accountService.transferMoney("tom","lucy",500);
        System.out.println("转账操作成功");
    }
}
// 打印结果
转账操作成功

打开AccountServiceImpl类中的错误代码,报错:java.lang.ArithmeticException: / by zero
此时数据库,tom用户的余额减了500,而lucy用户的余额却没有增加500
下面通过spring的声明式事务进行控制
在这里插入图片描述
导入Spring事务的相关的坐标,spring-jdbc坐标已经引入的spring-tx坐标

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.3.12</version>
</dependency>

配置目标类AccountServiceImpl (注解方式已完成)

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

使用advisor标签配置切面

<!-- 事务增强的aop -->
<aop:config>
    <!--配置切点表达式-->
    <aop:pointcut id="myPointcut" expression="execution(void com.mem.service.impl.*.*(..))"/>
    <!--配置织入关系,通知是谁? spring提供好的-->
    <aop:advisor advice-ref="" pointcut-ref="myPointcut"/>
</aop:config>

疑问:Spring提供的通知类是谁?是spring-tx包下的advice标签配置提供的
配置详情:

<!-- 引入tx的命名空间 -->
<beans xmlns:tx="http://www.springframework.org/schema/tx" 
       xsi:schemaLocation="http://www.springframework.org/schema/tx https://www.springframework.org/schema/tx/spring-tx.xsd"
>
<!--配置平台事务管理器 PlatformTransactionManager-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>
<!--配置Spring提供好的Advice 内部transaction-manager 需要一个平台事务管理器-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="*"/>
    </tx:attributes>
</tx:advice>
<!-- 事务增强的aop -->
<aop:config>
    <!--配置切点表达式-->
    <aop:pointcut id="myPointcut" expression="execution(void com.mem.service.impl.*.*(..))"/>
    <!--配置织入关系,通知是谁? spring提供好的-->
    <aop:advisor advice-ref="txAdvice" pointcut-ref="myPointcut"/>
</aop:config>

MyBatis作为持久层框架时,使用的平台事务管理器实现是DataSourceTransactionManager。
Hibernate作为持久层框架时,使用的平台事务管理器是HibernateTransactionManager。

事务定义信息:
其次,事务定义信息配置,每个事务有很多特性,例如:隔离级别、只读状态、超时时间等,这些信息在开发时 可以通过connection进行指定,而此处要通过配置文件进行配置
在这里插入图片描述
ame属性名称指定哪个方法要进行哪些事务的属性配置
方法名在配置时,也可以使用进行模糊匹配,例如:
在这里插入图片描述
此处需要区分的是切点表达式指定的方法与此处指定的方法的区别?
切点表达式,是过滤哪些方法可以进行事务增强;
事务属性信息的name,是指定哪个方法要进行哪些事务属性的配置
在这里插入图片描述
在这里插入图片描述
不需要特殊配置的在最后的加一个<tx:method name="
"/>
在这里插入图片描述
ead-only属性:设置当前的只读状态
如果是查询则设置为true,可以提高查询性能,如果是更新(增删改)操作则设置为false
在这里插入图片描述
timeout属性:设置事务执行的超时时间,单位是秒
如果超过该时间限制但事务还没有完成,则自动回滚事务 ,不在继续执行。默认值是-1,即没有超时时间限制
在这里插入图片描述
在这里插入图片描述
基于注解声明式事务控制

<!--配置Spring提供好的Advice 内部transaction-manager 需要一个平台事务管理器-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="*" isolation="REPEATABLE_READ" timeout="3" read-only="false" propagation="REQUIRED"/>
    </tx:attributes>
</tx:advice>
<!-- 事务增强的aop -->
<aop:config>
    <!--配置切点表达式-->
    <aop:pointcut id="myPointcut" expression="execution(void com.mem.service.impl.*.*(..))"/>
    <!--配置织入关系,通知是谁? spring提供好的-->
    <aop:advisor advice-ref="txAdvice" pointcut-ref="myPointcut"/>
</aop:config>

用@Transactional代替上面两个配置

@Service("accountService")
public class AccountServiceImpl implements AccountService {
    @Autowired
    private AccountMapper accountMapper;
    @Override
    @Transactional(isolation = Isolation.READ_COMMITTED,propagation = Propagation.SUPPORTS,timeout = 3)
    public void transferMoney(String outAccount, String inAccount, Integer money) {
        accountMapper.decrMoney(outAccount,money);
        int i = 1/0;
        accountMapper.incrMoney(inAccount,money);
    }
}

同样,使用的事务的注解,平台事务管理器仍然需要配置,还需要进行事务注解开关的开启

<!--配置平台事务管理器 PlatformTransactionManager-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>
<!-- 配置事务的注解驱动 transaction-manager:默认值为transactionManager 可以不设置-->
<tx:annotation-driven transaction-manager="transactionManager"/>

改成全注解的方式需要替换成@Bean和@EnableTransactionManagement

<?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"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd
       http://www.springframework.org/schema/tx https://www.springframework.org/schema/tx/spring-tx.xsd">
    <!--组件扫描-->
    <context:component-scan base-package="com.mem"/>
    <!--加载properties文件-->
    <context:property-placeholder location="classpath:jdbc.properties"/>

    <!-- 配置数据源信息 -->
    <bean id="dataSource" 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>

    <!-- 配置SqlSessionFactoryBean,作用将SqlSessionFactory存储到Spring容器中-->
    <bean class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!--MapperScannerConfigurer,作用扫描指定的包,产生Mapper对象,存储到Spring容器中-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.mem.mapper"/>
    </bean>

    <!-- 配置事务的注解驱动 transaction-manager:默认值为transactionManager 可以不设置-->
    <tx:annotation-driven transaction-manager="transactionManager"/>

    <!--配置平台事务管理器 PlatformTransactionManager-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!--配置Spring提供好的Advice 内部transaction-manager 需要一个平台事务管理器-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="*" isolation="REPEATABLE_READ" timeout="3" read-only="false" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>
    <!-- 事务增强的aop -->
    <aop:config>
        <!--配置切点表达式-->
        <aop:pointcut id="myPointcut" expression="execution(void com.mem.service.impl.*.*(..))"/>
        <!--配置织入关系,通知是谁? spring提供好的-->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="myPointcut"/>
    </aop:config>
</beans>

替换成全注解方式,如下:

@Configuration
@ComponentScan("com.mem") // <context:component-scan base-package="com.mem"/>
@PropertySource("classpath:jdbc.properties")  // <context:property-placeholder location="classpath:jdbc.properties"/>
/**
 * <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
 *     <property name="basePackage" value="com.mem.mapper"/>
 * </bean>
 */
@MapperScan("com.mem.mapper")
@EnableTransactionManagement // <tx:annotation-driven transaction-manager="transactionManager"/>
public class AccountConfig {

    @Bean
    public DataSource dataSource(
            @Value("${jdbc.driver}") String driver,
            @Value("${jdbc.url}") String url,
            @Value("${jdbc.username}") String username,
            @Value("${jdbc.password}") String password
    ){
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(driver);
        dataSource.setUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        return dataSource;
    }

    @Bean
    public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        return sqlSessionFactoryBean;
    }

    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource){
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
        transactionManager.setDataSource(dataSource);
        return transactionManager;
    }
}

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

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

相关文章

深挖C++赋值

详解赋值 const int a 10; int b a;&a 0x000000b7c6afef34 {56496} &a 0x000000b7c6afef34 {10} 3. &b 0x000000b7c6afef54 {10} 总结&#xff1a; int a 10 是指在内存中&#xff08;栈&#xff09;中创建一个int &#xff08;4 byte&#xff09;大小的空间…

vue项目使用eslint+prettier管理项目格式化

代码格式化、规范化说明 使用eslintprettier进行格式化&#xff0c;vscode中需要安装插件ESLint、Prettier - Code formatter&#xff0c;且格式化程序选择为后者&#xff08;vue文件、js文件要分别设置&#xff09; 对于eslint规则&#xff0c;在格式化时不会全部自动调整&…

让空间计算触手可及,VR手套何以点石成金?

引言 如何让一位母亲与她去世的小女儿“重逢”&#xff1f;韩国MBC电视台《I Met You》节目实现了一个“不可能”心愿。 在空旷的绿幕中&#xff0c;母亲Jang Ji-sung透过VR头显&#xff0c;看到了三年前因白血病去世的女儿Nayeon。当她伸出双手&#xff0c;居然能摸到女儿的…

多模态简述

多模态学习概念 【多模态简述-哔哩哔哩】 https://b23.tv/UrUyfln 定义&#xff1a; 模态&#xff1a;事物表达或感知的方式 多模态&#xff1a;研究异构和相互连接数据的科学&#xff0c;涵盖了从原始的器官信号到抽象概念的多种模态 语音和语言是理解人物交互的关键模态&am…

RabbitMQ-死信队列(golang)

1、概念 死信&#xff08;Dead Letter&#xff09;&#xff0c;字面上可以理解为未被消费者成功消费的信息&#xff0c;正常来说&#xff0c;生产者将消息放入到队列中&#xff0c;消费者从队列获取消息&#xff0c;并进行处理&#xff0c;但是由于某种原因&#xff0c;队列中的…

第8章利用CSS制作导航菜单

8.1 水平顶部导航栏 8.1.1 简单水平导航栏的设计与实现 8.1.1.1导航栏的创建 <nav>标签是 HIML5 新增的文档结构标签&#xff0c;用于标记导航栏&#xff0c;以便后续与网站的其他内整合&#xff0c;所以常用<nav>标签在页面上创建导航栏菜单区域。 例如,在<na…

「人眼视觉不再是视频消费的唯一形式」丨智能编解码和 AI 视频生成专场回顾@RTE2024

你是否想过&#xff0c;未来你看到的电影预告片、广告&#xff0c;甚至新闻报道&#xff0c;都可能完全由 AI 生成&#xff1f; 在人工智能迅猛发展的今天&#xff0c;视频技术正经历着一场前所未有的变革。从智能编解码到虚拟数字人&#xff0c;再到 AI 驱动的视频生成&#…

C++:哈希拓展-位图

目录 一.问题导入 二.什么是位图? 2.1如何确定目标数在哪个比特位? 2.2如何存放高低位 2.3位图模拟代码实现 2.3.1如何标记一个数 2.3.2如何重置标记 2.3.3如何检查一个数是否被标记 整体代码实现 标准库的Bitset 库中的bitset的缺陷 简单应用 一.问题导入 这道…

nacos-operator在k8s集群上部署nacos-server2.4.3版本踩坑实录

文章目录 操作步骤1. 拉取仓库代码2. 安装nacos-operator3. 安装nacos-server 坑点一坑点二nacos-ui页面访问同一集群环境下微服务连接nacos地址配置待办参考文档 操作步骤 1. 拉取仓库代码 &#xff08;这一步主要用到代码中的相关yml文件&#xff0c;稍加修改用于部署容器&…

Python爬虫----python爬虫基础

一、python爬虫基础-爬虫简介 1、现实生活中实际爬虫有哪些&#xff1f; 2、什么是网络爬虫&#xff1f; 3、什么是通用爬虫和聚焦爬虫&#xff1f; 4、为什么要用python写爬虫程序 5、环境和工具 二、python爬虫基础-http协议和chrome抓包工具 1、什么是http和https协议…

从北美火到中国,大数据洞察品牌“STANLEY”的突围之路

保守直筒大头的“硬汉”外形&#xff0c;以百变颜色踩中时尚命脉&#xff0c;与各路大牌“梦幻联动”&#xff0c;不少时尚弄潮儿没能逃过其“真香”诱惑。 这就是今年以来从北美火到中国的STANLEY&#xff0c;在“巨无霸”水杯中突围出属于自己的一条路。 最近STANLEY又整活…

Java结合ElasticSearch根据查询关键字,高亮显示全文数据。

由于es高亮显示机制的问题。当全文内容过多&#xff0c;且搜索中标又少时&#xff0c;就会出现高亮结果无法覆盖全文。因此需要根据需求手动替换。 1.根据es的ik分词器获取搜索词的分词结果。 es部分&#xff1a; //中文分词解析 post /_analyze {"analyzer":"…

Python绘制雪花

文章目录 系列目录写在前面技术需求完整代码代码分析1. 代码初始化部分分析2. 雪花绘制核心逻辑分析3. 窗口保持部分分析4. 美学与几何特点总结 写在后面 系列目录 序号直达链接爱心系列1Python制作一个无法拒绝的表白界面2Python满屏飘字表白代码3Python无限弹窗满屏表白代码4…

Linux性能优化之火焰图简介

Linux 火焰图&#xff08;Flame Graph&#xff09;是一种可视化工具&#xff0c;用于分析程序性能问题&#xff0c;尤其是 CPU 使用情况。它展示了程序中函数调用的层次结构和各个调用栈占用的时间比例。 以下是详细介绍&#xff0c;包括火焰图的工作原理、生成步骤和实际使用中…

Axure设计之文本编辑器制作教程

文本编辑器是一个功能强大的工具&#xff0c;允许用户在图形界面中创建和编辑文本的格式和布局&#xff0c;如字体样式、大小、颜色、对齐方式等&#xff0c;在Web端实际项目中&#xff0c;文本编辑器的使用非常频繁。以下是在Axure中模拟web端富文本编辑器&#xff0c;来制作文…

Python中的正则表达式教程

一、 正则表达式基础 1。1。概念介绍 正则表达式是用于处理字符串的强大工具,它并不是Python的一部分。 其他编程语言中也有正则表达式的概念,区别只在于不同的编程语言实现支持的语法数量不同。 它拥有自己独特的语法以及一个独立的处理引擎&#xff0c;在提供了正则表达式…

脑机接口、嵌入式 AI 、工业级 MR、空间视频和下一代 XR 浏览器丨RTE2024 空间计算和新硬件专场回顾

这一轮硬件创新由 AI 引爆&#xff0c;或许最大受益者仍是 AI&#xff0c;因为只有硬件才能为 AI 直接获取最真实世界的数据。 在人工智能与硬件融合的新时代&#xff0c;实时互动技术正迎来前所未有的创新浪潮。从嵌入式系统到混合现实&#xff0c;从空间视频到脑机接口&…

Python爬虫下载新闻,Flask展现新闻(2)

上篇讲了用Python从新闻网站上下载新闻&#xff0c;本篇讲用Flask展现新闻。关于Flask安装网上好多教程&#xff0c;不赘述。下面主要讲 HTML-Flask-数据 的关系。 简洁版 如图&#xff0c;页面简单&#xff0c;主要显示新闻标题。 分页&#xff0c;使用最简单的分页技术&…

Linux下编译MFEM

本文记录在Linux下编译MFEM的过程。 零、环境 操作系统Ubuntu 22.04.4 LTSVS Code1.92.1Git2.34.1GCC11.4.0CMake3.22.1Boost1.74.0oneAPI2024.2.1 一、安装依赖 二、编译代码 附录I: CMakeUserPresets.json {"version": 4,"configurePresets": [{&quo…

Win10/11 安装使用 Neo4j Community Edition

如果你下载的是 Neo4j Community Edition 的压缩包&#xff0c;意味着你需要手动解压并配置 Neo4j。以下是详细的使用步骤&#xff1a; 0. 下载压缩包 访问Neo4j官网&#xff0c;找到 Community Edition 版本并选择 4.x 或者 5.x 下载&#xff1a;https://neo4j.com/deployme…