0%

Spring笔记

Spring

IOC 控制反转

开发步骤

  1. 创建Maven工程,pom.xml导入依赖
<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.2.3.RELEASE</version>
    </dependency>
</dependencies>
  1. 在resources路径下创建spring.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 id="student" class="org.example.entity.Student"></bean>
</beans>
  1. IoC容器通过读取sping.xml文件,加载bean标签来创建文件
  2. 调用API获取IoC容器中所存在的对象
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spirng.xml");
Student student = (Student)applicationContext.getBean("student");
System.out.println(student);

IoC容器创建bean的两种方式

  • 无参构造函数
<bean id="student" class="org.example.entity.Student"></bean>

给成员变量赋值

<bean id="student" class="org.example.entity.Student">
    <property name="id" value = "1"></property>
    <property name="name" value="zhangsan"></property>
    <property name="age" value="22"></property>
</bean>
  • 有参构造函数
<bean id="student3" class="org.example.entity.Student">
    <constructor-arg name ="name" value = "网速"></constructor-arg>
    <constructor-arg name = "id" value="3"></constructor-arg>
    <constructor-arg name="age" value="23"></constructor-arg>
</bean>

从IoC容器中取bean

  • 通过id取值

    Student student = (Student)applicationContext.getBean("student");
  • 通过类型取值,当IoC容器中同时存在两个以上Student Bean的时候,会抛出异常

    Student student = (Student)applicationContext.getBean(Student.class);
    

bean的属性中如果包含有特殊字符,如下处理即可

<bean id="classes" class="org.example.entity.Classes">
    <property name="name" >
        <value ><![CDATA[<一班>]]></value>
    </property>
    <property name="id" value="1"></property>


</bean>

IoC DI

DI指bean之间的依赖注入,设置对象之间的级联关系。

Classes

@Data
public class Classes {
    private Integer id;
    private String name;
}

Student

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
    private Integer id;
    private String name;
    private Integer age;
    private Classes classes;
}

Spring-di.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 id="classes" class="org.example.entity.Classes">
        <property name="name" value="一班"></property>
        <property name="id" value="1"></property>


    </bean>

    <bean id="student" class="org.example.entity.Student">
        <property name="id" value="100"></property>
        <property name="name" value="张三"></property>
        <property name="age" value="21"></property>
        <property name="classes" ref="classes"></property>
    </bean>
</beans>

bean之间的级联需要ref属性完成映射,不能使用value,否则会抛出类型转换异常

classes


@Data
public class Classes {
    private Integer id;
    private String name;
    private List<Student> studentList;
}

spring-di.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 id="classes" class="org.example.entity.Classes">
        <property name="name" >
            <value ><![CDATA[<一班>]]></value>
        </property>
        <property name="id" value="1"></property>

        <property name="studentList" >
            <list>
                <ref bean="student"></ref>
                <ref bean="student2"></ref>
            </list>
        </property>

    </bean>

    <bean id="student" class="org.example.entity.Student">
        <property name="id" value="100"></property>
        <property name="name" value="张三"></property>
        <property name="age" value="21"></property>
<!--        <property name="classes" ref="classes"></property>-->
    </bean>

    <bean id="student2" class="org.example.entity.Student">
        <property name="id" value="200"></property>
        <property name="name" value="李四"></property>
        <property name="age" value="18"></property>
    </bean>
</beans>

Spring中的bean

bean是根据scope来生成,表示bean的作用域,scope有四种类型:

  • singleton 单例,表示通过Spring容器获取的对象是唯一的,默认值。
  • prototype,原型,表示通过Spring容器获取的对象是不同的。
  • request,请求,表示在一次HTTP请求内有效。
  • session,会话,表示一个用户会话内有效。

request和session适用于web项目。
singleton模式下,只要加载IoC容器,无论是否从IoC中取出bean,配置文件中的bean都会被创建,对象永远只有一个。

prototype模式下,如果不从IoC中取bean,则不创建对象,取一次创建一个对象,取多次bean创建多次。

Spring的继承

Spring的继承不同于Java中的继承,区别:java中的继承针对于类的,Spring的继承是针对于对象(bean)。

Spring的继承中,子bean可以继承父bean中所有成员变量的值。

<bean id="user1" class="org.example.entity.User" >
    <property name="id" value="1" ></property>
    <property name="name" value="用户"></property>
</bean>
<bean id="user2" class="org.example.entity.User" parent="user1">
    <property name="name" value="李四"></property>
</bean>

通过设置bean标签 parent属性建立继承关系,同时子bean可以覆盖父bean的属性值。
Spring的继承是针对对象的,所以子bean和父bean并不属于同一个数据类型,只要其成员变量列表一致即可。

Spring的依赖

用来设置两个bean之间的创建顺序。
IoC容器默认情况下是通过spring.xml中bean的配置顺序来决定创建顺序的,配置在前面的bean会先创建。
再不更改spring.xml配置顺序的前提下,通过设置bean之间的依赖关系,来调整bean的创建顺序。

<bean id="account" class="org.example.entity.Account" depends-on="user"></bean>
<bean id="user" class="org.example.entity.User" ></bean>

上述代码的结果是先创建User,在创建Account。

Spring读取外部资源

实际开发中,数据库的配置会单独保存到后缀名为properties的文件中,方便维护和修改,如果使用spring来加载数据源,就需要在spring.xml中读取properties中的数据,这就是读取外部数据。

jdbc.properties

user = root
password = root
url = jdbc:mysql://localhost:3306/library
driverName = com.mysql.cj.jdbc.Driver

Spring p 命名空间

p命名空间可以用来简化bean的配置。

<bean id="student" class="org.example.entity.Student" p:id="1" p:name="张三" p:age="18" p:classes-ref="classes"></bean>

<bean id="classes" class="org.example.entity.Classes" p:id="1" p:name="一班"></bean>

Spring工厂方法

IoC通过工厂模式创建bean有两种方式:

  • 静态工厂方法
  • 实例工厂方法

区别在于静态工厂不需要实例化,实例工厂需要实例化

静态工厂方法

  1. 创建Car类
@Data
@AllArgsConstructor
public class Car {
    private Integer num;
    private String brand;
}
  1. 创建静态工厂类、静态工厂方法
public class StacticCarFactory {
    private static Map<Integer, Car> carMap;
    static {
        carMap = new HashMap<>();
        carMap.put(1,new Car(1,"奥迪"));
        carMap.put(2,new Car(2,"奥拓"));
    }
    public static Car getCar(Integer num){
        return carMap.get(num);
    }
}
  1. spring.xml
<bean id="car1" class="org.example.factory.StacticCarFactory" factory-method="getCar">
    <constructor-arg value="1"></constructor-arg>
</bean>

factory-method指向静态方法
constructor-arg的value属性是调用静态方法传入的参数

实例方法

  1. 创建实例工厂类、工厂方法
public class InstanceCarFactory {
    private Map<Integer, Car> carMap;
    public InstanceCarFactory(){
        carMap = new HashMap<>();
        carMap.put(1,new Car(1,"奥迪"));
        carMap.put(2,new Car(2,"奥拓"));
    }
    public Car getCar(Integer num){
        return carMap.get(num);
    }
}
  1. spring.xml
<!--    实例工厂-->
    <bean id="instanceCarFactory" class="org.example.factory.InstanceCarFactory"></bean>
<!--    通过实例工厂获取Car-->
    <bean id="car2" factory-bean="instanceCarFactory" factory-method="getCar">
        <constructor-arg value="2"></constructor-arg>
    </bean>

区别:
静态工厂方法创建Car对象,不需要实例化工厂对象,因为静态工厂的静态方法,不需要创建对象即可调用,spring-xml中只需要配置一个bean,即最终的结果Car即可。

实例工厂方法创建Car对象。需要实例化工厂对象,因为getCar方法是非静态的,就必须通过实例化对象才能调用,所以必须创建工厂对象,spring.xml中需要配置两个bean,一个是工厂bean,一个是Car bean。

spring.xml中class + factory-method的形式是直接调用类中的工厂方法

spring.xml中factory-bean + factory-method的形式是调用工厂bean中的工厂方法,就必须先创建工厂bean。

Spring IoC自动装载 autowire

自动装载是spring提供的一种更加简便的方式来完成DI,不需要手动配置property。IoC容器会自动选择bean完成注入。

自动装置有两种方式:

  • byName,通过属性名来完成自动装载
  • bytype, 通过属性类型对应的数据类型完成自动装载。

byName的操作方式

  1. 创建person实体类
@Data
public class person {
    private Integer Id;
    private String name;
    private Car car;
}
  1. 在spring.xml中配置Car和Person对应的bean,并且通过自动装载完成依赖注入
<bean id="person" class="org.example.entity.Person" autowire="byName">
    <property name="id" value="1"></property>
    <property name="name" value="张三" ></property>

</bean>

<bean id="car" class="org.example.entity.Car">
    <constructor-arg name="num" value="1"></constructor-arg>
    <constructor-arg name="brand" value="奥迪"></constructor-arg>
</bean>

byType的操作方式

<bean id="person" class="org.example.entity.Person" autowire="byType">
    <property name="id" value="1"></property>
    <property name="name" value="张三" ></property>

</bean>

<bean id="car3" class="org.example.entity.Car">
    <constructor-arg name="num" value="1"></constructor-arg>
    <constructor-arg name="brand" value="奥迪"></constructor-arg>
</bean>

使用byType进行自动装载时,必须保证IoC中只有一个符合条件的bean,否则会抛出异常信息。

Spring IoC基于注解的开发

Spring IoC的作用是帮助开发者创建项目中所需要的bean,同时完成bean之间的依赖注入关系,DI。
实现该功能有两种方式。

  • 基于XML配置。
  • 基于注解。

基于注解有两步操作,缺一不可:

  1. 配置自动扫包
  2. 添加注释
<context:component-scan base-package="org.example.entity"></context:component-scan>

DI

@Data
@Component
public class User {
    private Integer id;
    private String name;
}
@Data
@Component(value = "myre")
public class Repository {
    @Autowired
    private User user;
}

@Autowired 默认是通过byType进行注入的,如果要改为byName,需要配合Qualifier注解来完成

@Autowired
@Qualifier(value = "us")
private User user;

表示将IoC中id为ds的bean注入到repository中。
实体类中普通的成员变量(String、包装类等)可以通过@value注解进行赋值

@Data
@Component(value = "us")
public class User {
    @Value("1")
    private Integer id;
    @Value("张三")
    private String name;

}

等同于spring.xml

<bean id="us" class="org.example.entity.User">
    <property name="id" value="1"></property>
    <property name="name" value="张三"></property>
</bean>

实际开发的使用

实际开发中我们会将程序分为三层:

  • Controller
  • Service
  • Repository(DAO)

关系 Controller–>Service–>DAO

@Component 注解是将标注的类加载到IoC容器中,实际开发中可以根据业务需求,分别使用@Controller、@Service、@Repository注解来标注控制层类、业务层类、持久层类。

@Override
public String doRepository(Double score) {
    String result = "";
    if(score<60){
        result = "不及格";
    }
    if(score>=60 && score<80)
    {
        result = "合格";
    }
    if (score >=80)
    {
        result = "优秀";
    }
    return result;
}

spring-annotation.xml

<context:component-scan base-package="org.example"></context:component-scan>

Spring AOP

AOP (Aspect Oriented Programming) 面向切面编程。
OOP(Object Oriented Programming)面向对象编程,用对象化的思想来完成程序。
AOP是对OOP的一个补充,是在另外一个维度上抽象出对象。
具体是指程序运行时动态地将非业务代码切入到业务代码中,从而实现程序的解耦合,将非业务代码抽象成一个对象,对对象编程就是面向切面编程。

AOP的优点:

  • 可以降低模块之间的耦合性
  • 提高代码的复用性
  • 提高代码的维护性
  • 集中管理非业务代码,便于维护
  • 业务代码不受非业务代码影响,逻辑更加清晰

通过一个例子来理解AOP

  1. 创建一个计算器接口 Cal
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);
}
  1. 创建接口的实现类Calimpl
public class CalImpl implements Cal {

    @Override
    public int add(int num1, int num2) {
        return num1+num2;
    }

    @Override
    public int sub(int num1, int num2) {
        return num1-num2;
    }

    @Override
    public int mul(int num1, int num2) {
        return num1*num2;
    }

    @Override
    public int div(int num1, int num2) {
        return num1/num2;
    }
}

日志打印

  • 在每个方法开始位置输出参数信息
  • 在每个方法结束位置输出结果信息

对于计算器来讲,加减乘除就是业务代码,日志打印就是非业务代码。
AOP如何实现?使用动态代理的方式来实现。
代理首先应该具备CalImpl的所有功能,并在此基础上,扩展出打印日志的功能。

  1. 删除CalImpl方法中所有打印日志的代码,只保留业务代码。
  2. 创建MyInvocationHandler类,实现InvocationHandler接口,生成动态代理类。

动态代理类,需要动态生成,需要获取到委托类的接口信息,根据这些接口信息,动态生成一个代理类,然后再由ClassLoader用来将动态生成的类加载到JVM中