Spring6全篇学习笔记

78 阅读13分钟

spring项目初始化

博主采用父子maven工程,父maven统一管理版本,多个子工程讲解项目。

要点:

  1. spring6要求最低java版本为java17
  2. spring6需要按要求创建xml文件
  3. maven要求最低3.6

实操:

  1. 创建maven项目,jdk版本选择17,删除父工程src
  2. 父工程设置spring6Junit测试api版本控制

引入版本控制,子工程未引入依赖前会爆红

引入spring-context等于引入spring相关的所有基础依赖,可以打开依赖面板查看

<properties>
  <maven.compiler.source>17</maven.compiler.source>
  <maven.compiler.target>17</maven.compiler.target>
  <spring.context.version>6.0.2</spring.context.version>
  <junit.api.version>5.3.1</junit.api.version>
</properties>
<dependencyManagement>
  <dependencies>
      <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-context</artifactId>
          <version>${spring.context.version}</version>
      </dependency>
      <dependency>
          <groupId>org.junit.jupiter</groupId>
          <artifactId>junit-jupiter-api</artifactId>
          <version>${junit.api.version}</version>
      </dependency>
  </dependencies>
</dependencyManagement>
  1. 创建子模块引入依赖
<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
    </dependency>
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-api</artifactId>
    </dependency>
</dependencies>

Spring入门

我们尝试把对象的创建过程交给ioc完成

  1. 写个类,包含一个方法
package com.atguigu;

//一个add类,包含一个addSome方法
public class add {
    void addSome(){
        System.out.println("com.atguigu.add......");
    }
    public static void main(String[] args) {
        add add = new add();
        add.addSome();
    }
}
  1. 在resource下创建一个spring的xml文件,bean.xml
  2. 学习第一个标签 bean标签 2条属性
    1. id 给bean一个唯一标识,让spring认识他
    2. class 路径,指向刚写的类
<?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="classAdd" class="com.atguigu.add"></bean>
</beans>

好的,我们完成了配置工作,我们测试一下。

package com.atguigu;


import org.junit.jupiter.api.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class testAddClass {

    @Test
    public void testAddClass1(){
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        add objAdd = (add)context.getBean("classAdd");
        System.out.println(objAdd);
    }
}
  1. new一个ClassPathXMLApplicationContext对象,填入我们的xml文件名

    1. 为什么是bean.xml而不是bean标签?
    2. 因为一个xml文件可以有多个bean
    3. 为什么填入xml文件名?
    4. 因为我们的xml文件是直接在resource文件夹下的,所以直接填写文件名就行了,spring会默认去找该文件夹

解析:

spring到底做了什么?

  1. 通过指定的xml文件找到bean
  2. 根据bean找到类,并把它加载到IOC容器中
  3. 利用反射创建无参的对象
  4. 返回给我们

我们改下代码(加上无参构造器),看看spring到底有没有用无参构造器

package com.atguigu;

public class add {
    public add(){
        System.out.println("add无参构造器被调用.....");
    }
    void addSome(){
        System.out.println("com.atguigu.add......");
    }
    public static void main(String[] args) {
        add add = new add();
        add.addSome();
    }
}

测试后发现调用了,我们手写一下Spring使用反射的步骤

@Test
public void testAddClass2() throws Exception{
    //抓取类的字节码文件
    Class<?> aClass = Class.forName("com.atguigu.add");
    //使用新方法获得公开的构造器,创建实例
    add addObj = (add)aClass.getDeclaredConstructor().newInstance();
    //调方法
    addObj.addSome();
}

spring把对象存到了哪里?

  1. idea双击shift,搜索DefaultListableBeanFactory(默认可集合化的bean工厂)

  2. 找到

private final Map<String, BeanDefinition> beanDefinitionMap;
  1. Spring把对象放到一个Map里面去了。

解析private final Map<String, BeanDefinition> beanDefinitionMap;

  1. 这个map的key就是我们给bean标签设置的id(唯一标识)
  2. valueBeanDefinition(bean定义)其实是spring对创建的对象进行了各种增强,添加各种强大功能。我们点开源码看一下
package org.springframework.beans.factory.config;

import org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.core.AttributeAccessor;
import org.springframework.core.ResolvableType;
import org.springframework.lang.Nullable;

public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
    String SCOPE_SINGLETON = "singleton";
    String SCOPE_PROTOTYPE = "prototype";
    int ROLE_APPLICATION = 0;
    int ROLE_SUPPORT = 1;
    int ROLE_INFRASTRUCTURE = 2;

    void setParentName(@Nullable String parentName);

    @Nullable
    String getParentName();

    void setBeanClassName(@Nullable String beanClassName);

    @Nullable
    String getBeanClassName();

    void setScope(@Nullable String scope);

    @Nullable
    String getScope();

    void setLazyInit(boolean lazyInit);

    boolean isLazyInit();

    void setDependsOn(@Nullable String... dependsOn);

    @Nullable
    String[] getDependsOn();

    void setAutowireCandidate(boolean autowireCandidate);

    boolean isAutowireCandidate();

    void setPrimary(boolean primary);

    boolean isPrimary();

    void setFactoryBeanName(@Nullable String factoryBeanName);

    @Nullable
    String getFactoryBeanName();

    void setFactoryMethodName(@Nullable String factoryMethodName);

    @Nullable
    String getFactoryMethodName();

    ConstructorArgumentValues getConstructorArgumentValues();

    default boolean hasConstructorArgumentValues() {
        return !this.getConstructorArgumentValues().isEmpty();
    }

    MutablePropertyValues getPropertyValues();

    default boolean hasPropertyValues() {
        return !this.getPropertyValues().isEmpty();
    }

    void setInitMethodName(@Nullable String initMethodName);

    @Nullable
    String getInitMethodName();

    void setDestroyMethodName(@Nullable String destroyMethodName);

    @Nullable
    String getDestroyMethodName();

    void setRole(int role);

    int getRole();

    void setDescription(@Nullable String description);

    @Nullable
    String getDescription();

    ResolvableType getResolvableType();

    boolean isSingleton();

    boolean isPrototype();

    boolean isAbstract();

    @Nullable
    String getResourceDescription();

    @Nullable
    BeanDefinition getOriginatingBeanDefinition();
}

里面非常详细的配置了各种属性和方法,包括作用域,是否是单例对象,是否懒加载......

详细了解IOC原理

所有Java对象的实例化和初始化,控制对象与对象之间的依赖关系。

引入log4j2

需要引入2个东西,

  1. log4j的核心
  2. log4j2 (对核心的实现)
<log4j.core.version>2.19.0</log4j.core.version>
<log4j2.imp.version>2.19.0</log4j2.imp.version>

<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>${log4j.core.version}</version>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-slf4j2-impl</artifactId>
    <version>${log4j2.imp.version}</version>
</dependency>

配置log4j2

  1. resource下创建log4j2.xml文件
  2. 粘贴
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <loggers>
        <!--
            level指定日志级别,从低到高的优先级:
                TRACE < DEBUG < INFO < WARN < ERROR < FATAL
                trace:追踪,是最低的日志级别,相当于追踪程序的执行
                debug:调试,一般在开发中,都将其设置为最低的日志级别
                info:信息,输出重要的信息,使用较多
                warn:警告,输出警告的信息
                error:错误,输出错误信息
                fatal:严重错误
        -->
        <root level="DEBUG">
            <appender-ref ref="spring6log"/>
            <appender-ref ref="RollingFile"/>
            <appender-ref ref="log"/>
        </root>
    </loggers>

    <appenders>
        <!--输出日志信息到控制台-->
        <console name="spring6log" target="SYSTEM_OUT">
            <!--控制日志输出的格式-->
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss SSS} [%t] %-3level %logger{1024} - %msg%n"/>
        </console>

        <!--文件会打印出所有信息,这个log每次运行程序会自动清空,由append属性决定,适合临时测试用-->
        <File name="log" fileName="d:/spring6_log/test.log" append="false">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/>
        </File>

        <!-- 这个会打印出所有的信息,
            每次大小超过size,
            则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,
            作为存档-->
        <RollingFile name="RollingFile" fileName="d:/spring6_log/app.log"
                     filePattern="log/$${date:yyyy-MM}/app-%d{MM-dd-yyyy}-%i.log.gz">
            <PatternLayout pattern="%d{yyyy-MM-dd 'at' HH:mm:ss z} %-5level %class{36} %L %M - %msg%xEx%n"/>
            <SizeBasedTriggeringPolicy size="50MB"/>
            <!-- DefaultRolloverStrategy属性如不设置,
            则默认为最多同一文件夹下7个文件,这里设置了20 -->
            <DefaultRolloverStrategy max="20"/>
        </RollingFile>
    </appenders>
</configuration>

跑起来看下日志

2023-09-22 15:23:58 045 [main] DEBUG org.springframework.context.support.ClassPathXmlApplicationContext - 


//下面这一句,启动类加载器
Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@ebaa6cb


2023-09-22 15:23:58 248 [main] DEBUG 
org.springframework.beans.factory.xml.XmlBeanDefinitionReader -

//加载了一个bean,从xml文件中
Loaded 1 bean definitions from class path resource [bean.xml]


2023-09-22 15:23:58 279 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory 


//从DefaultListableBeanFactory这个map中创建对象
- Creating shared instance of singleton bean 'classAdd'


add无参构造器被调用.....

复习log4j2的使用


private Logger logger = LoggerFactory.getLogger(testAddClass.class);


logger.info("使用日志功能");

讲解 BeanDefinitionReader

什么是BeanDefinitionReader?

上面我们讲了BeanDefinition(加强后的Bean),所谓的reader就是读取者,在这里解释为读取器。

他的作用是把各种格式的bean(xml.....)读取过来,可以理解为一个面向下层的IO接口

它本身是一个接口,有多个实现类

从结构上了解IOC

在Spring当中BeanFactory是非常重要的一个类,他可以说是IOC容器的基本实现,但我们一般更多的使用ApplicationContext而非BeanFactory,因为他太底层,功能也太简陋,Spring通过层层接口继承与实现来增强底层的功能

我们打开BeanFactory的结构图看一下

  1. 该接口在beans依赖下面,org.springframework.beans.factory;

  2. 指到该接口 点击ctrl + h

BeanFactory.png

ioc获取bean的方法

ClassPathXMLApplicationContext有多个参数可选,可以使用XXX.class来获取对象,可以指定接口或者对象,但要注意,多个实现类会造成歧义,导致报错

DI 依赖注入

使用setter方法注入

创建book类,给设置好setter方法

package com.atguigu;

public class book {
    private String bookName;
    private String author;

    public void setBookName(String bookName) {
        this.bookName = bookName;
    }

    public void setAuthor(String author) {
        this.author = author;
    }
}

学习第二个标签 properties标签 用来定义对象内部属性

1. name 指向对应的属性
2. value String类型的可以直接赋值
<bean id="book" class="com.atguigu.book">
    <property name="author" value="jack"/>
    <property name="bookName" value="啦啦啦"/>
</bean>

使用构造器方法注入

创建book类,给设置好有参构造器

package com.atguigu;

public class book {
    private String bookName;
    private String author;


  public book(String bookName, String author) {
      this.bookName = bookName;
      this.author = author;
  }

}

学习第二个标签 constructor-args标签 用来定义对象内部属性

1. name 指向对应的属性
2. value String类型的可以直接赋值
<bean id="book" class="com.atguigu.book">
    <constructor-arg name="author" value="jack"/>
    <constructor-arg name="bookName" value="啦啦啦"/>
</bean>

怎么注入特殊值?

  1. 怎么注入null值?
<constructor-arg name="bookName">
<null/>
</constructor-arg>
  1. 带有特殊符号怎么办
<bean id="book" class="com.atguigu.book">
    <constructor-arg name="author" value="jack"/>
    <constructor-arg name="bookName" value="a&lt;&gt;b"/>
</bean>
  1. 解决所有符号问题 Cdata区,ide快捷键CD
<constructor-arg name="bookName" >
    <value><![CDATA[!@#$%%^&&&&]]></value>
</constructor-arg>
  1. 复杂数据类型的引用

    1. 对象引用

      创建员工类和部门类

        package com.atguigu;
      
        public class dept {
            private String deptName;
      
            public String getDeptName() {
                return deptName;
            }
      
            public void setDeptName(String deptName) {
                this.deptName = deptName;
            }
      
        }
      

      员工类

      package com.atguigu;
      
      public class emp {
      
          private String name;
      
          private Integer age;
      
          private dept dept;
      
          public void setName(String name) {
              this.name = name;
          }
      
          public void setAge(Integer age) {
              this.age = age;
          }
      
          public void setDept(com.atguigu.dept dept) {
              this.dept = dept;
          }
      
          public void info() {
              System.out.println("deptName is " + 
              dept.getDeptName());
          }
      }
      
      1. 外部bean引用

      在xml里面直接创建两个对象的bean,使用setter方法给属性赋值,这里学习第三种标签 ref标签 指向创建的bean即可,相当于在xml内部定义并直接引用了。上代码,不再赘述

      <bean id="dept" class="com.atguigu.dept">
         <property name="deptName" value="保安部"/>
      </bean>
      
      <bean id="emp" class="com.atguigu.emp">
         <property name="name" value="jack"/>
         <property name="age" value="22"/>
         <property name="dept" ref="dept"/>
      </bean>
      
      
      1. 内部bean

      顾名思义就是直接把别的bean对象定义在自己内部。

        <bean id="emp" class="com.atguigu.emp">
            <property name="name" value="jack"/>
            <property name="age" value="22"/>
            <property name="dept">
                <bean id="dept" class="com.atguigu.dept">
                    <property name="deptName" value="生产部"/>
                </bean>
            </property>
        </bean>
      
      1. 级联复制

      我们学习第四个知识点,我们可以在引入外部bean的基础上,引入外bean的属性,在class后面加上 .属性名 这个东西要求两个bean对象都得有getter方法,而且会有一定的歧义,注意,我在下面的代码中定义了dept3,而我在下面调用 .属性名 时使用的是emp内部给的新name

      <bean id="dept3" class="com.atguigu.dept">
          <property name="deptName" value="保安部"/>
      </bean>
      
      <bean id="emp" class="com.atguigu.emp">
          <property name="name" value="jack"/>
          <property name="age" value="22"/>
          <property name="dept" ref="dept3"/>
          <property name="dept.deptName" value="技术研发部"/>
      </bean>
      
    2. 数组怎么写

      1. 给员工类加一个String[] hobby属性;
      2. 上代码 学习第五个新标签 array标签(配合value使用)
     <bean id="emp" class="com.atguigu.emp">
         <property name="hobby">
             <array>
                 <value>吃饭</value>
                 <value>睡觉</value>
                 <value>打豆豆</value>
             </array>
         </property>
         <property name="name" value="jack"/>
         <property name="age" value="22"/>
         <property name="dept" ref="dept3"/>
         <property name="dept.deptName" value="技术研发部"/>
     </bean>
    
    1. List怎么写?

    我们上面的代码里面,一直是给员工增加部门属性,我们现在尝试给部门增加一个 list<emp> 让部门独立出来,包含自己的员工。

    上代码

    1. 学习第六个新标签 list标签(配合ref使用)
    2. ref成为标签,第七个标签
    3. bean成为属性,新属性
    <bean id="tom" class="com.atguigu.emp">
      <property name="name" value="tom"/>
    </bean>
    <bean id="mary" class="com.atguigu.emp">
      <property name="name" value="mary"/>
    </bean>
    <bean id="jack" class="com.atguigu.emp">
      <property name="name" value="jack"/>
    </bean>
    
    <bean id="dept" class="com.atguigu.dept">
      <property name="deptName" value="保安部"/>
      <property name="empList">
          <list>
              <ref bean="tom"></ref>
              <ref bean="jack"></ref>
              <ref bean="mary"></ref>
          </list>
      </property>
    </bean>
    

其实现思路还是通过外部引用来实现的

  1. map 怎么写?

我们给dept 增加一个Map<String,emp>属性,用来演示map。这里的String是empId,当然,我们不需要给emp加id,随便写一个就行。

学习我们的第八个知识点 entry标签(entry的结构是固定的,看代码)

<bean id="dept" class="com.atguigu.dept">
 <property name="deptName" value="保安部"/>
 <property name="empMap">
     <map>
         <entry>
             <key>
                 <value>tom</value>
             </key>
             <ref bean="tom"></ref>
         </entry>
     </map>
 </property>
</bean>

改进map与list

上面的结构我们会发现,如果内容过多,则我们的单个bean中代码会变得非常冗余,而Spring为了解决这个事情,给我们提供了一个全新的标签 util标签 但该标签并非默认存在,需要我们引入新的约束

  1. 我们需要改一下约束就可以使用util提供的标签了,上代码
<?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/util
       http://www.springframework.org/schema/beans/spring-util.xsd
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">
   
</beans>

改进后的list

<bean id="tom" class="com.atguigu.emp">
    <property name="name" value="tom"/>
</bean>
<bean id="mary" class="com.atguigu.emp">
    <property name="name" value="mary"/>
</bean>
<bean id="jack" class="com.atguigu.emp">
    <property name="name" value="jack"/>
</bean>

<util:list id="emps">
    <ref bean="tom"></ref>
    <ref bean="jack"></ref>
    <ref bean="mary"></ref>
</util:list>

<bean id="dept" class="com.atguigu.dept">
    <property name="deptName" value="保安部"/>
    <property name="empList" ref="emps"/>
</bean>

同理,对于map的简化

<util:map id="empEntrys">
    <entry>
        <key>
            <value>tom</value>
        </key>
        <ref bean="tom"></ref>
    </entry>
</util:map>

p命名空间实现注入

同样需要我们修改约束来实现,他允许我们将标签变得更加简洁。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       添加下面这条命名空间
       xmlns:p="http://www.springframework.org/schema/p"
       
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="
       http://www.springframework.org/schema/util
       http://www.springframework.org/schema/util/spring-util.xsd
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="tom" class="com.atguigu.emp">
        <property name="name" value="tom"/>
    </bean>
    <bean id="mary" class="com.atguigu.emp">
        <property name="name" value="mary"/>
    </bean>
    <bean id="jack" class="com.atguigu.emp">
        <property name="name" value="jack"/>
    </bean>

    <util:list id="emps">
        <ref bean="tom"></ref>
        <ref bean="jack"></ref>
        <ref bean="mary"></ref>
    </util:list>


    更简洁的写法
    <bean id="dept2" class="com.atguigu.dept" p:deptName="jack" p:empList-ref="emps"></bean>



</beans>

spring引入外部属性文件

如何使用外部属性文件方式连接数据库

我们的目标是将Druid的配置交给Spring来做,而且是通过外部文件来设置数据库的配置信息。这样的好处是,我们不需要手动去配Druid,而且通过修改外部文件即可修改数据库相关的配置,而不需要对代码进行修改

  1. 先导入mysql跟Druid依赖

<mysql.connector.java.version>8.0.30</mysql.connector.java.version>
<druid.version>1.0.31</druid.version>

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>${mysql.connector.java.version}</version>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>${druid.version}</version>
</dependency>
  1. 写一个外部的Properties
jdbc.user=root
jdbc.password=密码
jdbc.url=jdbc:mysql://ip地址:端口/spring6?serverTimezone=Asia/Shanghai
jdbc.driver=com.mysql.cj.jdbc.Driver
  1. 看一下我们的常规方法(手动配置Druid,其实就是创建import com.alibaba.druid.pool.DruidDataSource;对象,并设置属性值)
package com.atguigu.jdbc;

import com.alibaba.druid.pool.DruidDataSource;
import org.junit.jupiter.api.Test;

public class testDriver {
    @Test
    public void t1(){
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setUrl("jdbc:mysql://不给你看/spring6?serverTimezone=Asia/Shanghai");
        druidDataSource.setUsername("root");
        druidDataSource.setPassword("不给你看");
        druidDataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
    }
}
  1. 配置命名空间引入外部Properties文件
<?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/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">

    <context:property-placeholder location="classpath:jdbc.properties"/>

</beans>

在上面的代码中我们引入了context命名空间,并引入了指定路径下的配置文件。此时,配置文件中的数据我们已经可以随便调用了。

  1. 我们直接通过xml给指定的对象赋值。
<?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/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">

    <context:property-placeholder location="classpath:jdbc.properties"/>

    <bean id="druid" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.user}"/>
        <property name="password" value="${jdbc.password}"/>
        <property name="driverClassName" value="${jdbc.driver}"/>
    </bean>

</beans>
  1. 后面不再演示。

Bean的作用域

其实就是bean对象是否为单例,当指定scope为单例时会警告,因为默认为单例。

学习新属性 Scope= singleton OR prototype

他们的区别在于,单例在容器初始化时就被创建,而多例则是在获取时才创建

<?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="scope" class="com.atguigu.scope.Oder" scope="singleton">
        <property name="orderId" value="10010"/>
    </bean>
</beans>
package com.atguigu;

import com.atguigu.scope.Oder;
import org.junit.jupiter.api.Test;
import org.springframework.context.support.BeanDefinitionDsl;
import org.springframework.context.support.ClassPathXmlApplicationContext;
@SuppressWarnings({"all"})
public class testScope {
    @Test
    public void t1(){
        ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("scope.xml");
        Oder oder = (Oder)classPathXmlApplicationContext.getBean("scope");
        System.out.println(oder);
        Oder oder1 = (Oder)classPathXmlApplicationContext.getBean("scope");
        System.out.println(oder1);
    }
}
D:\jdk-17.0.8\bin\java.exe -ea -Didea.test.cyclic.buffer.size=1048576 "-javaagent:D:\IDEA2023\IntelliJ IDEA 2023.2.2\lib\idea_rt.jar=61702:D:\IDEA2023\IntelliJ IDEA 2023.2.2\bin" -Dfile.encoding=UTF-8 -classpath "C:\Users\18329\.m2\repository\org\junit\platform\junit-platform-launcher\1.3.1\junit-platform-launcher-1.3.1.jar;C:\Users\18329\.m2\repository\org\apiguardian\apiguardian-api\1.0.0\apiguardian-api-1.0.0.jar;C:\Users\18329\.m2\repository\org\junit\platform\junit-platform-engine\1.3.1\junit-platform-engine-1.3.1.jar;C:\Users\18329\.m2\repository\org\junit\platform\junit-platform-commons\1.3.1\junit-platform-commons-1.3.1.jar;C:\Users\18329\.m2\repository\org\opentest4j\opentest4j\1.1.1\opentest4j-1.1.1.jar;C:\Users\18329\.m2\repository\org\junit\jupiter\junit-jupiter-engine\5.3.1\junit-jupiter-engine-5.3.1.jar;C:\Users\18329\.m2\repository\org\junit\jupiter\junit-jupiter-api\5.3.1\junit-jupiter-api-5.3.1.jar;D:\IDEA2023\IntelliJ IDEA 2023.2.2\lib\idea_rt.jar;D:\IDEA2023\IntelliJ IDEA 2023.2.2\plugins\junit\lib\junit5-rt.jar;D:\IDEA2023\IntelliJ IDEA 2023.2.2\plugins\junit\lib\junit-rt.jar;D:\Project\Praent\pro-01\target\test-classes;D:\Project\Praent\pro-01\target\classes;D:\libs\apache-maven-3.6.3\maven-repo\org\springframework\spring-context\6.0.2\spring-context-6.0.2.jar;D:\libs\apache-maven-3.6.3\maven-repo\org\springframework\spring-aop\6.0.2\spring-aop-6.0.2.jar;D:\libs\apache-maven-3.6.3\maven-repo\org\springframework\spring-beans\6.0.2\spring-beans-6.0.2.jar;D:\libs\apache-maven-3.6.3\maven-repo\org\springframework\spring-core\6.0.2\spring-core-6.0.2.jar;D:\libs\apache-maven-3.6.3\maven-repo\org\springframework\spring-jcl\6.0.2\spring-jcl-6.0.2.jar;D:\libs\apache-maven-3.6.3\maven-repo\org\springframework\spring-expression\6.0.2\spring-expression-6.0.2.jar;D:\libs\apache-maven-3.6.3\maven-repo\org\junit\jupiter\junit-jupiter-api\5.3.1\junit-jupiter-api-5.3.1.jar;D:\libs\apache-maven-3.6.3\maven-repo\org\apiguardian\apiguardian-api\1.0.0\apiguardian-api-1.0.0.jar;D:\libs\apache-maven-3.6.3\maven-repo\org\opentest4j\opentest4j\1.1.1\opentest4j-1.1.1.jar;D:\libs\apache-maven-3.6.3\maven-repo\org\junit\platform\junit-platform-commons\1.3.1\junit-platform-commons-1.3.1.jar;D:\libs\apache-maven-3.6.3\maven-repo\org\apache\logging\log4j\log4j-core\2.19.0\log4j-core-2.19.0.jar;D:\libs\apache-maven-3.6.3\maven-repo\org\apache\logging\log4j\log4j-api\2.19.0\log4j-api-2.19.0.jar;D:\libs\apache-maven-3.6.3\maven-repo\org\apache\logging\log4j\log4j-slf4j2-impl\2.19.0\log4j-slf4j2-impl-2.19.0.jar;D:\libs\apache-maven-3.6.3\maven-repo\org\slf4j\slf4j-api\2.0.0\slf4j-api-2.0.0.jar;D:\libs\apache-maven-3.6.3\maven-repo\mysql\mysql-connector-java\8.0.30\mysql-connector-java-8.0.30.jar;D:\libs\apache-maven-3.6.3\maven-repo\com\google\protobuf\protobuf-java\3.19.4\protobuf-java-3.19.4.jar;D:\libs\apache-maven-3.6.3\maven-repo\com\alibaba\druid\1.0.31\druid-1.0.31.jar" com.intellij.rt.junit.JUnitStarter -ideVersion5 -junit5 com.atguigu.testScope,t1
2023-09-24 13:27:48 668 [main] DEBUG org.springframework.context.support.ClassPathXmlApplicationContext - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@49c6c24f
2023-09-24 13:27:48 837 [main] DEBUG org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loaded 1 bean definitions from class path resource [scope.xml]
2023-09-24 13:27:48 858 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'scope'
com.atguigu.scope.Oder@7de0c6ae
com.atguigu.scope.Oder@7de0c6ae

Bean 的生命周期

Spring中Bean的生命周期主要分为8个阶段

  1. 调用无参构造器创建空对象
  2. 使用setter方法给对象赋值
  3. 调用后置处理器方法(初始化之前的)
  4. 初始化Bean对象
  5. 调用后置处理器方法(初始化之后的)
  6. bean对象交给开发者使用
  7. 调用销毁方法
  8. IOC容器关闭

下面我们通过代码来演示主要过程。

xml中的bean标签允许我们通过指定的标签来指定与其对应的方法

  1. init-method标签
  2. destroy-method标签
<?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="com.atguigu.life.User" init-method="initMethod" destroy-method="destroyMethod">
        <property name="name" value="jack"/>
    </bean>
</beans>

我们将几个方法设置好,并将各个步骤打印出来

package com.atguigu.life;

public class User {
    private String name;

    public User(){
        System.out.println("1.无参构造器被调用");
    }

    public void initMethod(){
        System.out.println("4.调用初始化方法");
    }

    public void destroyMethod(){
        System.out.println("7.调用销毁方法");
    }
    public String getName() {
        return name;
    }

    public void setName(String name) {
        System.out.println("2 给属性设置值");
        this.name = name;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + ''' +
                '}';
    }
}

下面是打印结果:

1.无参构造器被调用
2 给属性设置值
4.调用初始化方法
2023-09-24 14:04:00 616 [main] DEBUG org.springframework.context.support.ClassPathXmlApplicationContext - Closing org.springframework.context.support.ClassPathXmlApplicationContext@6fe1b4fb, started on Sun Sep 24 14:04:00 CST 2023
7.调用销毁方法

在上面的演示中,我们并没有展示2个后置处理器的执行。这是因为他们的配置比较特殊,我们需要指定一个类,来继承BeanPOSTProcessor接口,并重写该接口提供的2个方法(后置处理器Before方法和After方法),并将该类以Bean标签的形式生命在Xml文件内。

该方法其实并没有破坏最小入侵性原则,也没有改动原有代码,而是通过增加新的类来完成方法注入,

但该操作有一个问题,就是他会给容器内的所有对象都加上处理器!!!!!

package com.atguigu.life;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class MyPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("初始化之前执行");
        return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("初始化之后执行");
        return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
    }
}

xml声明该类

<bean id="myPostProcessor" class="com.atguigu.life.MyPostProcessor"/>

执行结果:

1.无参构造器被调用
2 给属性设置值
初始化之前执行
4.调用初始化方法
初始化之后执行
2023-09-24 14:29:32 261 [main] DEBUG org.springframework.context.support.ClassPathXmlApplicationContext - Closing org.springframework.context.support.ClassPathXmlApplicationContext@6fe1b4fb, started on Sun Sep 24 14:29:32 CST 2023
7.调用销毁方法

FactoryBean

  1. 什么是FactoryBean?

    相当于Spring提供的一个外部接口,该接口提供1个方法,会返回一个由使用者自己指定的对象

  2. 他的实用价值? 他可以帮助第三方来将复杂的代码进行封装,只对外提供一个简洁的对象获取方法,因为基于Spring平台,AOP相关的强大功能都可以使用

代码演示

package com.atguigu.factoryBean;

import com.atguigu.life.User;
import org.springframework.beans.factory.FactoryBean;

public class MyFactoryBean implements FactoryBean<User> {
    @Override
    public User getObject() throws Exception {
        return new User();
    }

    @Override
    public Class<?> getObjectType() {
        return null;
    }
}
<bean id="myFactoryBean" class="com.atguigu.factoryBean.MyFactoryBean"/>

类型断言

@Test
public void t3(){
    ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("life.xml");
    Object user =  classPathXmlApplicationContext.getBean("myFactoryBean");
    Assert.isInstanceOf(User.class,user);
}

基于XML的自动装配

我们先看一组代码

package com.atguigu.autoWire.Service;
import com.atguigu.autoWire.DAO.userDao;

public class addServiceImpl implements addService{

    private userDao user;


    public void setUser(userDao user) {
        this.user = user;
    }

    @Override
    public void add() {
        System.out.println("addService 被调用~");
        user.add();
    }
}

我们会发现,在Controller中,我们定义了自己的一个复杂数据类型的属性:addService,然后我们在方法中调用了该对象内部的方法add()

我们先思考一下如何给复杂类型addService赋值,这似乎并不难做到。

创建一个addService的bean,把它装配到addController的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="addService" class="com.atguigu.autoWire.Service.addServiceImpl"/>
    
    <bean id="addController" class="com.atguigu.autoWire.Controller.addController">
        <property name="addService" ref="addService"/>
    </bean>
</beans>

那么,我们想要在AddService中调用基础的DAO怎么办呢?是的,如法炮制即可,配置私有的复杂数据类型,设置他的Setter方法,Bean装配,ref引入。就像下面这样,我们就完成了对MVC的装配!他们之间的关系一目了然,只要有了对象,我们就可以一层一层的去调用他们的方法!

<?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="com.atguigu.autoWire.DAO.userDaoImpl"/>
    
    <bean id="addService" class="com.atguigu.autoWire.Service.addServiceImpl">
        <property name="user" ref="user"/>
    </bean>

    <bean id="addController" class="com.atguigu.autoWire.Controller.addController">
        <property name="addService" ref="addService"/>
    </bean>
</beans>

先别激动,Spring为我们提供了更加方便的装配方法,被称为自动装配

我们学习新的属性: autowire= byType(默认) or byName or constructor


新代码:

<?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="com.atguigu.autoWire.DAO.userDaoImpl"/>

    <bean id="addService" class="com.atguigu.autoWire.Service.addServiceImpl" autowire="byType"/>

    <bean id="addController" class="com.atguigu.autoWire.Controller.addController" autowire="byType">
        <property name="addService" ref="addService"/>
    </bean>
</beans>

注解方式Bean管理

开启注解扫描

Spring当中是默认不开启注解的,而如果想使用注解。则需要开启注解扫描,并指定对应的区域,让Spring进行管理。

  1. 需要使用我们的Context命名空间,通过component-scan标签,指定对应的包
<?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/context  
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/beans 
       http://www.springframework.org/schema/beans/spring-beans.xsd">

    <context:component-scan base-package="com.atguigu"/>
</beans>

有针对性的注解扫描(过滤)

Spring我么我们提供了2个新标签(在component-scan标签内使用):

  1. context:exclude-filter 指定需要排除在外的注解
  2. context:include-filter 除指定注解外全部排除

在这两个标签的基础上又提供了2个属性

  1. type 选项可以使annotation(指定注解) 或者 assignable(指定对应的包)
  2. expression 属性根据type指定注解类或者包位置。
<?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/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">


    <context:component-scan base-package="com.atguigu">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
        <context:exclude-filter type="assignable" expression="com.atguigu.User"/>
    </context:component-scan>
</beans>

Spring提供的注解

Spring对于Bean,为我们提供了4个注解

  1. @Repository(DAO层)
  2. @Controller(Controller层)
  3. @Service(Service层)
  4. @Component(父类)
package com.atguigu;

import org.springframework.stereotype.Repository;

@Repository
public class User {
    private String name;

    public String getName() {
        return name;
    }
    public void sayHi(){
        System.out.println("hello word!");
    }

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

测试

ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
User user = context.getBean(User.class);
user.sayHi();

@AutoWired 注解

@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
    boolean required() default true;
}

@Target注解

注明了该注解可以用在哪些位置

  1. CONSTRUCTOR 构造器上
  2. METHOD 方法上
  3. PARAMETER 参数上
  4. FIELD 字段上
  5. ANNOTATION_TYPE 注解上

boolean required() default true;

表示该注解必须写入的内容必须存在,否则报错。

@Autowired实现MVC

代码过多,只展示Service,用 @Component代替Bean声明,@AutoWired实现自动注入

package com.atguigu.autoWire.Service;

import com.atguigu.autoWire.DAO.orderImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class orderServiceImpl implements orderService {

    @Autowired
    private orderImpl order;
    @Override
    public void orderServiceRun() {
        System.out.println("orderService is run");
        order.printOrderId();
    }
}

@AutoWired的一些古怪写法

@AutoWired可以写在本类的有参构造器上, 也可以写在本类有参构造器的参数上,或者写在属性的setter方法上

最后一种奇怪的注入方法,不需要写@AutoWrited,只需要类内部只有一个有参构造器(如果同时有无参构造器,则会报错!),以后看见别懵就行

package com.atguigu.autoWire.Controller;

import com.atguigu.autoWire.Service.orderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

@Controller
public class orderController {


    private orderService orderService;


    public orderController(com.atguigu.autoWire.Service.orderService orderService) {
        this.orderService = orderService;
    }

    public void orderControllerRun(){
        System.out.println("orderController is run");
        orderService.orderServiceRun();
    }
}

@Qualifiar注解

我们的@AutoWired注解有一个缺陷,就是它只会按照类型进行注入,问题就在于,当我们有多个对象满足条件时,他就会乱套。

@Qualifiar注解解决了这个问题,他跟@AutoWired注解一起使用。他需要填入value值,值为对应对象的类名。

假设DAO层多了一个来自Redis的实现类

package com.atguigu.autoWire.DAO;

import org.springframework.stereotype.Repository;


@Repository
public class orderRedisDao implements orderDao{
    @Override
    public void printOrderId() {
        System.out.println("redisDao~~~");
    }
}

注入写法为:

@Autowired
@Qualifier(value = "orderRedisDao")
private orderDao order;

@Resource注解

  1. @Resource是java自己的注解
  2. 在java拓展包中,java8自带该注解
  3. 如果版本高于Java11或低于Java8,则需要导入如下的依赖才能使用
<dependency>
    <groupId>jakarta.annotation</groupId>
    <artifactId>jakarta.annotation-api</artifactId>
    <version>2.1.1</version>
</dependency>
  1. 该注解可以写在属性上或者setter方法上。

  2. 情况1:如果我们指定了对象名,则他会根据对象名进行查找。

@Resource(name = "myOrderService")
private com.atguigu.resource.Service.orderService orderService;


@Service("myOrderService")
public class orderServiceImpl implements orderService {
        .................

  1. 如果不指定对象名,他则会根据属性的名字。
@Resource
private orderDao order;


@Repository("order")
public class orderImpl implements orderDao {

  1. 如果两者都不存在,他就会按照类型去找。

如何避类名重复

当我们有多个同名的类同时出现在被扫描的范围内时,就会发生对象冲突,解决的办法就是给Bean对象指定唯一索引

Spring全注解开发

我们上面介绍的开发模式还有一点点小缺陷,我们还是需要一个xml文件,如何省略他呢?

我们需要创建一个类,使用@Configuration来表示我们是一个配置类,然后使用@ComponentScan注解指定对应的包。

@Configuration
@ComponentScan("com.atguigu")
public class Config {
}

新的上下文获取方式

ApplicationContext context = new AnnotationConfigApplicationContext(Config.class);

复习Java反射

一页代码复习Java反射

package com.atguigu.clazz;

import org.junit.jupiter.api.Test;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

@SuppressWarnings({"all"})
public class reviewClazz {
    @Test
    public void t1() throws Exception {
        /**
         * 获取字节码文件的3种方法
         */

        //类.class
        Class carClass = Car.class;

        //对象.getClass
        Class aClass = new Car().getClass();

        //Class.forName("全类名")
        Class aClass1 = Class.forName("com.atguigu.clazz.Car");

        /**
         * 实例化方法
         */
        Car car = (Car)carClass.getDeclaredConstructor().newInstance();

        /**
         * 抓取构造方法
         *
         */
        Constructor[] constructors = carClass.getConstructors();
        for (Constructor constructor : constructors) {
            System.out.println("类名"+constructor.getName()+"属性个数"+constructor.getParameterCount());
        }

        //获取所有构造方法(包括私有)
        for (Constructor c : carClass.getDeclaredConstructors()) {
            System.out.println("类名"+c.getName()+"属性个数"+c.getParameterCount());
        }
    }
    @Test
    public void t2() throws Exception {

        //类.class
        Class carClass = Car.class;
        /**
         * 获取指定的有参构造器 输入参数类型的class
         */
        Constructor constructor = carClass.getConstructor(String.class, int.class, String.class);
        Car car1 = (Car) constructor.newInstance("宾利", 100, "red");
        System.out.println(car1);
        /**
         * 如果是私有的就用 Declared获取
         * 需要爆破!!!
         */
        Constructor constructor2 = carClass.getDeclaredConstructor(String.class, int.class, String.class);
        //需要爆破!!!
        constructor2.setAccessible(true);
        Car car2 = (Car) constructor2.newInstance("马自达", 100, "red");
        System.out.println(car2);
    }
    @Test
    public void t3() throws Exception{
        /**
         * 属性操作,获取属性名,设置值
         * 因为属性大多是私有,所以我们直接选择Declared
         */
        Class carClass = Car.class;
        Car car1 = (Car)carClass.getDeclaredConstructor().newInstance();
        Field[] declaredFields = carClass.getDeclaredFields();
        for (Field field : declaredFields) {
            if (field.getName().equals("name")){
                field.setAccessible(true);
                field.set(car1,"保时捷");
            }
        }
        System.out.println(car1);

        /**
         * 对方法的操作
         */
        Method[] declaredMethods = carClass.getDeclaredMethods();
        for (Method method : declaredMethods) {
            if (method.getName().equals("run")){
                method.setAccessible(true);
                method.invoke(car1);
            }
        }
    }

}

手写IOC并完成依赖注入

直接贴代码了,作者自己写的,跟老师区别很大。建议看老师的,思路相通

public class myApplicationContext {


    private int subStarPoint;
    private ArrayList<String> classPaths = new ArrayList<>();

    //根据指定路径获取所有全类名;
    private void setClassPaths(File file) {
        if (file.isDirectory()) {
            for (File listFile : Objects.requireNonNull(file.listFiles())) {
                setClassPaths(listFile);
            }
            return;
        }
        if (file.getName().contains(".class")) {
            String PerfectPath = file.getAbsolutePath()
                    .substring(subStarPoint, file.getAbsolutePath().length() - 6)
                    .replaceAll("\\", "\.");
            classPaths.add(PerfectPath);
        }
    }

    //根据basePackage路径获取所有全类名;
    private void setClassPathsInBasePackage(String basePackage) {
        String newPath = basePackage.replaceAll("\.", "\\");
        try {
            Enumeration<URL> resources = Thread.currentThread().getContextClassLoader().getResources(newPath);
            while (resources.hasMoreElements()) {
                URL url = resources.nextElement();
                //指定包的绝对路径
                String AbsolutePath = URLDecoder.decode(url.getFile(), "utf-8");
                //从绝对路径开始到basePackage前一个字母的长度。
                subStarPoint = AbsolutePath.length() - basePackage.length() - 1;
                setClassPaths(new File(AbsolutePath));
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public myApplicationContext(String basePackage) {
        setClassPathsInBasePackage(basePackage);
        classPaths.forEach(classPath -> {
            try {
                Class aClass = Class.forName(classPath);
                if (aClass.isAnnotationPresent(Bean.class)) {
                    Class key = aClass.getInterfaces().length != 0 ? key = aClass.getInterfaces()[0] : aClass;
                    Beans.put(key, aClass.getDeclaredConstructor().newInstance());
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        });
        classPaths.forEach(classPath -> {
            try {
                Class aClass = Class.forName(classPath);
                for (Field declaredField : aClass.getDeclaredFields()) {
                    declaredField.setAccessible(true);
                    for (Annotation annotation : declaredField.getAnnotations()) {
                        if (annotation.annotationType().equals(Di.class)) {
                            Class objKey =  aClass;
                            if (objKey.getInterfaces().length>0)objKey=objKey.getInterfaces()[0];
                            Object bean = getBean(declaredField.getType());
                            Object bean1 = getBean(objKey);
                            declaredField.set(bean1,bean);
                        }
                    }
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        });

    }

    private Map Beans = new HashMap<Class, Object>();

    public Object getBean(Class cs) {
        return Beans.get(cs);
    }
}

这笔记写的太长卡的不行,决定分篇了,下篇正在写。