新职课(chapter9-1)

Linux

  • Linux基础这部分,看看笔记
  • 在Linux上安装JDK,解压tomcat,安装MySQL
    • 项目放到tomcat/webapps下即可
    • 记得关闭防火墙 systemctl stop firewalld
    • 虚拟机还是virtualbox吧,玛德

Maven

  • 有关Maven的部分可以大致看看笔记
  • 在Windows使用IDEA写项目,最后部署到Linux服务器
  • 使用骨架archetype创建webAPP项目,需要补上后端代码目录和测试目录
    1
    • webapp下存放前端代码,测试部分的resources不是必须的
    • 在pom中配置好依赖,刷新maven,就会自动下载到本地仓库
  • 仓库之间的关系
    2
  • 使用tomcat7提供运行环境,添加plugin,完整pom
    <?xml version="1.0" encoding="UTF-8"?>
    
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
      <modelVersion>4.0.0</modelVersion>
    
      <groupId>org.example</groupId>
      <artifactId>mvnwebdemo</artifactId>
      <version>1.0</version>
      <packaging>war</packaging>
    
      <name>mvnwebdemo Maven Webapp</name>
      <!-- FIXME change it to the project's website -->
      <url>http://www.example.com</url>
    
      <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.7</maven.compiler.source>
        <maven.compiler.target>1.7</maven.compiler.target>
      </properties>
    
      <dependencies>
        <dependency>
          <groupId>junit</groupId>
          <artifactId>junit</artifactId>
          <version>4.11</version>
          <scope>test</scope>
        </dependency>
        <dependency>
          <groupId>javax.servlet</groupId>
          <artifactId>javax.servlet-api</artifactId>
          <version>3.1.0</version>
          <scope>provided</scope>
        </dependency>
        <dependency>
          <groupId>javax.servlet.jsp</groupId>
          <artifactId>javax.servlet.jsp-api</artifactId>
          <version>2.2.1</version>
          <scope>provided</scope>
        </dependency>
      </dependencies>
    
      <build>
        <finalName>mvnwebdemo</finalName>
        <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
          <plugins>
            <plugin>
              <groupId>org.apache.tomcat.maven</groupId>
              <artifactId>tomcat7-maven-plugin</artifactId>
              <version>2.2</version>
              <configuration>
                <port>8080</port>
                <path>/demo</path>
              </configuration>
            </plugin>
            <plugin>
              <artifactId>maven-clean-plugin</artifactId>
              <version>3.1.0</version>
            </plugin>
            <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
            <plugin>
              <artifactId>maven-resources-plugin</artifactId>
              <version>3.0.2</version>
            </plugin>
            <plugin>
              <artifactId>maven-compiler-plugin</artifactId>
              <version>3.8.0</version>
            </plugin>
            <plugin>
              <artifactId>maven-surefire-plugin</artifactId>
              <version>2.22.1</version>
            </plugin>
            <plugin>
              <artifactId>maven-war-plugin</artifactId>
              <version>3.2.2</version>
            </plugin>
            <plugin>
              <artifactId>maven-install-plugin</artifactId>
              <version>2.5.2</version>
            </plugin>
            <plugin>
              <artifactId>maven-deploy-plugin</artifactId>
              <version>2.8.2</version>
            </plugin>
          </plugins>
        </pluginManagement>
      </build>
    </project>
    
    3
    • 想查什么依赖可以去远程仓库找: https://mvnrepository.com/
    • maven帮我们管理用到的jar包,其他的和之前无异,还是用Servlet(@+service)
    • maven构建项目,每一步都有命令,但必须在pom所在目录执行
    • maven有传递性和依赖性

Redis

  • 现在网站的特点:高并发、高容量、高效存储、高扩展、高可用
  • nosql—>Redis,nosql还有其他产品
    4
  • Redis的详细学习笔记,这里做一些补充
  • Redis是键值对存储,但是这个值有很多种类型!
  • 关于Redis**单线程(进程)**模型的理解
    • 因为非关系型数据库不存在约束,所以单线程多进程的集群非常合适
    • 具体问题结合场景理解,就不瞎猜了

事务管理

  • Redis服务类似前端的Ajax引擎;处于应用和数据库之间,提供高效访问
  • Redis是单线程的,所有的命令都会先放入一个队列中,但一个Redis服务器可能同时连接多个客户端,命令的执行顺序是不确定的,所以有redis事务,将一串命令当做整体加入队列,一起执行
  • 使用事务,multiexec命令
    127.0.0.1:6379> multi
    OK
    127.0.0.1:6379> set bookname java
    QUEUED
    127.0.0.1:6379> set bookname c++
    QUEUED
    127.0.0.1:6379> set bookname html
    QUEUED
    127.0.0.1:6379> exec
    1) OK
    2) OK
    3) OK
    

哨兵模式

  • 给集群分配一个站岗的,对Redis系统的运行情况监控,它是一个独立进程
  • 功能:监控,选举主机
  • 环境准备:一主两从,启动任一从机时,启动哨兵模式
    • Redis需要编译安装,安装后指定位置下会产生一个bin目录,这个机器就是redis server了
    • 我们复制两个安装后的redis,编辑配置文件,启动,当做从机(多进程模拟多机器,这也是发挥多核优势的方法)
    bind ip
    port 6379/6380/6381
    daemonize yes
    replicaof masterip masterport	# 从机需要配置
    
  • 启动服务器并查看主从信息
    cd Redis/
    ./bin/redis-server redis.conf	# 从机也这样启动
    ./bin/redis-cli -h ip -p 6379	# 连接客户端
    xxx> info replication
    
  • 在任一从机bin目录下新增文件 sentinel.conf
    sentinel monitor mastername ip 6379 1
    # mastername 监控主数据的名称,自定义
    # 1:最低通过票数	选举时只要有1骗票就可以成为主机
    
  • 将日志写入指定文件,然后启动哨兵
    ./redis-sentinel ./sentinel.conf >sent.log &	# 后台单独进程运行
    # 启动前确保主从正常运行
    ./redis-server sentinel.conf --sentinel
    
  • 虽然启动报错,但是主机宕机仍然能选举成功?
    8
  • kill掉master的进程,6381被选举,keys *查看数据都在
    9
  • 查看日志sent.log:
    10
  • 查看配置文件,已经被重写:
    11
    • 注:如果需要再次启动哨兵,需要删除myid唯一标示(最好是根据目前情况,重新配置一遍,不然就冲突了)
    • Ubuntu20.4切换到root用户需要:passwd root 重置密码
    • virtualbox使用host-only设置网络:cd /etc/netplan,默认是NAT-DHCP模式(没有下图ethernets部分),但不能连secureCRT所以换一下12
    • 参考链接,host-only不能上Internet,还要在主机网络连接中配置 VirtualBox Host-Only Network
    • 改为桥接模式,相当于和主机平起平坐,需要和主机的Internet在同一网段;可以再添加一块网卡搞成NAT模式(对应VMnet8)
      14
      13
  • 和主机平起平坐,需要虚拟机网卡使用DHCP自动获取IP,将主机视为交换机,然后搭建网桥br0接入主机
  • 还需要桌面点击一下Wired
  • secureCRT连不上Ubuntu20.4,秘钥不对,只能升级到8.5.3,烦死了

搭建集群

  • 集群是扩展Redis单线程的最好方式,同时让多台(master)机器处理指令,整个集群当做一个redis server(注意区分集群和主从
  • 搭建集群至少需要三台主机(master),每台再配一个从机,至少要六台机器
  • 编写脚本启动集群
    cd 7001
    ./bin/redis-server ./redis.conf
    cd ..
    cd 7002
    ./bin/redis-server ./redis.conf
    cd ..
    cd 7003
    ./bin/redis-server ./redis.conf
    cd ..
    cd 7004
    ./bin/redis-server ./redis.conf
    cd ..
    cd 7005
    ./bin/redis-server ./redis.conf
    cd ..
    cd 7006
    ./bin/redis-server ./redis.conf
    cd ..
    
  • 进入某一台主节点启动集群
    ./redis-cli --cluster create 192.168.100.68:7001 192.168.100.68:7002 192.168.100.68:7003 192.168.100.68:7004 192.168.100.68:7005 192.168.100.68:7006 --cluster-replicas 1
    
    14
    16
    • 主从自动分配好了,一主带一从
  • 输出以上信息证明集群启动成功
  • 连接集群测试,从任何一个节点连接都可以(集群对外是一个整体,内部分摊请求)
    17
    • 跳转到另外的节点上存储
  • 连接从节点也可写入,但一定会跳到它对应的主节点写入
    18
    • 三个主节点保存的key(槽)互通
    • 底层hash结构,键唯一
    • 这里的客户端就是我们的响应浏览器请求的服务器,槽数据怎么查?三个节点同时查(发给不同机器)!
    • 命令的执行顺序怎么保证呢?因为键是唯一的,所以同一键值的设置只会在同一节点,后来的命令肯定后执行(相当于先查后改)
  • 使用jedis连接集群
    public class TestRedis {
        @Test
        public void test01() throws IOException {
    // 创建一连接,JedisCluster对象,在系统中是单例存在
            Set<HostAndPort> nodes = new HashSet<HostAndPort>();
            nodes.add(new HostAndPort("192.168.100.68", 7001));
            nodes.add(new HostAndPort("192.168.100.68", 7002));
            nodes.add(new HostAndPort("192.168.100.68", 7003));
            nodes.add(new HostAndPort("192.168.100.68", 7004));
            nodes.add(new HostAndPort("192.168.100.68", 7005));
            nodes.add(new HostAndPort("192.168.100.68", 7006));
            JedisCluster cluster = new JedisCluster(nodes);
            // 执行JedisCluster对象中的方法,方法和redis指令一一对应。
            cluster.set("test1", "test111");
            String result = cluster.get("test1"); // 键存在没关系,覆盖!
            System.out.println(result);
    
            //存储List数据到列表中
            cluster.lpush("site-list", "java");
            cluster.lpush("site-list", "c");
            cluster.lpush("site-list", "mysql");
    
            // 获取存储的数据并输出
            List<String> list = cluster.lrange("site-list", 0 ,2);
            for(int i=0; i<list.size(); i++) {
                System.out.println("列表项为: "+list.get(i));
            }
    
            // 程序结束时需要关闭JedisCluster对象
            cluster.close();
            System.out.println("集群测试成功!");
        }
    }
    
  • 关闭集群就是kill所以进程?
  • shutdown掉某个节点,启动后加不回来?从机顶上了(7001没了)
    19
    • ./bin/redis-cli -c -h 192.168.100.68 -p 7001 shutdown

缓存雪崩

  • 一般分为虚拟机缓存,分布式缓存,数据库缓存
  • 一般查找缓存的步骤:
    1
  • 缓存雪崩:原本访问Redis缓存的请求,因为缓存到期失效,同一时间高并发访问数据库,把数据库搞坏
    2
  • 解决方案:
    • 缓存失效后,通过加锁或者队列来控制请求数据库的线程数量
      • 比如某个key失效了,但只允许查询次key的其中一个线程查询数据库和写缓存,其他线程等待
      • 虽然缓解了数据库压力,但降低了系统的吞吐量
    • 分析用户的行为,不同的key设置不同的过期时间,让缓存失效的时间点尽量均匀

缓存穿透

  • 用户查询数据,在数据库没有,自然在缓存中也不会有,用户每次查询这个key的时候,在缓存中找
    不到,每次都要去数据库再查询一遍,然后返回null
  • 解决方案:
    • 如果查询数据库也为空,直接设置一个默认值存放到缓存,这样第二次到缓冲中获取就有值了,而不会继续访问数据库,这种办法最简单粗暴
    • 或者存成null,这部分key单独设置缓存区域,每次查询的key进行预先校验,然后再放行给后面的正常缓存处理逻辑
      • 在给对应的key存放真值的时候,需要先清除对应的之前的空缓存

缓存击穿

  • 对于热点key,一旦失效会引起高并发访问数据库
  • 解决方案:
    • 使用锁,单机用synchronized,lock等,分布式用分布式锁
    • 缓存过期时间不设置,而是设置在key对应的value里。如果检测到存的时间超过过期时间则异步更新缓存
  • 和雪崩的区别在于:雪崩时多个key同时失效,击穿是热点key失效;但都会引起高并发访问数据库

分布式锁

  • 锁的根本目的是实现资源访问的有序,处理竞争
  • 锁主要分为三种
    • 线程锁:一个进程中(也可以说是在同一个JVM中,一个JVM一个进程)的多个线程,依靠共享内存实现加锁;(比如synchronized是共享对象头,显示锁Lock是共享某个变量(state))
    • 进程锁:进程之间是可以通信的,也是可以竞争相同的其他资源的;本质是各进程中线程的竞争,这就是一个机器上多个JVM之间了,这就要到共享资源出设置锁(超出进程内,多个进程间的事情不好把控)
    • 分布式锁:每个机器一个JVM进程,多个机器(进程),同时访问某个机器
  • 分布式锁可以基于很多种方式实现,比如zookeeper、redis
    • 基本原理相同:用一个状态值表示锁,对锁的占用和释放通过状态值来标识
    • 那么这个状态值的更新算法就很重要了!
  • Redis中的两个重要命令
    • SETNX key value,若此key 已经存在,则 SETNX 不做任何动作(SET if Not eXists),设置成功返回 1
    • GETSET key value,先获取key对应的value值,若不存在则返回nil,然后将旧的value更新为新的value
  • 分布式锁原理
    • 同一时刻只能有一个进程获取到(某个共同约定的key的操作权),执行加锁原语:SETNX lock.foo <current Unix time + lock timeout + 1>,这里设置了超时时间
      • 如果 SETNX返回 1 ,说明客户端(某进程)已经获得了锁,之后可以通过 DEL lock.foo 来释放锁(不删除就不能设置新值,即使过期)
      • 如果 SETNX返回 0 ,可以进入一个重试循环
    • 释放锁:锁信息必须是会过期超时的,不能让一个线程长期占有一个锁而导致死锁
    • 流程大致如图
      3
  • 问题:检测到某个持有锁的进程超时了,其他进程通过DEL方法获取锁,然后SETNX,这就存在一个竞态,删除这个锁的人就能获取这个锁(DEL权利太大)
  • 所以,其他进程要使用getset方法,DEL只能由持有锁的人使用!!!(解铃还须系铃人)
    • 先GET,如果过期了,执行:
    • GETSET lock.foo,此时拿到的时间戳如果仍然是超时的,那就说明如愿以偿拿到锁了(没人重置过期时间)
    • 如果有人快一步执行了上面的操作,那么拿到的时间戳肯定是个未超时的值,需要再次等待或重试
    • 也就是说getset这个命令会判断过期,然后决定是否把锁给你;但是你执行getset会重置持有锁进程的过期时间(如果一直有人getset岂不是永不过期?)
  • 还有个问题
    • 持有锁的客户端在解锁之前应该再检查一次自己的锁是否已经超时,再去做DEL操作
    • 因为可能客户端因为某个耗时的操作而挂起,操作完的时候锁因为超时已经被别人获得,这时就不必解锁了

Spring框架

  • 接下来学SSM框架中的spring
  • 框架是不完整的,是为扩展而设计的;和实现某个功能的类库不是一回事
  • Spring具有控制反转(IoC)和面向切面(AOP)两大核心
  • Java Spring 框架通过声明式方式进行事务管理,提高开发效率和质量
  • Spring 框架还是一个超级粘合平台,除了自己提供功能外,还提供粘合其他技术和框架的能力

体系结构

  • spring框架提供大概20个模块
    5
  • 打开官网,一大堆spring相关,也就是spring全家桶,我们先从最原始的Spring Framework开始

IoC

  • Ioc—Inversion of Control,即“控制反转”
    • 创建对象不是程序员来new了,而是交给spring框架完成
    • 类似tomcat容器自动帮我们创建Servlet对象
  • 不是什么技术,而是一种设计思想
    6
  • 后面的spring项目都放在一个empty工程下,每个项目通过new module创建
    6
  • 新建module,maven添加spring依赖,创建bean、配置文件、测试类
    public class Test01 {
        @Test
        public void test01() {
            String configPath = "application.xml";
            // 根据文件中配置的bean将需要的对象创建好
            ApplicationContext applicationContext = new ClassPathXmlApplicationContext(configPath);
            // 也可以使用BeanFactory,但是不常用
            Roy roy = (Roy)applicationContext.getBean("roy1");  // xml中定义
        }
    }
    
    7
  • 看一下配置文件,这个.xsd文件限定了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">
    <!--    相当于:springMap(id, "对象")
            将指定的对象放入容器中-->
        <bean id="roy1" class="vip.roykun.pojo.Roy"></bean>
    <!--    可以放入非自定义对象-->
        <bean id="date" class="java.util.Date"></bean>
    </beans>
    
    8
    • 设置scope;指定初始化和销毁方法;是否懒加载,区别如下
    • 使用有参构造创建对象
    • 使用工厂方法创建对象(静态/非静态),都不懒
    <?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">
        <!--相当于:springMap(id, "对象"), 将指定的对象放入容器中-->
    
        <!--singleton:默认值,单例:在容器启动的时候就已经创建了对象,而且整个容器只有为一个
    的一个对象 lazy-init默认是false 不懒-->
        <!--lazy-init=false 不延迟创建对象,容器加载的时候立即创建
                      true:默认不加载,使用对象的时候才去创建对象-->
        <bean id="roy1" class="vip.roykun.pojo.Roy" scope="singleton" lazy-init="true" init-method="init" destroy-method="destroy"></bean>
        <!--prototype:多例,在使用对象的时候才创建对象(getBean就是使用),每次都创建新的对象
        lazy-init默认是true,懒-->
        <bean id="roy2" class="vip.roykun.pojo.Roy" scope="prototype"></bean>
    
        <!--使用有参构造创建  实体类要有对应的方法-->
        <bean id="roy3" class="vip.roykun.pojo.Roy">
            <constructor-arg name="name" value="roy3"/>
            <constructor-arg name="age" value="18"/>
        </bean>
    
        <!--使用工厂静态方法创建对象-->
        <bean id="roy4" class="vip.roykun.pojo.MyFactory" factory-method="factory"></bean>
        <!--使用工厂非静态方法创建-->
        <bean id="factory" class="vip.roykun.pojo.MyFactory"></bean>
        <bean id="roy5" factory-bean="factory" factory-method="instance"></bean>
    
        <!--可以放入非自定义对象-->
        <bean id="date" class="java.util.Date"></bean>
    </beans>
    
    public class MyFactory {
        public static Roy factory() {
            System.out.println("static");
            return new Roy();
        }
    
        public Roy instance() {
            System.out.println("instance");
            return new Roy("roy4", 18);
        }
    }
    
    public class Test01 {
        @Test
        public void test01() {
            String configPath = "application.xml";
            // 根据文件中配置的bean,将需要的对象创建好,并放入容器
            ApplicationContext applicationContext = new ClassPathXmlApplicationContext(configPath);
            Roy roy1 = (Roy)applicationContext.getBean("roy1");  // xml中定义
            // Roy roy2 = (Roy)applicationContext.getBean("roy2");  // xml中定义
    
            // 非自定义对象
            Date date = (Date) applicationContext.getBean("date");
            System.out.println(date);
    
            // 获取容器中的对象个数
            int beanCount = applicationContext.getBeanDefinitionCount();
            System.out.println("容器中对象个数:"+beanCount);
            // 容器中对象名称
            String[] beanNames = applicationContext.getBeanDefinitionNames();
            for (String name : beanNames) {
                System.out.println(name);
            }
    
            // 会触发destroy方法
        
        }
    }
    
  • 这部分就是IoC最原始的实现方式

DI

  • dependency injection 依赖注入
  • IoC是一种思想,DI是它的具体实现,我们上面的factory-bean其实就是这个意思
    • 对象的创建,由容器完成
    • 初始化属性也由容器完成,称为注入
    • 通过配置完成,不用直接导包来new,就松耦合了!
  • 属性的注入有三种方式,例如我们给service注入dao(service类中有dao这个属性)
    • set方法注入(常用
    • 使用构造方法注入
    • 自动注入:byName(属性名称存在)、byType(属性类型唯一)
    <?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="dao" class="vip.roykun.dao.RoyDao"></bean>
        <!--set注入-->
        <bean id="service" class="vip.roykun.service.RoyService">
            <!--name=属性名,引用了dao-->
            <property name="royDao" ref="dao"></property>
        </bean>
    
        <!--使用构造方法注入属性-->
        <bean id="service2" class="vip.roykun.service.RoyService">
            <constructor-arg name="royDao" ref="dao"></constructor-arg>
        </bean>
    
        <!--自动注入:byName-->
        <bean id="royDao" class="vip.roykun.dao.RoyDao"></bean>
        <!--我需要一个royDao属性,正好上面bean的id是这个名字  缺点:必须有和属性同名的声明-->
        <bean id="service3" class="vip.roykun.service.RoyService" autowire="byName"></bean>
        <!--自动注入:byType  缺点:这个xml中只能有一个royDao类型的声明,但现在有 dao和royDao,于是报错-->
        <bean id="service4" class="vip.roykun.service.RoyService" autowire="byType"></bean>
    </beans>
    
    • 注意区别属性和引用属性
    • 例如上面的,普通属性可以使用namevalue设置
    • 引用属性使用ref
    public class TestDi {
        @Test
        public void test01() {
            String configPath = "di.xml";
            // 根据文件中配置的bean,将需要的对象创建好,并放入容器
            ApplicationContext applicationContext = new ClassPathXmlApplicationContext(configPath);
            RoyService royService = (RoyService) applicationContext.getBean("service");
            royService.add();
    
            RoyService royService2 = (RoyService) applicationContext.getBean("service2");
            royService2.add();
    
            RoyService royService3 = (RoyService) applicationContext.getBean("service3");
            royService3.add();
        }
    }
    

注解

  • 通过注解实现IoC,有点像web的路由注解
  • 上面的DI:类自动创建,属性的初始化有三种方式
  • 这里将类的创建通过注解交给容器,属性的初始化也通过注解
  • 先看类的创建Component是万能注解,但最好各层分开;最后需要xml中指定扫描
  • value参数就是xml中bean id 的值,方便从容器中get到
    // dao层用Repository
    @Repository(value = "royDao")
    public class RoyDao {
        RoyDao() {
            System.out.println("roy dao--------");
        }
    
        public void add() {
            System.out.println("dao add-----");
        }
    }
    
    // 这个value如果不写,就是类名,但小写字母开头
    @Service
    public class RoyService {
        private RoyDao royDao;
    
        // service调用dao
        // servlet最后调用service
        public void add() {
            royDao.add();
            System.out.println("service add----");
        }
    
        RoyService() {
            System.out.println("roy service -------");
        }
    }
    
    @Repository(value = "royDao")
    public class RoyDao {
        RoyDao() {
            System.out.println("roy dao--------");
        }
    
        public void add() {
            System.out.println("dao add-----");
        }
    }
    
  • 这个xml文件要注意了,需要增加context约束
    <?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:context="http://www.springframework.org/schema/context"
           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:component-scan base-package="vip.roykun.dao"></context:component-scan>
        <context:component-scan base-package="vip.roykun.service"></context:component-scan>
        <context:component-scan base-package="vip.roykun.controller"></context:component-scan>
    
    </beans>
    
    • 这个context可以直接写父包 vip.roykun,或者将多个要扫描的包写在一个context,分号隔开
  • 再看属性的初始化,通过在属性上加@Value注解实现;传入的值都是String,可以加到set方法上
    @Component("teambean")
    public class Team {
        @Value("Allen")
        private String name;
    
        private Integer age;
    
        public Team() {
        }
    
        public Team(String name, Integer age) {
            this.name = name;
            this.age = age;
        }
    
        @Override
        public String toString() {
            return "Team{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        @Value("18")
        public void setAge(Integer age) {
            this.age = age;
        }
    }
    
    • 可以直接加在set方法上
  • 再看引用属性的初始化,使用@Autowired()实现byType注解
    • require参数默认是true,表示引用必须存在
  • 或者再配合@Qualifier实现byName注解
    @Service
    public class TeamService {
        @Autowired()    // required = false
        @Qualifier("teamDao1")
        private TeamDao teamDao;
    
        public TeamService() {
            System.out.println("team service");
        }
    
        public TeamService(TeamDao teamDao) {
            this.teamDao = teamDao;
        }
    }
    
    • 和DI的类似,如果指定名字,这里用到dao层,需要那里的注解设置id
    @Repository("teamDao1")
    public class TeamDao {
        public TeamDao() {
            System.out.println("team dao");
        }
    }
    
  • 引用属性的初始化还可以通过@Resoource,Spring提供了对 jdk中@Resource注解的支持,默认按名称的方式注入
    @Controller
    public class TeamServlet {
        @Resource(name = "teamService1")
        private TeamService teamService;
    
        public TeamServlet() {
            System.out.println("team servlet");
        }
    
        public TeamServlet(TeamService teamService) {
            this.teamService = teamService;
        }
    
        public void add() {
            teamService.add();
        }
    }
    
    • 默认如果name没有匹配的就找type;如果指定了name/type,就只会按照指定的参数注入,找不到就会报错
    • 所以在service层要加上@Service(value = "teamService1")
  • 小结
    • spring IoC的实现有三类,但都需要resources/下配置xml实现
    • 原始方式:直接定义bean,指定类的路径,将创建对象、初始化属性的权利交给容器;可以使用工厂方法完成一切
    • DI方式:直接定义bean,指定类的路径,将创建对象的权利交给容器,但属性和引用属性的设置有三种方式
    • 注解方式:不直接定义bean,给各层类加注解,将创建对象的权利交给容器,属性的初始化用@Value,引用属性的初始化有两种方式;xml中指定要扫描的包即可
  • 下面学AOP
  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Roy_Allen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值