day25-day26

一、Spring引入

  1. 首先,在之前设计分层架构时,添加一个功能需要很多步骤。把时间精力放在业务层而不是放在持久层和表现层。<br /> 结合设计模式,需要考虑到设计的流程和效率。

二、Spring

2.1 概念:

  1. spring是分层的JavaSE/EE应用的full-stack轻量级开源框架。
  2. 内核:IOC (反转控制)、AOP(面向切面编程);

    控制反转:解耦的一种解决方案的统称。

  3. 提供:展现层(Spring MVC)、持久层(Spring JDBC)、业务层管理等众多企业级应用。

  4. 使用最多的Java EE企业应用开源框架。

    控制反转:
  5. 创建对象的权利发生了变化,创建对象的权力放在客户端、降低耦合

  6. 控制正转:由开发者创建对象。
  7. 解耦方式:
  • 依赖注入:通过客户端进行构造方法,创建对象的方式
  • 通过工厂模式:

    1. 通过一种标识,创建对应的类的实例:;例如可以使用全类名作为创建实例的的表示,通过反射机制创建实例。

延申————>Spring有自己对应的解耦方式

2.2 Spring的入门

2.2.1 发展历程:
  1. IBM :EJB思想
  2. EJB的发展
  3. 2017 最新Spring5.0

2.2.2 优势
  1. 方便解耦,简化开发。
  2. AOP编程支持: 使用面向切面编程,可以见简便地完成oop实现的工程。
  3. 声明式事物的支持: 提高开发效率
  4. 方便的测试:单元测试
  5. 方便集成各种优秀的框架:降低各种框架的使用难度。
  6. 降低了JavaEE API的使用难度 进行薄薄封装层。
  7. Java源码是经典学习范例。

2.2.3 Spring的体系结构

image.png
无论哪个模块都需要核心模块的支持,需要从核心模块获取实例。

2.2.3 Spring的体系结构
  1. Spring 的相关jar包支持存放路径

    D:\project\Spring5.02\spring-framework-5.0.2.release\spring-framework-5.0.2.RELEASE

  2. Spring 通常使用xml来作为配置文件

  3. 使用demo4j来解析

通过xml方式进行配置

  1. 配置环境

需要的支持包:
image.png

  • 点击打开:D:\project\Spring5.02\spring-framework-5.0.2.release\spring-framework-5.0.2.RELEASE\docs\spring-framework-reference\core.html
  • 创建xml文件并黏贴入约束
  • 获取约束方式:进入官网:ctrl+f 输入xmlns 查找约束

image.png

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://www.springframework.org/schema/beans
  5. http://www.springframework.org/schema/beans/spring-beans.xsd">
  6. </beans>

image.png

2.2.4 实例测试
  1. 首先,建立一个需要注册入spring核心容器的类
  2. 其次,注册

image.png
image.png

  1. 从核心容器中取出改类的实例:
  • 使用到的方法:注意添加路径
  • ClassPathXmlApplicationContext(“/xml/beans.xml”)
  • 取出对象使用到的方法
  • getBean(“students”)注意对象对应的参数是id,是核心容器中注册时对应的id。 ```java //创建核心容器对象 ClassPathXmlApplicationContext a = new ClassPathXmlApplicationContext(“/xml/beans.xml”);

//从核心容器中取出实例 Object students = a.getBean(“students”);

  1. 输出效果<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/26800293/1653122442309-227929c4-103a-40b0-a032-aa8c847f570f.png#clientId=u9b4783ee-f1ba-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=669&id=u09ea36e7&margin=%5Bobject%20Object%5D&name=image.png&originHeight=736&originWidth=1366&originalType=binary&ratio=1&rotation=0&showTitle=false&size=111943&status=done&style=none&taskId=ue246a249-795a-4513-9102-d6a356cdd35&title=&width=1241.8181549024982)
  2. <a name="Q3bS2"></a>
  3. ### 三、Spring依赖注入
  4. 定义:组件之间的依赖关系有容器运行决定,提升组件的重用频率,搭建可拓展平台。不需要关心很多内容。
  5. <a name="UlUM1"></a>
  6. ##### 3.1 通过set方法进行依赖注入
  7. 1. xml文件内进行,通过绑定类中的set方法,对数据进行赋值
  8. 简单数据:
  9. ```java
  10. <bean id = "students" class="com.czf.pojo.students">
  11. <property name="id" value = "123"></property>
  12. <property name="name" value="jack"></property>
  13. </bean>

对于简单数据而言,直接在bean下使用property 使用id绑定该类下的某个属性,该属性必须有set方法。

  1. 对于复杂数据而言,如日期类型的属性:这样的情况我们需要引入容器中已经存在的实例,在bean外部先申明,并引入该类型对应的全类名 ```java

  1. 3. 对于数组类型数据的set注入
  2. ```java
  3. <property name="strings">
  4. <array>
  5. <value>jack</value>
  6. <value>tom</value>
  7. </array>
  8. </property>
  1. 给map注入数据(键值对的形式)
    1. <property name="map">
    2. <map>
    3. <entry value="value1" key="key1"/>
    4. <entry value="value2" key="key2"/>
    5. </map>
    6. </property>

3.2 通过构造方法进行依赖注入
  1. <bean id="stuents" class="com.czf.pojo.students">
  2. <constructor-arg name="name" value="zc"></constructor-arg>
  3. <constructor-arg name="id" value="1222"></constructor-arg>
  4. </bean>
  • 需要注意平时在xml里创建实例需要类中必须要有无参构造
  • 但是通过”构造方法进行注入“不需要无参构造(有也可以),需要将每个参数属性进行赋值后就不会报错。

3.3 BeanFactory和ApplicationContext的区别
  • BeanFactory才是Spring容器中的顶层接口
  • ApplicationContext是它的子接口。

主要区别:

  • 创建对象的时间点不样。
  • ApplicationContext:只要读取配置文件默认情况下就会创建对象。
  • BeanFactory:什么使用什么时候创建对象

    四、相关内容

    4.1ApplicationContext接口的实现类
  1. ClassPathXmlApplicationContext:

它是从类的根路径下加载配置文件推荐使用这种

  1. FileSystemXmlApplicationContext:

它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置。

  1. AnnotationConfigApplicationContext:

当我们使用注解配置容器对象时,需要使用此类来创建spring容器。它用来读取注解。

4.2 bean标签
  1. 作用:用于配置对象让spring来创建,默认情况下他调用的是类中的无参构造函数,没有无参构造函数则会报错。
  2. 属性
  • id 唯一标识用于获取对象
  • class 指定权限定类名,用于反射创建对象,默认使用无参构造函数
  • scope:指定对象作用范围。

4.3 一些方法
  1. init_method 实例初始化时要用到的方法。
  2. destroy-method 实例销毁时调用的方法。
  3. scope 设置对象的作用范围 (单例或者多例)默认情况下创建的实例是单例的。单例(指向同一份内存地址)singleton 单例 prototype多例
    1. <bean id="students" class="com.czf.pojo.students" init-method="init" destroy-method="destroy" >
    1. public void init(){
    2. }
    3. public void destroy(){
    4. }

五、基于注解的IOC配置

5.1 基于注解 配置文件

  1. @Component: 把资源让spring来管理。相当于在xml中配置一个bean.
  2. @Controller: 般用于表现层的注解。
  3. @Service: 一般用于业务层的注解。
  4. @Repository: 般用于持久层的注解。

spring基于注解的方式进行解耦的过程

  1. 开启spring对注解的支持(开启一个扫描包)查看有没有新的注解

    spring程序在运行时会扫描指定包下的所有资源容器,然后检测对于资源 上有没有spring提供的注解。

  2. 如果有,通过反射得分方式添加到容器中,将类中的实例进行注入。

  3. servlet ——>service——>dao

(1) 添加约束:和之前一样到网站中搜索xmlns:co 复制如下约束

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans
  6. http://www.springframework.org/schema/beans/spring-beans.xsd
  7. http://www.springframework.org/schema/context
  8. http://www.springframework.org/schema/context/spring-context.xsd">
  9. <context:component-scan base-package="com.czf"></context:component-scan>
  10. </beans>

以注解来配置时,唯一标识为类名首字母小写。
(2)添加注解
image.png
(3)测试
image.png

5.2 用于注入数据的注解

  1. @Autowired(根据类型注入)
  2. @Qualifier (不能单独使用 ,与@Autowired 结合使用)
  3. @Resource(根据id注入的)
  4. @Value (用于注入基本数据类型和String类型)

5.2 新注解

新注解说明

  1. @Configuration 注解:声明当前类为核心配置类。
  2. @ComponentScan 开启一个扫描包

    @ComponentScan(“com.eagleslab”)

  3. @Bean 类似于xml中的bean标签,将当前方法的返回值最为一份实例存储 进核心容器中。

    @Bean(“user”)

  4. @PropertySource 用于导入别的核心配置类

  5. @Import 引入 别的核心配置类

    @Import({StuConfig.class,JdbcConfig.class})添加.class注解

  1. @Configuration
  2. @ComponentScan("com.eagleslab")
  3. @Import({StuConfig.class,JdbcConfig.class})
  4. public class SpringConfig {
  5. @Bean("user")
  6. public User getUser(){
  7. return new User(1001,"jack");
  8. }
  9. }

测试:@PropertySource 用于读取properties配置文件的

例如先将数据存入jdbc.properties内 db.username=‘1213’db.password=‘1213132’

  1. @Configuration
  2. //classpath声明当前的配置类在类路径下面
  3. @PropertySource("classpath:jdbc.properties")
  4. public class JdbcConfig {
  5. //使用value对数据进行注入
  6. @Value("${db.username}")
  7. private String username;
  8. @Value("${db.password}")
  9. private String password;
  10. //将返回的对象传入
  11. @Bean("jdbc")
  12. public Jdbc getJdbc(){
  13. return new Jdbc(username,password);
  14. }
  15. }

六、案例

需求:根据设计模式(工厂模式)+反射机制完成模拟springIOC基于注解的解耦过程。

设计思路:
首先,所有注解都得自己完成:

  1. 自己写一个注解:
    注解格式:接口文件、 ```java @Target(ElementType.FIELD) //用于修饰:作用于属性 @Retention(RetentionPolicy.RUNTIME)//保持性策略:用于给反射机制获取 public @interface AutoWired {

}

  1. ```java
  2. @Target(ElementType.TYPE)
  3. @Retention(RetentionPolicy.RUNTIME)
  4. public @interface Controller {
  5. }
  1. @Target(ElementType.TYPE)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. public @interface Service {
  4. }
  1. @Target(ElementType.TYPE)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. public @interface Repository {
  4. }
  1. 创建工厂类:用于存放组件的实例

    1. public class BeanFactory {
    2. //创建一个集合对象
    3. private static Map beansMap = new HashMap();
    4. //通过map存放对象
    5. public static Map getBeansMap() {
    6. return beansMap;
    7. }
    8. /**
    9. * 用于获取实例的方法
    10. * @return
    11. */
    12. public static Object getBean(String beanId){
    13. //通过map的get方法由id获取对象
    14. return beansMap.get(beanId);
    15. }
    16. }
  2. 对层架构进行修饰 ```java ——————————-接口—————————— public interface AccountService { void saveAccount(); }

———————————实现类—————————— @Service public class AccountServiceImpl implements AccountService {

  1. @AutoWired
  2. private AccountDao accountDao;
  3. @Override
  4. public void saveAccount() {
  5. accountDao.saveAccount();
  6. }

}

  1. 4. 设计扫描类
  2. 设计流程:
  3. - 设计方法1:①传入参数为全类名;②将全类名的点转化为/;③创建文件对象File读取该路径内容。 ④调用方法2
  4. - 设计迭代过滤符合条件的文件的方法2:①传入参数为File;②创建文件过滤对象;③迭代过滤文件夹下的文件,获取该文件夹下的所有文件的路径,对文件的路径进行处理。切割出符合我们使用的全类名并保存到数组中。
  5. ```java
  6. // 模拟spring底层基于注解的方式进行解耦
  7. public class ComponentScan {
  8. //创建集合对象,用于存放全类名
  9. private static List<String> fileList = new ArrayList<>();
  10. /**
  11. * 模拟装配实例、注入实例的过程
  12. * @param pathName
  13. */
  14. public static void getComponentScan(String pathName){
  15. //将.替换成/
  16. pathName = pathName.replace(".","/");
  17. //获取当前工程的绝对路径
  18. String path = ClassLoader.getSystemResource("").getPath() + pathName;
  19. //创建File对象
  20. File file = new File(path);
  21. //调用过滤文件的方法
  22. addFiles(file);
  23. //遍历全类名的集合
  24. for (String className : fileList){
  25. try {
  26. //获取字节码对象
  27. Class aClass = Class.forName(className);
  28. //根据反射机制来检测类上有没有自定义的注解
  29. Controller controller = (Controller)aClass.getAnnotation(Controller.class);
  30. Service service = (Service)aClass.getAnnotation(Service.class);
  31. Repository repository = (Repository)aClass.getAnnotation(Repository.class);
  32. //如果类上有以上任意一个注解,那么我们就创建该类的实例,将实例添加到容器中
  33. if (controller != null || service != null || repository != null){
  34. //根据反射机制将实例创建出来
  35. Object object = aClass.newInstance();
  36. //获取类的简类名
  37. String simpleName = aClass.getSimpleName();
  38. //将实例添加到工厂里面
  39. BeanFactory.getBeansMap().put(simpleName,object);
  40. }
  41. } catch (ClassNotFoundException e) {
  42. e.printStackTrace();
  43. } catch (IllegalAccessException e) {
  44. e.printStackTrace();
  45. } catch (InstantiationException e) {
  46. e.printStackTrace();
  47. }
  48. }
  49. //继续遍历集合,将需要注入的属性来完成实例的注入
  50. for (String className : fileList) {
  51. try {
  52. //获取字节码对象
  53. Class aClass = Class.forName(className);
  54. //根据反射机制来检测类上有没有自定义的注解
  55. Controller controller = (Controller)aClass.getAnnotation(Controller.class);
  56. Service service = (Service)aClass.getAnnotation(Service.class);
  57. Repository repository = (Repository)aClass.getAnnotation(Repository.class);
  58. //如果类上有以上任意一个注解,接下来要检测该类中有没有属性需要注入(查看属性有没有被AutoWired注解修饰)
  59. if (controller != null || service != null || repository != null){
  60. //获取该类该类中所有的属性
  61. Field[] declaredFields = aClass.getDeclaredFields();
  62. //遍历属性数组
  63. for (Field declaredField : declaredFields) {
  64. //检测属性上有没有AutoWired注解
  65. AutoWired autoWired = declaredField.getAnnotation(AutoWired.class);
  66. //如果该注解不为空,就意味着该属性需要注入(根据类型去注入)
  67. if (autoWired != null){
  68. //获取容器对象
  69. Map<Object,Object> beansMap = BeanFactory.getBeansMap();
  70. //将map集合转为set集合
  71. Set<Map.Entry<Object, Object>> entries = beansMap.entrySet();
  72. //遍历集合
  73. for (Map.Entry<Object, Object> entry : entries) {
  74. //获取实例所实现的接口字节码对象
  75. Class[] interfaces = entry.getValue().getClass().getInterfaces();
  76. //遍历接口数组
  77. for (Class anInterface : interfaces) {
  78. if (anInterface == declaredField.getType()){
  79. //打破封装
  80. declaredField.setAccessible(true);
  81. //给属性注入实例
  82. declaredField.set(BeanFactory.getBean(aClass.getSimpleName()),entry.getValue());
  83. }
  84. }
  85. }
  86. }
  87. }
  88. }
  89. } catch (ClassNotFoundException e) {
  90. e.printStackTrace();
  91. } catch (IllegalAccessException e) {
  92. e.printStackTrace();
  93. }
  94. }
  95. }
  96. /**
  97. * 定义一个递归的方法,用于过滤符合条件的文件
  98. *
  99. * file.listFiles 将所有的文件或者是文件夹切成一个数组
  100. * FileFilter 过滤文件的接口
  101. *
  102. */
  103. public static void addFiles(File file){
  104. File[] files = file.listFiles(new FileFilter() {
  105. @Override
  106. public boolean accept(File f) {
  107. //f.isDirectory() 判断file是不是文件夹
  108. if (f.isDirectory()){
  109. //如果是文件夹的话,继续调用递归的方法
  110. addFiles(f);
  111. }
  112. //我们过滤的文件时以.class结尾的
  113. return f.getPath().endsWith(".class");
  114. }
  115. });
  116. //遍历文件数组,将符合要求的文件切割成全类名的形式 com.eagleslab.ioc.account.dao.AccountDaoImpl
  117. for (File f : files) {
  118. //获取文件的路径
  119. String path = f.getPath();
  120. //将/替换成.
  121. path = path.replace("\\",".");
  122. //将com前面所有的字符串删掉
  123. path = path.substring(path.lastIndexOf("com"),path.length());
  124. //将.class删掉
  125. path = path.replace(".class","");
  126. //将切割好的全类名添加到集合中
  127. fileList.add(path);
  128. }
  129. }
  130. }