Spring 容器

news/2025/1/15 12:21:50/

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

文章目录

  • 前言
  • 前期准备
  • 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实现方案: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;}

再次运行:
在这里插入图片描述



http://www.ppmy.cn/news/1021831.html

相关文章

中级课程——CSRF

文章目录 案例原理挖掘 案例 原理 挖掘 挖掘详情 首先就是对目标敏感部位进行抓包分析&#xff0c;比如修改信息、转账、添加信息等等。通常一个数据包HTTP请求头里边都会有一个Referer&#xff0c;这个需要特别去验证。比如放到Burpsuit Repeater里边去测试&#xff1a;去掉…

【程序员养生】养胃

文章目录 1. 程序员在养胃方面需要注意什么&#xff1f;2. 为什么程序员需要注意养胃&#xff1f;3. 程序员如何养胃&#xff1f;4. 程序员何时应该养胃&#xff1f;5. 谁应该关注程序员的饮食和养生&#xff1f;6. 程序员在何处养胃&#xff1f;7. 程序员需要养胃多久&#xf…

mysql8.0.3集群搭建

下载mysql安装包&#xff1a; https://dev.mysql.com/downloads/mysql/5.7.html#downloads 准备环境 1、准备三台服务器并设置hosts 192.168.236.143 mysql1 192.168.236.144 mysql2 192.168.236.145 mysql32、设置免密登陆 #生成秘钥 ssh-keygen -t rsa #一直按Enter即可…

Kubernetes入门 三、命令行工具 kubectl

目录 语法操作示例资源操作Pod 与集群资源类型与别名格式化输出 kubectl 是 Kubernetes 集群的命令行工具&#xff0c;通过它能够对集群本身进行管理&#xff0c;并能够在集群上进行容器化应用的安装和部署。 语法 使用以下语法从终端窗口运行 kubectl 命令&#xff1a; kub…

第十五章、【Linux】例行性工作调度

15.1 什么是例行性工作调度 在不考虑硬件与服务器的链接状态下&#xff0c;Linux可以帮助提醒许多任务。Linux调度就是通过crontab与at这两个东西。 15.1.1 Linux工作调度的种类&#xff1a;at,cron 从上面的说明当中&#xff0c;我们可以很清楚的发现两种工作调度的方式&am…

将应用设置成系统App/获取Android设备SN号

1&#xff0c;和系统签名一致&#xff1b;&#xff08;签名设置签名文件&#xff09; 2&#xff0c;配置Manifest 至此你的App就是一个系统App了&#xff0c;可以执行一些系统App才能有的操作&#xff0c;如获取机器SN号&#xff1a; public String getSerialNumber() {Strin…

智慧工地源码,Spring Cloud+ Vue+UniApp开发,微服务架构

智慧工地源码&#xff0c;智慧工地云平台源码 智慧工地APP源码 智慧工地的核心是数字化&#xff0c;它通过传感器、监控设备、智能终端等技术手段&#xff0c;实现对工地各个环节的实时数据采集和传输&#xff0c;如环境温度、湿度、噪音等数据信息&#xff0c;将数据汇集到云…

前端高级面试题-JS

1. 原型 / 构造函数 / 实例 原型( prototype ): ⼀个简单的对象&#xff0c;⽤于实现对象的 属性继承。可以简单的理解成对象的爹。在 Firefox 和 Chrome 中&#xff0c;每个 JavaScript 对象中都包含⼀个__proto__ (⾮标准)的属性指向它爹(该对象的原型)&#xff0c;可 obj.p…