零基础学习 Java:Spring 一

第一部分
1,概述:先创建Java类,再把Java类定义到配置文件的 bean中,在启动项目时通过classpathxmlwebapplicationcontext
这个类这个容器去手动加载配置文件。将定义到配置文件的bean,通过控制反转,加载到spring容器中,再通过 java 反射机制把类实例化出来,可以从容器中 getbean 把实例获取出来,获取的是一个对象,转成定义的 class,再调用函数。

<bean id="spring16" class="com.qicong.s16.Spring16" autowire="byType"></bean>   
public class Spring16 {

public void printUser(){
        System.out.println("Spring16....");
    }
    
 public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Spring16 s16 = (Spring16)context.getBean("spring16"); 
        s16.printUser();   
    }
}        

2,配置文件:相当于一份需要被实例化的 java 类的名单。
定义xsd才能使用对应的标签。
配置文件的创建:可以在创建项目时创建 spring 的配置文件,也可以在创建项目后,单独创建配置文件,在src目录下new一个xml configuration file, spring configu,一般将配置文件起名为application configuration. xml。
在这里插入图片描述

3,bean
如何定义一个bean?
bean 的 id 属性:java类的唯一标识,class属性:java类的包名,java类的全路径。
bean 其实是一个实例化的 java 类。

 <bean id="user1" class="com.qicong.s15.User"/>

Core 一:
bean的加载原理就是控制反转,控制反转描述的是 bean 的生命周期,java 反射机制是 spring 的底层实例化 bean。

Java 的反射机制:通过全路径的字符串把 Java 类实例化出来。
尝试用 ClassForName 把类实例化出来,但是这么做类没有加载到 spring 容器中。

 //java的反射机制
        try {
            Spring11 s1 = (Spring11)Class.forName("com.qicong.Spring11").getConstructor().newInstance();
            s1.printSpring();
        }catch (Exception e){
            e.printStackTrace();
        }

Spring 是如何将类实例化出来的:spring 把配置文件中的 bean 加载到容器,key value 的 map 结构加载到容器,通过get key get id获取一个Java 类的全路径,通过 ClassForName,把类实例化出来,实例化出来以后,放到容器中,getbean获取出来,转成当前定义的class,就可以去调用函数了。

控制反转 Invertion of control:
项目启动时配置文件相当于一份需要被实例化的 Java 类的名单传到容器,容器中根据配置文件定义的 bean, 去注册 bean,找到 bean对应的位置并实例化bean,通过反射机制实例化bean,并放到缓存池中,当有http请求时,直接从缓存池中调用,当运行结束或项目停止时 bean 自动销毁。
在这里插入图片描述
bean的生命周期:
bean的定义:在配置文件中定义 bean。
bean的初始化:将 bean 加载到容器并实例化出来。
bean的使用:http 请求时从缓存池中调用。
bean的销毁:当运行结束,项目停止时,bean自动销毁。

感觉到将 bean 加载到 spring 中变得更复杂了?/ 为什么要把bean加载到容器?
我们不可能把所有的代码都写到一个类里,要用 Service 类去分担业务逻辑,对数据进行处理,如何调用Service中的方法?如果去new 一个 Service类,这个类将不在 spring 容器里,不在同一个运行环境中就无法调用 Service 中的方法,降低了开发的效率。当我们把bean统一放到容器中进行托管,就可以更专注于业务的开发,提升开发的效率。

bean 的 name 属性:
在 Spring 容器中,id 是 bean 唯一的标识符,name可以是多个,name="n1,n2,n3…"类似于别名,也可以通过别名去获取实例,id 和 name 至少要指定一个,如果 id 和 name 都没有,class 全类名作为name,也可以通过 class 包名去获取这个实例。

<bean id="spring11" name="spring11-1,spring11-2" class="com.qicong.Spring11"/>

在这里插入图片描述

Core 二:
1,bean的依赖注入DI:denpendency injection:
强依赖:在 a类 中使用 b类, 如果用 new 关键字写固定一个b类的实现类,当需要修改使用的实现类,需要修改代码。用接口也要用 new 关键字写固定一个实现类,修改也需要修改代码,代码写固定,关联性太强,耦合度太高,通过配置文件把依赖转为注入,降低软件的耦合度,提高代码的灵活性。
例子:修改 jdbc 配置文件,添加 jar 包就可以用代码连不同的数据库,不用在代码中写固定数据库的驱动。

如何实现注入?
通过 property 实现注入,name 是属性名,ref 是注入的 bean 的 id 名,value 是给属性赋值。
在这里插入图片描述
在类中写属性要用setter&getter方法。
如果包中有很多类可以注入时,修改property 的 ref 值就可以改变注入的类。
在这里插入图片描述
2,bean 的 autowire 属性:
autowire Byname:类属性名和被加载到容器的 bean 的 id 一致,实现自动注入。
在这里插入图片描述在这里插入图片描述
Bytype:通过类的属性的类型能获取到一个Java类,这个Java类也在容器中,且容器中通过这个Java类的 class 包路径能获取到与类属性类型获取一致的Java类,由此建立关联,实现自动注入。在这里插入图片描述在这里插入图片描述
candidate:针对 autowire Bytype 使用的。当出现:被注入的类有几个子类可以为实现类。
哪个父类或子类的 bean 加上 autowire candidate:ture 就选择将此父类或子类之一注入到类中。相当于primary。
该被注入的父类或子类中哪个加上 autowire candidate:false 就忽视哪个类。
在这里插入图片描述
总之父类和子类中只有一个能被注入。

3,bean 的 init-method 和 destroy-method 属性:
bean实例化时,回调 init-method 中对应的方法。
bean销毁时,回调 destroy-method 中对应的方法。
方法都定义在Java 类里面。方法里面可以打印内容。
代码实战:从容器中获取实例,启动项目实例化bean时,回调函数。从容器中手动销毁 bean时,context. register shutdown hook() 回调函数,打印内容。

<bean id="spring17" class="com.qicong.s17.Spring17" init-method="initSpring" destroy-method="destroySpring"/>
public class Spring17 {

    public void initSpring(){
        System.out.println("init s17");
    }

    public void destroySpring(){
        System.out.println("destroy s17");
    }

    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Spring17 s = (Spring17)context.getBean("spring17");

        context.registerShutdownHook();
    }

}

4,bean 的 lazy-init属性:
lazy-init:true 表示懒加载。懒加载指只注册,不实例化,http 请求使用时才实例化并放到缓存池中,从缓存池调用。与游戏类似,不一次性加载全部组件,玩到哪块加载哪一块的组件。
代码实战:懒加载在项目启动时,不调用实例化的回调函数, getBean 获取实例,才回调 init-method 中定义的函数。
在这里插入图片描述在这里插入图片描述
5,bean 的 scope 属性:
scope:bean 的作用范围。
singleton 默认的单例模式,在bean缓存池中只有一个此类的实例。
prototype 多例模式,每次使用都会实例化一个新的此类的实例。
代码实战:多次打印从容器中获取的对象,区分从缓存池中获取的是单例还是多例。用实例化的回调函数 init-method 证明多例模式。

<bean scope="singleton" id="spring19" class="com.qicong.s19.Spring19" init-method="initSpring19"/>
<bean scope="prototype" id="spring19" class="com.qicong.s19.Spring19" init-method="initSpring19"/>

多例模式调用时从缓存池中获取的实例在这里插入图片描述
6,bean 的 depend on 属性:
可用作 bean 的加载顺序设定,可依赖多个 bean,把被依赖的bean 的 id 或 name 写上。被依赖的 bean 要先实例化,被依赖的 bean 如果是懒加载,则懒加载失效。
代码实战:依赖懒加载的bean,启动项目时,先实例化懒加载的 bean,回调其 init-method 中对应的函数。(懒加载不会在项目启动时实例化到缓存池,被使用时才加载 ,被依赖就是被使用了所以懒加载失效了,就要先加载)

<bean lazy-init="true" id="spring18" class="com.qicong.s18.Spring18" init-method="initSpring18" />

<bean id="spring20" class="com.qicong.s20.Spring20" depends-on="spring18"/>

第二部分:
Spring 的注解:
@Override注解:重写父类的函数时使用。
@Deprecated:表明函数已弃用,不建议使用,出问题也不维护。
@ SuppressWarnings:未加泛型或定义的内容未使用,让编译器 Eclipse 忽略警告。
在这里插入图片描述

元注解:
@Retention:自创的注解保存在哪
RetentionPolicy 枚举类:
Source:保存在代码中。
Class:编译进class中,不添加到虚拟机。
RunTime:进虚拟机,在运行时通过反射获取信息。
@Targets:自创的注解在哪使用
在这里插入图片描述

使用元注解的例子:
Java注解自动生成 sql ,通过bean自动生成大量 sql,提高开发效率:
1,针对数据库表创建Java Bean类,user类映射类,属性名映射数据库的字段类。后续会创建父类,user 类继承父类,打印 sql 的方法中传入父类,可以打印子类 user 类或其他子类的 sql 语句。
在这里插入图片描述
2,使用元注解创建注解:
创建两个注解类:用在类上的注解,映射数据库的表名。
用在变量上的注解:映射数据库的字段。
定义注解类的 name 属性,在使用注解时,再给属性赋值,赋数据库的表名,字段名。
怎么定义注解类的 name 属性?类似于定义抽象函数的方法,没有函数体。
在这里插入图片描述在这里插入图片描述

3,写一个类去生成 sql:
main:给 user 类属性赋值,new 当前类调用 insert 的方法把 user 类传进去。

Insert into 1tablename (2columns)3Values.
insert:把user类的父类作为传入的参数。
获取表名。
获取类属性的数组。
遍历属性的数组,获取每个属性的注解数组,将每个注解的 name 属性的值放到columns 数组。
获取 values 数组
StringBuilder, append,StringJoin 串联 sql。
sout 将 StringBuilder 打印出来。
在 main 中给不同的子类赋值都可以把 sql 打印出来。
打印出来以后可以连接数据库,把 sql 语句插入到数据库中。

package com.qicong.s22;

import java.lang.reflect.Field;

/**
 * User: 祁大聪
 * SQL
 * INSERT INTO t_user('name','user_name','password','id')
 * VALUES ('三妮','sanNi','root123','100')
 */
public class JDBCTest {

    public void insert(LongBean bean) throws Exception{
        //这是表的名称的注解
        MyTable mt = bean.getClass().getAnnotation(MyTable.class);
        String tableName = mt.name();

        //这是所有的属性
        Field[] fields = bean.getClass().getFields();
        String[] columns = new String[fields.length];
        String[] values = new String[fields.length];
        for(int i = 0 ; i < fields.length; i++){
            MyColumn mc = fields[i].getAnnotation(MyColumn.class);
            columns[i] = "'" + mc.name() + "'";
            values[i] = "'" + fields[i].get(bean).toString() + "'";
        }

        //生成SQL
        StringBuilder sb = new StringBuilder("INSERT INTO " + tableName);
        sb.append("("
                + String.join(",",columns)
                + ") VALUES ("
                + String.join(",",values)
                + ")"
        );

        System.out.println(sb);

        //调用SQL

    }

    public static void main(String[] args) {
        User user = new User();
        user.id = 100L;
        user.name = "三妮";
        user.username = "sanNi";
        user.password = "root123";

        Student student = new Student();
        student.id = 100L;
        student.school = "xx大学";
        student.address = "北京";
        student.height = 180;

        //插入到数据库中
        JDBCTest jdbc = new JDBCTest();
        try { // ctrl + alt + t
            jdbc.insert(user);
            jdbc.insert(student);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

我们再回到配置文件中。
<context: component-scan base-package:" " />
context 容器去扫描哪些包,自动扫描这个包下所有带有 Spring 注解的类到容器,这个包下加了 @Component 注解的类会自动扫描到 context 容器中。
在这里插入图片描述

1,@Component中的 name 值相当于 bean 的 id,bean中的 id 当然不可以重复, 不写 name 时类的名称的第1个字母小写的全名为默认的 id 。
使用 @Component 注解的类,有不同包但同名的类,name改新不改老。因为老项目可能已经使用了这个 name。
在这里插入图片描述
2,@Service SpringBoot中处理业务逻辑的类使用的注解。

3,@Scope( Value= ConfigurableBeanFactory. Scope PROTOTYPE.)
可以多次打印从缓存池中获取的对象,查看是单例或多例模式。

4, @DependsOn(“被依赖的 bean 的 id”)被依赖的类先实例化。

5,@Bean (name=“与当前类的bean的 name 不同,容器中get此处的id可以 get 新加载到容器的bean”)
用在函数上的注解,用在返回当前 bean 的函数上的注解,用了以后会返回一个新的 bean 加载到容器。
代码实战:从容器中获取两个实例并打印出来。

在这里插入图片描述

6,DI 注解:
@ Autowire byType实现注入。
代码实战:获取当前类的实例,去调用被注入类的方法,打印内容。

@Primary:主要的优先的。
在接口被注入时,在接口的实现类之一上使用,表明优先调用此类实现接口,注入接口以后,能调用 此实现类的方法。
在这里插入图片描述
@Qualifier:限定名。
限定注入的接口使用的实现类。
如果实现类中使用 @Qualifier,把注入此实现类的 name 写固定了,那么在限定接口的实现类时写的 name 要与实现类中写固定的 name 相同。
注入成功以后,也可以从容器中用接口实现类默认的 id getBean。
如果实现类还是多例模式,通过 id 或 name 从缓存池中获取的实例不同。
在这里插入图片描述

7,配置注解
@Configuration

@ ComponentScan(basePackages=" “):代替 xml 文件中
<context: component-scan base-package:” " /> 会扫描这个包下所有带有 Component 注解的类到容器。
@ImportResource:引入配置文件,将配置文件中定义的 bean 也扫描的容器。
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值