【Java学习总结】Spring

一、Spring概述

1.Spring简介

2002年,Rod Jahnson首次推出了Spring框架雏形interface21框架。
2004年3月24日,Spring框架以interface21框架为基础,经过重新设计,发布了1.0正式版。
Spring理念 : 使现有技术更加实用 . 本身就是一个大杂烩 , 整合现有的框架技术。
Spring官网:https://spring.io/

2.Spring优点

  • Spring是一个开源的。
  • Spring是轻量级框架。
  • 支持事务的处理,支持对其他框架的整合。
  • IOC控制反转、AOP面向切面编程

3.Spring组成

在这里插入图片描述
Spring 框架是一个分层架构,由 7 个定义良好的模块组成。Spring 模块构建在核心容器之上,核心容器定义了创建、配置和管理 bean 的方式 。

  • 核心容器:核心容器提供 Spring 框架的基本功能。核心容器的主要组件是 BeanFactory,它是工厂模式的实现。BeanFactory 使用控制反转(IOC) 模式将应用程序的配置和依赖性规范与实际的应用程序代码分开。
  • Spring 上下文:Spring 上下文是一个配置文件,向 Spring 框架提供上下文信息。Spring 上下文包括企业服务,例如 JNDI、EJB、电子邮件、国际化、校验和调度功能。
  • Spring AOP:通过配置管理特性,Spring AOP 模块直接将面向切面的编程功能 , 集成到了 Spring框架中。所以,可以很容易地使 Spring 框架管理任何支持 AOP的对象。Spring AOP 模块为基于Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖组件,就可以将声明性事务管理集成到应用程序中。
  • Spring DAO:JDBC DAO 抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理和不同数据库供应商抛出的错误消息。异常层次结构简化了错误处理,并且极大地降低了需要编写的异常代码数量(例如打开和关闭连接)。Spring DAO 的面向 JDBC 的异常遵从通用的 DAO 异常层次结构。
  • Spring ORM:Spring 框架插入了若干个 ORM 框架,从而提供了 ORM 的对象关系工具,其中包括 JDO、Hibernate 和 iBatis SQL Map。所有这些都遵从 Spring 的通用事务和 DAO 异常层次结构。
  • Spring Web 模块:Web 上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文。所以,Spring 框架支持与 Jakarta Struts 的集成。Web 模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。
  • Spring MVC 框架:MVC 框架是一个全功能的构建 Web 应用程序的 MVC 实现。通过策略接口,MVC 框架变成为高度可配置的,MVC 容纳了大量视图技术,其中包括 JSP、Velocity、Tiles、iText 和 POI。

二、IoC(Inversion of Control,控制反转)

我们以前写的代码,如果程序写好后又需要增加新功能或修改功能,则需要对以前的代码进行大量的修改,这种方式的代码耦合性过高,牵一发而动全身。这种程序是有问题的,所以我们可以引入IoC的思想来解耦。

1.IOC的本质

控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法,也有人认为DI只是IoC的另一种说法。没有IoC的程序中 , 我们使用面向对象编程 , 对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式反转了。

传统开发模式:对象之间互相依赖
IoC 开发模式:IoC 容器安排对象之间的依赖

在这里插入图片描述
传统的开发模式就如图1一样,各个对象之间就像机械表中的齿轮,相互关联,如果一个齿轮出现问题需要更换,则整个系统都会出现问题。
在传统开发中,如果一个功能需要修改,则需要进行大量的对原生代码进行更改。对象之间的耦合度过高的系统,必然会出现牵一发而动全身的情形。
为了解决对象之间耦合度过高的问题,软件专家 Michael Mattson 提出了 IoC 理论,用来实现对象之间的 “解耦”。

在这里插入图片描述
IoC 的处理方式,简单来说,就是把复杂系统分解成相互合作的对象。这些对象类通过封装以后,内部实现对于外部是透明的,从而降低了解决问题的复杂度,而且可以灵活的被重用和扩展。
IoC 理论观点:借助于 “第三方” 实现具有依赖关系的对象之间的解。
如图2所示,通过一个第三方的IoC容器来粘合这4个对象,使得这些对象之间失去了耦合关系。

在这里插入图片描述
如图3,拿掉中间的IoC容器,即可使这四个对象完全失去了耦合关系。此时,如果想对其中的一个对象进行修改,就无需考虑其它对象的问题了。

2.HelloSpring

编写第一个Spring程序:HelloSpring!
(1)导入依赖

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.2.0.RELEASE</version>
        </dependency>

(2)编写实体类

public class Hello {
    
    
    private String str;

    public String getStr() {
    
    
        return str;
    }

    public void setStr(String str) {
    
    
        this.str = str;
    }

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

(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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--使用Spring来创建对象,在Spring中这些都称为Bean-->
    <bean id="hello" class="com.example.pojo.Hello">
        <property name="str" value="Spring"/>
    </bean>
</beans>

(4)写测试类

public class MyTest {
    
    
    public static void main(String[] args) {
    
    
        //获取Spring的上下文对象!
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        //我们的对象现在都在Spring中的管理了,我们要使用,直接去里面取出来就可以!
        Hello hello = (Hello) context.getBean("hello");
        System.out.println(hello.toString());
    }
}

在Java和Spring创建对象的对比:

     //类名 变量名 = new 类型();
     Hello hello = new Hello();
     <!--id = 变量名  class = new 的对象
     properties相当于给对象中的属性设置一个值-->
    <bean id="hello" class="com.example.pojo.Hello">
        <property name="str" value="Spring"/>
    </bean>

3.IoC创建对象方式

(1)通过无参构造来创建
创建实体类

public class User {
    
    
    private String name;

    public User() {
    
    
        System.out.println("User被无参构造创建了");;
    }

    public String getName() {
    
    
        return name;
    }

    public void setName(String name) {
    
    
        this.name = name;
    }

    public void show(){
    
    
        System.out.println("name="+name);
    }
}

编写applicationContext.xml配置文件

    <!--
        id:bean的唯一标识符,也就是相当于我们学的对象名
        class:bean对象所对应的全限定名:包名+类型
        name:也是别名,而且name可以同时取多个别名
    -->
    <bean id="user" class="com.example.pojo.User" name="user u2,u3;u4">
        <property name="name" value="hello"/>
    </bean>

测试

public class MyTest {
    
    
    public static void main(String[] args) {
    
    
        //Spring容器
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserT user = (UserT) context.getBean("user");
        user.show();
    }
}

(2)通过有参方法创建
applicationContext.xml有三种配置方式:

    <!--第一种,下标赋值!-->
    <bean id="user" class="com.example.pojo.User">
        <constructor-arg index="0" value="小明"/>
    </bean>

    <!--第二种方式,通过类型创建,不建议使用!-->
    <bean id="user" class="com.example.pojo.User">
        <constructor-arg type="java.lang.String" value="小明"/>
    </bean>

    <!--第三种,直接通过参数名设置!-->
    <bean id="user" class="com.example.pojo.User">
        <constructor-arg name="name" value="小明"/>
    </bean>

三、依赖注入(DI)

  • 依赖注入(Dependency Injection,DI)。
  • 依赖 : 指Bean对象的创建依赖于容器 . Bean对象的依赖资源。
  • 注入 : 指Bean对象所依赖的资源 , 由容器来设置和装配。

1.构造器注入

在上一节已将介绍过有参和无参构造器注入

2.set方法注入

通过一个实例来完成set注入:
创建Address类

public class Address {
    
    
    private String address;
    public String getAddress() {
    
    
        return address;
    }
    public void setAddress(String address) {
    
    
        this.address = address;
    }
    @Override
    public String toString() {
    
    
        return "Address{" +
                "address='" + address + '\'' +
                '}';
    }
}

创建Student类

public class Student {
    
    
    private String name;
    private Address address;
    private String[] books;
    private List<String> hobbies;
    private Map<String,String> card;
    private Set<String> games;
    private String money;
    private Properties info;
    //并加入get和set方法
}

在applicationontext.xml中实现set注入
(1)普通注入

        <property name="name" value="hello"/>

(2)Bean注入

        <property name="address" ref="address"/>

(3)数组注入

        <property name="books">
            <array>
                <value>红楼梦</value>
                <value>西游记</value>
                <value>水浒传</value>
                <value>三国演义</value>
            </array>
        </property>

(4)List注入

        <property name="hobbies">
            <list>
                <value>听音乐</value>
                <value>写代码</value>
                <value>看电影</value>
            </list>
        </property>

(5)Map注入

        <property name="card">
            <map>
                <entry key="身份证" value="123456123456781234"/>
                <entry key="银行卡" value="1234562222222211151"/>
            </map>
        </property>

(6)Set注入

        <property name="games">
            <set>
                <value>LOL</value>
                <value>Sekiro:Shadows Die Twice</value>
                <value>Dark Souls</value>
            </set>
        </property>

(7)null注入

        <property name="money">
            <null/>
        </property>

(8)Properties注入

        <property name="info">
            <props>
                <prop key="driver">20190525</prop>
                <prop key="url"></prop>
                <prop key="username">root</prop>
                <prop key="password">123456</prop>
            </props>
       </property>

3.扩展方式命名

p命名和c命名需要引入xml约束

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

(1)p命名注入properties

    <!--p命名空间注入,可以直接注入属性的值:properties-->
    <bean id="user" class="com.carry.pojo.User" p:name="秦疆" p:age="18"/>

(2)c命名注入constructor

    <!--c命名空间注入,通过构造器注入:construct-args-->
    <bean id="user2" class="com.carry.pojo.User" c:age="18" c:name="狂神" scope="prototype"/>

4.bean的作用域

类别 说明
Singleton 单例模式,Spring IoC 容器中只会存在一个共享的 Bean 实例,无论有多少个Bean 引用它,始终指向同一对象。该模式在多线程下是不安全的。
Prototype 原型模式,每次通过 Spring 容器获取 prototype 定义的 bean 时,容器都将创建一个新的 Bean 实例,每个 Bean 实例都有自己的属性和状态,而 singleton 全局只有一个对象。
Request 每次HTTP请求都会创建一个新的Bean,仅适用于WebApplicationContext环境 。
Session 同一个HTTP Session请求共享一个Bean,不同Session使用不同Bean,仅适用于WebApplicationContext环境 。

5.bean的自动装配

自动装配是使用spring满足bean依赖的一种方法
spring会在应用上下文中为某个bean寻找其依赖的bean。

普通装配:

	<bean id="cat" class="com.example.pojo.Cat"/>
	<bean id="dog" class="com.example.pojo.Dog"/>
	<bean id="person" class="com.example.pojo.Person">
		<property name="name" value="hello"/>
		<property name="dog" ref="dog"/>
		<property name="cat" ref="cat"/>
	</bean>

(1)byName

	<bean id="cat" class="com.example.pojo.Cat"/>
	<bean id="dog" class="com.example.pojo.Dog"/>
	<bean id="person" class="com.example.pojo.Person" autowire="byName">
	<property name="name" value="hello"/>

byName 会自动在容器上下文中查找和自己对象set方法后面的值对应的bean id。
如果此时将Cat中的bean id修改成别的名字,则无法完成自动装配,导致空指针异常。

(2)byType

	<bean id="cat" class="com.example.pojo.Cat"/>
	<bean id="dog" class="com.example.pojo.Dog"/>
	<bean id="person" class="com.example.pojo.Person" autowire="byType">
	<property name="name" value="hello"/>

byType 会自动在容器上下文中查找,和自己对象属性相同的值对应的Bean id。

byName: 需要保证所有的bean的id唯一,并且这个bean需要和自动注入的属性的set的方法的值一致!
byType: 需要保证所有的bean的class唯一,并且这个bean需要和自动注入的属性的类型一致!

(3)使用注解自动装配

1.xml中导入约束

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

2.配置注解的支持

<context:annotation-config/>
  • @Autowired:自动装配通过类型,名字
    如果Autowired不能唯一自动装配上属性,则需要通过@Qualifier(value = “xxx”)
  • @Nullable:字段标记了这个注释,说明这个字段可以为null;
  • @Resource:自动装配通过名字,类型
  • @Component:组件,放在类上,说明这个类被Spring管理了,就是bean!

四、代理模式

1.静态代理

角色分析:

  • 抽象角色 : 一般使用接口或者抽象类来实现
  • 真实角色 : 被代理的角色
  • 代理角色 : 代理真实角色 ; 代理真实角色后 , 一般会做一些附属的操作 .
  • 客户 : 使用代理角色来进行一些操作 .

(1)通过租房案例来熟悉静态代理模式

1.rent接口(抽象角色)

//租房
public interface Rent {
    
    
    public void rent();
}

2.proxy中介(代理角色)

public class Proxy {
    
    

    private Host host;

    public Proxy() {
    
    
    }

    public Proxy(Host host) {
    
    
        this.host = host;
    }

    public void rent(){
    
    
        host.rent();
    }

    //看房
    public void seeHouse(){
    
    
        System.out.println("中介带你看房");
    }

    //签合同
    public void contract(){
    
    
        System.out.println("签租赁合同");
    }

    //收中介费
    public void fare(){
    
    
        System.out.println("收中介费");
    }
}

3.Host房东(真实角色)

//房东
public class Host implements Rent{
    
    
    public void rent() {
    
    
        System.out.println("房东要出租房子!");
    }
}

4.Client客户

public class Client {
    
    
    public static void main(String[] args) {
    
    
        //房东要租房子
        Host host = new Host();
        //代理,中介帮房东租房子,但是呢?代理角一般会有一些附属操作!
        Proxy proxy = new Proxy(host);
        //你不用面对房东,直接找中介租房即可!
        proxy.rent();
    }
}

分析:在这个过程中,你直接接触的就是中介,就如同现实生活中的样子,你看不到房东,但是你依旧
租到了房东的房子通过代理,这就是所谓的代理模式,程序源自于生活,所以学编程的人,一般能够更
加抽象的看待生活中发生的事情

(2)通过静态代理模式进行增删改查

1.创建一个抽象角色

public interface UserService {
    
    
    public void add();
    public void delete();
}

2.创建一个真实角色,来实现抽象角色的功能

//真实对象
public class UserServiceImpl implements UserService{
    
    
    public void add() {
    
    
        System.out.println("增加了一个用户");
    }
    public void delete() {
    
    
        System.out.println("删除了一个用户");
    }
}

如果此时有了一个新的需求,让你增加一个日志功能,有两种思路。

  • 思路一:直接在实现类中进行修改。(需要改动原有代码,是大忌)
  • 思路二:通过代理实现。能够在不改变原有代码的基础上实现。

3.创建代理对象,实现日志功能。

public class UserServiceProxy implements UserService{
    
    
    private UserServiceImpl userService;
    public void setUserService(UserServiceImpl userService){
    
    
        this.userService = userService;
    }
    public void add() {
    
    
        log("add");
        userService.add();
    }
    public void delete() {
    
    
        log("delete");
        userService.delete();
    }
    //日志方法
    public void log(String msg){
    
    
        System.out.println("[DEBUG] 使用了"+msg+"方法");
    }
}

4.客户调用代理类,实现功能。

public class Client {
    
    
    public static void main(String[] args) {
    
    
        UserServiceImpl userService = new UserServiceImpl();
        UserServiceProxy proxy = new UserServiceProxy();
        proxy.setUserService(userService);
        proxy.add();
    }
}

静态代理的好处:

  • 可以使得我们的真实角色更加纯粹 . 不再去关注一些公共的事情 .
  • 公共的业务由代理来完成 . 实现了业务的分工 ,
  • 公共业务发生扩展时变得更加集中和方便 .
    缺点 :
  • 类多了 , 多了代理类 , 工作量变大了 . 开发效率降低 .
  • 我们想要静态代理的好处,又不想要静态代理的缺点,所以 , 就有了动态代理

2.动态代理

动态代理的角色和静态代理的一样 .
动态代理的代理类是动态生成的 . 静态代理的代理类是我们提前写好的
动态代理分为两类 : 一类是基于接口动态代理 , 一类是基于类的动态代理
代码实现
抽象角色

//租房
public interface Rent {
    
    
    public void rent();
}

真实角色

//房东
public class Host implements Rent {
    
    
    public void rent() {
    
    
        System.out.println("房东要出租房子!");
    }
}

动态代理

public class ProxyInvocationHandler implements InvocationHandler {
    
    
    //被代理的接口
    private Rent rent;
    public void setRent(Rent rent) {
    
    
        this.rent = rent;
    }

    //生成得到代理类
    public Object getProxy(){
    
    
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),rent.getClass().getInterfaces(),this);
    }

    //处理代理实例,并返回结果
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    
        //动态代理的本质,就是使用反射机制实现
        seeHouse();
        Object result = method.invoke(rent, args);
        fare();
        return result;
    }
    public void seeHouse(){
    
    
        System.out.println("中介带看房子");
    }
    public void fare(){
    
    
        System.out.println("收中介费");
    }
}

客户

public class Client {
    
    
    public static void main(String[] args) {
    
    
        //真实角色
        Host host = new Host();
        //代理角色:现在没有
        ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler();
        //通过调用程序处理角色来处理我们要调用的接口对象!
        proxyInvocationHandler.setRent(host);
        Rent proxy = (Rent) proxyInvocationHandler.getProxy();//这里的proxy就是动态生成的,我们并没有写~
        proxy.rent();
    }
}

3.编写通用工具类

public class ProxyInvocationHandler implements InvocationHandler {
    
    
    //被代理的接口
    private Object target;

    public void setTarget(Object target) {
    
    
        this.target = target;
    }

    //生成得到代理类
    public Object getProxy(){
    
    
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
    }

    //处理代理实例,并返回结果
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    
        log(method.getName());
        Object result = method.invoke(target, args);
        return result;
    }

    public void log(String msg){
    
    
        System.out.println("执行了"+msg+"方法");
    }
}

五、AOP(面向切面编程)

1.概述

AOP(Aspect Oriented Programming,面向切面编程),通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
OOP允许开发者定义纵向的关系,但并不适合定义横向的关系,例如日志功能。日志代码往往横向地散布在所有对象层次中,而与它对应的对象的核心功能毫无关系对于其他类型的代码,这种散布在各处的无关的代码被称为横切(cross cutting),在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。

2.AOP在Spring中的作用

  • 横切关注点:对哪些方法进行拦截,拦截后怎么处理,这些关注点称之为横切关注点。
  • Aspect(切面): Aspect 声明类似于 Java 中的类声明,在 Aspect 中会包含着一些 Pointcut 以及相应的 Advice。即,它是一个类。
  • Advice(通知):Advice 定义了在 pointcut 里面定义的程序点具体要做的操作,它通过 before、after 和 around 来区别是在每个 joint point 之前、之后还是代替执行的代码。即,它是一个方法。
  • Target(目标):被通知对象。
  • Proxy(代理):向目标对象应用通知后创建的对象。
  • Pointcut(切入点):表示一组 joint point,这些 joint point 或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的 Advice 将要发生的地方。
  • Joint point(连接点):表示在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它 joint point。

SpringAOP中,通过Advice定义横切逻辑。支持5中类型的Adcice。

通知类型 连接点 实现接口
前置通知 方法前 org.springframework.aop.MethodBeforeAdvice
后置通知 方法后 org.springframework.aop.AfterReturningAdvice
环绕通知 方法前后 org.aopalliance.intercept.MethodInterceptor
异常抛出通知 方法抛出异常 org.springframework.aop.ThrowsAdvice
引介通知 类中增加新的方法属性 org.springframework.aop.IntroductionInterceptor

3.在Spring中实现AOP

使用AOP织入,需要导入一个依赖包

        <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.4</version>
        </dependency>

(1)使用Spring API实现

1.编写业务接口

public interface UserService {
    
    
    public void add();
    public void delete();
}

2.编写实现类

public class UserServiceImpl implements UserService{
    
    
    public void add() {
    
    
        System.out.println("增加了一个用户");
    }
    public void delete() {
    
    
        System.out.println("删除了一个用户");
    }
}

3.编写增强类 , 我们编写两个 , 一个前置增强 一个后置增强。
前置增强:

public class log implements MethodBeforeAdvice {
    
    
    //method:要执行的目标对象的方法
    //args:参数
    //target:目标对象
    public void before(Method method, Object[] args, Object target) throws Throwable {
    
    
        System.out.println(target.getClass().getName()+"的"+method.getName()+"被执行了");
    }
}

后置增强:


public class AfterLog implements AfterReturningAdvice {
    
    
    //returnValue:返回值
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
    
    
        System.out.println("执行了"+method.getName()+"方法,返回结果为:"+returnValue);
    }
}

4.最后去Spring的文件applicationContext.xml中注册 , 并实现aop切入实现 , 注意导入约束 .

<?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:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--注册bean-->
    <bean id="userService" class="com.carry.service.UserServiceImpl"/>
    <bean id="log" class="com.carry.log.log"/>
    <bean id="afterLog" class="com.carry.log.AfterLog"/>

    <!--方式一:使用原生Spring API接口-->
    <!--配置aop:需要导入aop的约束-->
    <aop:config>
        <!--切入点:expression:表达式:execution(要执行的位置! * * * * *)-->
        <aop:pointcut id="pointcut" expression="execution(* com.example.service.UserServiceImpl.*(..))"/>
        <!--执行环绕增加-->
        <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
        <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
    </aop:config>
</beans>

5.测试

public class MyTest {
    
    
    public static void main(String[] args) {
    
    
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        //动态代理代理的是接口:注意点
        UserService userService = (UserService) context.getBean("userService");
        userService.add();
    }
}
  • Aop的重要性 : 很重要 . 一定要理解其中的思路 , 主要是思想的理解这一块 .
  • Spring的Aop就是将公共的业务 (日志 , 安全等) 和领域业务结合起来 , 当执行领域业务时 , 将会把公共业
    务加进来 . 实现公共业务的重复利用 . 领域业务更纯粹 , 程序猿专注领域业务 , 其本质还是动态代理 .

(2)自定义AOP

1.写我们自己的一个切入类。

public class DiyPointCut {
    
    

    public void before(){
    
    
        System.out.println("=====方法执行前=====");
    }

    public void after(){
    
    
        System.out.println("=====方法执行后=====");
    }
}

2.在Spring中配置。

    <!--方式二:自定义类-->
    <bean id="diy" class="com.carry.diy.DiyPointCut"/>
    <aop:config>
        <!--自定义切面,ref 要引用的类-->
        <aop:aspect ref="diy">
            <!--切入点-->
            <aop:pointcut id="point" expression="execution(* com.carry.service.UserServiceImpl.*(..))"/>
            <!--通知-->
            <aop:before method="before" pointcut-ref="point"/>
            <aop:after method="after" pointcut-ref="point"/>
        </aop:aspect>
    </aop:config>

(3)使用注解实现

1.编写一个注解实现的增强类

@Aspect//标注这个类是一个切面
public class AnnotationPointCut {
    
    

    @Before("execution(* com.carry.service.UserServiceImpl.*(..))")
    public void before(){
    
    
        System.out.println("=====方法执行前=====");
    }

    @After("execution(* com.carry.service.UserServiceImpl.*(..))")
    public void after(){
    
    
        System.out.println("=====方法执行后=====");
    }

    //在环绕增强中,我们可以给定一个参数,代表我们要获取处理切入的点;
    @Around("execution(* com.carry.service.UserServiceImpl.*(..))")
    public void around(ProceedingJoinPoint joinPoint) throws Throwable {
    
    
        System.out.println("环绕前");

        Signature signature = joinPoint.getSignature();//获得签名
        System.out.println("signature"+signature);

        //执行方法
        Object proceed = joinPoint.proceed();

        System.out.println("环绕后");
    }
}

2.在Spring配置文件中,注册bean,并增加支持注解的配置

    <!--方式三-->
    <bean id="annotationPointCut" class="com.carry.diy.AnnotationPointCut"/>
    <!--开启注解支持! JDK(默认 proxy-target-class="false")  cglib(proxy-target-class="true")-->
    <aop:aspectj-autoproxy/>

猜你喜欢

转载自blog.csdn.net/qq_43647936/article/details/121491282
今日推荐