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 部署到各种平台&…...
预训练语言模型T5-11B的简要介绍
文章目录 模型基本信息架构特点性能表现应用场景 T5-11B 是谷歌提出的一种基于 Transformer 架构的预训练语言模型,属于 T5(Text-To-Text Transfer Transformer)模型系列,来自论文 Colin Raffel, Noam Shazeer, Adam Roberts, Kat…...

pikachu靶场通关笔记20 SQL注入03-搜索型注入(GET)
目录 一、SQL注入 二、搜索型注入 三、源码分析 1、渗透思路1 2、渗透思路2 四、渗透实战 1、渗透准备 2、SQL注入探测 (1)输入百分号单引号 (2)万能注入语句 3、获取回显列orderby 4、获取数据库名database 5、获取…...

CICD实战(二)-----gitlab的安装与配置
1、安装gitlab所需要的依赖包与工具 sudo yum install wget net-tools sudo yum install curl policycoreutils openssh-server openssh-clients postfix -y 2、配置清华源 vim /etc/yum.repo.d/gitlab-ce.repo[gitlab-ce] namegitlab-ce baseurlhttp://mirrors.tuna.tsin…...

第4天:RNN应用(心脏病预测)
🍨 本文为🔗365天深度学习训练营 中的学习记录博客🍖 原作者:K同学啊 目标 具体实现 (一)环境 语言环境:Python 3.10 编 译 器: PyCharm 框 架: Pytorch (二)具体步骤…...
React Navive初识
文章目录 搭建开发环境安装 Node、homebrew、Watchman安装 Node安装 homebrew安装 watchman 安装 React Native 的命令行工具(react-native-cli)创建新项目编译并运行 React Native 应用在 ios 模拟器上运行 调试访问 App 内的开发菜单 搭建开发环境 在…...
PCB特种工艺应用扩展:厚铜、高频与软硬结合板
新能源汽车与消费电子驱动PCB特种工艺创新,厚铜板降阻30%,软硬结合板渗透率年增15%。 1. 厚铜板:新能源高压平台核心 技术突破:猎板PCB量产10oz厚铜板(传统为3oz),载流能力提升200%,…...
MATLAB实战:视觉伺服控制实现方案
以下是一个基于MATLAB的视觉伺服控制项目实现方案,结合实时图像处理、目标跟踪和控制系统设计。我们将使用模拟环境进行演示,但代码结构可直接应用于真实硬件。 系统架构 图像采集 → 目标检测 → 误差计算 → PID控制器 → 执行器控制 完整代码实现 …...

如何理解机器人课程的技术壁垒~壁垒和赚钱是两件不同的事情
答疑: 有部分朋友私聊说博客内容,越来越不适合人类阅读习惯…… 可以做这种理解,我从23年之后,博客会不会就是写给机器看的。 或者说我在以黑盒方式测试AI推荐的风格。 主观-客观-主观螺旋式发展过程。 2015最早的一篇博客重…...
为什么 uni-app 开发的 App 没有明显出现屏幕适配问题Flutter 开发的 App 出现了屏幕适配问题
🧩 一、为什么 uni-app 开发的 App 没有明显出现屏幕适配问题? ✅ 1. uni-app 是基于 H5 的运行环境(或类 H5) uni-app 默认使用的是 H5 的渲染引擎(如 WebView 或小程序渲染引擎)。在 H5 中,…...

学习数字孪生,为你的职业发展开辟新赛道
你有没有想过,未来十年哪些技能最吃香? AI、大数据、智能制造、元宇宙……这些词频繁出现在招聘市场和行业报告中。而在它们背后,隐藏着一个“看不见但无处不在”的关键技术——数字孪生(Digital Twin)。 它不仅在制造…...