Sping全面复习

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 {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println(beanName + ":postProcessBeforeInitialization");return bean;}@Overridepublic 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;}@Overridepublic 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 mapperPUBLIC "-//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 configurationPUBLIC "-//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
{@Testpublic 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 mapperPUBLIC "-//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
注:MapperScannerConfigurer的作用是将Mapper的对象存储到Spring容器中

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());}
}
 @Testpublic 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 {@Beanpublic 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;}@Beanpublic 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 {@Overridepublic 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 {@Overridepublic String[] selectImports(AnnotationMetadata annotationMetadata) {// 获得指定类型注解的全部信息Map<String, Object> annotationAttributes = annotationMetadata.getAnnotationAttributes(ComponentScan.class.getName());// 获得全部信息中basePackages信息String[] basePackages = (String[]) annotationAttributes.get("basePackages");// 打印结果是com.itheimaSystem.out.println(basePackages[0]);return new String[]{User2.class.getName()};} 
}

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

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {@Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {// 使用给定的BeanDefinitionRegistry参数,手动注册BeanDefinitionBeanDefinition 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 {@Overridepublic void show1() {System.out.println("show1 ...");}@Overridepublic 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;@Overridepublic 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;}}@Overridepublic 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 {@Overridepublic void show1() {System.out.println("show1 ...");}@Overridepublic 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 {@Overridepublic void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {System.out.println("后置通知*****");}@Overridepublic 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 {@Overridepublic 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 {@Autowiredprivate AccountMapper accountMapper;@Overridepublic 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 {@Autowiredprivate 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.xsdhttp://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsdhttp://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 {@Beanpublic 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;}@Beanpublic SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();sqlSessionFactoryBean.setDataSource(dataSource);return sqlSessionFactoryBean;}@Beanpublic PlatformTransactionManager transactionManager(DataSource dataSource){DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();transactionManager.setDataSource(dataSource);return transactionManager;}
}

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

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

相关文章

【Linux学习】【Ubuntu入门】1-3 ubuntu连接USB设备

1.打开VMware&#xff0c;打开新建的虚拟机&#xff0c;插入U盘&#xff0c;可在弹出对话框进行选择USB连接到主机或连接到虚拟机。&#xff08;长时间未操作默认连接主机&#xff09; 2.若USB在连接主机的情况下&#xff0c;可通过右键点击右下角进行连接到虚拟机。 3.若已连接…

炼码LintCode--数据库--基础语法--刷题笔记_01

目录 炼码LintCode数据库入门级别的笔记未完待续~~~ 炼码LintCode 数据库 入门级别的笔记 笔记如下&#xff0c;把所有涉及到的入门级别的知识点简单总结了一下。 以及一点点举一反三的写法。 增 INSERT INTO 表名 (列1, 列2, ...) VALUES (值1, 值2, ...);批量增 INSERT INT…

docker:docker: Get https://registry-1.docker.io/v2/: net/http: request canceled

无数次的拉镜像让人崩溃&#xff1a; rootnode11:~/ragflow/docker# more rag.sh #export HTTP_PROXYhttp://192.168.207.127:7890 #export HTTPS_PROXYhttp://192.168.207.127:7890 #export NO_PROXYlocalhost,127.0.0.1,.aliyun.com docker compose -f docker-compose-gpu-C…

Flutter:使用Future发送网络请求

pubspec.yaml配置http的SDK cupertino_icons: ^1.0.8 http: ^1.2.2请求数据的格式转换 // Map 转 json final chat {name: 张三,message: 吃饭了吗, }; final chatJson json.encode(chat); print(chatJson);// json转Map final newChat json.decode(chatJson); print(newCha…

llama-cpp模型轻量化部署与量化

一、定义 定义配置环境遇到的问题&#xff0c;交互模式下模型一直输出&#xff0c;不会停止模型量化Qwen1.5-7B 案例demo 二、实现 定义 主要应用与cpu 上的部署框架。由c完成。配置环境 https://github.com/ggerganov/llama.cpp https://github.com/echonoshy/cgft-llm/blo…

阅读《当代反无人机系统技术综述》笔记

目录 文献基本信息 序言 一、关键技术 1.1射频(RF)分析仪 1.2雷达 1.3视觉传感器和图像处理 1.4声学传感器 二、发展趋势 文献基本信息 题名&#xff1a;当代反无人机系统技术综述 作者&#xff1a;蒋罗婷 来源&#xff1a;电子质量 发表时间&#xff1a;2023-02-2…

【Lucene】倒排表和词典:提升搜索效率的关键数据结构

倒排表和词典&#xff1a;提升搜索效率的关键数据结构 倒排表&#xff08;Inverted Index&#xff09;和词典&#xff08;Term Dictionary&#xff09;是 Lucene 中用于加速搜索的关键数据结构&#xff0c;它们帮助系统在庞大的文档集合中快速定位包含特定关键词的文档。以下是…

RN开发遇到的坑

1 、 RN 启动崩溃 https://blog.csdn.net/qq_31915745/article/details/108125671 2、修改报红⻚ https://blog.csdn.net/weixin_43969056/article/details/104757926 3 、编译不过去提示 glog-0.3.5 有问题&#xff0c; 找到 / 项⽬ /node_modules/react-native/scripts/ io…

基于Multisim信号波形发生器电路正弦波方波三角波锯齿波(含仿真和报告)

【全套资料.zip】正弦方波三角波锯齿波方波占空比可调频率可调电路Multisim仿真设计数字电子技术 文章目录 功能一、Multisim仿真源文件二、原理文档报告资料下载【Multisim仿真报告讲解视频.zip】 功能 1.设计一个能够产生多个信号输出的信号发生器&#xff0c; 要求输出波形…

GC9A01驱动移植(HALL库)

最近在做LVGL的移植&#xff0c;用到的屏幕驱动时GC9A01的&#xff0c;记录一下学习历程&#xff0c;防止日后遗忘。 这款屏幕我使用的是SPI协议&#xff0c;参考了部分稚辉菌大佬这个项目的程序&#xff1a; 【自制】我做了个能动的迷你电脑配件&#xff01;【软核】_哔哩哔…

PCIe板卡标准尺寸

一、板卡尺寸说明 两种PCIe外接卡的高度&#xff0c;即全高&#xff08;Standard height&#xff09;111.15 mm (4.376 inches)和半高&#xff08;half height&#xff09;68.90 mm (2.731 inches)&#xff1b; 两种PCIe外接卡的长度&#xff1a;全长&#xff08;full length&a…

docker 阿里云镜像加速

在阿里云首页点击产品-容器-容器镜像服务ACR 无需购买&#xff0c;直接进去控制台创建个人版 完成后点击镜像加速器 选择对应的系统&#xff0c;按照操作文档完成

KPaaS洞察|异构系统中用户角色与权限分类及管理解决方案

多个异构系统的使用已经成为企业常态。每个系统通常有自己独立的用户角色和权限设置&#xff0c;导致权限管理复杂且容易出现冲突。如何在多个异构系统中统一、有效地进行用户角色和权限管理&#xff0c;已成为企业保障数据安全和提升管理效率的关键挑战。通过集中式权限管理平…

食品配送管理系统(源码+文档+部署+讲解)

食品配送管理系统是成品商业化项目&#xff0c;系统可基于源码二开。 系统概述 餐饮食品配送&#xff0c;包含配送人APP、下单APP、管理端等&#xff0c;实现订餐、配餐&#xff0c;用于食品店、中央厨房等订餐、团餐业务 本项目名称为食品配送系统&#xff0c;是针对食品配…

领夹麦克风哪个品牌音质最好?无线领夹麦克风可以唱歌吗?

随着短视频和直播行业的蓬勃发展&#xff0c;无线领夹麦克风已成为内容创作者提升音质体验的关键一环。但遗憾的是&#xff0c;市场上充斥着太多夸大其词、华而不实的宣传&#xff0c;诸如“一键降噪”、“无损传输”等概念满天飞&#xff0c;让消费者难以分辨真伪。许多朋友在…

大模型学习笔记------BLIP模型的再思考

大模型学习笔记------BLIP模型的再思考 1、BLIP推理---如何进行“图生文”2、BLIP推理---如何进行视觉问答&#xff08;Visual Question Answering, VQA&#xff09;3、BLIP推理---如何进行图文检索&#xff08;Image-text retrieval&#xff09;任务4、总结 上一篇文章上文中讲…

c# 调用c++ 的dll 出现找不到函数入口点

今天在调用一个设备的dll文件时遇到了一点波折&#xff0c;因为多c 不熟悉&#xff0c;调用过程张出现了找不到函数入口点&#xff0c;一般我们使用c# 调用c 文件&#xff0c;还是比较简单。 [DllImport("AtnDll2.dll",CharSet CharSet.Ansi)]public static extern …

H5BuildX发行uniapp h5版本的正确姿势

在manifest.json中配置基础路径 在上传到服务器后&#xff0c;需要将打包后的文件夹修改为基础路径中相同的文件名 否则网页的css、js等资源文件会因为路径问题始终访问不了

C++(Qt)软件调试---符号转换工具cv2pdb (24)

C(Qt)软件调试—符号转换工具cv2pdb &#xff08;24&#xff09; 文章目录 C(Qt)软件调试---符号转换工具cv2pdb &#xff08;24&#xff09;[toc]1、概述&#x1f41c;2、下载cv2pdb&#x1fab2;3、qt程序使用cv2pdb&#x1f9a7;1.1 方法1&#xff1a;命令行1.2 方法2&#…

MySQL技巧之跨服务器数据查询:高级篇-先调用A数据库的MySql存储过程再复制到B数据库的表中

MySQL技巧之跨服务器数据查询&#xff1a;高级篇-先调用A数据库的MySql存储过程再复制到B数据库的表中 基础篇已经描述&#xff1a;借用微软的SQL Server ODBC 即可实现MySQL跨服务器间的数据查询。 而且还介绍了如何获得一个在MS SQL Server 可以连接指定实例的MySQL数据库的…