当前位置: 首页 > news >正文

Spring 容器

提示:英语很重要,单词是读的,是拼出来的,和拼音一样

文章目录

  • 前言
  • 前期准备
  • 1️⃣ DI 依赖注入
    • 1、xml 配置方式
    • 2、注解方式 annotation❗
      • 相关注解
      • Spring中Bean的作用域❗
      • @Scope() 注解
      • @Qualifier("XXXServiceImpl") 指定哪个实现类
    • 3、javaConfig方式
    • BeanFactory与ApplicationContext的区别是?❗
  • 2️⃣ AOP 面向对象
    • 一、JDK 动态代理
    • 二、Cglib 字节码生成
    • 抽取重复代码
      • 总结
    • 五种通知类型
    • 性能监测案例


前言

提示:controller→service→dao互相依赖

Spring容器最大的作用:

  1. 帮我们管理很多类对象的创建
  2. 帮我们管理他们对象之间的彼此依赖注入

Spring实现依赖注入有三种方式:注解方式(官方推荐方式)、xml配置文件方式、javaConfig方式。

Spring两大核心
DI:依赖注入(Dependency Injection) AOP:面向切面编程(Aspect Oriented Programming)

Spring框架的优点如下

  1. 非侵入式设计,最小化应用程序代码对框架的依赖。
  2. 解耦、简化开发,降低组件之间的耦合性。
  3. 支持AOP,提高程序的复用性。
  4. 支持声明式事务处理,方便管理事务。
  5. 方便程序测试,提供对Junit4的支持。
  6. 方便集成各种优秀框架,如Struts、Hibernate、MyBatis、Quartz等。
  7. 简化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 文件夹

  1. 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>
  1. 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 配置方式

大致过程:

  1. dao(接口和实现类、配置xml、测试)
  2. service(接口和实现类、配置xml、测试)
  3. controller(直接写类、配置xml、测试)

在main/java文件夹下新建包:com.spring.xml,然后在下面新建dao、service、controller包

dao 包下新建接口:这里以 Goods 商品 取名

在里面加上一个方法

public interface GoodsDao![请添加图片描述](https://img-blog.csdnimg.cn/aca00a926a4c4e7998e6caf4a0d8ac8b.png){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方法只有xmljavaConfig需要
在这里插入图片描述
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❗

注解:

  1. 依赖创建对象:@Component(通用)
  2. 依赖注入注解:@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();}

在这里插入图片描述

相关注解

上面案例说了两个注解:

  1. 依赖创建对象:@Component(通用)
  2. 依赖注入注解:@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方式

  1. 在项目初期时,会做一些项目配置
  2. 某个类不是我们能更改的代码,但是这个类还需要容器管理对象的时候加 javaConfig
  3. 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 动态代理

  1. 运用方式

在使用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();}
}
  1. 何时使用

JDK动态代理适用于对实现了接口的对象进行代理,它的运行时效率相对较高、实现较为简单,适用于大部分场景下的对象代理。

  1. 适用场景
  • 对实现了接口的对象进行代理;
  • 需要在运行时动态生成代理类;
  • 对于单继承模型较为适用。
  1. 优缺点

JDK动态代理的优势在于:

  • 运行时效率相对较高;
  • 实现较为简单。

缺点在于:

  • 只能代理实现了接口的对象。

二、Cglib 字节码生成

  1. 运用方式

在使用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();}
}
  1. 何时使用

Cglib 字节码生成适用于对未实现接口的对象进行代理,代理逻辑灵活,适用于复杂逻辑下的对象代理。

  1. 适用场景
  • 对未实现接口的对象进行代理;
  • 需要在运行时动态生成代理类;
  • 对于单继承模型较为适用。
  1. 优缺点

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();}

请添加图片描述


总结

一、定义一个切面/方面类

二、增加两种类型的方法:

  1. 通知(抽取出来的方法信息,重读代码,加注解@Before("xxx()")
  2. 定义切入点的方法

三、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+"业务开始执行");}
}

在这里插入图片描述

五种通知类型

方面代码一般也称为通知:定义一个“切面”要实现的功能。通知有五种:

  1. 前置通知@Before:在某连接点(JoinPoint 就是要织入的业务方法)之前执行的通知。
  2. 后置通知@After:当某连接点退出时执行的通知(不论是正常结束还是发生异常)。
  3. 返回通知@AfterReturning:(最终通知)在这里可以得到业务方法的返回值。但在发生异常时无法得到返回值。
  4. 环绕通知@Around:包围一个连接点的通知,也就是在业务方法执行前和执行后执行的通知。
  5. 异常通知@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 容器

提示&#xff1a;英语很重要&#xff0c;单词是读的&#xff0c;是拼出来的&#xff0c;和拼音一样 文章目录 前言前期准备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是一门广泛应用于企业级应用程序开发的高级编程语言&#xff0c;具有较高的学习和职业发展价值。但是&#xff0c;在选择Java培训课程时&#xff0c;很多人会遇到一个问题&#xff1a;Java培训课程哪个品牌好?小编经过多方分析和比较&#xff0c;从专业培训的角度&#xf…...

leetcode19. 删除链表的倒数第 N 个结点

题目&#xff1a;leetcode19. 删除链表的倒数第 N 个结点 描述&#xff1a; 给你一个链表&#xff0c;删除链表的倒数第 n 个结点&#xff0c;并且返回链表的头结点。 思路&#xff1a; 让前面的节点比后面的节点先走n1步&#xff0c;因为从链表的尾节点的下一个节点开始&…...

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之四种事物

目录 结构事物 行为事物 分组事物&#xff1a; 注释事物 结构事物 1.类(Class) -类是对一组具有相同属性、方法、关系和语义的对象的描述。一个类实现一个或多个接口 2.接口(interface) -接口描述 了一个类或构件的一个服务的操作集。接口仅仅是定义了一组操作的规范&…...

盒子模型和新盒子模型及区别

盒子模型是用于描述 HTML 元素在页面中占据的空间的概念。它将每个元素视为一个矩形框&#xff0c;由内容区域、内边距、边框和外边距组成。这个传统的盒子模型也被称为 "标准盒子模型"。 新盒子模型是指使用 CSS3 中的 box-sizing 属性设置为 border-box 后的一种盒…...

移动端Vue组件库-vant

Vant 是有赞前端团队开源的移动端vue组件库&#xff0c;适用于手机端h5页面。 鉴于百度搜索不到vant官方网址&#xff0c;分享一下vant组件库官网地址&#xff0c;方便新手使用 vant官网地址Vant 4 - A lightweight, customizable Vue UI library for mobile web apps. 通过 …...

Java课题笔记~ JSP内置对象

(1)九个内置对象 jsp的内置对象&#xff1a;JSP内置对象是不需要声明和创建就可以在JSP页面脚本中使用的成员变量。 九个内置对象&#xff1a; 1.out对象 在JSP页面中&#xff0c;经常需要向客户端发送文本内容&#xff0c;这时&#xff0c;可以使用out对象来实现。out对象…...

数据结构笔记--链表经典高频题

目录 前言 1--反转单向链表 2--反转单向链表-II 3--反转双向链表 4--打印两个有序链表的公共部分 5--回文链表 6--链表调整 7--复制含有随机指针结点的链表 8--两个单链表相交问题 前言 面经&#xff1a; 针对链表的题目&#xff0c;对于笔试可以不太在乎空间复杂度&a…...

Android Ble蓝牙App(三)特性和属性

Ble蓝牙App&#xff08;三&#xff09;特性使用 前言正文一、获取属性列表二、属性适配器三、获取特性名称四、特性适配器五、加载特性六、显示特性和属性七、源码 前言 在上一篇中我们完成了连接和发现服务两个动作&#xff0c;那么再发现服务之后要做什么呢&#xff1f;发现服…...

日常BUG——使用Long类型作id,后端返回给前段后精度丢失问题

&#x1f61c;作 者&#xff1a;是江迪呀✒️本文关键词&#xff1a;日常BUG、BUG、问题分析☀️每日 一言 &#xff1a;存在错误说明你在进步&#xff01; 一、问题描述 数据库long类型Id: 前端返回的Id实体类: Data ApiModel("xxx") public class …...

【C++初阶】string类的常见基本使用

&#x1f466;个人主页&#xff1a;Weraphael ✍&#x1f3fb;作者简介&#xff1a;目前学习C和算法 ✈️专栏&#xff1a;C航路 &#x1f40b; 希望大家多多支持&#xff0c;咱一起进步&#xff01;&#x1f601; 如果文章对你有帮助的话 欢迎 评论&#x1f4ac; 点赞&#x1…...

【ArcGIS Pro二次开发】(60):按图层导出布局

在使用布局导图时&#xff0c;会遇到如下问题&#xff1a; 为了切换图层和导图方便&#xff0c;一般情况下&#xff0c;会把相关图层做成图层组。 在导图的时候&#xff0c;如果想要按照图层组进行分开导图&#xff0c;如上图&#xff0c;想导出【现状图、规划图、管控边界】3…...

docker-desktop数据目录迁移

1.退出docker-desktop后执行 wsl --list -v 如下 NAME STATE VERSION * docker-desktop Stopped 2docker-desktop-data Stopped 22.执行以下命令进行数据导出&#xff1a;&#xff08;需要等待命令执行完成&#xff09…...

03.利用Redis实现缓存功能---解决缓存穿透版

学习目标&#xff1a; 提示&#xff1a;学习如何利用Redis实现添加缓存功能解决缓存穿透版 学习产出&#xff1a; 缓存穿透讲解图&#xff1a; 解决方案&#xff1a; 采用缓存空对象采用布隆过滤器 解决方案流程图&#xff1a; 1. 准备pom环境 <dependency><gro…...

全景图!最近20年,自然语言处理领域的发展

夕小瑶科技说 原创 作者 | 小戏、Python 最近这几年&#xff0c;大家一起共同经历了 NLP&#xff08;写一下全称&#xff0c;Natural Language Processing&#xff09; 这一领域井喷式的发展&#xff0c;从 Word2Vec 到大量使用 RNN、LSTM&#xff0c;从 seq2seq 再到 Attenti…...

Mybatis参数传递

Map传参, #{}里的key要一一对应不能乱写&#xff0c;如果不存在则会填充NULL&#xff0c;不会报错 Map<String, Object> map new HashMap<>(); // 让key的可读性增强 map.put("carNum", "103"); map.put("brand", "奔驰E300L&…...

手动实现 Spring 底层机制 实现任务阶段一编写自己 Spring 容器-准备篇【2】

&#x1f600;前言 手动实现 Spring 底层机制的第2篇 实现了任务阶段一编写自己 Spring 容器-准备篇【2】 &#x1f3e0;个人主页&#xff1a;尘觉主页 &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是尘觉&#xff0c;希望我的文章可以帮助到大家&#xff0c;您的…...

部署模型并与 TVM 集成

本篇文章译自英文文档 Deploy Models and Integrate TVM tvm 0.14.dev0 documentation 更多 TVM 中文文档可访问 →Apache TVM 是一个端到端的深度学习编译框架&#xff0c;适用于 CPU、GPU 和各种机器学习加速芯片。 | Apache TVM 中文站 本节介绍如何将 TVM 部署到各种平台&…...

预训练语言模型T5-11B的简要介绍

文章目录 模型基本信息架构特点性能表现应用场景 T5-11B 是谷歌提出的一种基于 Transformer 架构的预训练语言模型&#xff0c;属于 T5&#xff08;Text-To-Text Transfer Transformer&#xff09;模型系列&#xff0c;来自论文 Colin Raffel, Noam Shazeer, Adam Roberts, Kat…...

pikachu靶场通关笔记20 SQL注入03-搜索型注入(GET)

目录 一、SQL注入 二、搜索型注入 三、源码分析 1、渗透思路1 2、渗透思路2 四、渗透实战 1、渗透准备 2、SQL注入探测 &#xff08;1&#xff09;输入百分号单引号 &#xff08;2&#xff09;万能注入语句 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应用(心脏病预测)

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 目标 具体实现 &#xff08;一&#xff09;环境 语言环境&#xff1a;Python 3.10 编 译 器: PyCharm 框 架: Pytorch &#xff08;二&#xff09;具体步骤…...

React Navive初识

文章目录 搭建开发环境安装 Node、homebrew、Watchman安装 Node安装 homebrew安装 watchman 安装 React Native 的命令行工具&#xff08;react-native-cli&#xff09;创建新项目编译并运行 React Native 应用在 ios 模拟器上运行 调试访问 App 内的开发菜单 搭建开发环境 在…...

PCB特种工艺应用扩展:厚铜、高频与软硬结合板

新能源汽车与消费电子驱动PCB特种工艺创新&#xff0c;厚铜板降阻30%&#xff0c;软硬结合板渗透率年增15%。 1. 厚铜板&#xff1a;新能源高压平台核心 技术突破&#xff1a;猎板PCB量产10oz厚铜板&#xff08;传统为3oz&#xff09;&#xff0c;载流能力提升200%&#xff0c…...

MATLAB实战:视觉伺服控制实现方案

以下是一个基于MATLAB的视觉伺服控制项目实现方案&#xff0c;结合实时图像处理、目标跟踪和控制系统设计。我们将使用模拟环境进行演示&#xff0c;但代码结构可直接应用于真实硬件。 系统架构 图像采集 → 目标检测 → 误差计算 → PID控制器 → 执行器控制 完整代码实现 …...

如何理解机器人课程的技术壁垒~壁垒和赚钱是两件不同的事情

答疑&#xff1a; 有部分朋友私聊说博客内容&#xff0c;越来越不适合人类阅读习惯…… 可以做这种理解&#xff0c;我从23年之后&#xff0c;博客会不会就是写给机器看的。 或者说我在以黑盒方式测试AI推荐的风格。 主观-客观-主观螺旋式发展过程。 2015最早的一篇博客重…...

为什么 uni-app 开发的 App 没有明显出现屏幕适配问题Flutter 开发的 App 出现了屏幕适配问题

&#x1f9e9; 一、为什么 uni-app 开发的 App 没有明显出现屏幕适配问题&#xff1f; ✅ 1. uni-app 是基于 H5 的运行环境&#xff08;或类 H5&#xff09; uni-app 默认使用的是 H5 的渲染引擎&#xff08;如 WebView 或小程序渲染引擎&#xff09;。在 H5 中&#xff0c;…...

学习数字孪生,为你的职业发展开辟新赛道

你有没有想过&#xff0c;未来十年哪些技能最吃香&#xff1f; AI、大数据、智能制造、元宇宙……这些词频繁出现在招聘市场和行业报告中。而在它们背后&#xff0c;隐藏着一个“看不见但无处不在”的关键技术——数字孪生&#xff08;Digital Twin&#xff09;。 它不仅在制造…...