Spring 容器
提示:英语很重要,单词是读的,是拼出来的,和拼音一样
文章目录
- 前言
- 前期准备
- 1️⃣ DI 依赖注入
- 1、xml 配置方式
- 2、注解方式 annotation❗
- 相关注解
- Spring中Bean的作用域❗
- @Scope() 注解
- @Qualifier("XXXServiceImpl") 指定哪个实现类
- 3、javaConfig方式
- BeanFactory与ApplicationContext的区别是?❗
- 2️⃣ AOP 面向对象
- 一、JDK 动态代理
- 二、Cglib 字节码生成
- 抽取重复代码
- 总结
- 五种通知类型
- 性能监测案例
前言
提示:controller→service→dao互相依赖
Spring容器最大的作用:
- 帮我们管理很多类对象的创建
- 帮我们管理他们对象之间的彼此依赖注入
Spring实现依赖注入有三种方式:注解方式(官方推荐方式)、xml配置文件方式、javaConfig方式。
Spring两大核心
DI:依赖注入(Dependency Injection) AOP:面向切面编程(Aspect Oriented Programming)
Spring框架的优点如下
- 非侵入式设计,最小化应用程序代码对框架的依赖。
- 解耦、简化开发,降低组件之间的耦合性。
- 支持AOP,提高程序的复用性。
- 支持声明式事务处理,方便管理事务。
- 方便程序测试,提供对Junit4的支持。
- 方便集成各种优秀框架,如Struts、Hibernate、MyBatis、Quartz等。
- 简化Java EE API的使用,提供对JDBC、JavaMail等API的封装,降低应用难度。
提示:以下是本篇文章正文内容,下面案例可供参考
前期准备
第一步:新建项目Maven项目

在下一步中勾选第一个选项,创建一个简单的项目,最后填写相关信息后完成

第二步:完善 pom.xml 文件
项目建成后在 pom.xml 文件中导入相关代码,比如:
缺什么加什么
<dependencies><!-- 此依赖会关联引用Spring中的所有基础jar包 --><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.2.8.RELEASE</version></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.8.7</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-log4j12</artifactId><version>1.7.15</version></dependency><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version></dependency></dependencies>
第三步:配置 main/resources 文件夹
bean.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beansxmlns="http://www.springframework.org/schema/beans"xmlns:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-4.1.xsd"></beans>
log4j.properties配置文件,可以显示 log 输出
### direct log messages to stdout ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %m%n
log4j.rootLogger=debug,stdout
第四步:接下来新建测试类
package com.spring;import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class AppTestUnit
{ApplicationContext context = null;@Beforepublic void before() throws Exception {// xml配置文件方式实现 配置文件的路径context = new ClassPathXmlApplicationContext("bean.xml");// 注解方式实现,包名被更改时,这里对应的包名也要改//context = new AnnotationConfigApplicationContext("com.spring");}@Test public void test() {}}
1️⃣ DI 依赖注入
原来是叫 IOC(Spring 的Bean中主要包含三种装配方式)
依赖注入的原理:
将对象的依赖关系从代码中移除,而是通过外部容器来管理这些依赖关系,将某一个需要的依赖,右容器注入
1、xml 配置方式
大致过程:
- dao(接口和实现类、配置xml、测试)
- service(接口和实现类、配置xml、测试)
- controller(直接写类、配置xml、测试)
在main/java文件夹下新建包:com.spring.xml,然后在下面新建dao、service、controller包
dao 包下新建接口:这里以 Goods 商品 取名
在里面加上一个方法
public interface GoodsDao{void insertGoods();
}
紧接着着这个包下创建一个同名+Impl的实现类
项目创建好后,在实现类后面跟上implements+要实现的接口名,然后保存添加未实现的方法

在里面加一句输出,代表执行了
@Overridepublic void insertGoods() {System.out.println("成功向MySQL数据库中增加一行商品数据");}
接下来需要在bean.xml文件中进行一个配置,因为 dao 类对象要容器创建
<!-- 在xml中进行配置,配置好的类的对象创建 就由spring容器负责 --> <!-- 实现类创建对象 -->
<bean id="goodsdao" class="com.spring.xml.dao.GoodsDaoImpl"></bean>
<!-- id="dao包接口名" class="实现类的限定名" -->
测试代码:传名字
getBean后的值是刚才在bean.xml中配置的id值 goodsdao,然后加强转(有一个向下的转型)

@Test public void test() {// 从容器context中获得getBean使用的对象GoodsDaoImpl dao = (GoodsDaoImpl) context.getBean("goodsdao");dao.insertGoods(); }
测试运行:

getBean还有另一个重载的方法:传类的信息(常用)
两种实现方法结果一致
@Test public void test() {// 从容器context中获得getBean使用的对象
// GoodsDaoImpl dao = (GoodsDaoImpl) context.getBean("goodsdao");GoodsDaoImpl dao = context.getBean(GoodsDaoImpl.class);dao.insertGoods(); }
接下来写 service 包,在里面写个接口
public interface GoodsService {void addGoods();
}
再写一个实现类,与 service 包接口同名+Impl
第一步:添加未实现的方法
第二步:定义 dao 类型的变量(体现业务依赖 dao )
第三步:加set方法只有xml和javaConfig需要

XyzServiceImpl 代码:
public class GoodsServiceImpl implements GoodsService{//2、添加dao类型的变量(体现业务依赖dao)private GoodsDao dao;// 完成需要的dao对象的注入public void setDao(GoodsDao dao) {this.dao = dao;}// 3、加set方法只有xml和javaConfig需要@Overridepublic void addGoods() { // 1、添加未实现的方法 // 4、输出System.out.println("商品业务——addGoods");// 业务类调 daodao.insertGoods();}
}
现在ServiceImpl 实现类写完了,因为业务类对象也要容器创建,所以接下来在bean.xml内加上容器
property标记代表所需依赖注入的配置
property 中的 name 属性名指的是ServiceImpl 实现类中添加dao类型的变量
property 中的 ref 属性指的是要注入的对象 id

<!-- id="小写service包接口名" class="实现类的限定名" -->
<bean id="goodsservice" class="com.springtest.service.GoodsServiceImpl">
<!-- service需要dao注入进来,property标记代表所需依赖注入的配置-->
<property name="dao" ref="goodsdao"></property>
</bean>
回到测试类写service测试,先注释或删掉dao的测试
@Test public void test() {// 从容器context中获得getBean使用的对象
// GoodsDaoImpl dao = (GoodsDaoImpl) context.getBean("goodsdao");
// GoodsDaoImpl dao = context.getBean(GoodsDaoImpl.class);
// dao.insertGoods(); GoodsServiceImpl service = context.getBean(GoodsServiceImpl.class);// 证明依赖注入成功:调service方法时,dao的方法也输出了service.addGoods();}

最后写controller
public class GoodsController {// 2.增加接口体现依赖private GoodsService service;// 3.生成set方法public void setService(GoodsService service) {this.service = service;}
// 1.doGet()public void doGet() {System.out.println("获取请求数据");// 4.调用service方法service.addGoods();System.out.println("生成相应");}
}
接下来写controller的bean.xml配置

<!-- id="小写controller类名" class="Controller类的限定名" -->
<bean id="goodscontroller" class="com.springtest.controller.GoodsController">
<property name="service" ref="goodsservice"></property>
</bean>
controller测试:先注释或删掉 dao 和 service 的测试
@Test public void test() {// 从容器context中获得getBean使用的对象
// GoodsDaoImpl dao = (GoodsDaoImpl) context.getBean("goodsdao");
// GoodsDaoImpl dao = context.getBean(GoodsDaoImpl.class);
// dao.insertGoods(); // GoodsServiceImpl service = context.getBean(GoodsServiceImpl.class);
// // 证明依赖注入成功:调service方法时,dao的方法也输出了
// service.addGoods();GoodsController controller = context.getBean(GoodsController.class);controller.doGet();}

2、注解方式 annotation❗
注解:
- 依赖创建对象:
@Component(通用) - 依赖注入注解:
@Autowired
在main/java文件夹下新建包:com.spring.annotation,然后在下面新建dao、service、controller包
先在dao包下新建接口和接口的实现类,这里取名 business
接口代码:
public interface BusinessDao {void updateBusiness();
}
接口的实现类:
// 注解功能等同于xml中的bean标记 abc是自定义名字
@Component
public class BusinessDaoImpl implements BusinessDao{@Overridepublic void updateBusiness() {// 添加未实现的方法System.out.println("修改商家数据");}}
测试代码:
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class AppTestAnnotation
{ApplicationContext context = null;@Beforepublic void before() throws Exception {
// 这里配置的包名,当包名被改变时,这里也需要被改变context = new AnnotationConfigApplicationContext("com.spring");// 创建注解容器时,需要指定扫描的包名// 那么容器创建时会自动的扫描这个包以及子包中的所有类// 找有Component注解的类}@Test public void test() {}
}
接下来写注解Test 的内容,看数据能否被取出
@Test public void test() {BusinessDaoImpl dao = context.getBean(BusinessDaoImpl.class);dao.updateBusiness();}

在BusinessDaoImpl实现类的注解中可以加上自定义名字:@Component("abc")

接下来在service中新建接口以及接口对应的实现类
接口代码:
public interface BusinessService {void update();
}
接口对应的实现类:
// 3.添加注解
@Component
public class BusinessServiceImpl implements BusinessService{// 4.自定装配实现依赖注入@Autowired // 2.增加dao类型的属性(接口)private BusinessDao dao;@Overridepublic void update() {// 1.添加未实现的方法// 5.输出 调用依赖dao的方法System.out.println("修改业务");dao.updateBusiness();}}
在测试类中写service测试:先注释或删掉掉dao的测试
@Test public void test() {BusinessServiceImpl service = context.getBean(BusinessServiceImpl.class);service.update();}

接下来在controller包中新建controller类
@Component // 2.加注解
public class BusinessController {@Autowired // 3.依赖注入注解 自动装配private BusinessService service; // 1.加依赖的属性(接口)// 4.加方法public void doPost() {System.out.println("获取请求数据");service.update();System.out.println("生成响应");}}
测试类测试:
@Test public void test() {BusinessController controller = context.getBean(BusinessController.class);controller.doPost();}

相关注解
上面案例说了两个注解:
- 依赖创建对象:
@Component(通用) - 依赖注入注解:
@Autowired
下面还有三个注解,这三个注解功能一样,但更有语义化
将一个类声明为Spring容器管理的bean的注解有:
@Component:通用的注解,可标注任意类为 Spring 组件。如果一个Bean不知道属于哪个层,可以使用
@Repository:对应数据层即 Dao 层,主要用于数据库相关操作。整合mybatis时,用 @Mapper
@Service:对应服务层,主要涉及一些复杂的逻辑,需要用到 Dao层。
@Controller:对应 Spring MVC控制层,主要用户接受用户请求并调用 Service 层返回数据给前端页面。
如果把上面案例中的 dao 层中的 @Component 注解换为:@Repository
service 中的 @Component 注解换为:@Service
controller 中的 @Component 注解换为:@Controller
那么测试输出一样,也就是说:
Spring中Bean的作用域❗
Spring中Bean的作用域及各自意义
@Scope() 注解:设置Bean的作用域。值如下:
| 作用域 | 说明 |
|---|---|
singleton | 单例模式,在整个Spring IoC容器中,使用singleton定义的Bean将只有一个实例 |
prototype | 原型模式,每次通过容器的getBean方法获取prototype定义的Bean时,都将产生一个新的Bean实例 |
request | 对于每次HTTP请求,使用request定义的Bean都将产生一个新实例,即每次HTTP请求将会产生不同的Bean实例。只有在Web应用中使用Spring时,该作用域才有效 |
session | 对于每次HTTP Session,使用session定义的Bean豆浆产生一个新实例。同样只有在Web应用中使用Spring时,该作用域才有效 |
globalsession | 每个全局的HTTP Session,使用session定义的Bean都将产生一个新实例。典型情况下,仅在使用portlet context的时候有效。同样只有在Web应用中使用Spring时,该作用域才有效 |
@Scope() 注解
在累的前面加上@Scope()
@Component
@Scope("prototype") //每次都是一个新对象
public class Hello {}
测试代码:默认应该是单例
@Test public void test() {// 为了测试每次取出的都是同一个对象for(int i=0; i<10;i++) {Hello s = context.getBean(Hello.class);System.out.println(s);} }
输出信息:

@Qualifier(“XXXServiceImpl”) 指定哪个实现类
在service包中新建实现类BusinessServiceImpl1,让他也实现BusinessService接口
@Service
public class BusinessServiceImpl1 implements BusinessService{@Overridepublic void update() {// TODO 自动生成的方法存根System.out.println("asdfg");}}
现在有两个实现类对象存在
测试类代码执行:
@Test public void test() {BusinessController controller = context.getBean(BusinessController.class);controller.doPost(); }
报错信息:
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.spring.annotation.service.BusinessService' available: expected single matching bean but found 2: businessServiceImpl,businessServiceImpl1at org.springframework.beans.factory.config.DependencyDescriptor.resolveNotUnique(DependencyDescriptor.java:220)at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1285)at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1227)at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:640)... 37 more
原因:期望一个匹配对象,但是现在有两个businessServiceImpl和businessServiceImpl1
在BusinessController 类中加一个@Qualifier注解,让他指定哪个实现类

输出结果:

3、javaConfig方式
- 在项目初期时,会做一些项目配置
- 某个类不是我们能更改的代码,但是这个类还需要容器管理对象的时候加 javaConfig
- javaconfig方式必须给配置类增加
@Configuration注解
在main/java文件夹下新建包:com.spring.javaconfig,然后在下面新建dao、service包
dao 包下新建接口及同名的接口+Impl实现类
接口中代码:
public interface UserDao {void query();
}
实现类代码:
public class UserDaoImpl implements UserDao{@Overridepublic void query() {// TODO 自动生成的方法存根System.out.println("查询用户数据");}}
在com.spring.javaconfig 包下新建类 AppConfig,里面暂时放 dao 的方法
@Configuration // javaconfig方式必须给配置类增加此注解
public class AppConfig {@Bean // 有Bean注解的方法,它返回的对象就会交由spring容器管理public UserDaoImpl dao() {return new UserDaoImpl();}}
测试类:
@Test public void test() {UserDaoImpl dao = context.getBean(UserDaoImpl.class);dao.query();}

新建service接口及其实现类
接口代码:
public interface UserService {void query();
}
接口实现类:
public class UserServiceImpl implements UserService{// Service依赖Daoprivate UserDao dao;public void setDao(UserDao dao) {// set方法单独生成this.dao = dao;}@Overridepublic void query() {// TODO 自动生成的方法存根System.out.println("查询用户业务");dao.query();}}
在AppConfig 类里面再加一个service方法:
@Bean// 可以接口/实现类public UserService service(UserDao dao) {UserServiceImpl service = new UserServiceImpl();service.setDao(dao);return service;}
测试代码:
@Test public void test() {UserServiceImpl service = context.getBean(UserServiceImpl.class);service.query();}

BeanFactory与ApplicationContext的区别是?❗
BeanFactory:
Bean工厂(org.springframework.beans.factory.BeanFactory)是Spring框架最核心的接口,提供了IoC的配置机制,使管理不同类型的Java对象成为可能。特点是:采用延迟加载Bean,直到第一次使用Bean实例时才会创建Bean。
ApplicationContext:
应用上下文(org.springframework.context.ApplicationContext),继承自BeanFactory,提供了更多面向应用的功能,比如国际化支持、框架事件体系,更易于创建实际应用。
2️⃣ AOP 面向对象
简单的说它就是把我们程序重复的代码抽取出来
在需要执行的时候,使用动态代理的技术,在不修改源码的基础上,对我们的已有方法进行增强。
应用场景:记录日志、监控方法运行时间 (监控性能)、权限控制、缓存优化 、事务管理
AOP 内注解:
@Pointcut:定义切点,指定在哪些方法或类上应用增强。
@Before:在目标方法执行之前执行增强。
@AfterReturning:在目标方法正常返回后执行增强。
@AfterThrowing:在目标方法抛出异常后执行增强。
@Around:在目标方法执行前后执行增强。
@DeclareParents:为被切入对象引入新的接口和实现类。
两种AOP实现方案:JDK 动态代理、Cglib 字节码生成
一、JDK 动态代理
- 运用方式
在使用JDK动态代理时,需要实现InvocationHandler接口,并在方法中实现代理逻辑,同时利用Proxy类生成代理对象。下面是一个简单的实现示例:
public interface HelloWorld {void sayHello();
}public class HelloWorldImpl implements HelloWorld {@Overridepublic void sayHello() {System.out.println("Hello World!");}
}public class MyInvocationHandler implements InvocationHandler {private Object target;public MyInvocationHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("Before method:" + method.getName());Object result = method.invoke(target, args);System.out.println("After method:" + method.getName());return result;}
}public class Main {public static void main(String[] args) {HelloWorld helloWorld = new HelloWorldImpl();MyInvocationHandler handler = new MyInvocationHandler(helloWorld);HelloWorld proxyInstance = (HelloWorld) Proxy.newProxyInstance(helloWorld.getClass().getClassLoader(),helloWorld.getClass().getInterfaces(), handler);proxyInstance.sayHello();}
}
- 何时使用
JDK动态代理适用于对实现了接口的对象进行代理,它的运行时效率相对较高、实现较为简单,适用于大部分场景下的对象代理。
- 适用场景
- 对实现了接口的对象进行代理;
- 需要在运行时动态生成代理类;
- 对于单继承模型较为适用。
- 优缺点
JDK动态代理的优势在于:
- 运行时效率相对较高;
- 实现较为简单。
缺点在于:
- 只能代理实现了接口的对象。
二、Cglib 字节码生成
- 运用方式
在使用Cglib字节码生成时,需要引入Cglib的依赖,继承MethodInterceptor接口,并在方法中实现代理逻辑。下面是一个简单的实现示例:
public interface HelloWorld {void sayHello();
}public class HelloWorldImpl {public void sayHello() {System.out.println("Hello World!");}
}public class MyMethodInterceptor implements MethodInterceptor {@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {System.out.println("Before method:" + method.getName());Object result = methodProxy.invokeSuper(o, objects);System.out.println("After method:" + method.getName());return result;}
}public class Main {public static void main(String[] args) {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(HelloWorldImpl.class);enhancer.setCallback(new MyMethodInterceptor());HelloWorldImpl proxy = (HelloWorldImpl) enhancer.create();proxy.sayHello();}
}
- 何时使用
Cglib 字节码生成适用于对未实现接口的对象进行代理,代理逻辑灵活,适用于复杂逻辑下的对象代理。
- 适用场景
- 对未实现接口的对象进行代理;
- 需要在运行时动态生成代理类;
- 对于单继承模型较为适用。
- 优缺点
Cglib 字节码生成的优势在于:
- 代理逻辑的灵活性,可以代理未实现接口的对象。
缺点在于:
- 运行时效率相对较低;
- 实现复杂。
三、JDK 动态代理和 Cglib 字节码生成的比较
JDK动态代理和Cglib字节码生成都是Java中常用的实现动态代理的方式,它们各自之间的优缺点如下:
JDK动态代理的优点在于运行时效率相对较高、实现较为简单;缺点在于只能代理实现了接口的对象。
Cglib字节码生成的优点在于代理逻辑的灵活性,可以代理未实现接口的对象;缺点在于运行时效率相对较低、实现复杂。
在使用 JDK动态代理和Cglib字节码生成时,需要根据具体对象的特点及使用场景来决定使用哪种方式。对于实现了接口的对象,可以优先考虑使用JDK动态代理,因为它的运行时效率相对较高、实现较为简单;对于未实现接口的对象,可以优先考虑使用Cglib字节码生成,因为它代理逻辑灵活,可以代理未实现接口的对象。
抽取重复代码
AOP核心思想把重复的代码从方法中抽取出来
在main/java文件夹下新建包:com.spring.aop,然后在下面新建aop、service包
先在service包内增加代码,创建接口及其实现类
public interface FoodService {void insert();void delete();
}
实现类代码:在开始执行前的输出视为重复代码
@Service
public class FoodServiceImpl implements FoodService{@Overridepublic void insert() {// 添加未实现的方法System.out.println("insert方法开始执行");// 模拟调用dao层代码System.out.println("增加业务代码");}@Overridepublic void delete() {// 添加未实现的方法System.out.println("delete方法开始执行");// 模拟调用dao层代码System.out.println("删除业务代码");}}
测试代码:
@Test public void test() {FoodServiceImpl service = context.getBean(FoodServiceImpl.class);service.insert();service.delete();}

因为方法中有重复代码,这时候适用于AOP,接下来定义一个切面代码
在aop包内新建类MyAspect,并把之前的重复代码注释或删掉
//基本语法
@Component
@Aspect // 方面 切面
@EnableAspectJAutoProxy //开启切面动态代理
public class MyAspect {// 切入点 配置的是切入执行的方法信息 execution是函数,后面加(),如果包名改变这里也需要改@Pointcut("execution(* com.spring.serve.*.*(..))")// 第一个*:所切入执行方法的返回值类型(这里不区分返回值类型),这个方法所在的类的信息:完整的限定名// 第二个*:切入到serve包所有类的方法// 第三个*:切入到哪些方法// (..):方法中的所有参数public void pt() {}// 抽取出来的重复代码,封装到方面中的一个通知,把之前的重复代码注释或删掉// 通知抽取方法@Before("pt()") //通知和切入点的联系public void before(JoinPoint jp) {// JoinPoint 连接点,代表aop切入的方法信息String methodName = jp.getSignature().getName();System.out.println(methodName+"业务开始执行");}
}
测试类代码:
因为他会动态生成一个新的类,所以更改测试代码(改接口)
@Test public void test() {FoodService service = context.getBean(FoodService.class);service.insert();service.delete();}

总结
一、定义一个切面/方面类
二、增加两种类型的方法:
- 通知(抽取出来的方法信息,重读代码,加注解
@Before("xxx()") - 定义切入点的方法
三、AOP中的切入点
// 切入点 配置的是切入执行的方法信息 execution是函数,后面加(),如果包名改变这里也需要改@Pointcut("execution(* com.spring.serve.*.*(..))")// 第一个*:所切入执行方法的返回值类型(这里不区分返回值类型),这个方法所在的类的信息:完整的限定名// 第二个*:切入到serve包所有类的方法// 第三个*:切入到哪些方法,这里是所有方法// (..):方法中的所有参数
接下来在通知的方法中加一个参数,从参数里面拿到切入方法的相关信息
public void pt() {}// 抽取出来的重复代码,封装到方面中的一个通知,把之前的重复代码注释或删掉// 通知抽取方法@Before("pt()") //通知和切入点的联系public void before(JoinPoint jp) {// JoinPoint 连接点,代表aop切入的方法信息String methodName = jp.getSignature().getName();System.out.println(methodName+"业务开始执行");}
}

五种通知类型
方面代码一般也称为通知:定义一个“切面”要实现的功能。通知有五种:
- 前置通知
@Before:在某连接点(JoinPoint 就是要织入的业务方法)之前执行的通知。 - 后置通知
@After:当某连接点退出时执行的通知(不论是正常结束还是发生异常)。 - 返回通知
@AfterReturning:(最终通知)在这里可以得到业务方法的返回值。但在发生异常时无法得到返回值。 - 环绕通知
@Around:包围一个连接点的通知,也就是在业务方法执行前和执行后执行的通知。 - 异常通知
@AfterThrowing:在业务方法发生异常时执行的通知。
后置通知:
@After("pt()")public void after() {System.out.println("后置通知执行了");}
测试输出后:在最后面输出了——后置通知执行了
返回通知:
@AfterReturning("pt()")public void afterReturn() {System.out.println("返回通知执行了");}
在service接口文件中给他加一个带返回值的方法
int update();
然后在service接口实现类中添加方法
@Overridepublic int update() {// TODO 自动生成的方法存根System.out.println("修改业务");return 0;}
在测试类中加上修改方法并注释掉之前的方法(因为没有返回值)
@Test public void test() {FoodService service = context.getBean(FoodService.class);
// service.insert();
// service.delete();service.update();}
让他发生异常就不会执行了
@Overridepublic int update() {// TODO 自动生成的方法存根System.out.println("修改业务");String string = null;string.length();return 0;}
发生空指针异常
接下来还是在MyAspect类中写异常通知:
@AfterThrowing("pt()")public void AfterThrowingException() {System.out.println("异常通知执行了");}
把前面长度length那一行注释掉再运行:修改业务输出
环绕通知:
@Around("pt()") public Object around(ProceedingJoinPoint jp) {System.out.println("环绕通知--1执行了");Object object = null;// 切入的方法执行try {object = jp.proceed();} catch (Throwable e) {// TODO 自动生成的 catch 块e.printStackTrace();}System.out.println("环绕通知--2执行了");return object;}

性能监测案例
@Component
@Aspect
@EnableAspectJAutoProxy
public class PerformanceAspect {@Pointcut("execution(* com.spring.serve.*.*(..))")public void cut() {}@Around("cut()")public Object executedTime(ProceedingJoinPoint jp) {long begin = System.currentTimeMillis();Object object = null;try {object = jp.proceed();} catch (Throwable e) {// TODO 自动生成的 catch 块e.printStackTrace();}long end = System.currentTimeMillis();String methodName = jp.getSignature().getName();System.out.println(methodName+"方法执行共花费"+(end-begin)+"毫秒");return object;}
}
测试运行后:
@Test public void test() {FoodService service = context.getBean(FoodService.class);service.update();}

因为方法很小,执行时间很短,所有看不出来
接下来在接口实现类中改造update方法,人为加个休眠
@Overridepublic int update() {// TODO 自动生成的方法存根System.out.println("修改业务");String string = null;//string.length();try {// 随机数Thread.sleep(new Random().nextInt(1000));} catch (InterruptedException e) {// TODO 自动生成的 catch 块e.printStackTrace();}return 0;}
再次运行:

相关文章:
Spring 容器
提示:英语很重要,单词是读的,是拼出来的,和拼音一样 文章目录 前言前期准备1️⃣ DI 依赖注入1、xml 配置方式2、注解方式 annotation❗相关注解Spring中Bean的作用域❗Scope() 注解Qualifier("XXXServiceImpl") 指定哪…...
【腾讯云Cloud Studio实战训练营】使用React快速构建点餐H5
文章目录 前言一、Cloud Studio是什么二、Cloud Studio特点三、Cloud Studio使用1.访问官网2.账号注册3.模板选择4.模板初始化5.H5开发安装 antd-mobile安装 Less安装 normalize上传项目需要的素材替换App.js主文件项目启动、展示 6.发布仓库 总结 前言 随着云计算产业的发展&…...
Java培训课程哪个品牌好?快拿小本本记好
Java是一门广泛应用于企业级应用程序开发的高级编程语言,具有较高的学习和职业发展价值。但是,在选择Java培训课程时,很多人会遇到一个问题:Java培训课程哪个品牌好?小编经过多方分析和比较,从专业培训的角度…...
leetcode19. 删除链表的倒数第 N 个结点
题目:leetcode19. 删除链表的倒数第 N 个结点 描述: 给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。 思路: 让前面的节点比后面的节点先走n1步,因为从链表的尾节点的下一个节点开始&…...
c51单片机串行通信示例代码(单片机--单片机通信)(附带proteus线路图)
//这个发送端代码 #include "reg51.h" #include "myheader.h" #define uchar unsigned char long int sleep_i0; long int main_i0; void main() {uchar sendx[6]{2,0,2,3,8,1};sleep(2000);TMOD0x20;TH10XF4;//根据波特率计算公式这里需要设置为这么多才能…...
UML之四种事物
目录 结构事物 行为事物 分组事物: 注释事物 结构事物 1.类(Class) -类是对一组具有相同属性、方法、关系和语义的对象的描述。一个类实现一个或多个接口 2.接口(interface) -接口描述 了一个类或构件的一个服务的操作集。接口仅仅是定义了一组操作的规范&…...
盒子模型和新盒子模型及区别
盒子模型是用于描述 HTML 元素在页面中占据的空间的概念。它将每个元素视为一个矩形框,由内容区域、内边距、边框和外边距组成。这个传统的盒子模型也被称为 "标准盒子模型"。 新盒子模型是指使用 CSS3 中的 box-sizing 属性设置为 border-box 后的一种盒…...
移动端Vue组件库-vant
Vant 是有赞前端团队开源的移动端vue组件库,适用于手机端h5页面。 鉴于百度搜索不到vant官方网址,分享一下vant组件库官网地址,方便新手使用 vant官网地址Vant 4 - A lightweight, customizable Vue UI library for mobile web apps. 通过 …...
Java课题笔记~ JSP内置对象
(1)九个内置对象 jsp的内置对象:JSP内置对象是不需要声明和创建就可以在JSP页面脚本中使用的成员变量。 九个内置对象: 1.out对象 在JSP页面中,经常需要向客户端发送文本内容,这时,可以使用out对象来实现。out对象…...
数据结构笔记--链表经典高频题
目录 前言 1--反转单向链表 2--反转单向链表-II 3--反转双向链表 4--打印两个有序链表的公共部分 5--回文链表 6--链表调整 7--复制含有随机指针结点的链表 8--两个单链表相交问题 前言 面经: 针对链表的题目,对于笔试可以不太在乎空间复杂度&a…...
Android Ble蓝牙App(三)特性和属性
Ble蓝牙App(三)特性使用 前言正文一、获取属性列表二、属性适配器三、获取特性名称四、特性适配器五、加载特性六、显示特性和属性七、源码 前言 在上一篇中我们完成了连接和发现服务两个动作,那么再发现服务之后要做什么呢?发现服…...
日常BUG——使用Long类型作id,后端返回给前段后精度丢失问题
😜作 者:是江迪呀✒️本文关键词:日常BUG、BUG、问题分析☀️每日 一言 :存在错误说明你在进步! 一、问题描述 数据库long类型Id: 前端返回的Id实体类: Data ApiModel("xxx") public class …...
【C++初阶】string类的常见基本使用
👦个人主页:Weraphael ✍🏻作者简介:目前学习C和算法 ✈️专栏:C航路 🐋 希望大家多多支持,咱一起进步!😁 如果文章对你有帮助的话 欢迎 评论💬 点赞…...
【ArcGIS Pro二次开发】(60):按图层导出布局
在使用布局导图时,会遇到如下问题: 为了切换图层和导图方便,一般情况下,会把相关图层做成图层组。 在导图的时候,如果想要按照图层组进行分开导图,如上图,想导出【现状图、规划图、管控边界】3…...
docker-desktop数据目录迁移
1.退出docker-desktop后执行 wsl --list -v 如下 NAME STATE VERSION * docker-desktop Stopped 2docker-desktop-data Stopped 22.执行以下命令进行数据导出:(需要等待命令执行完成)…...
03.利用Redis实现缓存功能---解决缓存穿透版
学习目标: 提示:学习如何利用Redis实现添加缓存功能解决缓存穿透版 学习产出: 缓存穿透讲解图: 解决方案: 采用缓存空对象采用布隆过滤器 解决方案流程图: 1. 准备pom环境 <dependency><gro…...
全景图!最近20年,自然语言处理领域的发展
夕小瑶科技说 原创 作者 | 小戏、Python 最近这几年,大家一起共同经历了 NLP(写一下全称,Natural Language Processing) 这一领域井喷式的发展,从 Word2Vec 到大量使用 RNN、LSTM,从 seq2seq 再到 Attenti…...
Mybatis参数传递
Map传参, #{}里的key要一一对应不能乱写,如果不存在则会填充NULL,不会报错 Map<String, Object> map new HashMap<>(); // 让key的可读性增强 map.put("carNum", "103"); map.put("brand", "奔驰E300L&…...
手动实现 Spring 底层机制 实现任务阶段一编写自己 Spring 容器-准备篇【2】
😀前言 手动实现 Spring 底层机制的第2篇 实现了任务阶段一编写自己 Spring 容器-准备篇【2】 🏠个人主页:尘觉主页 🧑个人简介:大家好,我是尘觉,希望我的文章可以帮助到大家,您的…...
部署模型并与 TVM 集成
本篇文章译自英文文档 Deploy Models and Integrate TVM tvm 0.14.dev0 documentation 更多 TVM 中文文档可访问 →Apache TVM 是一个端到端的深度学习编译框架,适用于 CPU、GPU 和各种机器学习加速芯片。 | Apache TVM 中文站 本节介绍如何将 TVM 部署到各种平台&…...
springboot 百货中心供应链管理系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,百货中心供应链管理系统被用户普遍使用,为方…...
Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误
HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误,它们的含义、原因和解决方法都有显著区别。以下是详细对比: 1. HTTP 406 (Not Acceptable) 含义: 客户端请求的内容类型与服务器支持的内容类型不匹…...
Keil 中设置 STM32 Flash 和 RAM 地址详解
文章目录 Keil 中设置 STM32 Flash 和 RAM 地址详解一、Flash 和 RAM 配置界面(Target 选项卡)1. IROM1(用于配置 Flash)2. IRAM1(用于配置 RAM)二、链接器设置界面(Linker 选项卡)1. 勾选“Use Memory Layout from Target Dialog”2. 查看链接器参数(如果没有勾选上面…...
Linux云原生安全:零信任架构与机密计算
Linux云原生安全:零信任架构与机密计算 构建坚不可摧的云原生防御体系 引言:云原生安全的范式革命 随着云原生技术的普及,安全边界正在从传统的网络边界向工作负载内部转移。Gartner预测,到2025年,零信任架构将成为超…...
MySQL中【正则表达式】用法
MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现(两者等价),用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例: 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...
WebRTC调研
WebRTC是什么,为什么,如何使用 WebRTC有什么优势 WebRTC Architecture Amazon KVS WebRTC 其它厂商WebRTC 海康门禁WebRTC 海康门禁其他界面整理 威视通WebRTC 局域网 Google浏览器 Microsoft Edge 公网 RTSP RTMP NVR ONVIF SIP SRT WebRTC协…...
基于Uniapp的HarmonyOS 5.0体育应用开发攻略
一、技术架构设计 1.混合开发框架选型 (1)使用Uniapp 3.8版本支持ArkTS编译 (2)通过uni-harmony插件调用原生能力 (3)分层架构设计: graph TDA[UI层] -->|Vue语法| B(Uniapp框架)B --&g…...
Qt Quick Controls模块功能及架构
Qt Quick Controls是Qt Quick的一个附加模块,提供了一套用于构建完整用户界面的UI控件。在Qt 6.0中,这个模块经历了重大重构和改进。 一、主要功能和特点 1. 架构重构 完全重写了底层架构,与Qt Quick更紧密集成 移除了对Qt Widgets的依赖&…...
aurora与pcie的数据高速传输
设备:zynq7100; 开发环境:window; vivado版本:2021.1; 引言 之前在前面两章已经介绍了aurora读写DDR,xdma读写ddr实验。这次我们做一个大工程,pc通过pcie传输给fpga,fpga再通过aur…...
信息收集:从图像元数据(隐藏信息收集)到用户身份的揭秘 --- 7000
目录 🌐 访问Web服务 💻 分析源代码 ⬇️ 下载图片并保留元数据 🔍 提取元数据(重点) 👤 生成用户名列表 🛠️ 技术原理 图片元数据(EXIF 数据) Username-Anarch…...
