Spring 学习笔记

时之世 发布于 2024-09-14 549 次阅读 预计阅读时间: 17 分钟 最后更新于 2024-11-21 3691 字 无~


AI 摘要

Spring is a framework that helps with the aspect of Inversion of Control (IoC). In IoC, objects are not created by themselves, but by the Spring container. There are two ways to create objects in IoC: XML configuration file and through Java code. Another important concept in Spring is Dependency Injection (DI), where objects are initialized by the container. DI has two main ways of injection: Constructor injection and Setter injection.(Constructor 注入和 Setter 注入).

入门 Spring

  • Spring 官方网址:spring.io
  • 导入 Jar 包
  • IoC:控制反转 :对象不是由自己创建,而是由 Spring 容器来创建
  • 创建对象方式—XML 方式
    • applicationContext.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">
       <!--配置对象
       - name 别名, 可以用 「,」 和 「 」 隔开
       - lazy-init="true" 懒加载
       - scope="prototype" prototype 多例 (默认是单例) 表示该 Bean 的作用域为多例。每次请求该 Bean 时,都会创建一个新的实例
       -->
       <bean id="user" class="pojo.User" name="dud,sos kok" lazy-init="true" scope="prototype"/>
       <alias name="user" alias="person,people kin" /><!-- 别名 ( 可以配置多个别名 ) -->
    </beans>
  • 两种方式来创建 IoC 对象 public class MyTest {
       @Test
       public void test(){
           // 加载配置文件 创建 IOC 容器 (相对路径)
           ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml", "applicationContext.xml");
           // 获取配置创建的对象
           User user = (User) context.getBean("user");

           System.out.println(user.toString());
      }

       @Test
       public void test2(){
           // 加载配置文件 创建 IOC 容器 (绝对路径)
           ApplicationContext context2 = new FileSystemXmlApplicationContext("D:\\JavaWeb\\2024_Spring\\spring\\hellospring\\src\\main\\resources\\application.xml");
           User user2 = context2.getBean("user",User.class);

           System.out.println(user2.toString());
      }
    }

DI 依赖注入

  • 依赖什么?注入什么?
    • 依赖注入指在对象创建时设置初始值
    • 依赖: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"
          xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
       <bean id="user" class="pojo.User" p:name="shizhishi">
           <!-- 依赖注入
           - constructor-arg 是 通过构造器参数注入依赖 对象创建时就需要确定的
           -->
           <constructor-arg name="name" value="shizhishi" />
           <constructor-arg name="age" value="18"/>
           <constructor-arg name="email" value="null" />
           <constructor-arg name="address" value="杭州" />
           <constructor-arg name="phone"  value="null" />
            <!-- 构造器注入方式 2 -->
           <constructor-arg index="0" value="shizhishi" />
           <constructor-arg index="1" value="18"/>

    <!-- 构造器注入方式 3 -->
           <constructor-arg type="java.lang.String" value="shizhishi" />
           <constructor-arg type="int" value="18"/>
       </bean>
    </beans>
  • 第二种:Setter 方式注入<?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:p="http://www.springframework.org/schema/p"
          xmlns:c="http://www.springframework.org/schema/c"
          xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
       <!-- 依赖注入
           - property 是 通过 setter 方法注入依赖 对象创建后可以通过 setter 方法设置
           -->
       <bean id="address" class="pojo.Address">
           <!-- 常量注入-->
           <property name="city" value="杭州" />
       </bean>

       <bean id="user" class="pojo.User" />

       <bean id="student" class="pojo.Student">
           <!-- 常量注入-->
           <property name="name" value="shizhishi" />
           <!-- 引用其他 bean-->
           <!-- <ref bean="address" /> -->
           <property name="address" ref="address">
           </property>
           <!-- 数组注入-->
           <property name="powers">
               <array>
                   <value> 时间</value>
                   <value> 空间</value>
               </array>
           </property>
           <!-- 集合注入-->
           <property name="list">
               <list>
                   <value> 时间</value>
                   <value> 空间</value>
               </list>
           </property>
           <!-- map 注入-->
           <property name="map">
               <map>
                   <entry key="1" value="时间" />
                   <entry key="2" value="空间" />
               </map>
           </property>

           <property name="properties">
               <props>
                   <prop key="1"> 时间</prop>
                   <prop key="2"> 空间</prop>
               </props>
           </property>
       </bean>
    </beans>
  • 第三种—扩展注入<!-- 配置 bean
        - P: 通过 p 命名空间注入属性
        - 是一种简化了的依赖注入方式,直接在<bean> 标签内使用属性名前缀 p: 来定义依赖。
        - 主要用于 setter 注入,不需要额外的<property> 标签。
        -->
       <bean id="user" class="pojo.User" p:name="shizhishi">
           
    <!--
       - c: c 命名空间与 p: 命名空间类似,但 p: 命名空间更常用,p: 命名空间是 Spring 的默认命名空间,所以可以不用写。
       - c: 的作用是:主要用于通过构造函数进行依赖注入。
       -->
       <bean id="address" class="pojo.Address" c:city="北京">
           
           <!--
    - 可选性: 使用 c 命名空间时,一次注入所有依赖。而 p 命名空间允许选择依赖注入。
      - 初始化顺序: 由于 c 命名空间是通过构造函数注入,所以依赖项会在对象实例化时就确定下来。而 p 命名空间则是在对象实例化后通过 setter 方法注入依赖。
    -->
  • 自动装配 Autowire<?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
          xmlns:context="http://www.springframework.org/schema/context"
          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
          http://www.springframework.org/schema/context
          http://www.springframework.org/schema/context/spring-context.xsd">
        <!--开启注解支持-->
       <context:annotation-config></context:annotation-config>


       <bean id="user" class="pojo.User" autowire="byName">
           <!--
            - 自动装配:
            - autowire="byName" 根据属性名进行注入 Spring 容器会尝试通过依赖的属性名称 (即 Bean 中的 setter 方法对应的属性名) 来匹配容器中已定义的 Bean 。
            - autowire="byType" 根据属性类型进行注入 不适用于存在多个相同类型的属性
            - autowire="constructor" 根据构造函数进行注入
            - 手动装配
            - 加入 property 标签,手动注入
            -->
           <property name="name" value="时之世"/>
           <property name="cat" ref="cat"/>
           <property name="cat1" ref="cat"/>
           <property name="dog" ref="dog"/>
       </bean>
    </beans>
  • 注解/**
        * Autowired: 自动装配,通过类型匹配,如果匹配到多个,则通过属性名匹配
        * required: 是否必须,默认为 true,如果为 false,则表示这个属性不是必须的,如果匹配不到,则不报错,直接为 null
        * Qualifier: 指定 bean 的 id,如果 Autowired 匹配不到,则通过 Qualifier 匹配
        * */
       @Autowired(required = false)
       @Qualifier("cat1")
       private Cat cat1;

       private Cat cat;

       /**
        * Resource: 自动装配 通过名称匹配, 如果匹配到多个,则通过属性名匹配, 是 jdk 自带的注解
        * */

       @Resource(name = "dog")
       private Dog dog;

Java Annotation

自定义注解 (包含三个不同的作用域)

/**
* @author shizhishi
*/ //定义一个注解
@Target(ElementType.TYPE)//类上
@interface Myannotation {
   //注解的属性
   String name();
}


/**
* @author shizhishi
*/
@Target(ElementType.METHOD)//方法上
@interface Myannotation1 {
   //注解的属性
   String name();
}

/**
* @author shizhishi
*/
@Target(ElementType.FIELD)//属性上
@interface Myannotation2 {
   //注解的属性
   String name();
}

@Myannotation(name = "zhangsan")
public class TextAnnotation {

   @Myannotation2(name = "wangwu")
   private String name;

   @Myannotation1(name = "lisi")
   void test() {

  }
}

通过反射获取注解上的值

// 通过反射获取注解信息
   public static  void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
//       Class c = Class.forName("StudentDao");
       //或
       Class c = StudentDao.class;
       Annotation[] annotations = c.getAnnotations();
       for (Annotation annotation : annotations) {
           System.out.println(annotation);
           //result: @Select("111111")
      }
       //获取类上注解的 Value 值
       Select select = (Select) c.getAnnotation(Select.class);
       System.out.println(select.value());
       //result: 111111

       //获取方法上的注解
       Method methods = c.getDeclaredMethod("findAll");
       Select select1 =  methods.getAnnotation(Select.class);
       System.out.println(select1.value());
  }


Spring Annotation

  • 第一步:扫描包    <!--自动装配-->
       <context:annotation-config />
       <!--扫描包-->
       <context:component-scan base-package="com.niit.*" />
  • 基本的注解 @Component 把普通 pojo 实例化到 spring 容器中,相当于配置文件中的<bean id="" class=""/>@Controller 控制层 (web),效果等同于 @Component@Service 业务层,效果等同于 @Component@Respository 持久层,效果等同于 @Component

无配置文件开发:基于 Java 类

  • 主要是对比 applicationContext.xml 配置文件/**
    * @author shizhishi
    * Configuration 注解表示当前类是一个配置类,相当于 spring 的配置文件
    */
    @Configuration
    @ComponentScans({  @ComponentScan(value = {"service","pojo"})})// 扫描包,相当于之前写的<context:component-scan base-package="service,pojo"/>
    public class MyConfig {
       //最基本的配置,注册一个 bean,就相当于我们之前写的一个 bean 标签
    //   @Bean
    //   public Student getStudent(){
    //       return new Student();
    //   }    
  • 不同类型的 bean@Test
       public void test() {
           ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
           // 基本类型的 bean
    //       Student student = context.getBean("getStudent", Student.class);
           // 引用类型的 bean
           Student student = context.getBean("student", Student.class);
           System.out.println(student.name);
      }

AOP 面向切面编程

  • 切面和切点
    • 切面:是通知和切点的集合,定义了
    • 切点:缩小了切面的内容,可以指定在匹配的方法上执行哪些通知
  • aop java 自带接口
    • MethodBeforeAdvice :前置通知
    • AfterReturningAdvice :后置通知
    • DynamicIntroductionAdvice :环绕通知
    • ThrowsAdvice :异常通知
  • 导入 Jar 包<!-- 导入切面编程的依赖包 (植入包)-->
           <dependency>
               <groupId>org.aspectj</groupId>
               <artifactId>aspectjweaver</artifactId>
               <version>1.9.7</version>
           </dependency>
  • 配置切面 (启用 jdk 动态代理)<?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
          xmlns:aop="http://www.springframework.org/schema/aop"
          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
          http://www.springframework.org/schema/aop
          http://www.springframework.org/schema/aop/spring-aop.xsd">
    <bean id="studentService" class="aop.StudentServiceImpl"/>
       <bean id="logBefore" class="aop.LogBefore"/>
       <bean id="logAfter" class="aop.LogAfter"/>
       <!-- 配置切面 -->

       <!-- <aop:aspectj-autoproxy proxy-target-class="true"/>
       - proxy-target-class 默认为 false,表示使用 jdk 动态代理,设置为 true 表示使用 cglib 动态代理。
       - 默认情况下,Spring AOP 是基于动态代理实现的,当目标对象实现了接口,Spring AOP 会使用 JDK 动态代理;当目标对象没有实现接口,Spring AOP 会使用 CGLIB 动态代理。
       -->
       <aop:aspectj-autoproxy>
           <aop:include name="logBefore"/>
           <aop:include name="logAfter" />
       </aop:aspectj-autoproxy>

       
       <!-- 配置切点 -->
       <aop:config>
           <!--
            定义一个 AOP 切点 studentServicePointcut
            作用:此切点用于匹配 aop 包下的 StudentService 类中的所有方法调用。
                 * 表示返回类型任意,aop.StudentService 表示指定类,.*(..) 表示类中的所有方法及任意参数。
           -->
           <aop:pointcut id="studentServicePointcut" expression="execution(* aop.StudentService.*(..))"/>

           <!--
            配置 AOP 切面以应用前置通知到特定的服务方法上。
            参数:
               advice-ref: 指定名称为 logBefore 的前置通知,该通知会在切点定义的方法被调用之前执行。
               pointcut-ref: 指定名称为 studentServicePointcut 的切点,该切点定义了哪些方法会被 advice-ref 指定的通知所拦截。
            作用:
                当匹配 studentServicePointcut 定义的方法被调用时,系统将首先执行 logBefore 通知中的逻辑。
           -->
           <aop:advisor advice-ref="logBefore" pointcut-ref="studentServicePointcut"/>
           <aop:advisor advice-ref="logAfter" pointcut-ref="studentServicePointcut"/>
       </aop:config>
    </beans>
  • 配置切面 (指定代理目标类)@Component
    public class DiyPointCut {
       public void before(){
           System.out.println("before");
      }
       public void after(){
           System.out.println("after");
      }
       public void around(){
           System.out.println("around");
      }
    }<?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
          xmlns:context="http://www.springframework.org/schema/context"
          xmlns:aop="http://www.springframework.org/schema/aop"
          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
          http://www.springframework.org/schema/aop
          http://www.springframework.org/schema/aop/spring-aop.xsd
          http://www.springframework.org/schema/context
          http://www.springframework.org/schema/context/spring-context.xsd">
    <aop:config proxy-target-class="true"><!--指定代理目标类-->
           <aop:aspect ref="diyPointCut">
               <!--切点-->
               <aop:pointcut id="pointCut" expression="execution(* aop2.StudentServiceImpl.*(..))"/>
               <!--通知-->
               <aop:before method="before" pointcut-ref="pointCut"/>
               <aop:after method="after" pointcut-ref="pointCut"/>
           </aop:aspect>
       </aop:config>
    </beans>
  • 使用注解开发/**
    * @author shizhishi
    */
    @Component
    @Aspect
    public class AnnotationPointCut {
       @Before("execution(* aop2.StudentServiceImpl.*(..))")
       public void before(){
           System.out.println("AnnotationPointCut.before");
      }
       @After("execution(* aop2.StudentServiceImpl.*(..))")
       public void after(){
           System.out.println("AnnotationPointCut.after");
      }
       @Around("execution(* aop2.StudentServiceImpl.*(..))")
       public Object around(ProceedingJoinPoint pjp){
           System.out.println("AnnotationPointCut.around");
           return null;
      }
    }