Spring框架底层逻辑学习

如何使用IOC

  • 创建Maven工程,pom.xml添加依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>aispringioc</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
 <!--添加依赖-->
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version> 5.3.13</version> <!--版本-->
        </dependency>
    </dependencies>

</project>
  • 创建实体类Student
package com.my.entity;

import lombok.Data;

@Data
public class Student {
    private long id;
    private String name;
    private int age;
}

  • 传统的开发方式,手动new Student
Student student = new Student();
        student.setId(1L);
        student.setAge(22);
        student.setName("张三");
        System.out.println(student);
  • 通过IOC创建对象,在配置文件中添加需要管理的对象,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" xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd">
    <bean id="student01" class="com.my.entity.Student">
        <property name="id" value="1"></property>
        <property name="name" value="张三"></property>
        <property name="age" value="22"></property>
    </bean>
        
    <bean id="student02" class="com.my.entity.Student"></bean>
    <bean id="student03" class="com.my.entity.Student"></bean>
</beans>
  • 从IOC中获取对象
//加载配置文件
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        Student student01 = (Student) applicationContext.getBean("student01");
        System.out.println(student01);

配置文件

  • 通过配置 bean 标签来完成对象的管理。
    • id:对象名。
    • class:对象的模板类。(所有交给IOC容器来管理的类必须有无参构造函数,因为Spring底层是通过反射机制来创建对象,调用的是无参构造)
  • 对象的成员变量通过property标签完成赋值。
    • name:成员变量名。
    • value:成员变量值(基本数据类型,String可以直接赋值,如果是其他的引用类型,不能通过value赋值)
    • ref:将IOC中的另外一个bean赋给当前的成员变量(DI)
    <bean id="student01" class="com.my.entity.Student">
        <property name="id" value="1"></property>
        <property name="name" value="张三"></property>
        <property name="age" value="22"></property>
        <property name="address" ref="address01"></property>
    </bean>
    <bean id="address01" class="com.my.entity.Address">
        <property name="id" value="1"></property>
        <property name="name" value="天空之城"></property>
    </bean>

给bean注入集合

<bean id="student" class="com.southwind.entity.Student">
	 <property name="id" value="2"></property>
	 <property name="name" value="李四"></property>
	 <property name="age" value="33"></property>
	 <property name="addresses">
		 <list>
			 <ref bean="address"></ref>
			 <ref bean="address2"></ref>
		 </list>
 	</property>
</bean>
<bean id="address" class="com.southwind.entity.Address">
	 <property name="id" value="1"></property>
	 <property name="name" value="科技路"></property>
</bean>
<bean id="address2" class="com.southwind.entity.Address">
	 <property name="id" value="2"></property>
	 <property name="name" value="⾼新区"></property>
</bean

scope作用域

Spring 管理的 bean 是根据 scope 来⽣成的,表示 bean 的作⽤域,共4种,默认值是 singleton。

  • singleton:单例,表示通过 IoC 容器获取的 bean 是唯⼀的。
  • prototype:原型,表示通过 IoC 容器获取的 bean 是不同的。
  • request:请求,表示在⼀次 HTTP 请求内有效。
  • session:回话,表示在⼀个⽤户会话内有效。
    request 和 session 只适⽤于 Web 项⽬,⼤多数情况下,使⽤单例和原型较多。
    prototype 模式当业务代码获取 IoC 容器中的 bean 时,Spring 才去调⽤⽆参构造创建对应的 bean。
    singleton 模式⽆论业务代码是否获取 IoC 容器中的 bean,Spring 在加载 spring.xml 时就会创建
    bean。

Spring的继承

与 Java 的继承不同,Java 是类层⾯的继承,⼦类可以继承⽗类的内部结构信息;Spring 是对象层⾯的继承,⼦对象可以继承⽗对象的属性值。

<bean id="student2" class="com.southwind.entity.Student">
	 <property name="id" value="1"></property>
	 <property name="name" value="张三"></property>
	 <property name="age" value="22"></property>
	 <property name="addresses">
		 <list>
			 <ref bean="address"></ref>
			 <ref bean="address2"></ref>
		 </list>
	 </property>
</bean>
<bean id="address" class="com.southwind.entity.Address">
	 <property name="id" value="1"></property>
	 <property name="name" value="科技路"></property>
</bean>
<bean id="address2" class="com.southwind.entity.Address">
	 <property name="id" value="2"></property>
	 <property name="name" value="⾼新区"></property>
</bean>
<bean id="stu" class="com.southwind.entity.Student" parent="student2">
 	<property name="name" value="李四"></property>
</bean>

Spring 的继承关注点在于具体的对象,⽽不在于类,即不同的两个类的实例化对象可以完成继承,前提是⼦对象必须包含⽗对象的所有属性,同时可以在此基础上添加其他的属性。

Spring的依赖

与继承类似,依赖也是描述 bean 和 bean 之间的⼀种关系,配置依赖之后,被依赖的 bean ⼀定先创建,再创建依赖的 bean,A 依赖于 B,先创建 B,再创建 A。

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

<bean id="student" class="com.southwind.entity.Student" dependson="user"></bean>
 	<bean id="user" class="com.southwind.entity.User"></bean>
</beans>

Spring的p命名空间

p 命名空间是对 IoC / DI 的简化操作,使⽤ p 命名空间可以更加⽅便的完成 bean 的配置以及 bean 之间的依赖注⼊。

<?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:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
    http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd
">
    <bean id="student" class="com.my.entity.Student" p:id="1"
          p:name="张三" p:age="22" p:address-ref="address"></bean>
    <bean id="address" class="com.my.entity.Address" p:id="2"
          p:name="科技路"></bean>
</beans>
package test;

import com.my.entity.Student;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test_p {
    public static void main(String[] args) {
        ApplicationContext applicationContext =
                new ClassPathXmlApplicationContext("spring-p.xml");
        Student student01 = (Student) applicationContext.getBean("student");
        System.out.println(student01);
    }
}

Spring的工厂方法

IoC 通过⼯⼚模式创建 bean 的⽅式有两种:

  • 静态⼯⼚⽅法
  • 实例⼯⼚⽅法

静态工厂方法
实体类

package com.my.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Car {
    private long id;
    private String name;
}

静态工厂方法

package com.my.factory;

import com.my.entity.Car;

import java.util.HashMap;
import java.util.Map;

public class StaticCarFactory {
    private static Map<Long, Car> carMap;
    static {
        carMap = new HashMap<Long,Car>();
        carMap.put(1L,new Car(1L,"宝马"));
        carMap.put(2L,new Car(2L,"奔驰"));
    }
    public static Car getCar(long id){
        return carMap.get(id);
    }
}

配置文件

<!--  配置静态工厂创建 Car  -->
<bean id="car" class="com.my.factory.StaticCarFactory" factory-method="getCar">
    <constructor-arg value="2"></constructor-arg>
</bean>

实例工厂方法

package com.my.factory;

import com.my.entity.Car;

import java.util.HashMap;
import java.util.Map;

public class InstanceCarFactory {
    private Map<Long, Car> carMap;

    public InstanceCarFactory(){
        carMap = new HashMap<Long,Car>();
        carMap.put(1L,new Car(1L,"宝马"));
        carMap.put(2L,new Car(2L,"奔驰"));
    }
    public  Car getCar(long id){
        return carMap.get(id);
    }
}

<!--  配置实例工厂对象  -->
    <bean id="carFactor" class="com.my.factory.InstanceCarFactory"></bean>
<!--  配置实例工厂创建的car  -->
    <bean id="car2" factory-bean="carFactor" factory-method="getCar">
        <constructor-arg value="1"></constructor-arg>
    </bean>

IOC自动装载(Autowire)

IoC 负责创建对象,DI 负责完成对象的依赖注入,通过配置 property 标签的 ref 属性来完成,同时Spring 提供了另外⼀种更加简便的依赖注入方式:自动装载,不需要手动配置 property,IoC 容器会自动选择 bean 完成注入。
自动装载有两种方式:

  • byName:通过属性名自动装载
  • byType:通过属性的数据类型自动装载

byName

    <bean id="car" class="com.my.entity.Car">
        <property name="id" value="1"></property>
        <property name="name" value="宝马"></property>
    </bean>
    <bean id="person" class="com.my.entity.Person" autowire="byName">
        <property name="id" value="11"></property>
        <property name="name" value="张三"></property>
    </bean>

byType

    <bean id="car" class="com.my.entity.Car">
        <property name="id" value="1"></property>
        <property name="name" value="宝马"></property>
    </bean>
    <bean id="person" class="com.my.entity.Person" autowire="byType">
        <property name="id" value="11"></property>
        <property name="name" value="张三"></property>
    </bean>

byType 需要注意,如果同时存在两个及以上的符合条件的 bean 时,自动装载会抛出异常。

AOP

AOP:Aspect Oriented Programming ⾯向切⾯编程。
AOP 的优点:

  • 降低模块之间的耦合度。
  • 使系统更容易扩展。
  • 更好的代码复⽤。
  • 非业务代码更加集中,不分散,便于统⼀管理。
  • 业务代码更加简洁存粹,不参杂其他代码的影响。
    AOP 是对⾯向对象编程的⼀个补充,在运⾏时,动态地将代码切⼊到类的指定⽅法、指定位置上的编程思想就是⾯向切⾯编程。将不同⽅法的同⼀个位置抽象成⼀个切⾯对象,对该切⾯对象进⾏编程就是AOP。

如何使用?

  • 创建Maven工程,pom.xml添加;
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>5.3.13</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.3.13</version>
        </dependency>
    </dependencies>
  • 创建一个计算器接口Cal,定义4个方法。
package com.my.util;

public interface Cal {
    public int add(int num1,int num2);
    public int sub(int num1,int num2);
    public int mul(int num1,int num2);
    public int div(int num1,int num2);
}
  • 创建接口的实现类CalImpl。
package com.my.util.impl;

import com.my.util.Cal;

public class CalImpl implements Cal {


    @Override
    public int add(int num1, int num2) {
        System.out.println("add方法的参数值是"+"["+num1+","+num2+"]");
        int result=num1+num2;
        System.out.println("add方法的结果是"+result);
        return result;
    }

    @Override
    public int sub(int num1, int num2) {
        System.out.println("sub方法的参数值是"+"["+num1+","+num2+"]");
        int result=num1-num2;
        System.out.println("sub方法的结果是"+result);
        return result;
    }

    @Override
    public int mul(int num1, int num2) {
        System.out.println("mul方法的参数值是"+"["+num1+","+num2+"]");
        int result=num1*num2;
        System.out.println("mul方法的结果是"+result);
        return result;
    }

    @Override
    public int div(int num1, int num2) {
        System.out.println("div方法的参数值是"+"["+num1+","+num2+"]");
        int result=num1/num2;
        System.out.println("div方法的结果是"+result);
        return result;
    }
}

上述代码中,日志信息和业务逻辑的耦合性很高,不利于系统的维护,使用 AOP 可以进⾏优化,如何来实现 AOP?使⽤动态代理的⽅式来实现。
给业务代码找⼀个代理,打印⽇志信息的⼯作交个代理来做,这样的话业务代码就只需要关注⾃身的业务即可。

package com.my.util.impl;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;

public class MyInvocationHandler implements InvocationHandler {
    //接收代理对象
    private Object object=null ;
    //返回代理对象
    public Object bind(Object object){
        this.object=object;
        return Proxy.newProxyInstance
                (object.getClass().getClassLoader(),object.getClass().getInterfaces(),this);
//               委托对象的运行实类      类加载器           根据委托对象的运行实类获取所有接口
//        通过反射机制获得动态代理对象
    }
    @Override
//    生成公共类的打印参数和打印结果的,把应用代码和日志信息剥离开
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println(method.getName()+"方法的参数值是"+ Arrays.toString(args));
        Object result=method.invoke(this.object,args);
        System.out.println(method.getName()+"方法的结果是"+ result);
        return result;
    }
}

以上是通过动态代理实现 AOP 的过程,⽐较复杂,不好理解,Spring 框架对 AOP 进⾏了封装,使⽤Spring 框架可以⽤⾯向对象的思想来实现 AOP。
Spring 框架中不需要创建InvocationHandler,只需要创建⼀个切⾯对象,将所有的⾮业务代码在切⾯对象中完成即可,Spring 框架底层会⾃动根据切⾯类以及⽬标类⽣成⼀个代理对象。
需要用到注解@Component,需要注入依赖

<dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.13</version>
        </dependency>

LoggerAspect

package com.my.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;
@Aspect//使之成为切面对象
@Component//交给IOC创建对象,让IOC管理这个对象
public class LoggerAspect {
//        切割CalImpl里面的所有方法,在执行业务之前
    @Before("execution(public int com.my.util.impl.CalImpl.*(..))")
//         连接点:连接before方法与目标方法;把目标方法的信息抽象成joinPoint
    public void before(JoinPoint joinPoint){
        //获取方法名,getSignature:获取方法
        String name = joinPoint.getSignature().getName();
        //获取参数
        String args = Arrays.toString(joinPoint.getArgs());
        System.out.println(name+"方法的参数值是"+ args);
    }
    //        切割CalImpl里面的所有方法,在执行业务之前
    @After("execution(public int com.my.util.impl.CalImpl.*(..))")
//         连接点:连接before方法与目标方法;把目标方法的信息抽象成joinPoint
    public void after(JoinPoint joinPoint){
        //获取方法名,getSignature:获取方法
        String name = joinPoint.getSignature().getName();
        //获取参数
//        String args = Arrays.toString(joinPoint.getArgs());
        System.out.println(name+"方法执行完毕");
    }
    @AfterReturning(value = "execution(public int com.my.util.impl.CalImpl.*(..))",returning = "res")
    public void afterReturning(JoinPoint joinPoint,Object res){
        //获取方法名,getSignature:获取方法
        String name = joinPoint.getSignature().getName();
        //获取参数
//        String args = Arrays.toString(joinPoint.getArgs());
        System.out.println(name+"方法的结果是:"+res);
    }
    @AfterThrowing(value = "execution(public int com.my.util.impl.CalImpl.*(..))",throwing = "exception")
    public void afterThrowing(JoinPoint joinPoint,Exception exception){
        //获取方法名,getSignature:获取方法
        String name = joinPoint.getSignature().getName();
        //获取参数
//        String args = Arrays.toString(joinPoint.getArgs());
        System.out.println(name+"方法抛出的异常:"+exception);
    }
}

LoggerAspect 类定义处添加的两个注解:

  • @Aspect :表示该类是切⾯类。
  • @Component :将该类的对象注⼊到 IoC 容器。
    具体方法处添加的注解:
  • @Before :表示方法执行的具体位置和时机。
    CalImpl 也需要添加 @Component ,交给 IoC 容器来管理。
package com.my.util.impl;

import com.my.util.Cal;
import lombok.Data;
import org.springframework.stereotype.Component;

@Component
@Data
public class CalImpl implements Cal {
    @Override
    public int add(int num1, int num2) {
//        System.out.println("add方法的参数值是"+"["+num1+","+num2+"]");
        int result=num1+num2;
//        System.out.println("add方法的结果是"+result);
        return result;
    }

    @Override
    public int sub(int num1, int num2) {
//        System.out.println("sub方法的参数值是"+"["+num1+","+num2+"]");
        int result=num1-num2;
//        System.out.println("sub方法的结果是"+result);
        return result;
    }

    @Override
    public int mul(int num1, int num2) {
//        System.out.println("mul方法的参数值是"+"["+num1+","+num2+"]");
        int result=num1*num2;
//        System.out.println("mul方法的结果是"+result);
        return result;
    }

    @Override
    public int div(int num1, int num2) {
//        System.out.println("div方法的参数值是"+"["+num1+","+num2+"]");
        int result=num1/num2;
//        System.out.println("div方法的结果是"+result);
        return result;
    }
}

spring.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:context="http://www.springframework.org/schema/context"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
    http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd
">
<!--    自动扫描    -->
    <context:component-scan base-package="com.my"></context:component-scan>
<!--    Aspect注解生效,为目标类自动生成代理对象     -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

</beans>

context:component-scancom.my 包中的所有类进⾏扫描,如果该类同时添加了@Component ,则将该类扫描到 IoC 容器中,即 IoC 管理它的对象。
aop:aspectj-autoproxy 让 Spring 框架结合切⾯类和⽬标类⾃动⽣成动态代理对象。

  • 切面:横切关注点被模块化的抽象对象。
  • 通知:切⾯对象完成的⼯作。
  • ⽬标:被通知的对象,即被横切的对象。
  • 代理:切⾯、通知、⽬标混合之后的对象。
  • 连接点:通知要插⼊业务代码的具体位置。
  • 切点:AOP 通过切点定位到连接点。

学习链接:

https://www.bilibili.com/video/BV1Gt411N7HF/?p=8&spm_id_from=pageDriver&vd_source=1518a3c10a08cab7a7bd2b85527fc9fa

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值