• Spring()


    一、导学

    二、

    1.入门程序

    spring快照版本是最新的版本,未发布。需要用到

    下面这个不需要配置仓库,直接写在依赖中就行

    引入spring相关依赖

    1. "1.0" encoding="UTF-8"?>
    2. <project xmlns="http://maven.apache.org/POM/4.0.0"
    3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    5. <modelVersion>4.0.0modelVersion>
    6. <groupId>com.gringroupId>
    7. <artifactId>spring-002-firstartifactId>
    8. <version>1.0-SNAPSHOTversion>
    9. <packaging>jarpackaging>
    10. <properties>
    11. <maven.compiler.source>17maven.compiler.source>
    12. <maven.compiler.target>17maven.compiler.target>
    13. <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
    14. properties>
    15. <dependencies>
    16. <dependency>
    17. <groupId>org.springframeworkgroupId>
    18. <artifactId>spring-contextartifactId>
    19. <version>6.0.10version>
    20. dependency>
    21. <dependency>
    22. <groupId>junitgroupId>
    23. <artifactId>junitartifactId>
    24. <version>4.13.2version>
    25. <scope>testscope>
    26. dependency>
    27. dependencies>
    28. project>

    疑惑:

    1.类加载路径

    类路径classpath指的是编译后路径即:
    本项目[或模块目录]/target/[项目或模块名]/WEB-INF/classes

    原项目中java目录下的文件和recource目录下的文件页都被打包到了此类路径下。

    如下图: java目录中的文件和recource目录中的文件经过编译后,都会放到WEB-INF/classes目录下:

    把test写成private后,外界调用不了这个测试方法。

    一个小细节:发现构造方法,不是在getBean()时创建的。

    证明如下:

    2.spring6启用log4j2框架

    上去就报错了 

    Cannot reconnect

    第一步:引入依赖

    1. <dependency>
    2. <groupId>org.apache.logging.log4jgroupId>
    3. <artifactId>log4j-coreartifactId>
    4. <version>2.19.0version>
    5. dependency>
    6. <dependency>
    7. <groupId>org.apache.logging.log4jgroupId>
    8. <artifactId>log4j-slf4j2-implartifactId>
    9. <version>2.19.0version>
    10. dependency>

    第二步:在类的根路径下提供log4j2.xml配置文件(文件名固定为:log4j2.xml,文件必须放到类根路径下。)

    1. "1.0" encoding="UTF-8"?>
    2. <configuration>
    3. <loggers>
    4. <root level="DEBUG">
    5. <appender-ref ref="spring6log"/>
    6. root>
    7. loggers>
    8. <appenders>
    9. <console name="spring6log" target="SYSTEM_OUT">
    10. <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss SSS} [%t] %-3level %logger{1024} - %msg%n"/>
    11. console>
    12. appenders>
    13. configuration>

    第三步:使用日志框架

    1. Logger logger = LoggerFactory.getLogger(FirstSpringTest.class);
    2. logger.info("我是一条日志消息");

    在配置文件中,已经指定了 debug日志级别, 很有意思。当打印日志时,如果低于这个级别,则不会打印出来。

    除此之外,你可以在配置文件中去指定这个级别。

    三、 spring对loc(控制反转) 的实现

    1.set注入。

    配合property标签,

    注意 name是 本类的set方法的名字:去掉set,首字母小写

    ref 是所要引用 的其他类。

    总之,通过 本类的set方法对所引用的类进行注入。

    2.构造注入

    利用   ,  name属性可被 索引,类型推断 替代。 
    

    3.内部bean和外部

    没有那么复杂,声明在外面的bean叫做 外部bean,有自己的 id 和class .

                            而内部bean写在中,不需要class

    4.给简单类型注入

    疑惑:为什么要给它注入。

    下面是spring中认为的基本数据类型,不使用ref来引用,而是通过name属性。

    但要注意 Date类型的注入,要满足它所要求的格式。

    在实际开发中,不会用Date这种注入方式,应该有更好的。

    经典:

    注入空字符串

    注入的值含有 特殊符号

    第二种方案,只能用标签

    p命名空间

    c命名空间

    schemaLocation

    验证xml文档

    util命名空间

    下面这两个 自动装配 是基于set方法 的        

    根据名字自动装配

    根据类型自动装配        

    根据类型进行自动装配时,在有效的配置文件当中,某种类型的实例只能有一个

    引入外部属性配置文件,context命名空间

    当加载时,会默认先加载windows中的环境变量,因此要在properties文件中的key前 加上 jdbc前缀

    下面是引入步骤:

    我们都知道编写数据源的时候是需要连接数据库的信息的,例如:driver url username password等信息。这些信息可以单独写到一个属性配置文件中吗,这样用户修改起来会更加的方便。当然可以。

    创建实现数据源的类

    引入配置文件,并可以用${}进行根据key取值

    四、bean作用域

    单例和多例(singleton  ans  protope)

    自定义scope, 同一个线程有一个bean

    五、GoF之工厂模式

    简单工厂模式(静态 工厂方法模式

    简单工厂模式是工厂方法模式的一种特殊实现。

    工厂方法模式

    解决了 简单工厂模式违背 ocp的原则

    抽象工厂模式

    五、bean的获取方式

    1.通过构造方法实例化(利用无参构造)

    在spring配置文件中直接配置类路径,spring会自动调用该类的无参构造方法。

    2.通过简单工厂模式(静态工厂方法模式)

    底层还是new() 创建一个对象

    3.通过工厂方法模式

    4.实现 接口(惊讶)

    接口里可以有默认方法。

    这是对第3中方式的优化:spring提供了一个抽象工厂的接口,我们只要去实现这个接口,就创建出了具体的工厂 ,我们通过这个具体的工厂来创建bean.

    备注:我们这个具体的工厂 也是一个bean,因为它是通过构造方法创建出来的,是一个特殊的bean,可以去创建普通的bean

    通过Factory工厂bean 主要是对普通bean进行加工处理

    beanFactory和FactoryBean  的区别

    注入自定义Date

    六、bean的生命周期

    五步法

    1.创建bean

    2.属性赋值(即注入)

    3.初始化bean ,init方法自己写,自己配,方法名随意

    第五步,销毁bean,自己写,自己配,方法名随意

    在配置文件中指定 初始化方法和销毁方法。

    必须手动关闭spring容器,就是ApplicationContext接口 实现类的close()方法,记得强转一下类型(转成实现类的类型),因为接口没有这个方法。调用close()后,会自动调用 bean指定的销毁方法

    七步法

    在bean初始化,加上 之前,之后这两步

    bean后处理器 只作用当前作用域的所有bean

    bean后处理器也需要在spring配置文件中注册

    十步法

    新加的3个点位,看是否实现其对应的特定接口,在这三个点位都会去检查bean是否实现了某个接口,则spring容器会调用这个接口中的方法

    before之前

    before之后

    销毁bean之前或者说使用bean之后

    点位3实现一系列接口,会有相应的方法。

    bean的作用域不同,那么生命周期也不同

    多例 ,spring容器只管理一部分生命周期,当客户端得到bean后,容器就不再管理该bean 

    自己new的对象怎样交给spring容器管理

    七、Bean的循环依赖问题

    1.什么是循环依赖

    A对象中有B属性。B对象中有A属性。这就是循环依赖。我依赖你,你也依赖我。

    曝光

    只有bean的scope为singleton 才会进行曝光 , 

    多例  + 多例 +set注入 

    多例 +单例 + set注入

    单例+构造注入

    构造注入的时候需要赋值。

    spring解决循环依赖的机理(精彩)

    往map里放bean对象就是曝光的过程,可以在内存中找到它。

    再分析下面的源码:

    上图,从三级缓存中拿到工厂创建的bean对象后,会将bean对象放到二级缓存中,然后根据beanName把三级缓存中的bean对象删除。

    八、回顾反射机制

    通过反射可获得某个类的方法,然后调用它,例如可以获得set方法

    1. package com.itheima.a02reflectdemo1;
    2. import java.lang.reflect.InvocationTargetException;
    3. import java.lang.reflect.Method;
    4. public class ReflectDemo6 {
    5. public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
    6. //1.获取字节码文件对象
    7. Class clazz = Class.forName("com.itheima.a02reflectdemo1.Student");
    8. //2.获取一个对象
    9. //需要用这个对象去调用方法
    10. Student s = new Student();
    11. //3.获取一个指定的方法
    12. //参数一:方法名
    13. //参数二:参数列表,如果没有可以不写
    14. Method eatMethod = clazz.getMethod("eat",String.class);
    15. //运行
    16. //参数一:表示方法的调用对象
    17. //参数二:方法在运行时需要的实际参数
    18. //注意点:如果方法有返回值,那么需要接收invoke的结果
    19. //如果方法没有返回值,则不需要接收
    20. String result = (String) eatMethod.invoke(s, "重庆小面");
    21. System.out.println(result);
    22. }
    23. }
    24. public class Student {
    25. private String name;
    26. private int age;
    27. public String gender;
    28. public String address;
    29. public Student() {
    30. }
    31. public Student(String name) {
    32. this.name = name;
    33. }
    34. private Student(String name, int age) {
    35. this.name = name;
    36. this.age = age;
    37. }
    38. /**
    39. * 获取
    40. * @return name
    41. */
    42. public String getName() {
    43. return name;
    44. }
    45. /**
    46. * 设置
    47. * @param name
    48. */
    49. public void setName(String name) {
    50. this.name = name;
    51. }
    52. /**
    53. * 获取
    54. * @return age
    55. */
    56. public int getAge() {
    57. return age;
    58. }
    59. /**
    60. * 设置
    61. * @param age
    62. */
    63. public void setAge(int age) {
    64. this.age = age;
    65. }
    66. public String toString() {
    67. return "Student{name = " + name + ", age = " + age + "}";
    68. }
    69. private void study(){
    70. System.out.println("学生在学习");
    71. }
    72. private void sleep(){
    73. System.out.println("学生在睡觉");
    74. }
    75. public String eat(String something){
    76. System.out.println("学生在吃" + something);
    77. return "学生已经吃完了,非常happy";
    78. }
    79. }

    九、手写spring

    com.grin   :代表开发

    com.myspringframework :代表框架

    为什么要向下转型,因为Element的方法更丰富

    十、

    1.回顾注解

    2.声明Bean的注解

    如果value不用默认值的话,用户使用注解后必须在里面写 相应的值

    3.spring注解的使用

    • 第一步:加入aop的依赖
    • 第二步:配置xml文件,假如context上下文空间,并设置约束。过去,我们在引入外部配置文件时,也会用到context空间
    • 第三步:在配置文件中指定扫描的包
    • 第四步:在Bean类上使用注解

    value的注意点: 

    多个包进行扫描怎样解决?

    1.将要扫描的包用 逗号 隔开

    2.指定父包

    1. 用逗号隔开
    2. "1.0" encoding="UTF-8"?>
    3. <beans xmlns="http://www.springframework.org/schema/beans"
    4. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    5. xmlns:context="http://www.springframework.org/schema/context"
    6. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    7. http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    8. <context:component-scan base-package="com.powernode.spring6.bean,com.powernode.spring6.bean2"/>
    9. beans>

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

    3.选择性实例化bean

    1.第一种解决方案

    2.第二种解决方案

    4.@Value

    适用范围是  简单类型

      

    1. 回顾一下,spring的简单类型有哪些?
    2. public class BeanUtils{
    3. //.......
    4. /**
    5. * Check if the given type represents a "simple" property: a simple value
    6. * type or an array of simple value types.
    7. *

      See {@link #isSimpleValueType(Class)} for the definition of simple

    8. * value type.
    9. *

      Used to determine properties to check for a "simple" dependency-check.

    10. * @param type the type to check
    11. * @return whether the given type represents a "simple" property
    12. * @see org.springframework.beans.factory.support.RootBeanDefinition#DEPENDENCY_CHECK_SIMPLE
    13. * @see org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#checkDependencies
    14. * @see #isSimpleValueType(Class)
    15. */
    16. public static boolean isSimpleProperty(Class type) {
    17. Assert.notNull(type, "'type' must not be null");
    18. return isSimpleValueType(type) || (type.isArray() && isSimpleValueType(type.getComponentType()));
    19. }
    20. /**
    21. * Check if the given type represents a "simple" value type: a primitive or
    22. * primitive wrapper, an enum, a String or other CharSequence, a Number, a
    23. * Date, a Temporal, a URI, a URL, a Locale, or a Class.
    24. *

      {@code Void} and {@code void} are not considered simple value types.

    25. * @param type the type to check
    26. * @return whether the given type represents a "simple" value type
    27. * @see #isSimpleProperty(Class)
    28. */
    29. public static boolean isSimpleValueType(Class type) {
    30. return (Void.class != type && void.class != type &&
    31. (ClassUtils.isPrimitiveOrWrapper(type) ||
    32. Enum.class.isAssignableFrom(type) ||
    33. CharSequence.class.isAssignableFrom(type) ||
    34. Number.class.isAssignableFrom(type) ||
    35. Date.class.isAssignableFrom(type) ||
    36. Temporal.class.isAssignableFrom(type) ||
    37. URI.class == type ||
    38. URL.class == type ||
    39. Locale.class == type ||
    40. Class.class == type));
    41. }
    42. //........
    43. }

    5.@Autowired    和  @Qualifier

    @Autowired 能够标注的位置

    @Autowired 根据类型进行装配。

    回顾一下原来在进行类型自动装配时,会出现错误的情况。

    接口newClazz有两个实现类,分别是Clazz 和Clazz2 

    类Student中,有类型为newClazz的属性。

    当我把Clazz和Clazz2注册一下类型,此时让Student类根据类型装配就会报错,因为此时有多个类型,即有两个实现类,不知道选哪一个。

    6.@Resource

    6.全注解式开发

    启示:

    1.当使用@Component  @Controller @Service @Respository 注解 时,还需要配置xml文件来进行包扫描后,会将带有这些注解的类创建对象,放到集合中。

            此时仅仅完成了创建对象,并未进行注入

    十一、JDBC Template

    十二、GoF代理模式

    1.作用

    ①当一个对象需要收到保护时,可以考虑使用代理模式保护对象

    ②需要进行功能增强时,可以找一个代理进行增强。

    ③A对象无法与B对象直接进行交互时,也可以使用代理模式来解决。

    代理模式有三大对象

    第一个角色:目标对象(演员)

    第二个角色:代理对象(替身演员)

    第三个角色:目标对象和代理对象的公共接口(演员和替身演员需要有相同的动作)

    代理模式的实现方式

    静态代理

    第一种方案:直接在源代码中进行更改。

            缺点:违背了OCP原则;代码没有进行复用

    第二种方案中  写一个类继承目标对象,在目标对象中重写父类方法。

            优点:遵守OCP原则

             缺点:继承关系的耦合度太高

    第三种方案  让A类作为B类的属性,为了进一步降低耦合,再将A类所实现的接口作为属性的类型。

            优点:降低了耦合度

            缺点:当有多个目标对象时,需要创建多个代理对象,类会急剧递增。

    静态代理总结

    动态代理

    概述

    jdk动态代理

    创建目标对象,然后 创建代理对象,然后  调用代理对象的代理方法

    1. public class Client {
    2. public static void main(String[] args) {
    3. // 创建目标对象
    4. OrderService target = new OrderServiceImpl();
    5. // 创建代理对象
    6. OrderService orderServiceProxy = (OrderService) Proxy.newProxyInstance(target.getClass().getClassLoader(),
    7. target.getClass().getInterfaces(),
    8. new TimerInvocationHandler(target));
    9. // 调用代理对象的代理方法
    10. orderServiceProxy.detail();
    11. orderServiceProxy.modify();
    12. orderServiceProxy.generate();
    13. }
    14. }

    创建 实现 InvocationHandler接口 的实现类。

            原因:因为在 创建代理对象时,需要用到该接口的实现类。

    重写的invoke方法 的三个参数

    invoke方法执行过程中,使用method可以调用目标对象的目标方法, 在调用目标对象的目标方法前后,可以加上一段代码,用来添加你想要的增强的功能

    注意invoke方法的返回值,如果 代理对象调用方法后需要返回值,那么invoke()方法必须目标对象的目标方法的返回值返回。(也就是invoke方法参数中method执行后的返回值)

    1. public class TimerInvocationHandler implements InvocationHandler {
    2. // 目标对象
    3. private Object target;
    4. // 通过构造方法来传目标对象
    5. public TimerInvocationHandler(Object target) {
    6. this.target = target;
    7. }
    8. @Override
    9. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    10. // 目标执行之前增强。
    11. long begin = System.currentTimeMillis();
    12. // 调用目标对象的目标方法
    13. Object retValue = method.invoke(target, args);
    14. // 目标执行之后增强。
    15. long end = System.currentTimeMillis();
    16. System.out.println("耗时"+(end - begin)+"毫秒");
    17. // 一定要记得返回哦。
    18. return retValue;
    19. }
    20. }

    十三、面向切面编程AOP

    概述

    Spring的AOP使用的动态代理是:JDK动态代理 + CGLIB动态代理技术。Spring在这两种动态代理中灵活切换,如果是代理接口,会默认使用JDK动态代理,如果要代理某个类,这个类没有实现接口,就会切换使用CGLIB。当然,你也可以强制通过一些配置让Spring只使用CGLIB。

    切面是在业务流程中,与业务逻辑不挂钩的通用代码。

    AOP:将与业务逻辑无关的通用的非业务逻辑代码,能够单独的提取出来,形成一个切面,然后把横向的切面应用到 业务逻辑当中的过程。

    专业叫法:交叉业务 :在业务流程中,与业务逻辑不挂钩的通用代码。

    七大术语

    连接点,切点,通知,切面,   织入(weaving),代理对象,目标对象     

    • 通知 Advice
    • 通知又叫增强,就是具体你要织入的代码。
      • 通知包括:
      • 前置通知
      • 最终通知
      • 异常通知
      • 环绕通知
      • 后置通知

    切面:切点+通知  (切点是一个方法)

    切点表达式

    SpringAOP实现之概述

    spring底层用的  jdk自带的动态代理和cglib库的动态代理 来实现 AOP.

    jdk自带的只能代理拥有接口的目标对象。

    cglib通过继承既能代理 没有接口的目标对象,也能代理有 接口的目标对象。

    (注意:可以在配置文件里进行修改)

    @Aspect这个注解很关键

    实践:引入依赖,引了很长时间

    1. "1.0" encoding="UTF-8"?>
    2. <project xmlns="http://maven.apache.org/POM/4.0.0"
    3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    5. <modelVersion>4.0.0modelVersion>
    6. <groupId>com.gringroupId>
    7. <artifactId>spring-007-aop-aspectartifactId>
    8. <version>1.0-SNAPSHOTversion>
    9. <properties>
    10. <maven.compiler.source>17maven.compiler.source>
    11. <maven.compiler.target>17maven.compiler.target>
    12. <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
    13. properties>
    14. <dependencies>
    15. <dependency>
    16. <groupId>org.springframeworkgroupId>
    17. <artifactId>spring-contextartifactId>
    18. <version>6.0.10version>
    19. dependency>
    20. <dependency>
    21. <groupId>junitgroupId>
    22. <artifactId>junitartifactId>
    23. <version>4.13.2version>
    24. <scope>testscope>
    25. dependency>
    26. <dependency>
    27. <groupId>org.springframeworkgroupId>
    28. <artifactId>spring-aopartifactId>
    29. <version>6.0.10version>
    30. dependency>
    31. <dependency>
    32. <groupId>org.springframeworkgroupId>
    33. <artifactId>spring-aspectsartifactId>
    34. <version>6.0.10version>
    35. dependency>
    36. dependencies>
    37. project>

    对spring的动态代理产生了误解。

    理由如下:有两个类,logAspect切面类和UserService 类

    我在logAspect类上加了@Before(切点表达式)前置通知,但是当我调用logAspect类的增强方法时,目标方法并没有执行。

    但其实是我理解错了,应该获取 目标对象: UserService的bean,然后调用目标对象的方法,此时切面类所设置的通知才会生效

    基于AspectJ的AOP注解式开发

    我对 环绕通知   的实际使用感兴趣。

    原来是通过ProceedingJoint

    通知类型:

    切面的顺序

    通过注解Order(数字) 可以解决。

    我们知道,业务流程当中不一定只有一个切面,可能有的切面控制事务,有的记录日志,有的进行安全控制,如果多个切面的话,顺序如何控制:可以使用@Order注解来标识切面类,为@Order注解的value指定一个整数型的数字,数字越小,优先级越高

    通用切点

    其他类 也可以引用 某个类定义的通用切点

    通过@Pointcut注解

    通过连接点获得目标方法

    全注解开发

    通过配置相关注解的类来进行 组件扫描 和 启用aspectj的自动代理机制

    测试一下

    基于XML进行配置(了解一下)

    AOP的实际案例:事务处理

    当有多条DML语句,这时需要开启事务,提交事务 ,或异常时捕捉事务。 可以看出关于事务处理的步骤是很固定的,此时可以利用AOP,复用事务处理的重复代码。

    AOP的实际案例:安全日志(别忘了通用切点)

    定义的通用切点  在使用时可以用  ||  运算符隔开。

    十四、spring对事务的支持

    忘记:@Resource注解

    添加事务控制

    编程式事务:手动去写。  (麻烦)

    声明式事务:①基于注解方式(important)  ②基于xml文件配置

    注解实现:

    1.由于使用的JdbcTemplate 因此为了事务管理,需要引入DataSourceTransactionManager.

    配置事物管理器时,其中数据源 是它的属性。 

    2.如果想用注解,就要在xml文件中  启动  事务注解驱动器。

    事务传播行为:

    SUPPORT :原先有事务,就以事务的方式执行,没有就以非事务的方式执行

    mandatory:adj 强制性

    MANDATORY 有就加入,没有就抛异常

    REQUEIRES_NEW 不管有没有,开启一个新的

    NOT_SUPPORTED:不管有还是没有,无论如何都要开启一个新的,不存在事务的嵌套,原先的事务被挂起

    事务隔离级别

    事务超时(重要且有坑)

    只读事务

    基于xml实现的配置可以进行模糊匹配(不熟练)

    参考资料:

    【文字+图示】Java项目中类路径classpath具体指的是哪个路径_超周到的程序员的博客-CSDN博客

  • 相关阅读:
    Windows版本 - MySQL卸载
    Linpack安装测试流程记录
    操作系统备考学习 day2 (1.3.2 - 1.6)
    Node.js 入门教程 14 使用 exports 从 Node.js 文件中公开功能
    在 Vue 中控制表单输入
    Java 函数式编程
    Git 21 天打卡:day01 git 安装编译详细步骤
    【JavaWeb】你这么厉害,知道RBAC权限模型和ABAC权限模型吗?
    写Python爬虫又被屏蔽了,你现在需要一个稳定的代理IP
    在verdi波形中显示状态机名字
  • 原文地址:https://blog.csdn.net/qq_64071349/article/details/133858797