Spring02
文章目录
- 1. IOC/DI注解开发
- 2. IOC/DI注解开发管理第三方bean
- 3. Spring整合
- 4. AOP简介
- 5. AOP入门案例
- 6. AOP工作流程
- 7. AOP配置管理
- 8. AOP事务管理
1. IOC/DI注解开发
注解开发定义bean用的是2.5版提供的注解,纯注解开发用的是3.0版提供的注解
pom.xml添加依赖
<dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.2.10.RELEASE</version></dependency>
</dependencies>
-
注解开发定义bean
-
第一步:Dao上添加注解
在BookDaoImpl类上添加@Component注解@Component("bookDao") public class BookDaoImpl implements BookDao {public void save() {System.out.println("book dao save ..." );} }注意:@Component注解不可以添加在接口上,因为接口是无法创建对象的
XML与注解配置的对应关系:

-
第二步:配置Spring的注解包扫描
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><context:component-scan base-package="com.itheima"/> </beans>说明:
component-scan
- component:组件,Spring将管理的bean视作自己的一个组件
- scan:扫描
base-package指定Spring框架扫描的包路径,它会扫描指定包及其子包中的所有类上的注解。
- 包路径越多[如:com.itheima.dao.impl],扫描的范围越小速度越快
- 包路径越少[如:com.itheima],扫描的范围越大速度越慢
- 一般扫描到项目的组织名称即Maven的groupId下[如:com.itheima]即可
-
其他
(1)@Component注解如果不起名称,会有一个默认值就是当前类名首字母小写,所以也可以按照名称获取
(2)对于@Component注解,还衍生出了其他三个注解@Controller、@Service、@Repository,这三个注解和@Component注解的作用是一样的,方便我们后期在编写类的时候能很好的区分出这个类是属于表现层、业务层还是数据层的类。
-
-
纯注解开发模式
- 步骤1:创建配置类
创建一个配置类SpringConfig@Configuration//在配置类上添加`@Configuration`注解,将其标识为一个配置类,替换`applicationContext.xml` @ComponentScan("com.itheima")//在配置类上添加包扫描注解`@ComponentScan`替换`<context:component-scan base-package=""/>` public class SpringConfig { }//@ComponentScan注解用于设定扫描路径,此注解只能添加一次,多个数据请用数组格式 //@ComponentScan({com.itheima.service","com.itheima.dao"}) - 步骤4:创建运行类并执行
public class AppForAnnotation {public static void main(String[] args) {ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);BookDao bookDao = (BookDao) ctx.getBean("bookDao");System.out.println(bookDao);BookService bookService = ctx.getBean(BookService.class);System.out.println(bookService);} }
- 步骤1:创建配置类
-
注解开发bean作用范围与生命周期管理
-
Bean的作用范围
要想将BookDaoImpl变成非单例,只需要在其类上添加@scope注解@Repository //@Scope设置bean的作用范围 @Scope("prototype") public class BookDaoImpl implements BookDao {public void save() {System.out.println("book dao save ...");} } -
Bean的生命周期
(1)在BookDaoImpl中添加两个方法,init和destroy,方法名可以任意
(2)只需要在对应的方法上添加@PostConstruct和@PreDestroy注解即可@Repository public class BookDaoImpl implements BookDao {public void save() {System.out.println("book dao save ...");}@PostConstruct //在构造方法之后执行,替换 init-methodpublic void init() {System.out.println("init ...");}@PreDestroy //在销毁方法之前执行,替换 destroy-methodpublic void destroy() {System.out.println("destroy ...");} }(3)要想看到两个方法执行,需要注意的是
destroy只有在容器关闭的时候,才会执行,所以需要修改App的类public class App {public static void main(String[] args) {AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);BookDao bookDao1 = ctx.getBean(BookDao.class);BookDao bookDao2 = ctx.getBean(BookDao.class);System.out.println(bookDao1);System.out.println(bookDao2);ctx.close(); //关闭容器} }
注意:@PostConstruct和@PreDestroy注解如果找不到,需要导入下面的jar包
<dependency><groupId>javax.annotation</groupId><artifactId>javax.annotation-api</artifactId><version>1.3.2</version> </dependency>找不到的原因是,从JDK9以后jdk中的javax.annotation包被移除了,这两个注解刚好就在这个包中
-

- 注解开发依赖注入
-
注解实现按照类型注入
在属性上添加@Autowired注解@Service public class BookServiceImpl implements BookService {@Autowiredprivate BookDao bookDao;// public void setBookDao(BookDao bookDao) { // this.bookDao = bookDao; // }public void save() {System.out.println("book service save ...");bookDao.save();} }注意:
- @Autowired可以写在属性上,也可也写在setter方法上,最简单的处理方式是
写在属性上并将setter方法删除掉 - 自动装配基于反射设计创建对象并通过暴力反射为私有属性进行设值, 所以此处无需提供setter方法
- @Autowired默认按照类型自动装配,如果IOC容器中同类的Bean找到多个,就按照变量名和Bean的名称匹配
- @Autowired可以写在属性上,也可也写在setter方法上,最简单的处理方式是
-
注解实现按照名称注入
@Service public class BookServiceImpl implements BookService {@Autowired@Qualifier("bookDao1")private BookDao bookDao;public void save() {System.out.println("book service save ...");bookDao.save();} }(1)@Qualifier注解后的值就是需要注入的bean的名称
(2)@Qualifier不能独立使用,必须和@Autowired一起使用 -
简单数据类型注入
使用@Value注解,将值写入注解的参数中@Repository("bookDao") public class BookDaoImpl implements BookDao {@Value("itheima")private String name;public void save() {System.out.println("book dao save ..." + name);} } -
注解读取properties配置文件
@Value一般会被用在从properties配置文件中读取内容进行使用
(1)jdbc.propertiesname=itheima888(2)在配置类上添加
@PropertySource注解@Configuration @ComponentScan("com.itheima") @PropertySource("jdbc.properties") public class SpringConfig { }(3)使用@Value读取配置文件中的内容
@Repository("bookDao") public class BookDaoImpl implements BookDao {@Value("${name}")private String name;public void save() {System.out.println("book dao save ..." + name);} }- 如果读取的properties配置文件有多个,可以使用
@PropertySource的属性来指定多个@PropertySource({"jdbc.properties","xxx.properties"}) @PropertySource注解属性中不支持使用通配符*,运行会报错@PropertySource({"*.properties"})@PropertySource注解属性中可以把classpath:加上,代表从当前项目的根路径找文件@PropertySource({"classpath:jdbc.properties"})
- 如果读取的properties配置文件有多个,可以使用
-
2. IOC/DI注解开发管理第三方bean
-
不引用外部配置类
(1)在配置类中添加一个方法,在方法上添加@Bean注解@Configuration public class SpringConfig {@Beanpublic DataSource dataSource(){DruidDataSource ds = new DruidDataSource();ds.setDriverClassName("com.mysql.jdbc.Driver");ds.setUrl("jdbc:mysql://localhost:3306/spring_db");ds.setUsername("root");ds.setPassword("root");return ds;} } //不能使用`DataSource ds = new DruidDataSource()`, //因为DataSource接口中没有对应的setter方法来设置属性 //如果有多个bean要被Spring管理,直接在配置类中多些几个方法,方法上添加@Bean注解即可。(2)从IOC容器中获取对象并打印
public class App {public static void main(String[] args) {AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);DataSource dataSource = ctx.getBean(DataSource.class);System.out.println(dataSource);} } -
引入外部配置类
对于数据源的bean,我们新建一个JdbcConfig配置类,并把数据源配置到该类下。public class JdbcConfig {@Beanpublic DataSource dataSource(){DruidDataSource ds = new DruidDataSource();ds.setDriverClassName("com.mysql.jdbc.Driver");ds.setUrl("jdbc:mysql://localhost:3306/spring_db");ds.setUsername("root");ds.setPassword("root");return ds;} }配置类如何能被Spring配置类加载到,并创建DataSource对象在IOC容器中?
-
方式一:使用包扫描引入
步骤1:在Spring的配置类上添加包扫描@Configuration @ComponentScan("com.itheima.config") public class SpringConfig {}步骤2:在JdbcConfig上添加配置注解
JdbcConfig类要放入到com.itheima.config包下,需要被Spring的配置类扫描到即可@Configuration public class JdbcConfig {@Beanpublic DataSource dataSource(){DruidDataSource ds = new DruidDataSource();ds.setDriverClassName("com.mysql.jdbc.Driver");ds.setUrl("jdbc:mysql://localhost:3306/spring_db");ds.setUsername("root");ds.setPassword("root");return ds;} } -
方式二:使用
@Import引入
这种方案可以不用加@Configuration注解,但是必须在Spring配置类上使用@Import注解手动引入需要加载的配置类
在Spring配置类中引入@Configuration //@ComponentScan("com.itheima.config") @Import({JdbcConfig.class}) public class SpringConfig {}注意:
- 扫描注解可以移除
- @Import参数需要的是一个数组,可以引入多个配置类。
- @Import注解在配置类中只能写一次
-
-
注解开发实现为第三方bean注入资源
- 简单数据类型
类中提供四个属性,使用@Value注解引入值
现在的数据库连接四要素还是写在代码中,需要做的是将这些内容提取到jdbc.properties配置文件:运用注解读取properties配置文件的知识public class JdbcConfig {@Value("com.mysql.jdbc.Driver")private String driver;@Value("jdbc:mysql://localhost:3306/spring_db")private String url;@Value("root")private String userName;@Value("password")private String password;@Beanpublic DataSource dataSource(){DruidDataSource ds = new DruidDataSource();ds.setDriverClassName(driver);ds.setUrl(url);ds.setUsername(userName);ds.setPassword(password);return ds;} } - 引用数据类型
(1)在SpringConfig中扫描BookDao
扫描的目的是让Spring能管理到BookDao,也就是说要让IOC容器中有一个bookDao对象
(2)在JdbcConfig类的方法上添加参数@Configuration @ComponentScan("com.itheima.dao") @Import({JdbcConfig.class}) public class SpringConfig { }
引用类型注入只需要为bean定义方法设置形参即可,容器会根据类型自动装配对象@Bean public DataSource dataSource(BookDao bookDao){System.out.println(bookDao);DruidDataSource ds = new DruidDataSource();ds.setDriverClassName(driver);ds.setUrl(url);ds.setUsername(userName);ds.setPassword(password);return ds; }
- 简单数据类型
对比XML配置和注解的开发实现:

3. Spring整合
-
Spring整合Mybatis
-
步骤1:项目中导入整合需要的jar包
<dependency><!--Spring操作数据库需要该jar包--><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>5.2.10.RELEASE</version> </dependency> <dependency><!--Spring与Mybatis整合的jar包这个jar包mybatis在前面,是Mybatis提供的--><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId><version>1.3.0</version> </dependency> -
步骤2:创建Spring的主配置类
//配置类注解 @Configuration //包扫描,主要扫描的是项目中的AccountServiceImpl类 @ComponentScan("com.itheima") public class SpringConfig { } -
步骤3:创建数据源的配置类
在配置类中完成数据源的创建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;@Beanpublic DataSource dataSource(){DruidDataSource ds = new DruidDataSource();ds.setDriverClassName(driver);ds.setUrl(url);ds.setUsername(userName);ds.setPassword(password);return ds;} } -
步骤4:主配置类中读properties并引入数据源配置类
@Configuration @ComponentScan("com.itheima") @PropertySource("classpath:jdbc.properties") @Import(JdbcConfig.class) public class SpringConfig { } -
步骤5:创建Mybatis配置类并配置SqlSessionFactory
public class MybatisConfig {//定义bean,SqlSessionFactoryBean,用于产生SqlSessionFactory对象@Beanpublic SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();//设置模型类的别名扫描ssfb.setTypeAliasesPackage("com.itheima.domain");//设置数据源ssfb.setDataSource(dataSource);return ssfb;}//定义bean,返回MapperScannerConfigurer对象@Beanpublic MapperScannerConfigurer mapperScannerConfigurer(){MapperScannerConfigurer msc = new MapperScannerConfigurer();msc.setBasePackage("com.itheima.dao");return msc;} }说明:
-
使用SqlSessionFactoryBean封装SqlSessionFactory需要的环境信息

- SqlSessionFactoryBean是前面我们讲解FactoryBean的一个子类,在该类中将SqlSessionFactory的创建进行了封装,简化对象的创建,我们只需要将其需要的内容设置即可。
- 方法中有一个参数为dataSource,当前Spring容器中已经创建了Druid数据源,类型刚好是DataSource类型,此时在初始化SqlSessionFactoryBean这个对象的时候,发现需要使用DataSource对象,而容器中刚好有这么一个对象,就自动加载了DruidDataSource对象。
-
使用MapperScannerConfigurer加载Dao接口,创建代理对象保存到IOC容器中
- 这个MapperScannerConfigurer对象也是MyBatis提供的专用于整合的jar包中的类,用来处理原始配置文件中的mappers相关配置,加载数据层的Mapper接口类
- MapperScannerConfigurer有一个核心属性basePackage,就是用来设置所扫描的包路径
-
-
步骤6:主配置类中引入Mybatis配置类
@Configuration @ComponentScan("com.itheima") @PropertySource("classpath:jdbc.properties") @Import({JdbcConfig.class,MybatisConfig.class}) public class SpringConfig { } -
步骤7:编写运行类
在运行类中,从IOC容器中获取Service对象,调用方法获取结果public class App2 {public static void main(String[] args) {ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);AccountService accountService = ctx.getBean(AccountService.class);Account ac = accountService.findById(1);System.out.println(ac);} } -
步骤8:运行程序

支持Spring与Mybatis的整合就已经完成了,其中主要用到的两个类分别是:
- SqlSessionFactoryBean
- MapperScannerConfigurer
-
-
Spring整合Junit
-
步骤1:引入依赖
<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> -
步骤2:编写测试类
//设置类运行器 @RunWith(SpringJUnit4ClassRunner.class) //设置Spring环境对应的配置类 @ContextConfiguration(classes = {SpringConfiguration.class}) //加载配置类 //@ContextConfiguration(locations={"classpath:applicationContext.xml"})//加载配置文件 public class AccountServiceTest {//支持自动装配注入bean@Autowiredprivate AccountService accountService;@Testpublic void testFindById(){System.out.println(accountService.findById(1));}@Testpublic void testFindAll(){System.out.println(accountService.findAll());} }注意:
- 单元测试,如果测试的是注解配置类,则使用
@ContextConfiguration(classes = 配置类.class) - 单元测试,如果测试的是配置文件,则使用
@ContextConfiguration(locations={配置文件名,...})
- 单元测试,如果测试的是注解配置类,则使用
-
4. AOP简介
(1)AOP(Aspect Oriented Programming)面向切面编程,一种编程范式,指导开发者如何组织程序结构
(2)AOP作用:在不惊动原始设计的基础上为其进行功能增强(无入侵式/无侵入式)
-
AOP核心概念

(1)前面一直在强调,Spring的AOP是对一个类的方法在不进行任何修改的前提下实现增强。对于上面的案例中BookServiceImpl中有save,update,delete和select方法,这些方法我们给起了一个名字叫连接点(2)在BookServiceImpl的四个方法中,
update和delete只有打印没有计算万次执行消耗时间,但是在运行的时候已经有该功能,那也就是说update和delete方法都已经被增强,所以对于需要增强的方法我们给起了一个名字叫切入点(3)执行BookServiceImpl的update和delete方法的时候都被添加了一个计算万次执行消耗时间的功能,将这个功能抽取到一个方法中,换句话说就是存放共性功能的方法,我们给起了个名字叫通知
(4)通知是要增强的内容,会有多个,切入点是需要被增强的方法,也会有多个,那哪个切入点需要添加哪个通知,就需要提前将它们之间的关系描述清楚,那么对于通知和切入点之间的关系描述,我们给起了个名字叫切面
(5)通知是一个方法,方法不能独立存在需要被写在一个类中,这个类我们也给起了个名字叫通知类
- 连接点(JoinPoint):程序执行过程中的任意位置,粒度为执行方法、抛出异常、设置变量等
- 在SpringAOP中,理解为方法的执行
- 切入点(Pointcut):匹配连接点的式子
- 在SpringAOP中,一个切入点可以描述一个具体方法,也可也匹配多个方法
- 一个具体的方法:如com.itheima.dao包下的BookDao接口中的无形参无返回值的save方法
- 匹配多个方法:所有的save方法,所有的get开头的方法,所有以Dao结尾的接口中的任意方法,所有带有一个参数的方法
- 连接点范围要比切入点范围大,是切入点的方法也一定是连接点,但是是连接点的方法就不一定要被增强,所以可能不是切入点。
- 在SpringAOP中,一个切入点可以描述一个具体方法,也可也匹配多个方法
- 通知(Advice):在切入点处执行的操作,也就是共性功能
- 在SpringAOP中,功能最终以方法的形式呈现
- 通知类:定义通知的类
- 切面(Aspect):描述通知与切入点的对应关系
- 连接点(JoinPoint):程序执行过程中的任意位置,粒度为执行方法、抛出异常、设置变量等
5. AOP入门案例
需求为:使用SpringAOP的注解方式完成在方法执行的前打印出当前系统时间
-
步骤1:添加依赖
pom.xml<dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.4</version> </dependency>因为
spring-context中已经导入了spring-aop,所以不需要再单独导入spring-aop -
步骤2:定义通知类和通知
通知就是将共性功能抽取出来后形成的方法,共性功能指的就是当前系统时间的打印public class MyAdvice {public void method(){System.out.println(System.currentTimeMillis());} }类名和方法名没有要求,可以任意
-
步骤3:定义切入点
切入点定义依托一个不具有实际意义的方法进行,即无参数、无返回值、方法体无实际逻辑public class MyAdvice {@Pointcut("execution(void com.itheima.dao.BookDao.update())") //设置切入点方法private void pt(){}public void method(){System.out.println(System.currentTimeMillis());} } -
步骤4:制作切面
切面是用来描述通知和切入点之间的关系public class MyAdvice {@Pointcut("execution(void com.itheima.dao.BookDao.update())")private void pt(){}//设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法前运行@Before("pt()")public void method(){System.out.println(System.currentTimeMillis());} } -
步骤5:将通知类配给容器并标识其为切面类
@Component @Aspect //设置当前类为AOP切面类 public class MyAdvice {@Pointcut("execution(void com.itheima.dao.BookDao.update())")private void pt(){}@Before("pt()")public void method(){System.out.println(System.currentTimeMillis());} } -
步骤6:开启注解格式AOP功能
@Configuration @ComponentScan("com.itheima") @EnableAspectJAutoProxy //开启注解格式AOP功能 public class SpringConfig { }
6. AOP工作流程
-
流程1:Spring容器启动
- 容器启动就需要去加载bean,哪些类需要被加载呢?
- 需要被增强的类,如:BookServiceImpl
- 通知类,如:MyAdvice
- 注意此时bean对象还没有创建成功
-
流程2:读取所有切面配置中的切入点

上面这个例子中有两个切入点的配置,但是第一个ptx()并没有被使用,所以不会被读取 -
流程3:初始化bean
- 匹配失败,创建原始对象
- 匹配失败说明不需要增强,直接调用原始对象的方法即可。
- 匹配成功,创建原始对象(目标对象)的代理对象
- 匹配成功说明需要对其进行增强
- 对哪个类做增强,这个类对应的对象就叫做目标对象
- 因为要对目标对象进行功能增强,而采用的技术是动态代理,所以会为其创建一个代理对象
- 最终运行的是代理对象的方法,在该方法中会对原始方法进行功能增强
- 匹配失败,创建原始对象
-
流程4:获取bean执行方法
- 获取的bean是原始对象时,调用方法并执行,完成操作
- 获取的bean是代理对象时,根据代理对象的运行模式运行原始方法与增强的内容,完成操作
-
AOP核心概念
- 目标对象(Target):原始功能去掉共性功能对应的类产生的对象,这种对象是无法直接完成最终工作的
- 代理(Proxy):目标对象无法直接完成工作,需要对其进行功能回填,通过原始对象的代理对象实现
SpringAOP的本质或者可以说底层实现是通过代理模式
7. AOP配置管理
-
AOP切入点表达式
切入点表达式:要进行增强的方法的描述方式

描述方式一:(接口)执行com.itheima.dao包下的BookDao接口中的无参数update方法execution(void com.itheima.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:动作关键字,描述切入点的行为动作,例如execution表示执行到指定切入点
- public:访问修饰符,还可以是public,private等,可以省略
- User:返回值,写返回值类型
- com.itheima.service:包名,多级包使用点连接
- UserService:类/接口名称
- findById:方法名
- int:参数,直接写参数的类型,多个类型用逗号隔开
- 异常名:方法定义中抛出指定异常,可以省略
-
通配符
作用:简化配置-
*:单个独立的任意符号,可以独立出现,也可以作为前缀或者后缀的匹配符出现execution(public * com.itheima.*.UserService.find*(*))匹配com.itheima包下的任意包中的UserService类或接口中所有find开头的带有一个参数的方法
-
..:多个连续的任意符号,可以独立出现,常用于简化包名与参数的书写execution(public User com..UserService.findById(..))匹配com包下的任意包中的UserService类或接口中所有名称为findById的方法
-
+:专用于匹配子类类型execution(* *..*Service+.*(..))这个使用率较低,描述子类的,做JavaEE开发,继承机会就一次,使用都很慎重,所以很少用它。*Service+,表示所有以Service结尾的接口的子类。
-
-
书写技巧
- 所有代码按照标准规范开发,否则以下技巧全部失效
- 描述切入点通常描述接口,而不描述实现类,如果描述到实现类,就出现紧耦合了
- 访问控制修饰符针对接口开发均采用public描述(可省略访问控制修饰符描述)
- 返回值类型对于增删改类使用精准类型加速匹配,对于查询类使用*通配快速描述
- 包名书写尽量不使用..匹配,效率过低,常用*做单个包描述匹配,或精准匹配
- 接口名/类名书写名称与模块相关的采用*匹配,例如UserService书写成*Service,绑定业务层接口名
- 方法名书写以动词进行精准匹配,名词采用匹配,例如getById书写成getBy,selectAll书写成selectAll
- 参数规则较为复杂,根据业务方法灵活调整
- 通常不使用异常作为匹配规则
-
-
AOP通知类型
5种通知类型-
前置通知
追加功能到方法执行前@Component @Aspect public class MyAdvice {@Pointcut("execution(void com.itheima.dao.BookDao.update())")private void pt(){}@Before("pt()")//此处也可以写成 @Before("MyAdvice.pt()"),不建议public void before() {System.out.println("before advice ...");} } -
后置通知
追加功能到方法执行后,不管方法执行的过程中有没有抛出异常都会执行@Component @Aspect public class MyAdvice {@Pointcut("execution(void com.itheima.dao.BookDao.update())")private void pt(){}@After("pt()")public void after() {System.out.println("after advice ...");} } -
环绕通知(重点)
可以追加功能到方法执行的前后@Component @Aspect public class MyAdvice {@Pointcut("execution(void com.itheima.dao.BookDao.update())")private void pt(){}@Pointcut("execution(int com.itheima.dao.BookDao.select())")private void pt2(){}@Around("pt2()")public Object aroundSelect(ProceedingJoinPoint pjp) throws Throwable {System.out.println("around before advice ...");//表示对原始操作的调用Object ret = pjp.proceed();System.out.println("around after advice ...");return ret;} }注意事项:
- 环绕通知必须依赖形参ProceedingJoinPoint才能实现对原始方法的调用,进而实现原始方法调用前后同时添加通知
- 通知中如果未使用ProceedingJoinPoint对原始方法进行调用将跳过原始方法的执行
- 对原始方法的调用可以不接收返回值,通知方法设置成void即可,如果接收返回值,最好设定为Object类型
- 原始方法的返回值如果是void类型,通知方法的返回值类型可以设置成void,也可以设置成Object
- 由于无法预知原始方法运行后是否会抛出异常,因此环绕通知方法必须要处理Throwable异常
-
返回后通知(了解)
追加功能到方法执行后,只有方法正常执行结束后才进行@Component @Aspect public class MyAdvice {@Pointcut("execution(void com.itheima.dao.BookDao.update())")private void pt(){}@Pointcut("execution(int com.itheima.dao.BookDao.select())")private void pt2(){}@AfterReturning("pt2()")public void afterReturning() {System.out.println("afterReturning advice ...");} } -
抛出异常后通知(了解)
追加功能到方法抛出异常后,只有方法执行出异常才进行@Component @Aspect public class MyAdvice {@Pointcut("execution(void com.itheima.dao.BookDao.update())")private void pt(){}@Pointcut("execution(int com.itheima.dao.BookDao.select())")private void pt2(){}@AfterReturning("pt2()")public void afterThrowing() {System.out.println("afterThrowing advice ...");} }
-
-
业务层接口执行效率-案例
-
需求:任意业务层接口执行均可显示其执行效率(执行时长)
-
步骤1:开启SpringAOP的注解功能
在Spring的主配置文件SpringConfig类中添加注解@EnableAspectJAutoProxy -
步骤2:创建AOP的通知类
- 该类要被Spring管理,需要添加@Component
- 要标识该类是一个AOP的切面类,需要添加@Aspect
- 配置切入点表达式,需要添加一个方法,并添加@Pointcut
@Component @Aspect public class ProjectAdvice {//配置业务层的所有方法@Pointcut("execution(* com.itheima.service.*Service.*(..))")private void servicePt(){}public void runSpeed(){} } -
步骤3:添加环绕通知
在runSpeed()方法上添加@Around
@Component @Aspect public class ProjectAdvice {//配置业务层的所有方法@Pointcut("execution(* com.itheima.service.*Service.*(..))")private void servicePt(){}//@Around("ProjectAdvice.servicePt()") 可以简写为下面的方式@Around("servicePt()")public Object runSpeed(ProceedingJoinPoint pjp){Object ret = pjp.proceed();return ret;} } -
步骤4:完成核心业务,记录万次执行的时间
@Component @Aspect public class ProjectAdvice {//配置业务层的所有方法@Pointcut("execution(* com.itheima.service.*Service.*(..))")private void servicePt(){}//@Around("ProjectAdvice.servicePt()") 可以简写为下面的方式@Around("servicePt()")public void runSpeed(ProceedingJoinPoint pjp){long start = System.currentTimeMillis();for (int i = 0; i < 10000; i++) {pjp.proceed();}long end = System.currentTimeMillis();System.out.println("业务层接口万次执行时间: "+(end-start)+"ms");} } -
步骤5:程序优化
@Component @Aspect public class ProjectAdvice {//配置业务层的所有方法@Pointcut("execution(* com.itheima.service.*Service.*(..))")private void servicePt(){}//@Around("ProjectAdvice.servicePt()") 可以简写为下面的方式@Around("servicePt()")public void runSpeed(ProceedingJoinPoint pjp){//获取执行签名信息Signature signature = pjp.getSignature();//通过签名获取执行操作名称(接口名)String className = signature.getDeclaringTypeName();//通过签名获取执行操作名称(方法名)String methodName = signature.getName();long start = System.currentTimeMillis();for (int i = 0; i < 10000; i++) {pjp.proceed();}long end = System.currentTimeMillis();System.out.println("万次执行:"+ className+"."+methodName+"---->" +(end-start) + "ms");} }
-
-
AOP通知获取数据
- 获取切入点方法的参数,所有的通知类型都可以获取参数
- JoinPoint:适用于前置、后置、返回后、抛出异常后通知
- ProceedingJoinPoint:适用于环绕通知
- 获取切入点方法返回值,前置和抛出异常后通知是没有返回值,后置通知可有可无,所以不做研究
- 返回后通知
- 环绕通知
- 获取切入点方法运行异常信息,前置和返回后通知是不会有,后置通知可有可无,所以不做研究
- 抛出异常后通知
- 环绕通知
-
获取参数
-
非环绕通知获取方式
在方法上添加JoinPoint,通过JoinPoint来获取参数@Component @Aspect public class MyAdvice {@Pointcut("execution(* com.itheima.dao.BookDao.findName(..))")private void pt(){}@Before("pt()")public void before(JoinPoint jp) Object[] args = jp.getArgs();System.out.println(Arrays.toString(args));System.out.println("before advice ..." );}//...其他的略 } -
环绕通知获取方式
环绕通知使用的是ProceedingJoinPoint,因为ProceedingJoinPoint是JoinPoint类的子类,所以对于ProceedingJoinPoint类中应该也会有对应的getArgs()方法@Component @Aspect public class MyAdvice {@Pointcut("execution(* com.itheima.dao.BookDao.findName(..))")private void pt(){}@Around("pt()")public Object around(ProceedingJoinPoint pjp)throws Throwable {Object[] args = pjp.getArgs();System.out.println(Arrays.toString(args));Object ret = pjp.proceed();return ret;}//其他的略 } -
pjp.proceed()方法是有两个构造方法,分别是:

-
调用无参数的proceed,当原始方法有参数,会在调用的过程中自动传入参数
-
所以调用这两个方法的任意一个都可以完成功能
-
但是当需要修改原始方法的参数时,就只能采用带有参数的方法,如下:
@Component @Aspect public class MyAdvice {@Pointcut("execution(* com.itheima.dao.BookDao.findName(..))")private void pt(){}@Around("pt()")public Object around(ProceedingJoinPoint pjp) throws Throwable{Object[] args = pjp.getArgs();System.out.println(Arrays.toString(args));args[0] = 666;Object ret = pjp.proceed(args);return ret;}//其他的略 }有了这个特性后,我们就可以在环绕通知中对原始方法的参数进行拦截过滤,避免由于参数的问题导致程序无法正确运行,保证代码的健壮性。
-
-
-
获取返回值
-
环绕通知获取返回值
@Component @Aspect public class MyAdvice {@Pointcut("execution(* com.itheima.dao.BookDao.findName(..))")private void pt(){}@Around("pt()")public Object around(ProceedingJoinPoint pjp) throws Throwable{Object[] args = pjp.getArgs();System.out.println(Arrays.toString(args));args[0] = 666;Object ret = pjp.proceed(args);return ret;}//其他的略 }上述代码中,
ret就是方法的返回值,我们是可以直接获取,不但可以获取,如果需要还可以进行修改。 -
返回后通知获取返回值
@Component @Aspect public class MyAdvice {@Pointcut("execution(* com.itheima.dao.BookDao.findName(..))")private void pt(){}@AfterReturning(value = "pt()",returning = "ret")public void afterReturning(Object ret) {System.out.println("afterReturning advice ..."+ret);}//其他的略 }注意:
(1)参数名的问题

(2)afterReturning方法参数类型的问题
参数类型可以写成String,但是为了能匹配更多的参数类型,建议写成Object类型
(3)afterReturning方法参数的顺序问题

-
-
获取异常
-
环绕通知获取异常
只需要将异常捕获,就可以获取到原始方法的异常信息
@Component @Aspect public class MyAdvice {@Pointcut("execution(* com.itheima.dao.BookDao.findName(..))")private void pt(){}@Around("pt()")public Object around(ProceedingJoinPoint pjp){Object[] args = pjp.getArgs();System.out.println(Arrays.toString(args));args[0] = 666;Object ret = null;try{ret = pjp.proceed(args);}catch(Throwable t){t.printStackTrace();}return ret;}//其他的略 } -
抛出异常后通知获取异常
@Component @Aspect public class MyAdvice {@Pointcut("execution(* com.itheima.dao.BookDao.findName(..))")private void pt(){}@AfterThrowing(value = "pt()",throwing = "t")public void afterThrowing(Throwable t) {System.out.println("afterThrowing advice ..."+t);}//其他的略 }
-
- 获取切入点方法的参数,所有的通知类型都可以获取参数
-
密码数据兼容处理-案例
-
需求是使用AOP将参数进行统一处理,不管输入的密码
root前后包含多少个空格,最终控制台打印的都是true -
步骤1:开启SpringAOP的注解功能
@Configuration @ComponentScan("com.itheima") @EnableAspectJAutoProxy public class SpringConfig { } -
步骤2:编写通知类
@Component @Aspect public class DataAdvice {@Pointcut("execution(boolean com.itheima.service.*Service.*(*,*))")private void servicePt(){}} -
步骤3:添加环绕通知
@Component @Aspect public class DataAdvice {@Pointcut("execution(boolean com.itheima.service.*Service.*(*,*))")private void servicePt(){}@Around("DataAdvice.servicePt()")// @Around("servicePt()")这两种写法都对public Object trimStr(ProceedingJoinPoint pjp) throws Throwable {Object ret = pjp.proceed();return ret;}} -
步骤4:完成核心业务,处理参数中的空格
@Component @Aspect public class DataAdvice {@Pointcut("execution(boolean com.itheima.service.*Service.*(*,*))")private void servicePt(){}@Around("DataAdvice.servicePt()")// @Around("servicePt()")这两种写法都对public Object trimStr(ProceedingJoinPoint pjp) throws Throwable {//获取原始方法的参数Object[] args = pjp.getArgs();for (int i = 0; i < args.length; i++) {//判断参数是不是字符串if(args[i].getClass().equals(String.class)){args[i] = args[i].toString().trim();}}//将修改后的参数传入到原始方法的执行中Object ret = pjp.proceed(args);return ret;}}
-
8. AOP事务管理
-
Spring事务简介
-
事务作用:在数据层保障一系列的数据库操作同成功同失败
-
Spring事务作用:在数据层或**业务层**保障一系列的数据库操作同成功同失败
-
Spring为了管理事务,提供了一个平台事务管理器
PlatformTransactionManager

PlatformTransactionManager只是一个接口,Spring还为其提供了一个具体的实现:

从名称上可以看出,我们只需要给它一个DataSource对象,它就可以帮你去在业务层管理事务。其内部采用的是JDBC的事务。所以说如果你持久层采用的是JDBC相关的技术,就可以采用这个事务管理器来管理你的事务。而Mybatis内部采用的就是JDBC的事务,所以后期我们Spring整合Mybatis就采用的这个DataSourceTransactionManager事务管理器 -
案例:转账
需求: 实现任意两个账户间转账操作
Spring事务管理具体的实现步骤为:-
步骤1:在需要被事务管理的方法上添加注解
public interface AccountService {/*** 转账操作* @param out 传出方* @param in 转入方* @param money 金额*///配置当前接口方法具有事务public void transfer(String out,String in ,Double money) ; }@Service public class AccountServiceImpl implements AccountService {@Autowiredprivate AccountDao accountDao;@Transactionalpublic void transfer(String out,String in ,Double money) {accountDao.outMoney(out,money);int i = 1/0;accountDao.inMoney(in,money);} }注意:
@Transactional可以写在接口类上、接口方法上、实现类上和实现类方法上
- 写在接口类上,该接口的所有实现类的所有方法都会有事务
- 写在接口方法上,该接口的所有实现类的该方法都会有事务
- 写在实现类上,该类中的所有方法都会有事务
- 写在实现类方法上,该方法上有事务
- 建议写在实现类或实现类的方法上
-
步骤2:在JdbcConfig类中配置事务管理器
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;@Beanpublic DataSource dataSource(){DruidDataSource ds = new DruidDataSource();ds.setDriverClassName(driver);ds.setUrl(url);ds.setUsername(userName);ds.setPassword(password);return ds;}//配置事务管理器,mybatis使用的是jdbc事务@Beanpublic PlatformTransactionManager transactionManager(DataSource dataSource){DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();transactionManager.setDataSource(dataSource);return transactionManager;} }**注意:**事务管理器要根据使用技术进行选择,Mybatis框架使用的是JDBC事务,可以直接使用
DataSourceTransactionManager -
步骤3:开启事务注解
在SpringConfig的配置类中开启@Configuration @ComponentScan("com.itheima") @PropertySource("classpath:jdbc.properties") @Import({JdbcConfig.class,MybatisConfig.class //开启注解式事务驱动 @EnableTransactionManagement public class SpringConfig { }
-
-
-
Spring事务角色
开启Spring的事务管理后

- transfer上添加了@Transactional注解,在该方法上就会有一个事务T
- AccountDao的outMoney方法的事务T1加入到transfer的事务T中
- AccountDao的inMoney方法的事务T2加入到transfer的事务T中
- 这样就保证他们在同一个事务中,当业务层中出现异常,整个事务就会回滚,保证数据的准确性。
- 事务管理员:发起事务方,在Spring中通常指代业务层开启事务的方法
- 事务协调员:加入事务方,在Spring中通常指代数据层方法,也可以是业务层方法
- Spring事务属性
-
事务配置

上面这些属性都可以在@Transactional注解的参数上进行设置。-
readOnly:true只读事务,false读写事务,增删改要设为false,查询设为true。
-
timeout:设置超时时间单位秒,在多长时间之内事务没有提交成功就自动回滚,-1表示不设置超时时间。
-
rollbackFor:当出现指定异常进行事务回滚
Spring的事务只会对Error异常和RuntimeException异常及其子类进行事务回顾,其他的异常类型是不会回滚的
例如:可以使用rollbackFor属性来设置出现IOException异常不回滚@Service public class AccountServiceImpl implements AccountService {@Autowiredprivate AccountDao accountDao;@Transactional(rollbackFor = {IOException.class})public void transfer(String out,String in ,Double money) throws IOException{accountDao.outMoney(out,money);//int i = 1/0; //这个异常事务会回滚if(true){throw new IOException(); //这个异常事务就不会回滚}accountDao.inMoney(in,money);}} -
noRollbackFor:当出现指定异常不进行事务回滚
-
rollbackForClassName等同于rollbackFor,只不过属性为异常的类全名字符串
-
noRollbackForClassName等同于noRollbackFor,只不过属性为异常的类全名字符串
-
isolation设置事务的隔离级别
- DEFAULT :默认隔离级别, 会采用数据库的隔离级别
- READ_UNCOMMITTED : 读未提交
- READ_COMMITTED : 读已提交
- REPEATABLE_READ : 重复读取
- SERIALIZABLE: 串行化
-
-
转账业务追加日志案例
- 需求:无论转账操作是否成功,均进行转账操作的日志留痕

对于上述案例的分析:
- log方法、inMoney方法和outMoney方法都属于增删改,分别有事务T1,T2,T3
- transfer因为加了@Transactional注解,也开启了事务T
- 前面我们讲过Spring事务会把T1,T2,T3都加入到事务T中
- 所以当转账失败后,所有的事务都回滚,导致日志没有记录下来
- 这和我们的需求不符,这个时候我们就想能不能让log方法单独是一个事务呢?
要想解决这个问题,就需要用到事务传播行为,需要用到之前我们没有说的
propagation属性 - 需求:无论转账操作是否成功,均进行转账操作的日志留痕
-
事务传播行为:事务协调员对事务管理员所携带事务的处理态度。
-
1.修改logService改变事务的传播行为
@Service public class LogServiceImpl implements LogService {@Autowiredprivate LogDao logDao;//propagation设置事务属性:传播行为设置为当前操作需要新事务@Transactional(propagation = Propagation.REQUIRES_NEW)public void log(String out,String in,Double money ) {logDao.log("转账操作由"+out+"到"+in+",金额:"+money);} }运行后,就能实现我们想要的结果,不管转账是否成功,都会记录日志
-
2.事务传播行为的可选值

-
-
相关文章:
Spring02
文章目录 1. IOC/DI注解开发2. IOC/DI注解开发管理第三方bean3. Spring整合4. AOP简介5. AOP入门案例6. AOP工作流程7. AOP配置管理8. AOP事务管理 1. IOC/DI注解开发 注解开发定义bean用的是2.5版提供的注解,纯注解开发用的是3.0版提供的注解 pom.xml添加依赖 &l…...
Linux系统中的高级内核模块调试技术
引言 在Linux系统中进行高级内核模块开发时,调试是不可或缺的重要环节。调试技术能够帮助开发人员发现和解决代码中的错误和问题,提高开发效率和代码质量。本文将深入探讨Linux系统中高级内核模块调试的技术和方法,包括常用的调试工具、调试…...
竞赛报名管理系统asp.net+sqlserver
竞赛报名管理系统 功能简单 内容单调 适合学习 asp.net 三层架构 sqlserver2022数据库 账号登陆注册 用户管理 克赛管理 竞赛报名 竞赛评分 公告维护 修改密码 新增竞赛 2019数据库版本低 附加不了 需要高版本数据库 说明文档 运行前附加数据库.mdf(或sql生成数据…...
Python爬虫核心面试题2
网络爬虫 1. 什么是HTTP协议?它有哪些常见的请求方法?2. 在进行网络爬虫时,如何判断一个网站是否允许被爬取?3. 在使用HTTP请求时,如何处理重定向?4. 解释HTTP状态码200、404、500的含义。5. 什么是Session…...
【2024年华数杯全国大学生数学建模竞赛】C题:老外游中国 问题思路分析及Python代码实现
【2024 年华数杯全国大学生数学建模竞赛】C题:老外游中国 问题思路分析及Python代码实现 1 题目 最近,“city 不 city”这一网络流行语在外国网红的推动下备受关注。随着我国过境免签政策的落实,越来越多外国游客来到中国,通过网…...
HTTP/2:让网络飞起来
文章目录 一、HTTP/2 的基本概念和背景二、HTTP/2 的主要特性和优势2.1 二进制帧2.2 多路复用2.3 头部压缩2.4 服务器推送 三、HTTP/2 的实现和部署四、HTTP/2 与现有技术的比较五、HTTP/2 与 Web 性能优化六、结束语:让 HTTP/2 助力你的 Web 开发 今天我们来聊聊一…...
C++ primer plus 第17 章 输入、输出和文件:刷新输出缓冲区
C primer plus 第17 章 输入、输出和文件:刷新输出缓冲区 C primer plus 第17 章 输入、输出和文件:刷新输出缓冲区 文章目录 C primer plus 第17 章 输入、输出和文件:刷新输出缓冲区17.2.3刷新输出缓冲区 17.2.3刷新输出缓冲区 如果程序使…...
项目总结2
文件的分片上传 格外功能是:秒传,断点续传。 今天最惨,上午找bug,下午一直在修改,晚上脑子what了,混乱的很,数据表之间的逻辑不清晰,导致我传值,还有操作数据库一直有问…...
PXE实现自动批量安装部署操作系统
PXE简介 PXE(Preboot eXecution Environment)是一种在计算机启动时使用网络接口从远程服务器获取操作系统安装和启动信息的技术。通过PXE,计算机可以从局域网中的PXE服务器上下载操作系统安装文件,并进行自动化的操作系统部署或故…...
Cyber Weekly #18
赛博新闻 1、Google 狂卷小模型,2B 参数 Gemma 2 赶超 GPT-3.5 Google本周发布了开源的轻量级、高性能模型 Gemma 2 2B。它拥有 20 亿参数,是从更大规模的模型中提炼而来的,在 LMSYS 大模型竞技场的得分超越了 GPT-3.5 和 Mixtral 8x7B。该…...
Open Interpreter - 开放解释器
文章目录 一、关于演示它是如何工作的?与 ChatGPT 的代码解释器比较 二、快速开始三、更多操作1、互动聊天2、程序化聊天3、开始新的聊天4、保存和恢复聊天5、自定义系统消息6、更改模型7、在本地运行 Open Interpreter终端Python上下文窗口,最大令牌 8、…...
“八股文”:程序员的福音还是梦魇?
——一场关于面试题的“代码战争” 在程序员的世界里,“八股文”这个词儿可谓是“如雷贯耳”。不,咱们可不是说古代科举考试中的那种八股文,而是指程序员面试中的那些固定套路的题目。如今,各大中小企业在招聘程序员时࿰…...
数据结构第2天作业 8月3日
单向链表 typedef int datatype; //由于有效数据不一定是正数,所以将数据重命名。typedef struct lklst{ //不能是无名结构体了,因为定义指针域的时候需要使用union{int len; //头结点时候使用;datatype data; …...
设计界的新宠:5款热门UI在线设计软件评测
随着用户界面设计行业的蓬勃发展,越来越多的设计师进入用户界面设计。选择一个方便的用户界面设计工具尤为重要!除了传统的用户界面设计工具,在线用户界面设计工具也受到越来越多设计师的青睐。这种不受时间、地点、计算机配置限制的工作方法…...
github添加ssh密钥,通过ssh方式推送代码
左手编程,右手年华。大家好,我是一点,关注我,带你走入编程的世界。 公众号:一点sir,关注领取python编程资料 很多人在使用github的时候,如果还是使用https的方式推送代码的话,可能会…...
Python设计模式 - 抽象工厂模式
定义 抽象工厂模式是一种创建型设计模式,它提供了一种创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。 产品等级结构与产品族 为了更好地理解抽象工厂模式,先引入两个概念: 产品等级结构:就是产品的…...
【JavaEE初阶】懒汉模式与饿汉模式及指令重排序问题
目录 📕 单例模式 🌳 饿汉模式 🚩 线程安全 🎍 懒汉模式 🚩 懒汉模式-单线程版 🚩 懒汉模式-多线程版 🎄 指令重排序 📕 单例模式 单例模式是一种经典的设计模式,…...
Vue3使用Cascader 级联选择器如何获取值并提交信息
我写了一个用户对象,有address地址字段,我怎么将用户选择的级联数据selectedValue值传给address,并将对象返回给后端,核心代码实现了该问题。 <script> 核心代码: //获取住址并更新给addresslet selectedValue…...
Python面试整理-第三方库
Python社区提供了大量的第三方库,这些库扩展了Python的功能,覆盖了从数据科学到网络应用开发等多个领域。以下是一些非常流行和广泛使用的第三方库: 1. NumPy ● 用途:数值计算。 ● 特点:提供了一个强大的N维数组对象和大量用于数学运算的函数。 ● 应用场景:科学计算、…...
电脑添加虚拟网卡与ensp互联,互访
一、按照过程 1、打开设备管理器 2、点击网络适配器,点击左上角操作,点击“添加过时硬件” 3、下一页 4、选择“安装我手动从列表选择的硬件”,下一页 5、下拉,选择“网络适配器”,下一页 6、厂商选择“Microsoft”&…...
Java 语言特性(面试系列2)
一、SQL 基础 1. 复杂查询 (1)连接查询(JOIN) 内连接(INNER JOIN):返回两表匹配的记录。 SELECT e.name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id d.dept_id; 左…...
React第五十七节 Router中RouterProvider使用详解及注意事项
前言 在 React Router v6.4 中,RouterProvider 是一个核心组件,用于提供基于数据路由(data routers)的新型路由方案。 它替代了传统的 <BrowserRouter>,支持更强大的数据加载和操作功能(如 loader 和…...
django filter 统计数量 按属性去重
在Django中,如果你想要根据某个属性对查询集进行去重并统计数量,你可以使用values()方法配合annotate()方法来实现。这里有两种常见的方法来完成这个需求: 方法1:使用annotate()和Count 假设你有一个模型Item,并且你想…...
大数据学习(132)-HIve数据分析
🍋🍋大数据学习🍋🍋 🔥系列专栏: 👑哲学语录: 用力所能及,改变世界。 💖如果觉得博主的文章还不错的话,请点赞👍收藏⭐️留言Ǵ…...
人机融合智能 | “人智交互”跨学科新领域
本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...
【C++特殊工具与技术】优化内存分配(一):C++中的内存分配
目录 一、C 内存的基本概念 1.1 内存的物理与逻辑结构 1.2 C 程序的内存区域划分 二、栈内存分配 2.1 栈内存的特点 2.2 栈内存分配示例 三、堆内存分配 3.1 new和delete操作符 4.2 内存泄漏与悬空指针问题 4.3 new和delete的重载 四、智能指针…...
【JVM面试篇】高频八股汇总——类加载和类加载器
目录 1. 讲一下类加载过程? 2. Java创建对象的过程? 3. 对象的生命周期? 4. 类加载器有哪些? 5. 双亲委派模型的作用(好处)? 6. 讲一下类的加载和双亲委派原则? 7. 双亲委派模…...
RSS 2025|从说明书学习复杂机器人操作任务:NUS邵林团队提出全新机器人装配技能学习框架Manual2Skill
视觉语言模型(Vision-Language Models, VLMs),为真实环境中的机器人操作任务提供了极具潜力的解决方案。 尽管 VLMs 取得了显著进展,机器人仍难以胜任复杂的长时程任务(如家具装配),主要受限于人…...
C++课设:简易日历程序(支持传统节假日 + 二十四节气 + 个人纪念日管理)
名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、为什么要开发一个日历程序?1. 深入理解时间算法2. 练习面向对象设计3. 学习数据结构应用二、核心算法深度解析…...
计算机基础知识解析:从应用到架构的全面拆解
目录 前言 1、 计算机的应用领域:无处不在的数字助手 2、 计算机的进化史:从算盘到量子计算 3、计算机的分类:不止 “台式机和笔记本” 4、计算机的组件:硬件与软件的协同 4.1 硬件:五大核心部件 4.2 软件&#…...
