spring框架总结

spring

spring两大核心机制

  • Ioc:工厂模式
  • AOP:代理模式

一、IoC

  • IoC是spring框架的灵魂,控制反转。

    框架实现了new操作

    Student stu = new Student();
    
  • lombok可以帮助开发者 自动生成实体类相关的方法

    使用前需要安装插件:Lombok

    		<!-- Lombok -->
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
            </dependency>
    

    实体类上加@Data注解

    @AllArgConstructor 有参构造

    @NoArgConstructor 无参构造

  • 实体类中所有的成员变量 类型建议使用包装类

1.开发步骤

  1. 创建工程,导依赖

    		<!-- Spring -->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
            </dependency>
    
  2. 在resource路径下创建spring.xml文件

    <bean id="student" class="com.nuc.gjq.entity.Student"></bean>
    
  3. Ioc容器通过读取spring.xml配置文件,加载bean标签来创建队形

  4. 调用API获取Ioc容器已经创建的对象

    //Ioc容器创建对象
    ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");
    Student stu = (Student)ac.getBean("student");
    System.out.println(stu);
    

2.IoC容器创建bean属性注入的两种方式

  • setter注入

    <bean id="student" class="com.nuc.gjq.entity.Student"></bean>
    

    给成员变量赋值

    <bean id="student" class="com.nuc.gjq.entity.Student">
    	<property name="id" value="1"></property>
        <property name="name" value="张三"></property>
        <property name="age" value="23"></property>
    </bean>
    
  • 构造函数注入

    实体类中有三个参数的构造函数

    <bean id="student" class="com.nuc.gjq.entity.Student">
    	<constructor-arg name="id" value="2"></constructor-arg>
        <constructor-arg name="name" value="李四"></constructor-arg>
        <constructor-arg name="id" value="18"></constructor-arg>
    </bean>
    

    可以没有name,IoC会通过(id,name,age)顺序给参数赋值

    也可以用index

    <bean id="student" class="com.nuc.gjq.entity.Student">
    	<constructor-arg index="0" value="2"></constructor-arg>
        <constructor-arg index="1" value="李四"></constructor-arg>
        <constructor-arg index="2" value="18"></constructor-arg>
    </bean>
    
  • 注解注入

    <context:component-scan base-package="com.nuc.gjq.*"></context:component-scan>
    

    在程序中添加注解@Component,@Controller,@Mapper,@Service,实体类在字段上加@Value

3.通过IoC容器取bean

  • 通过id取值

    Student stu = (Student)ac.getBean("student");
    
  • 通过类型取值

    当IoC容器中同时存在两个以上的StudentBean的时候就会出异常,因为此时没有唯一的bean

    Student stu = (Student)ac.getBean(Student.class);
    

4.bean处理特殊符号

<!-- Classes -->
<bean id="classes" class="com.nuc.gjq.entity.Classes">
	<property name="id" value="1"></property>
    <property name="name">
    	<value>
        	<![CDATA[<一班>]]>   <!-- name的值为<一班>> -->
        </value>
    </property>
</bean>

5.IoC DI

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

    //student类
    @Data
    public Student(){
        private Integer id;
        private String name;
        private Integer age;
        private Classes classes;
    }
    
    //Classes类
    @Data
    public Classes(){
        private Integer id;
        private String name;
    }
    
    <!-- Classes -->
    <bean id="classes" class="com.nuc.gjq.entity.Classes">
    	<property name="id" value="1"></property>
        <property name="name" value="一班"></property>
    </bean>
    <!-- Student -->
    <bean id="student" class="com.nuc.gjq.entity.Student">
    	<property name="id" value="1"></property>
        <property name="name" value="张三"></property>
        <property name="age" value="23"></property>
        <property nema="classes" ref="classes"></property>
    </bean>
    
    ApplicationContext ac = new ClassPathXmlApplicationContext("spring-di.xml");
    //String[] names = ac.getBeanDefinitionNames();
    Classes classes = (Classes)ac.getBean("classes");
    Student stu = (Student)ac.getBean("student");
    System.out.println(stu);
    System.out.println(classes);
    

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

  • 班级对应多个学生

    //Classes类
    @Data
    public Classes(){
        private Integer id;
        private String name;
        private List<Student> student
    }
    
    <!-- Classes -->
    <bean id="classes" class="com.nuc.gjq.entity.Classes">
    	<property name="id" value="1"></property>
        <property name="name" value="一班"></property>
        <property name="studentList">
        	<list>
            	<ref bean="student"</ref>
                <ref bean="student2"></ref>
            </list>
        </property>
    </bean>
    <!-- Student -->
    <bean id="student" class="com.nuc.gjq.entity.Student">
    	<property name="id" value="1"></property>
        <property name="name" value="张三"></property>
        <property name="age" value="23"></property>
        <!--<property nema="classes" ref="classes"></property>-->
    </bean>
    <!-- Student -->
    <bean id="student2" class="com.nuc.gjq.entity.Student">
    	<property name="id" value="2"></property>
        <property name="name" value="李四"></property>
        <property name="age" value="18"></property>
        <!--<property nema="classes" ref="classes"></property>-->
    </bean>
    

6.spring中的bean

  • bean是根据scope来生成,表示bean的作用域, scope有4中类型:

    singleton,单例模式,表示通过spring容器获取的对象是唯一的,默认的

    prototype,原型模式,表示通过spring容器获取的对象是不同的

    request,请求,表示在一次HTTP请求内有效

    session,会话,表示在一个用户会话内有效

  • request,session适用于Web项目

  • 实例说明

    <!-- Student -->
    <bean id="student" class="com.nuc.gjq.entity.Student" scope="prototype">
    	<property name="id" value="1"></property>
        <property name="name" value="张三"></property>
        <property name="age" value="23"></property>
        <property nema="classes" ref="classes"></property>
    </bean>
    
    ApplicationContext ac = new ClassPathXmlApplicationContext("spring-di.xml");
    Student stu = (Student)ac.getBean("student");
    Student stu2 = (Student)ac.getBean("student");
    System.out.println(stu == stu2);
    
    • 默认为singleton,结果为true,使用的是唯一的对象

      只要执行加载IoC容器,无论是否从IoC中取出bean,配置文件中的都会被创建,无论取多少次都是唯一的一个bean对象

    • scope为prototype时,结果为false,每次调用都会新建一个bean对象

      如果不从IoC中取bean,就不创建对象,取一次bean,就会创建一个对象,取多少次,创建多少个

7.spring的继承

  • spring继承不同于java中的继承

    区别:java中的继承是针对于类的,spring的继承是针对于对象的(bean)

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

  • 实例说明

    <!-- Student -->
    <bean id="student" class="com.nuc.gjq.entity.Student">
    	<property name="id" value="1"></property>
        <property name="name" value="张三"></property>
        <property name="age" value="23"></property>
        <property nema="classes" ref="classes"></property>
    </bean>
    <!-- Student -->
    <bean id="student" class="com.nuc.gjq.entity.Student" parent="student">
        <property name="name" value="李四"></property>
    </bean>
    
    • 通过设置bena标签的parent属性建立继承关系,子bean可以完全继承,也可以通过值覆盖
    • spring的继承是针对对象的,子bean和父bean并不属于同一个数据类型,只要成员变量列表一直即可

8.spring的依赖

  • 用来设置两个bean的创建顺序

  • IoC容器默认情况下是通过spring.xml中的bean的配置顺序来决定创建顺序的,配置在前面的bean先创建

  • 在不更改spring.xml配置顺序的前提下,通过设置bean之间的依赖关系来调整bean的创建顺序

    <bean id="student" class="com.nuc.gjq.entity.Student" depends-on="user">
        <property name="name" value="李四"></property>
    </bean>
    <bean id="student" class="com.nuc.gjq.entity.Student">
        <property name="name" value="李四"></property>
    </bean>
    

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

9.spring读取外部资源

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

  • 实例说明

    在resouse中创建student.properties文件

    id=1
    name="张三"
    age=23
    
    <!-- 导入外部资源 -->
    <context:property-placeholder location="classpath:user.properties"></context:property-placeholder>
    <!-- SpringEL -->
    <bean id="student" class="com.nuc.gjq.entity.Student">
    	<property name="id" value="${id}"></property>
        <property name="name" value="${name}"></property>
        <property name="age" value="${age}"></property>
    </bean>
    

10.spring P 命名空间

  • P命名空间用来简化bean的配置

  • 实例说明

    <bean id="student" class="com.nuc.gjq.entity.Student" p:id=1 p:name="张三" p:age=23 p:classes-ref="classes"></bean>
    <bean id="classes" class="com.nuc.gjq.entity.Classes" p:id=1 p:name="一班"></bean>
    

11.spring工厂方法

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

    静态工厂方法

    实例工厂方法

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

  • 静态工厂方法

    1. 创建Car类

      @Data
      @AllArgsConstructor
      public class Car{
      	private Integer num;
      	private String brand;
      }
      
    2. 创建静态工程类,静态工厂方法

      public class StaticCarFactory{
          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);
          }
      }
      
    3. spring.xml

      <bean id = "car" class="com.nuc.gjq.factory.StaticCarFactory" factory-method="getCar">
      	<constructor-arg value="1"></constructor-arg>
      </bean>
      

      factory-method:指向静态方法

      constructor-arg:value属性是调用静态方法传入的参数

  • 实例工厂方法

    1. 实例工程类,工厂方法

      public class InstanceCarFactory{
          private static Map<Integer,Car> carMap;
          public InstanceCarFactory(){
              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);
          }
      }
      
    2. spring.xml

      <!-- 实例工厂 -->
      <bean id="instanceCarFactory" class="com.nuc.gjq.factory.InstanceCarFactory"></bean>
      <!-- 通过实例工厂获取Car -->
      <bean id="car" factory-bean="instanceCarFactory" factory-method="getCar">
      	<constructor-arg value="1"></constructor-arg>
      </bean>
      
  • 区别:

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

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

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

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

12.Spring IoC自动装载 autowire

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

  • 自动装载有两种方式:

    1. byName:通过属性名完成自动装载
    2. byType:通过属性对应的数据类型来完成自动装载
  • byName

    通过id在IoC中找classes类,注入到student中

    //student类
    @Data
    public Student(){
        private Integer id;
        private String name;
        private Integer age;
        private Classes classes;
    }
    
    //Classes类
    @Data
    public Classes(){
        private Integer id;
        private String name;
    }
    
    <!-- Classes -->
    <bean id="classes" class="com.nuc.gjq.entity.Classes">
    	<property name="id" value="1"></property>
        <property name="name" value="一班"></property>
    </bean>
    <!-- Student -->
    <bean id="student" class="com.nuc.gjq.entity.Student" autowire="byName">
    	<property name="id" value="1"></property>
        <property name="name" value="张三"></property>
        <property name="age" value="23"></property>
    </bean>
    
  • byType

    通过Student类中的找到Classes类,再到IoC中找Classes类,注入到student中

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

    <!-- Classes -->
    <bean id="classes" class="com.nuc.gjq.entity.Classes">
    	<property name="id" value="1"></property>
        <property name="name" value="一班"></property>
    </bean>
    <!-- Student -->
    <bean id="student" class="com.nuc.gjq.entity.Student" autowire="byType">
    	<property name="id" value="1"></property>
        <property name="name" value="张三"></property>
        <property name="age" value="23"></property>
    </bean>
    

13.Spring IoC基于注解的开发

  • Spring IoC的作用是帮助开发者创建项目中所需要的bean,同时完成bean之间的依赖注入关系,DI

    实现该功能有两种方式:

    • 基于XML配置
    • 基于注解
  • 基于注解有两步操作,缺一不可

    1. 配置自动扫包

      <context:component-scan base-package="com.nuc.gjq.entity"></context:component-scan>
      
    2. 添加注解

      在实体类上添加注解:@Component

  • 实例说明

    //student类
    @Data
    @Component
    public Student(){
        @Value("1")
        private Integer id;
        @Value("张三")
        private String name;
        @Value("23")
        private Integer age;
        @Autowired //DI操作(级联)   默认通过byType装载
        @Qualifier(value="cs") //修改为通过byName装载
        private Classes classes;
    }
    
    //Classes类
    @Data
    @Component(value="cs") //修改名称
    public Classes(){
        private Integer id;
        private String name;
    }
    

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

     	@Autowired //DI操作(级联)   默认通过byType装载
        @Qualifier(value="cs") //修改为通过byName装载
        private Classes classes;
    

    表示IoC中id为cs的bean注入到Student中

     //student类
    @Data
    @Component
    public Student(){
        @Value("1")
        private Integer id;
        @Value("张三")
        private String name;
        @Value("23")
        private Integer age;
    }
    

    实体类中普通的成员变量(String,包装类等)可以通过@Value注解进行赋值

    等同于spring.xml中

    <bean id="student" class="com.nuc.gjq.entity.Student" autowire="byType">
    	<property name="id" value="1"></property>
        <property name="name" value="张三"></property>
        <property name="age" value="23"></property>
    </bean>
    

14.实际开发的使用

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

    • Controller
    • Service
    • Mapper/Dao

    controller -> Service -> Dao

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

二、AOP

  • AOP 面向切面编程
  • OOP 面向对象编程,用对象化的思想来完成程序
  • AOP是对OOP的一个补充,是在另一个维度上抽象出对象
  • 具体是指程序运行时动态的将非业务代码切入到业务代码中,从而实现程序的解耦合,将非业务代码抽象成一个对象,对对象编程就是面向切面编程
  • AOP优点
    • 可以降低模块之间的耦合性
    • 提高代码的复用性
    • 提高代码的维护性
    • 集中管理非业务代码,便于维护
    • 业务代码不受非业务代码的影响,逻辑更加清晰

1.AOP如何实现

  • 使用动态代理的方式进行实现

  • 代理首先应该具备该方法的所有功能,并在此基础上,扩展出打印日志的功能

  • 实例应用

    1. 创建一个计算器接口Cal

      package com.nuc.gjq.aop;
      
      /**
       * @auther 中北大学——高靖奇
       * @date 2021/10/13
       */
      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);
      }
      
    2. 创建接口实现类

      package com.nuc.gjq.aop.Impl;
      
      import com.nuc.gjq.aop.Cal;
      
      /**
       * @auther 中北大学——高靖奇
       * @date 2021/10/13
       */
      public class CalImpl implements Cal {
      
          @Override
          public int add(int num1, int num2) {
              int result = num1 + num2;
              return result;
          }
          @Override
          public int sub(int num1, int num2) {
              int result = num1 - num2;
              return result;
          }
          @Override
          public int mul(int num1, int num2) {
              int result = num1 * num2;
              return result;
          }
          @Override
          public int div(int num1, int num2) {
              int result = num1 / num2;
              return result;
          }
      }
      
      

      日志打印:(1)在每个方法开始位置输出参数信息

      ​ (2)在每个方法结束位置输出结果信息

    3. 删除所有非业务代码,只保留业务代码

    4. 创建MyInvocation类,实现Invocation接口,生成动态代理类(底层实现机制)

      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;
          }
      }
      
      import com.nuc.aop.impl.CalImpl;
      public class Test {
          public static void main(String[] args) {
      		//实例化委托对象
              Cal cal = new CalImpl();
      		//获取代理对象
              MyInvocationHandler myInvocationHandler = new MyInvocationHandler();
              Cal proxy = (Cal) myInvocationHandler.bind(cal);
              proxy.add(10,3);
              proxy.sub(10,3);
              proxy.mul(10,3);
              proxy.div(10,3);
          }
      }
      

      动态代理类,需要动态⽣成,需要获取到委托类的接⼝信息,根据这些接⼝信息动态⽣成⼀个代理类,然后再由 ClassLoader 将动态⽣成的代理类加载到 JVM。

      上述代码通过动态代理机制实现了业务代码的解耦合,这是spring AOP 的底层实现机制,真正在使用spring AOP进行开发时,不需要这么复杂,可以用更好的方式来完成开发

2.Spring AOP的开发步骤

  1. 创建切面类

    @Component
    @Aspect
    public class LoggerAspect {
    
        @Before("execution(public int com.nuc.gjq.aop.Impl.CalImpl.*(..))")
        public void befor(JoinPoint joinPoint){
            String name = joinPoint.getSignature().getName();
            String arge = Arrays.toString(joinPoint.getArgs());
            System.out.println(name+"方法的参数是: " + arge);
        }
        
       @After("execution(public int com.nuc.gjq.aop.Impl.CalImpl.*(..))")
        public void after(JoinPoint joinPoint){
            String name = joinPoint.getSignature().getName();
            System.out.println(name+"方法执行完毕! ");
        }
        
        @AfterReturning(value = "execution(public int com.nuc.gjq.aop.Impl.CalImpl.*(..))",returning = "result")
        public void afterReturn(JoinPoint joinPoint,Object result){
            String name = joinPoint.getSignature().getName();
            System.out.println(name+"方法的结果是"+result);
        }
        
        @AfterThrowing(value = "execution(public int com.nuc.gjq.aop.Impl.CalImpl.*(..))",throwing = "ex")
        public void afterThrowing(JoinPoint joinPoint,Exception ex){
            String name = joinPoint.getSignature().getName();
            System.out.println(name+"方法抛出异常:"+ex);
        }
    }
    
  2. 委托类也需要添加@Component

    在实现类中添加@Component注解

  3. spring.xml配置自动扫包

    <!-- 自动扫包 -->
    <context:component-scan base-package="com.nuc.gjq.aop"></context:component-scan>
    <!-- 为委托对象自动生成代理对象 -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    
  4. @Component:将切面类加载到IoC容器中

    @Aspect:表示该类是一个切面类

    @Before:表示方法的执行时机是在业务方法之前,execution表达式表示切入点是CallImpl类中的方法

    @After:表示方法执行在业务方法之后

    @AfterReturning:表示在业务方法返回结果之后执行,returning是将业务方法的形参进行绑定

    @AfterThrowing:表示业务抛异常的时候执行,throwing是将业务方法的异常与切面类方法的形参进行绑定

  5. aop:aspectj-autoproxy:Spring IoC容器会结合切面对象和委托对象自动生成动态代理对象

3.AOP的概念

  • 切面对象:根据切面抽象出来的对象,CallImpl所有方法中需要加入日志的部分,抽象成一个切面类LoggerAspect类
  • 通知:切面对象具体执行的代码,即非业务代码,LoggerAspect对象打印日志的代码
  • 目标:被横切的对象,即CallIml,将通知加入其中
  • 代理:切面对象,通知,目标混合之后的结果,即我们使用JDK动态代理机制创建的对象
  • 连接点:需要被横切的位置,即通知要插入业务代码的具体位置
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值