Spring6详细学习笔记(IOC+AOP)

news2024/12/23 13:54:12

一、Spring系统架构介绍

1.1、定义

Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器(框架)。Spring官网
Spring是一款主流的Java EE 轻量级开源框架,目的是用于简化Java企业级引用的开发难度和开发周期。从简单性、可测试性和松耦合度的角度而言,任何Java应用都可以从Spring中受益。Spring框架提供自己提供功能外,还提供整合其他技术和框架的能力。
Spring中默认Bean的名称是对应类的首字母小写

1.2、Spring核心

​ Spring指的是Spring Framework,通常我们称之为Spring框架。Spring框架是一个分层的面向切面的Java应用程序的一站式解决框架,它是Spring技术栈的核心和基础,是为了解决企业级引用开发的复杂性而创建的。

​ Spring有两个核心模块:IoC和AOP。

  1. Ioc:Inverse of Control的简写,为 控制反转,指把创建对象交给Spring进行管理。
  2. ​ AOP:Aspect Oriented Programming 的简写,为 面向对象编程。AOP用来封装多个类的公共行为,将那些与业务无关,却为业务模块共同调用的逻辑封装起来,减少系统的重复代码,降低模块间的耦合度。另外,AOP还解决一些系统层面上的问题,比如日志、事务、权限等。

1.3、Spring Framework的特点

  1. 控制反转:IoC,反转资源获取方向;把自己创建的资源、向环境索取资源变为环境将资源准备好,我们享受资源注入。
  2. 面向切面编程:AOP,在不修改源代码的基础上增强代码功能。
  3. 容器:SpringIoC是一个容器,因为它包含并管理组件对象的生命周期;组件享受到了容器化的管理,替程序员屏蔽了组件创建过程中的大量细节,极大降低了使用门槛,大幅度提高了开发效率。
  4. 一站式:在IOC和AOP的基础上可以整合各种企业应用的开源框架和优秀的第三方库,而且在Spring旗下的项目已经覆盖了广泛领域,很多方面的功能性需求可以在SpringFramework 的基础上全部使用Spring来实现。
    在这里插入图片描述
    在这里插入图片描述

1.4、Spring优点

1、Spring是一个开源免费的框架 , 容器 .
2、Spring是一个轻量级的框架(导入包就可以用) , 非侵入式的 (引入后不会对原来的代码造成影响).
3、控制反转 IoC , 面向切面 Aop
4、对事物的支持 , 对框架的支持

1.5、使用准备

Spring6要求最低使用JDK17版本
Maven依赖包Spring WebMVC和Spring JDBC:
在这里插入图片描述

<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>6.1.11</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>6.1.11</version>
</dependency>

1.6、Spring Boot 与Spring Cloud

  • Spring Boot 是 Spring 的一套快速配置脚手架,可以基于Spring Boot 快速开发单个微服务;
  • SpringCloud是基于Spring Boot实现的;
  • Spring Boot专注于快速、方便集成的单个微服务个体,SpringCloud关注全局的服务治理框架;
  • Spring Boot使用了约束优于配置的理念,很多集成方案已经帮你选择好了,能不配置就不配置 ,
    Spring Cloud很大的一部分是基于Spring Boot来实现,Spring Boot可以离开SpringCloud独立使用开发项目,但是Spring Cloud离不开Spring Boot,属于依赖的关系。
  • SpringBoot在SpringClound中起到了承上启下的作用,如果你要学习SpringCloud必须要学习SpringBoot。

二、入门案例

2.1、基本开发步骤

在这里插入图片描述

2.2、创建工程与案例实现

在这里插入图片描述
创建子模块:在这里插入图片描述在这里插入图片描述

1、第一步:子工程的Pom中添加依赖

        <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>6.1.11</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/junit/junit -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>

2、第二步:创建类,定义属性方法

public class User {
    public void add(){
        System.out.println("Spring 测试!");
    }
}

3、第三步:按照Spring要求创建配置文件(XML格式)

创建spring配置文件:resources目录下创建bean.xml
在这里插入图片描述
配置相关信息(完成类对象创建)

<!--完成user对象创建
        id属性:唯一标识
        class属性:要创建的对象所在类的绝对路径
    -->
    <bean id="user" class="cn.tedu.spring.User"></bean>

在这里插入图片描述

4、测试

public class Test {
    @org.junit.Test
    public void testUserObject01(){
        //加载Spring配置文件,创建对象
        ApplicationContext context=new ClassPathXmlApplicationContext("bean.xml");
        //获取创建的对象,默认创建的对象时Object类型,转为对应类的类型即可Object user = context.getBean("user");
        User user =(User) context.getBean("user");
        //使用对象进行方法调用
        user.add();

    }
}

在这里插入图片描述

2.3 、案例分析

1、对象创建,无参构造是否执行?

public class User {
    public User() {
        System.out.println("这是User类的无参构造!");
    }

    public void add(){
        System.out.println("Spring 测试!");
    }
}

在这里插入图片描述

2、不用new的形式,还可以怎样创建对象?

在这里插入图片描述

1、反射-创建对象的过程
    @org.junit.Test
    public void Test() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        //01\通过Class.forName获取需要创建对象的类路径
        Class<?> aClass = Class.forName("com.bipt.User");
        //02\调用方法创建对象
        User user =(User) aClass.getDeclaredConstructor().newInstance();
        //03\测试对象
        user.add();
    }

在这里插入图片描述

3、创建的对象放哪里?

双击Shift输入DefaultListableBeanFactory:名字中的BeanFactory可以理解为Bean工厂用来生产Bean
在这里插入图片描述
创建的对象存放再Map集合中:

private final Map<String, BeanDefinition> beanDefinitionMap;
this.beanDefinitionMap = new ConcurrentHashMap

集合中的<Key,Value>
key:唯一标识
value:类的定义(描述信息)
在这里插入图片描述

可以查看 BeanDefinition 的源码,有类的描述信息,是否初始化的状态等等

2.4 Log4J2的日志框架(2.XX)

在这里插入图片描述

1、引入依赖

         <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <version>2.15.0</version>
        </dependency>
        
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.15.0</version>
        </dependency>
        
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-slf4j-impl</artifactId>
            <version>2.15.0</version>
        </dependency>

2、配置文件

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} %-5level %logger{36} - %msg%n"/>
        </Console>
        <File name="File" fileName="log-output/app.log" append="false">
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} %-5level %logger{36} - %msg%n"/>
        </File>
    </Appenders>
    <Loggers>
        <Root level="debug">
            <AppenderRef ref="Console"/>
            <AppenderRef ref="File"/>
        </Root>
    </Loggers>
</Configuration>

在这里插入图片描述
在这里插入图片描述

3、通过Logger对象手动向日志插入

在这里插入图片描述

   @org.junit.Test
    public void testUserObject01(){
        /*创建Log对象*/
        Logger logger = LoggerFactory.getLogger(Test.class);

        //加载Spring配置文件,能够读取到User类的路径信息,用于后面通过反射创建对象
        ApplicationContext context=new ClassPathXmlApplicationContext("bean.xml");
        //获取创建的对象,默认创建的对象时Object类型,转为对应类的类型即可Object user = context.getBean("user");
        User user =(User) context.getBean("user");
        //使用对象进行方法调用
        user.add();
        logger.info("调用Logger成功");
    }

在这里插入图片描述

三、容器:IOC

3.1、IOC容器

1、简介

  • Spring通过IoC容器管理所有的Java对象的实例化和初始化控制着对象与对象之间的依赖关系
  • 我们将由IoC容器管理的Java对象称为 Spring Bean,它与使用关键字 new 创建的Java对象没有任何区别
    在这里插入图片描述

2、IOC容器创建bean的过程

在这里插入图片描述

3、依赖注入

DI (Dependency Injection):依赖注入,依赖注入实现了控制反转的思想,是指Spring创建对象的过程中,将对象依赖属性通过配置进行注入。

依赖注入常见的实现方式有两种:

  1. set注入
  2. 构造注入

所以 IoC 是一种控制反转的思想,而 DI 是对IoC的一种具体实现。
Bean管理:指Bean对象的创建,以及Bean对象中属性的赋值(或Bean对象之间关系的维护)

4、IOC容器实现

Spring中的IoC容器就是IoC思想的一个落地产品实现。IoC容器中管理的组件也叫做bean。在创建bean之前,首先需要创建IoC容器,Spring提供了IoC容器的两种实现方式

① BeanFactory

​ 这是IoC容器的基本实现,是Spring内部使用的接口。面向Spring本身,不提供给开发人员使用。我们使用下面它的子接口ApplicationContext

//加载Spring配置文件,能够读取到User类的路径信息,用于后面通过反射创建对象
//ClassPathXmlApplicationContext是ApplicationContext 接口的一个实现类
ApplicationContext context=new ClassPathXmlApplicationContext("bean.xml");

② ApplicationContext

​ BeanFactory的子接口,提供了更多高级特性,面向Spring的使用者,几乎所有场合都使用 ApplicationContext,而不是底层的BeanFactory
在这里插入图片描述
在这里插入图片描述

3.2、基于XMl管理bean

3.2.1、 获取bean的三种方式

1、环境配置

在这里插入图片描述
创建Spring配置文件bean.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--通过标签完成类对象创建bean标签-->
    <!--完成user对象创建
        id属性:唯一标识
        class属性:要创建的对象所在类的绝对路径
    -->
    
</beans>

创建实体类:User

public class User {
    private String name;
    private int age;
    public User() {
        System.out.println("这是User类的无参构造!");
    }

    public void add(){
        System.out.println("Spring 测试!");
    }
}

2、根据Id、类型、Id+类型获取bean

在这里插入图片描述

    @org.junit.Test
    public void testUserObject01(){

        //加载Spring配置文件,能够读取到User类的路径信息,用于后面通过反射创建对象
        ApplicationContext context=new ClassPathXmlApplicationContext("bean.xml");
        //获取创建的对象,默认创建的对象时Object类型,转为对应类的类型即可Object user = context.getBean("user");
        User user =(User) context.getBean("user");
        System.out.println("1-根据Id获取bean"+user);

        User user2 = context.getBean(User.class);
        System.out.println("2-根据类型获取bean:" + user2);

        User user3 = context.getBean("user", User.class);
        System.out.println("3-根据id和类型获取bean:" + user3);

在这里插入图片描述

3、问题:

当根据类型获取bean时,要求IoC容器中指定类型的bean只能有一个,当配置两个时会抛出异常,通过类型获取的时候期望创建单实例bean在这里插入图片描述在这里插入图片描述

4、解决

当一个bean配置文件中对于同一个类有多个bean时,获取bean对象要么通过id获取要么通过类型+id方式获取。
在这里插入图片描述

5、根据接口类型获取bean

在这里插入图片描述
在这里插入图片描述
问题1:如果组件类实现了接口,可以通过接口类型获取bean吗?
在这里插入图片描述
问题2:接口有多个实现类,通过接口类型获取bean可以吗?
在这里插入图片描述在这里插入图片描述
在这里插入图片描述

6、总结

在这里插入图片描述

3.2.2、依赖注入

类有属性,创建对象过程中,向属性注入具体的值,所谓的注入其实就是给依赖的成员变量赋值
在这里插入图片描述

创建实体类

package com.bipt.DI;

/**
 * @author Pengwei Qu
 * @Date 2024/9/3 13:43
 */
public class Book {
    private String bookName;
    private String bookAuthor;

    public Book() {
        
    }

    public Book(String bookName, String bookAuthor) {
        this.bookName = bookName;
        this.bookAuthor = bookAuthor;
    }

    public String getBookName() {
        return bookName;
    }

    public void setBookName(String bookName) {
        this.bookName = bookName;
    }

    public String getBookAuthor() {
        return bookAuthor;
    }

    public void setBookAuthor(String bookAuthor) {
        this.bookAuthor = bookAuthor;
    }
}

在这里插入图片描述
下面通过配置文件实现

3.2.3、依赖注入之Setter注入

在这里插入图片描述

    <!--通过Set方法完成注入-->
    <bean id="book" class="com.bipt.DI.Book">
        <property name="bookName" value="前端开发"></property>
        <property name="bookAuthor" value="Jack"></property>
    </bean>
    @Test
    public void DiTest01(){
        ApplicationContext applicationContext= new ClassPathXmlApplicationContext("beanDI.xml");
        Book book =(Book) applicationContext.getBean("book", "com.bipt.DI.Book");
        System.out.println(book);/*由于实体类Book中重写了toString直接输出对象输出的是属性值*/
    }

3.2.4、依赖注入之构造注入

有参构造进行测试

    <bean id="book_construct" class="com.bipt.DI.Book">
        <constructor-arg name="bookName" value="Java基础"/>
        <constructor-arg  name="bookAuthor" value="Tom"/>
    </bean>
    @Test
    public void DiTest02(){
        ApplicationContext applicationContext= new ClassPathXmlApplicationContext("beanDI.xml");
        Book book =(Book) applicationContext.getBean("book_construct", "com.bipt.DI.Book");
        System.out.println(book);/*由于实体类Book中重写了toString直接输出对象输出的是属性值*/
    }

3.2.5、特殊值处理

1、字面量

string number = 10;
声明一个变量number,初始化为 10,此时number就不代表字符number了,而是作为一个变量的名字。当引用number时,实际拿到的结果是 10
而如果number是带引号的 “number” ,则它不是一个变量,而代表 number 本身这个字符串。
这就是字面量,所以字面量没有引申含义,就是我们看到的这个数据本身

<!-- 使用value属性给bean的属性赋值时,spring会把value的属性值看作是字面量 -->
<property name="number" value="1016"></property>

2、null

在这里插入图片描述
使用或者null标签实现注入。

    <!--通过Set方法完成注入-->
    <bean id="book" class="com.bipt.DI.Book">
        <property name="bookName" value="前端开发"></property>
        <property name="bookAuthor" value="Jack"></property>
        <property name="other">
            <null></null>
        </property>
    </bean>
    
    <!--通过构造方法注入-->
    <bean id="book_construct" class="com.bipt.DI.Book">
        <constructor-arg name="bookName" value="Java基础"/>
        <constructor-arg  name="bookAuthor" value="Tom"/>
        <constructor-arg  name="other" >
            <null></null>
        </constructor-arg>
    </bean>

3、Xml实体

在这里插入图片描述

4、CDATA区

CDATA区,是xml中一种特有的写法,在CDATA区中可以包含特殊符号
表示方式:
​ <![CDATA[内容]]> ,在内容区域可以存放普通字符和特殊符号

3.2.6、对象类型注入

在这里插入图片描述

1、引用外部bean:ref

在这里插入图片描述

2、引用内部bean

在这里插入图片描述

3、级联赋值

在这里插入图片描述

3.2.7、数组类型属性注入

<!-- 创建Person对象并注入属性 -->
<bean id="person" class="cn.tedu.spring.diarray.Person">
    <!-- 普通属性注入 -->
    <property name="name" value="孙悟空"/>
    <property name="age" value="36"/>
    <!-- 数组属性注入,使用<array>标签 -->
    <property name="hobby">
        <array>
            <value>抽烟</value>
            <value>喝酒</value>
            <value>烫头</value>
        </array>
    </property>
</bean>

3.2.8、集合类型的注入

1、List集合

Teacher类

public class Teacher {

    // 老师姓名
    private String tName;
    // 老师所教学生的对象,放到List集合中
    private List<Student> studentList;

    public String gettName() {
        return tName;
    }

    public void settName(String tName) {
        this.tName = tName;
    }

    public List<Student> getStudentList() {
        return studentList;
    }

    public void setStudentList(List<Student> studentList) {
        this.studentList = studentList;
    }

    @Override
    public String toString() {
        return "Teacher{" +
                "tName='" + tName + '\'' +
                ", studentList=" + studentList +
                '}';
    }
}


Student类:

public class Student {
    // 学生姓名、年龄
    private String sName;
    private String age;

    public String getsName() {
        return sName;
    }

    public void setsName(String sName) {
        this.sName = sName;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "sName='" + sName + '\'' +
                ", course='" + course + '\'' +
                '}';
    }
}


<!-- 创建2个Student对象,用于Teacher对象的注入 -->
<bean id="stu1" class="cn.tedu.spring.dimap.Student">
    <property name="sName" value="梁山伯"/>
    <property name="age" value="43"/>
</bean>
<bean id="stu2" class="cn.tedu.spring.dimap.Student">
    <property name="sName" value="祝英台"/>
    <property name="age" value="33"/>
</bean>

<!-- 创建Teacher类的bean对象,并注入属性 -->
<bean id="teacher" class="cn.tedu.spring.dimap.Teacher">
    <!-- 普通属性注入 -->
    <property name="tName" value="沙师弟"/>
    <!-- List集合属性注入 -->
    <property name="studentList">
        <list>
            <ref bean="stu1"/>
            <ref bean="stu2"/>
        </list>
    </property>
</bean>

2、Map集合

// 老师姓名
    private String tName;
    // 老师所教学生的对象,放到List集合中
    private List<Student> studentList;
    //Map集合
    private Map<String,Student> map;
 <bean id="stu3" class="com.bipt.DI_Collection.Student">
        <property name="sName" value="刘备"/>
        <property name="age" value="35"/>
 </bean>
    <!--老师类Map注入-->
    <bean id="tea2" class="com.bipt.DI_Collection.Teacher">
        <property name="tName" value="李老师"/>
        <property name="map">
            <map>
                 <entry>
                    <key><value>1001</value></key>
                    <ref bean="stu3"/>
                </entry>
                <entry>
                    <key><value>1002</value></key>
                    <ref bean="stu2"/>
                </entry>
            </map>
        </property>
    </bean>

3、引用集合类型bean注入

因为要使用utils标签,在xml配置文件中引入util约束

xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/util http://www.springframework.org/schema/beans/spring-util.xsd"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"
<!-- Map集合util标签 -->
<util:map id="xxx"></util:map>

<!-- List集合util标签 -->
<util:list id="xxx"></util:list>

<!--引用集合类型bean注入-->
<bean id="stuUtil" class="cn.tedu.spring.dilistmap.Student">
    <property name="sName" value="孔慈"/>
    <property name="age" value="36"/>
    <property name="teacherMap" ref="teacherMap"></property>
    <property name="courseList" ref="courseList"></property>
</bean>

<util:map id="teacherMap">
    <entry>
        <key>
            <value>10000</value>
        </key>
        <value>小泽老师</value>
    </entry>
    <entry>
        <key>
            <value>10001</value>
        </key>
        <value>王老师</value>
    </entry>
</util:map>

<util:list id="courseList">
    <value>Spring</value>
    <value>SpringMVC</value>
    <value>MyBatis</value>
</util:list>

3.2.9、p命名空间注入

这也是一种注入方式,可以在xml中定义命名空间或者叫名称空间,可以简化xml代码。
① 在xml配置文件中定义命名空间

xmlns:p="http://www.springframework.org/schema/p"

② 在xml文件进行命名空间属性注入

<!-- p命名空间注入: 注入学生属性 -->
<bean id="studentp" class="cn.tedu.spring.iocxml.dimap.Student" p:sid="100" p:sname="铁锤妹妹" p:courseList-ref="courseList" p:teacherMap-ref="teacherMap">

3.2.10、引入外部属性文件

  • 当前所有的配置和数据都在xml文件中,一个文件中有很多bean,修改和维护起来很不方便,生产环境中会把特定的固定值放到外部文件中,然后引入外部文件进行注入,比如数据库连接信息。
  • 将外部文件中的数据引入xml配置文件进行注入,xml文件中的内容就不需要更改,如果更改数据直接更改外部文件的数据就行

1、以数据库文件为例:引入连接数据库的依赖包

<!--01mysql包-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.33</version>
        </dependency>
<!--连接池依赖-->            
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.2.8</version>
</dependency>

在这里插入图片描述
Spring配置文件修改:在这里插入图片描述
引入外部文件jdbc.properties
在这里插入图片描述

<!-- 引入外部属性文件 jdbc.properties-->
    <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
    <!-- 完成数据库信息注入 -->
    <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="url" value="${jdbc.url}"></property>
        <property name="username" value="${jdbc.user}"></property>
        <property name="password" value="${jdbc.password}"></property>
        <property name="driverClassName" value="${jdbc.driver}"></property>
    </bean>

jdbc.properties文件:

jdbc.user=root
jdbc.password=123456
jdbc.url=jdbc:mysql://localhost:3306/mybatis01?useSSL=false&useUnicode=true&characterEncoding=UTF-8&useServerPrepStmts=true
jdbc.driver=com.mysql.jdbc.Driver

spring中properties文件中的属性为什么要加jdbc作为前缀?
如果属性文件中配置的不是jdbc.username,而是username=root666,那么使用${username}获取到的不是root666,而是计算机的名称。

原因:系统属性的优先级比我们属性文件中的高,替换了我们的username=root666。

3.2.11、Bean的作用域范围Scope

bean的作用域,是指在交给spring创建bean对象时,可以指定是单实例还是多实例,通过bean标签中的scope属性来指定,默认是单实例。

  • 单实例
    单实例(Singleton)是指某个类只能创建唯一的一个实例对象,并且该类提供一个全局的访问点(静态方法)来让外界获取这个实例,常常用在那些只需要一个实例来处理所有任务的场景下,例如配置类或数据库连接池等。
  • 多实例
    多实例(Multiple Instance)则是指可以在同一个类的定义下,创建多个实例对象。每个对象都是相互独立的,有自己的状态和行为;常常用于需要同时处理多个任务的场景。

在这里插入图片描述

1、单实例scope=“singleton”

    <!--默认就是singleton单实例-->
    <bean id="Scope1" class="com.bipt.Scope.Order" scope="singleton">
        <property name="orderId" value="001"/>
    </bean>

在这里插入图片描述

2、多实例scope=“prototype”

    <bean id="Scope1" class="com.bipt.Scope.Order" scope="prototype">
        <property name="orderId" value="001"/>
    </bean>

在这里插入图片描述

3.2.12、Bean的生命周期

在这里插入图片描述
① 创建包life,创建类User

package cn.tedu.spring.life;

import org.springframework.beans.BeansException;

public class User {
    private String username;

    // 1.无参数构造
    public User(){
        System.out.println("1-bean对象创建,调用无参数构造。");
    }

    // 3.初始化阶段
    public void initMethod(){
        System.out.println("3-bean对象初始化,调用指定的初始化方法");
    }

    // 5.销毁阶段
    public void destoryMethod(){
        System.out.println("5-bean对象销毁,调用指定的销毁方法");
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
        // 2.给bean对象属性赋值
        System.out.println("2-通过set方法给bean对象赋值。");
    }

    @Override
    public String toString() {
        return "User{" +
                "username='" + username + '\'' +
                '}';
    }
}

② 创建spring配置文件 bean-life.xml

<bean id="user" class="cn.tedu.spring.iocxml.life.User" scope="singleton" 
      init-method="initMethod" destroy-method="destroyMethod">
    <property name="username" value="聂风"></property>
</bean>

③ 创建测试类TestUser测试
ClassPathXmlApplicationContext类中才有close方法,所以拆改那就context对象的时候要使用类,不再用ApplicationContext接口。

@Test
public void testUser(){
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean-life.xml");
    User user = context.getBean("user", User.class);
    // 4.bean对象初始化完成,可以使用
    System.out.println("4-bean对象初始化完成,开发者可以使用了。");
    // 销毁bean
    context.close();
}

1、初始化前后两步测试

在这里插入图片描述
1、后置处理器处理演示,新建类MyBeanPost实现BeanPostProcessor

public class MyBeanPost implements BeanPostProcessor {
    // BeanPostProcessor接口
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("3之前:bean后置处理器,在初始化之前执行。" + beanName + ":" + bean);
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("3之后:bean后置处理器,在初始化之后执行。" + beanName + ":" + bean);
        return bean;
    }
}

2、配置文件中配置

<!-- bean的后置处理器需要放到IoC容器中才能生效 -->
<bean id="myBeanPost" class="cn.tedu.spring.life.MyBeanPost"></bean>

3.2.13FactoryBean

在这里插入图片描述
在这里插入图片描述

public class MyFactoryBean implements FactoryBean<User> {
    @Override
    public User getObject() throws Exception {
        return new User();
    }

    @Override
    public Class<?> getObjectType() {
        return User.class;
    }
}
<bean id="user" class="com.bipt.FactoryBean.MyFactoryBean">
public class FactoryBeanTest {
    @Test
    public void Test(){
        ApplicationContext app1=new ClassPathXmlApplicationContext("beanFactoryBean.xml");
        User user = (User) app1.getBean("user");
        System.out.println(user);
    }
}

3.2.14自动装配

自动装配说明:
根据指定的策略,在IoC容器中匹配某一个bean,自动为指定的bean中的所依赖的类类型或者接口类型属性赋值。
在这里插入图片描述
① 创建包auto,创建部门和员工的两个java类

② 部门类 Dept

public class Dept {
    private String dName;

    @Override
    public String toString() {
        return "Dept{" +
                "dName='" + dName + '\'' +
                '}';
    }

    public String getdName() {
        return dName;
    }

    public void setdName(String dName) {
        this.dName = dName;
    }
}


③ 员工类 Emp,员工类中定义部门的对象,并在配置文件中的Emp对应的bean标签引入autoWire

public class Emp {
    private String eName;
    private Dept dept;

    @Override
    public String toString() {
        return "Emp{" +
                "eName='" + eName + '\'' +
                ", dept=" + dept +
                '}';
    }

    public String geteName() {
        return eName;
    }

    public void seteName(String eName) {
        this.eName = eName;
    }

    public Dept getDept() {
        return dept;
    }

    public void setDept(Dept dept) {
        this.dept = dept;
    }
}

在这里插入图片描述在这里插入图片描述
④ 创建spring配置文件bean-auto.xml

<!--通过byType和byName自动装配-->
<bean id="dept" class="cn.tedu.spring.iocxml.auto.Dept">
    <property name="dName" value="技术部"></property>
</bean>

<!--autowire="byType" 或者 autowire="byName"-->
<bean id="emp" class="cn.tedu.spring.iocxml.auto.Emp" autowire="byType">
    <property name="eName" value="步惊云"></property>
</bean>

3.3、基于注解管理bean

  • 从Java5开始,Java增加了对注解(Annotation)的支持,它是代码中的一种特殊标记,可以在编译、类加载和运行时被读取,执行相应的处理。开发人员可以通过注解在不改变原有代码和逻辑的情况下,在源代码中嵌入补充信息。
  • ​ Spring从2.5版本开始提供了对注解技术的全面支持,我们可以使用注解来实现自动装配,简化Spring的xml配置。

3.3.1、使用步骤

Spring通过注解实现自动装配:

  • 1、引入SpringFramework依赖
<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.24</version>
    </dependency>
</dependencies>

  • 2、开启组件扫描
    Spring默认不使用注解装配Bean,因此需要在Spring的xml配置中,通过context:component-scan元素开启Spring Beans的自动扫描功能。开启此功能后,Spring会自动从扫描指定的包(basepackage属性设置)及其子包下的所有类,如果类上使用了@Component注解,就将该类装配到容器中。

在这里插入图片描述

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd

       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 2.开启组件扫描,让spring可以通过注解方式实现bean管理,包括创建对象、属性注入 -->
    <!-- base-package:扫描哪个包中的注解,在com.bipt的包或者子包中建了类,在
     类上、属性上、方法上加了spring的@Component注解,这里就能扫描到-->
   <context:component-scan base-package="com.bipt"></context:component-scan>
    
</beans>

  • 3、使用注解定义
    Spring提供了以下多个注解,这些注解可以直接标注在java类上,将它们定义成Spring Bean。在这里插入图片描述
  • 4、Bean 依赖注入:@Autowired注入@Resource注入
    单独使用@Autowired注解,默认根据类型装配(byType)
    @Autowired注解有一个required属性,默认值是true,表示在注入的时候要求被注入的Bean必须存在,如果不存在则报错。如果required属性设置为false,表示注入的Bean存在或者不存在都没关系,存在就注入,不存在也不报错。
    在这里插入图片描述在这里插入图片描述

3.3.2、@Autowired,默认bytype装配

1、基本环境配置

在这里插入图片描述

2、@AutoWired在属性上注入

在这里插入图片描述
1、bean对象创建,通过加注解实现bean对象创建,注意:注解是加在实现类上不是接口上
在这里插入图片描述

创建了三个bean对象
2、在Controller实现类中注入Service,在Service实现类中注入Dao
在这里插入图片描述

@Controller
public class UserControllerImp implements UserController{
    // 注入service
    // 第一种方式:属性注入
    @Autowired // 根据类型找到对象,完成注入
    private UserService userService;

    public void addController(){
        System.out.println("Controller实现类中的方法...............);");
        userService.addService();
    }
}

在这里插入图片描述

@Service
public class UserServiceImp implements UserService{
    // 第一种方式:属性注入
    @Autowired
    private UserDao userDao;
    @Override
    public void addService() {
        System.out.println("Service实现类中的方法...............");
    }
}

UserImp

@Repository
public class UserDaoImp implements UserDao{
    @Override
    public void addDao() {
        System.out.println("Dao实现类中的方法...............");
    }
}

测试:

 @org.junit.Test
    public void testUser2(){
        ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
        UserController controller = context.getBean(UserController.class);
        controller.addController();
    }

在这里插入图片描述

3、@AutoWired在set方法上注入

// 方式二:通过set方法注入
private UserService userService;

@Autowired
public void setUserService(UserService userService) {
    this.userService = userService;
}

4、@AutoWired在构造方法上注入

// 第三种方式:构造方法注入
private UserService userService;

@Autowired
public UserController(UserService userService) {
    this.userService = userService;
}

5、@AutoWired在形参上注入

// 第四种方式:形参注入
private UserService userService;

public UserController(@Autowired UserService userService) {
    this.userService = userService;
}

6、@Autowire注解和@Qualifier注解联合

而@Autowired注解根据byType定位,如果某接口存在两个实现类,会报错,需要通过@Qualifier指定使用哪个实现类。

创建第二个实现类:

@Repository
public class UserDaoImp2 implements UserDao{
    @Override
    public void addDao() {
        System.out.println("UserDao第二个实现类");
    }
}

在这里插入图片描述在这里插入图片描述
在这里插入图片描述

通过注解@Qualifier解决问题:value = "userDaoImp2"默认是类名首字母小写,在Spring框架中,@Qualifier注解通常与@Autowired注解一起使用,以便在存在多个相同类型Bean的情况下,指定要注入的具体Bean。其中@Qualifier注解的value属性用于指定注入的Bean的名称。传入的类名首字母小写:这是因为Spring在默认情况下,Bean的名称是类名的首字母小写的形式

@Service
public class UserServiceImp implements UserService{
    // 第一种方式:属性注入
    @Autowired
    @Qualifier(value = "userDaoImp2")
    private UserDao userDao;
    @Override
    public void addService() {
        System.out.println("Service实现类中的方法...............");
        userDao.addDao();
    }

}

在这里插入图片描述

3.3.3、 @Resource注入,默认ByName装配

在这里插入图片描述
在这里插入图片描述

<!-- https://mvnrepository.com/artifact/jakarta.annotation/jakarta.annotation-api -->
<dependency>
    <groupId>jakarta.annotation</groupId>
    <artifactId>jakarta.annotation-api</artifactId>
    <version>2.1.1</version>
</dependency>

1、根据名称进行注入

1、默认根据名称注入:
在这里插入图片描述
2、如果名称找不到或没配置使用属性名注入在这里插入图片描述在这里插入图片描述
3、@Service(value=“类名”)类名没有配置,属性名也和类@Repository(value = “UserDaoImp2”)中的UserDaoImp2不一样,会自动变成根据类型查找ByType
在这里插入图片描述

3.3.4面对多个实现类两种注解方式的解决方法

报错:由于有两个实现类,不指定使用哪个
在这里插入图片描述

1、@Autowire注解和@Qualifier注解联合

@Qualifier(value = “userDaoImp2”):value指定使用哪个类,传入的是类名首字母小写

@Service
public class UserServiceImp implements UserService{
    // 第一种方式:属性注入
    @Autowired
    @Qualifier(value = "userDaoImp2")
    private UserDao userDao;
    @Override
    public void addService() {
        System.out.println("Service实现类中的方法...............");
        userDao.addDao();
    }

}

2、@Resource注解

通过name = "UserDaoImp2"指定类,UserDaoImp2是对应类@Repository(value = “UserDaoImp2”)注解中 定义的别名,@Resource(name = “UserDaoImp2”)中的value不可以像@Qualifier注解中的value直接可以使用类首字母小写名。

@Resource(name = "UserDaoImp2")
    private UserDao userDao;

3.3.5、全注解开发

全注解开发就是不再使用spring配置文件了,写一个配置类来代替配置文件。
在这里插入图片描述
① 工程下创建包:config,创建类SpringConfig

// 配置类
@Configuration
// 开启组件扫描
@ComponentScan("com.bipt")
public class SpringConfig {
}

测试类:原本加载配置文件.xml的语句改成**加载配置类**AnnotationConfigApplicationContext(SpringConfig.class);

public class TestUserControllerAnno {
    public static void main(String[] args) {
        // 加载配置类
    ApplicationContext context =new AnnotationConfigApplicationContext(SpringConfig.class);
        UserController controller = context.getBean(UserController.class);
        controller.add();
    }
}

四、手写IOC

五、AOP

5.1、概述

5.1.1、概念

AOP(Aspect Oriented Programming)是一种设计思想,是软件设计领域中的面向切面编程,它是面向对象编程的一种补充和完善,它以通过预编译方式运行期动态代理方式实现,在不修改源代码的情况下,给程序动态统一添加额外功能的一种技术。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

5.1.2、作用

  • 代码重用和模块化:AOP可以将一些通用的行为(例如日志记录、安全控制、事务管理等)抽象出来,形成可重用的模块,避免在每个业务逻辑中都重复编写这些代码。
  • 分离关注点:AOP将不同的关注点分离开来,使得各个模块间职责更加清晰明确,代码的可读性和可维护性也更强。
  • 简化开发:AOP可以帮助开发人员将关注点从业务逻辑中分离出来,使得开发更加简单明了。
  • 提高系统的可扩展性:在系统需求变化时,只需要修改AOP模块而不是修改业务逻辑,这可以使得系统更加易于扩展和维护。
  • 降低代码耦合度:AOP的作用是将不同的关注点分离开来,这可以避免代码之间的紧耦合,提高代码的可复用性和可维护性。

5.2、代理

  • ​ 代理模式是一种结构型设计模式,它使得代理对象可以代表另一个对象进行访问。它是二十三种设计模式中的一种,属于结构型模式。
  • 它的作用就是通过提供一个代理类,让我们在调用目标方法的时候,不再是直接对目标方法进行调用,而是通过代理类间接调用让不属于目标方法核心逻辑的代码从目标方法中剥离出来——解耦。调用目标方法时先调用代理对象的方法,减少对目标方法的调用和打扰,同时让附加功能能够集中在一起也有利于统一维护。
    在这里插入图片描述在这里插入图片描述
    代理:将非核心逻辑剥离出来以后,封装这些非核心逻辑的类、对象、方法

5.2.0、场景

1、场景一:最简单计算器功能实现

1、对于一个计算器,拥有加减乘除的功能,相当于一个计算器类对应着加减乘除四个方法
(1)定义一个计算器接口:

public interface Calculator {
    //加
    public int add1(int i, int j);
    //减
    public int sub2(int i, int j);
    //乘
    public int mul3(int i, int j);
    //除
    public int div4(int i, int j);
}

(2)计算器接口实现类

package com.bipt;

/**
 * @author Pengwei Qu
 * @Date 2024/9/4 21:24
 */
public class CalculatorImpl implements Calculator{
    @Override
    public int add1(int i, int j) {
        // 核心业务逻辑
        int addResult=i+j;
        System.out.println("计算结果是:"+addResult);
        return  addResult;
    }

    @Override
    public int sub2(int i, int j) {
        // 核心业务逻辑
        int subResult=i-j;
        System.out.println("计算结果是:"+subResult);
        return subResult;
    }

    @Override
    public int mul3(int i, int j) {
        // 核心业务逻辑
        int mulResult=i*j;
        System.out.println("计算结果是:"+mulResult);
        return mulResult;
    }

    @Override
    public int div4(int i, int j) {
        // 核心业务逻辑
        int divResult=i/j;
        System.out.println("计算结果是:"+divResult);
        return divResult;
    }
}

需要哪一个功能直接new出来实现类,通过对象调用即可

2、带有日志功能的计算器

在每一个方法中增加日志功能:在调用加减乘除方法进行计算时,在开始计算的前后有一个输出日志提示的功能。
在这里插入图片描述在这里插入图片描述

package com.bipt;

/**
 * @author Pengwei Qu
 * @Date 2024/9/4 21:24
 */
public class CalculatorImpl implements Calculator{
    @Override
    public int add1(int i, int j) {
        // 附加功能由代理类中的代理方法来实现
        System.out.println("[日志] add1 方法开始了,参数是:" + i + "," + j);
        // 核心业务逻辑
        int Result=i+j;

        System.out.println("[日志] add1 方法结束了,结果是:" + Result);
        return  Result;
    }

    @Override
    public int sub2(int i, int j) {
        System.out.println("[日志] sub2 方法开始了,参数是:" + i + "," + j);
        // 核心业务逻辑
        int Result=i-j;

        System.out.println("[日志] sub2 方法结束了,结果是:" + Result);
        return Result;
    }

    @Override
    public int mul3(int i, int j) {
        System.out.println("[日志] mul3 方法开始了,参数是:" + i + "," + j);
        // 核心业务逻辑
        int Result=i*j;
        System.out.println("[日志] mul3 方法结束了,结果是:" + Result);
        return Result;
    }

    @Override
    public int div4(int i, int j) {
        System.out.println("[日志] div4 方法开始了,参数是:" + i + "," + j);
        // 核心业务逻辑
        int Result=i/j;

        System.out.println("[日志] div4 方法结束了,结果是:" + Result);
        return Result;
    }
}

问题分析:

  1. 对于加减乘除每一个方法,对然增加了日志功能,但是主要功能还是加减乘除计算,计算功能前后添加的提示语句只是辅助功能,即使这些辅助功能没有,照样可以实现加减乘除功能。
  2. 现在核心功能和辅助功能全部定义在同一个类中,而且每一个核心功能前后添加的辅助功能都一样在这里插入图片描述
  3. 因此可以把辅助功能提出来专门用一个类写也就是代理类。核心功能还用原来的类只实现加减乘除的功能即可。

5.2.1、静态代理

在静态代理中,代理类是在编译时期创建的,代理类委托类实现相同的接口或继承相同的类,并在代理类中实现委托类中的方法,在调用委托类的方法之前或之后执行一些附加操作。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  1. 抽象角色:《interface》Subject,通过接口或抽象类声明真实角色实现的业务方法。
  2. 代理角色:Proxy,实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作
  3. 真实角色:RealSubject,实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。

(1)核心代码类(代理目标)

/*核心方法类*/
public class Core_Calculator implements Calculator{
    
    @Override
    public int add1(int i, int j) {
        int result = i + j;
        return result;
    }

    @Override
    public int sub2(int i, int j) {
        int result = i - j;
        return result;
    }

    @Override
    public int mul3(int i, int j) {
        int result = i * j;
        return result;
    }

    @Override
    public int div4(int i, int j) {
        int result = i / j;
        return result;
    }
}

(2)以下代码是代理角色的代码:

//代理类
public class CalculatorStaticProxy implements Calculator{
    // 将被代理的目标对象声明为成员变量
    private Calculator target;

    //通过构造函数赋值
    public CalculatorStaticProxy(Calculator target) {
        this.target = target;
    }

    @Override
    public int add1(int i, int j) {
        // 附加功能由代理类中的代理方法来实现
        System.out.println("[日志] add 方法开始了,参数是:" + i + "," + j);

        // 通过目标对象来实现核心业务逻辑
        int addResult = target.add1(i, j);

        // 附加功能由代理类中的代理方法来实现
        System.out.println("[日志] add 方法结束了,结果是:" + addResult);

        return addResult;

    }

    @Override
    public int sub2(int i, int j) {
        // 附加功能由代理类中的代理方法来实现
        System.out.println("[日志] sub2 方法开始了,参数是:" + i + "," + j);

        // 通过目标对象来实现核心业务逻辑
        int addResult = target.sub2(i, j);

        // 附加功能由代理类中的代理方法来实现
        System.out.println("[日志] sub2 方法结束了,结果是:" + addResult);
        return addResult;
    }

    @Override
    public int mul3(int i, int j) {
        // 附加功能由代理类中的代理方法来实现
        System.out.println("[日志] mul3 方法开始了,参数是:" + i + "," + j);

        // 通过目标对象来实现核心业务逻辑
        int addResult = target.mul3(i, j);

        // 附加功能由代理类中的代理方法来实现
        System.out.println("[日志] mul3 方法结束了,结果是:" + addResult);
        return addResult;
    }

    @Override
    public int div4(int i, int j) {
        // 附加功能由代理类中的代理方法来实现
        System.out.println("[日志] div4 方法开始了,参数是:" + i + "," + j);

        // 通过目标对象来实现核心业务逻辑
        int addResult = target.div4(i, j);

        // 附加功能由代理类中的代理方法来实现
        System.out.println("[日志] div4 方法结束了,结果是:" + addResult);
        return addResult;
    }
}

在这里插入图片描述

1、静态类存在的问题

核心类代码确实是抽出来了,其余的非核心代码通过代理的方式,代理类进行编写

  • 问题一:代理类中的非核心代码写死了,如果换个需求,这个需求也是非核心代码,需要另外再创建一个代理类。
  • 问题二:非核心类只是从核心代码中抽出来了,又写在了代理类中,实际上非核心代码并没有真正意义的抽离出来。在这里插入图片描述在这里插入图片描述在这里插入图片描述

5.2.2、动态代理(动态创建代理对象)

  • 动态代理就可以实现以上问题的解决,将非核心代码(日志代码)集中到一个代理类中,将来任何日志需求都可以直接通过这一个代理类实现。
  • 在动态代理中,代理类是在运行时期动态创建的。它不需要事先知道委托类的接口或实现类,而是在运行时期通过 Java 反射机制动态生成代理类在这里插入图片描述

在这里插入图片描述
创建动态代理的源码:
第一个参数:CLassLoader:加载动态生成代理类的类加载器
第二个参数:CLass[]interfaces:目标对象实现的所有接口cLass类型数组 ,目标类实现了哪些接口,这些接口的class类型存入数组
第三个参数:InvocationHandler:设置代理对象实现目标对象方法的过程

InvocationHandler handler = new MyInvocationHandler(...);  
Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),  
        new class<?>[]{Foo.class},  
        InvocationHandler handler);

创建一个代理工厂类

package org.example;  
  
import lombok.val;  
  
import javax.print.attribute.standard.JobKOctets;  
import java.lang.reflect.InvocationHandler;  
import java.lang.reflect.Method;  
import java.lang.reflect.Proxy;  
  
public class ProxyFactory {  
    Object target;//目标对象,需要找代理的类,拥有核心功能的类  
  
    public ProxyFactory(Object target) {  
        this.target = target;  
    }  
  
    public Object getProxy() {  

		//第一个参数:CLassLoader:加载动态生成代理类的类加载器  
        ClassLoader cLassLoader = target.getClass().getClassLoader();  
        //第二个参数:CLass[]interfaces:目标对象实现的所有接口cLass类型数组  
        Class[] classes = target.getClass().getInterfaces(); 
        //第三个参数:InvocationHandler:设置代理对象实现目标对象方法的过程 
        //匿名内部类的方式InvocationHandler()是一个接口
        InvocationHandler invocationHandler = new InvocationHandler() {  
            @Override  
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  
                //调用方法前日志  
                System.out.println("[动态代理][调用前日志]" + method.getName() + "参数:" + args);  
                //调用目标方法  ,此处的invoke与方法名invoke不是一个东西
                Object result = method.invoke(target, args);  
                //调用方法后日志  
                System.out.println("[动态代理][调用后日志]" + method.getName() + "参数:" + args);  
                return result;  
            }  
        };  
        
        return Proxy.newProxyInstance(cLassLoader, classes, invocationHandler);  
    }  
}

编写测试类

@Test  
public void calculatorTest(){  
    ProxyFactory proxyFactory=new ProxyFactory(new CalculatorImpl());  
    Calculator proxy=(Calculator) proxyFactory.getProxy();  
    proxy.add(1,1);  
}

输出结果

[动态代理][调用前日志]add参数:[Ljava.lang.Object;@7d0587f1
result=2
[动态代理][调用后日志]add参数:[Ljava.lang.Object;@7d0587f1

5.2.3动态代理的两种类型

在这里插入图片描述

5.3、AOP

5.3.1概念

AOP(Aspect Oriented Programming)是一种设计思想,是软件设计领域中的面向切面编程,它是面向对象编程的一种补充和完善,它以通过预编译方式运行期动态代理方式实现,在不修改源代码的情况下,给程序动态统一添加额外功能的一种技术。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间耦合度降低,提高程序的可重用性,同时提高了开发的效率。

5.3.2相关术语

1、横切关注点在这里插入图片描述在这里插入图片描述

2、通知(增强)

在这里插入图片描述在这里插入图片描述

3、切面

在AOP中,切面指的是横切关注点和通知的组合,它是一个模块化的横向分割,可以理解为一个横向的切片。切面是对横切关注点和通知的封装,它包含了一组切点和通知,用于描述在何处、何时、以及如何执行横切逻辑。切面可以在不修改原代码的情况下,对原有的代码进行功能的增强或改变。通常,切面是以一个类的形式存在的,它包含了一个或多个通知和一个或多个切点。

简单来说:切面就是封装通知方法的类在这里插入图片描述

4、目标对象

在AOP(Aspect-Oriented Programming)中,**目标(Target)**是指被通知的对象或者被切面所影响的对象。它是应用程序中的一个具体元素,可以是类、接口、方法或者字段等。
简单点说,目标就是被代理的目标对象

5、代理对象

在AOP(Aspect-Oriented Programming)中,**代理(Proxy)**是一种设计模式,用于控制对目标对象的访问,并在访问过程中插入额外的逻辑。
简单点说,代理就是向目标对象应用通知(增强)之后创建的代理对象

6、连接点

在 AOP 中,连接点(Join Point)表示在程序执行过程中能够插入一个切面的点,例如方法调用、异常处理、字段访问等。连接点定义了在程序中的哪个位置可以应用切面。切面可以在连接点前后增加额外的处理逻辑,从而影响程序的行为。通俗地讲,连接点就是在程序执行中可以被拦截的地方
在这里插入图片描述

7、切入点

在 AOP 中,切入点(Join Point)是指程序执行过程中明确的点,通常是方法调用的时候,也可以是异常处理程序的处理过程。切入点定义了哪些方法是需要被拦截或增强的例如:我只希望乘法和加法对应的实现方法进行增强,是 AOP 中最重要的概念之一。切入点通常以方法的形式被定义,比如某个类的所有方法、某个包下的所有方法等等。在 AOP 中,通常会使用表达式语言定义切入点,如使用 Spring AOP 中的 @Pointcut 注解定义

表达式:在这里插入图片描述

5.3.3解用AspectJ注解实现步骤

在这里插入图片描述在这里插入图片描述
在这里插入图片描述

1、引入依赖

<!-- https://mvnrepository.com/artifact/org.springframework/spring-aop -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>6.1.11</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>6.0.10</version>
</dependency>

2、创建目标接资源(实现类)没有接口默认使用cglib代理

在这里插入图片描述

@Service("userService")
public class UserService {//目标类
    public  void login(){//目标方法
        System.out.println("正在登录............");
    }
}
  1. 创建切面类
    @Aspect表示这个类是一个切面类
    @Component注解保证这个切面类能够放入IOC容器
@Aspect
@Component
public class LogAspect {
    // 前置通知
    // 异常通知
    // 返回通知
    // 后置通知
    // 环绕通知
    ……
}

在这里插入图片描述

3、配置Spring文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--
        基于注解的AOP的实现:
        1、将目标对象和切面交给IOC容器管理(注解+扫描)
        2、开启AspectJ的自动代理,为目标对象自动生成代理
        3、将切面类通过注解@Aspect标识
    -->
    <context:component-scan base-package="com.bipt.AOP_Annotation"></context:component-scan>

    <aop:aspectj-autoproxy />
</beans>

测试:IOC容器能否正常创建bean

public class Test {
    @org.junit.Test
    public void test(){
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("bean.xml");
        UserService userService = applicationContext.getBean("userService", UserService.class);
        userService.login();
    }
}

4、设置切面类

/设置切入点与通知类型/
在这里插入图片描述

@Aspect
@Component
public class LogAspect {
    // 前置通知
    @Before("execution(public int com.atguigu.aop.annotation.CalculatorImpl.*(..))")
public void beforeMethod(JoinPoint joinPoint){
    // getSignature()获取连接点签名,getName()获取连接点名称
    String methodName = joinPoint.getSignature().getName();
    String args = Arrays.toString(joinPoint.getArgs());
    System.out.println("Logger-->前置通知,方法名:"+methodName+",参数:"+args);
}

    // 异常通知
    @AfterThrowing(value = "execution(* com.atguigu.aop.annotation.CalculatorImpl.*(..))", throwing = "ex")
public void afterThrowingMethod(JoinPoint joinPoint, Throwable ex){
    String methodName = joinPoint.getSignature().getName();
    System.out.println("Logger-->异常通知,方法名:"+methodName+",异常:"+ex);
}

    // 返回通知
    @AfterReturning(value = "execution(* com.atguigu.aop.annotation.CalculatorImpl.*(..))", returning = "result")
public void afterReturningMethod(JoinPoint joinPoint, Object result){
    String methodName = joinPoint.getSignature().getName();
    System.out.println("Logger-->返回通知,方法名:"+methodName+",结果:"+result);
}


    // 后置通知
      @After("execution(* com.atguigu.aop.annotation.CalculatorImpl.*(..))")
    public void afterMethod(JoinPoint joinPoint){
        String methodName = joinPoint.getSignature().getName();
        System.out.println("Logger-->后置通知,方法名:"+methodName);
    }


    // 环绕通知
    @Around("execution(* com.atguigu.aop.annotation.CalculatorImpl.*(..))")
public Object aroundMethod(ProceedingJoinPoint joinPoint){
    String methodName = joinPoint.getSignature().getName();
    String args = Arrays.toString(joinPoint.getArgs());
    Object result = null;
    try {
        System.out.println("环绕通知-->目标对象方法执行之前");
        //目标对象(连接点)方法的执行
        result = joinPoint.proceed();
        System.out.println("环绕通知-->目标对象方法返回值之后");
    } catch (Throwable throwable) {
        throwable.printStackTrace();
        System.out.println("环绕通知-->目标对象方法出现异常时");
    } finally {
        System.out.println("环绕通知-->目标对象方法执行完毕");
    }
    return result;
}
}

5.3.4重用切入点

1、声明

@Pointcut("execution(* com.atguigu.aop.annotation.*.*(..))")
public void pointCut(){}

2、同切面使用

在这里插入代码片@Before("pointCut()")
public void beforeMethod(JoinPoint joinPoint){
    String methodName = joinPoint.getSignature().getName();
    String args = Arrays.toString(joinPoint.getArgs());
    System.out.println("Logger-->前置通知,方法名:"+methodName+",参数:"+args);
}

3、不同切面使用

@Before("com.atguigu.aop.CommonPointCut.pointCut()")
public void beforeMethod(JoinPoint joinPoint){
    String methodName = joinPoint.getSignature().getName();
    String args = Arrays.toString(joinPoint.getArgs());
    System.out.println("Logger-->前置通知,方法名:"+methodName+",参数:"+args);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2114513.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Qt-常用控件(3)-输入类

1. QLineEdit QLineEdit 用来表示单行输入框.可以输入一段文本,但是不能换行 核心属性 属性说明text输入框中的文本inputMask输入内容格式约束maxLength最大长度frame是否添加边框echoMode显示方式. QLineEdit::Normal :这是默认值&#xff0c;文本框会显示输入的文本。QLineE…

物联网之流水LED灯、正常流水灯、反复流水灯、移动流水灯

MENU 硬件电路设计软件程序设计正常流水LED灯反复流水LED灯移动流水LED灯 硬件电路设计 材料名称数量直插式LED1kΩ电阻杜邦线(跳线)若干面包板1 每一个LED的正极与开发板一个GPIO引脚相连&#xff0c;并串联一个电阻&#xff0c;负极接GND。 当然也可以选择只使用一个电阻。 软…

DELTA_IA-ASD_ASDA-A2简明教程

该文章仅供参考&#xff0c;编写人不对任何实验设备、人员及测量结果负责&#xff01;&#xff01;&#xff01; 0 引言 文章主要介绍电机的硬件连接、软件配置、转动调试以及软件控制。文章中提到的内容在产品手册中都有说明&#xff0c;强烈建议在操作前通读产品手册&#…

1-17 平滑处理——中值滤波 opencv树莓派4B 入门系列笔记

目录 一、提前准备 二、代码详解 cv2.medianBlur函数用于对图像进行中值滤波。中值滤波是一种去噪声的技术&#xff0c;可以有效地去除图像中的盐和胡椒噪声。函数的两个参数如下&#xff1a; 三、运行现象 四、完整代码贴出 一、提前准备 1、树莓派4B 及 64位系统 2、提前…

【2024 版】最新 kali linux 入门及常用简单工具介绍(非常详细)

一、介绍 kali Linux Kali Linux 是一个基于 Debian 的 Linux 发行版&#xff0c;主要用于数字取证和渗透测试。它预装了大量的安全审计和渗透测试工具&#xff0c;被广泛应用于网络安全领域。 &#xff08;一&#xff09;特点 工具丰富&#xff1a;集成了数百种用于渗透测试…

华为eNSP:NAT Server(端口映射)

一、拓扑图 二、配置过程 此处省略设备地址以及路由配置过程 1、服务器开启ftp服务 2、路由器配置nat server [r4]int g0/0/2#进入流量出接口 [r4-GigabitEthernet0/0/2]nat server protocol tcp global 192.168.3.11 ftp inside 192.168.2.1 ftp# …

Java数组(详解版)

数组的定义&#xff1a; 什么是数组&#xff1a; 数组&#xff1a;可以看成是相同类型元素的一个集合。在内存中是一段连续的空间。 1. 数组中存放的元素其类型相同 2. 数组的空间是连在一起的 3. 每个空间有自己的编号&#xff0c;其实位置的编号为 0 &#xff0c;即数组…

Nuxt3入门:过渡效果(第5节)

你好同学&#xff0c;我是沐爸&#xff0c;欢迎点赞、收藏、评论和关注。 Nuxt 利用 Vue 的 <Transition> 组件在页面和布局之间应用过渡效果。 一、页面过渡效果 你可以启用页面过渡效果&#xff0c;以便对所有页面应用自动过渡效果。 nuxt.config.js export defaul…

冒泡排序——基于Java的实现

简介 冒泡排序&#xff08;Bubble Sort&#xff09;是一种简单的排序算法&#xff0c;适用于小规模数据集。其基本思想是通过重复遍历待排序的数组&#xff0c;比较相邻的元素并交换它们的位置&#xff0c;以此将较大的元素逐步“冒泡”到数组的末尾。算法的名称源于其运行过程…

动手学习RAG: 向量模型

在世界百年未有之变局与个人自暴自弃的间隙中&#xff0c;我们学一点RAG。 RAG是一种独特的应用&#xff0c;“一周写demo&#xff0c;优化搞半年”&#xff0c;我甚至听说它能破解幻术。 为了理解其优化中的关键一环&#xff0c;我们先看下文本向量。文本向量除了是RAG检索的…

# 键盘字母上有下标数字,输入时怎么一键去掉,关闭键盘上的下标数字。‌

键盘字母上有下标数字&#xff0c;输入时怎么一键去掉&#xff0c;关闭键盘上的下标数字。‌ 一、问题描述&#xff1a; 如下图&#xff0c;有的笔记本电脑键盘上&#xff0c;没有数字小键盘&#xff0c;数字小键盘会和字母混和在一起&#xff0c;这样打字时&#xff0c;不容…

AI在医学领域:MASL多模态辅助诊断声带麻痹

声带麻痹&#xff08;Vocal Cord Paralysis, VP&#xff09;&#xff0c;也称为喉瘫痪&#xff0c;是指由于支配声带的神经受损导致声带运动障碍的疾病。这种状况可以是单侧或双侧的&#xff0c;通常由脑部、颈部、胸部的肿瘤、外伤、炎症&#xff0c;以及各种全身疾病引起。这…

复数随机变量(信号)的方差和协方差矩阵的计算

怎么计算复数随机变量的方差和协方差矩阵&#xff1f; 使得其与MATLAB中var函数和cov函数的结果一致。 前言 复信号在信号处理中随处可见&#xff0c;关于复信号&#xff08;复随机变量&#xff09;的方差和协方差矩阵该如何计算呢&#xff1f;本文给出了复信号的方差和协方差矩…

什么是大数据、有什么用以及学习内容

目录 1.什么是大数据&#xff1f; 2.大数据有什么用&#xff1f; 2.1商业与营销&#xff1a; 2.2医疗与健康&#xff1a; 2.3金融服务&#xff1a; 2.4政府与公共服务&#xff1a; 2.5交通与物流&#xff1a; 2.6教育与个性化学习&#xff1a; 3.学习大数据需要学习哪…

C++笔试强训12、13、14

文章目录 笔试强训12一、选择题1-5题6-10题 二、编程题题目一题目二 笔试强训13一、选择题1-5题6-10题 二、编程题题目一题目二 笔试强训14一、选择题1-5题6-10题 二、编程题题目一题目二 笔试强训12 一、选择题 1-5题 引用&#xff1a;是一个别名&#xff0c;与其被引用的实…

认知杂谈54

I I 内容摘要&#xff1a; 这篇内容主要有以下几个要点&#xff1a;首先&#xff0c;沟通不在一个调时可学习人际交往心理学知识、线上课程及关注名师来改善。其次&#xff0c;挑房子、工作、搭档和人生伴侣要谨慎&#xff0c;找心灵相通能共同进步的人。再者&#xff0c;远离…

AI周报(9.1-9.7)

AI应用-Tidal 引领海洋养殖革命 Tidal团队&#xff0c;一个源自Alphabet X的创新项目&#xff0c;今年七月顺利从X实验室毕业&#xff0c;成为一家独立的公司。Tidal正在通过人工智能技术改变海洋养殖&#xff0c;特别是鲑鱼养殖。Tidal的总部位于挪威特隆赫姆&#xff0c;他们…

Java-数据结构-栈和队列-Stack和Queue (o゚▽゚)o

文本目录&#xff1a; ❄️一、栈(Stack)&#xff1a; ▶ 1、栈的概念&#xff1a; ▶ 2、栈的使用和自实现&#xff1a; ☑ 1&#xff09;、Stack(): ☑ 2&#xff09;、push(E e): ☑ 3&#xff09;、empty(): ☑ 4&#xff09;、peek(E e): ☑ 5&#xff09;、pop(E e): …

将添加功能的抽屉剥离,在父组件调用思路

一、新建组件 新建AddRoleEditerDrawer.vue<template><div><el-drawer v-model"dialog" title"添加角色" :before-close"handleClose" direction"rtl" colse"cancelForm"class"demo-drawer" moda…

基于UE5和ROS2的激光雷达+深度RGBD相机小车的仿真指南(五):Blender锥桶建模

前言 本系列教程旨在使用UE5配置一个具备激光雷达深度摄像机的仿真小车&#xff0c;并使用通过跨平台的方式进行ROS2和UE5仿真的通讯&#xff0c;达到小车自主导航的目的。本教程默认有ROS2导航及其gazebo仿真相关方面基础&#xff0c;Nav2相关的学习教程可以参考本人的其他博…