Spring基础之IOC、DI、注解

一、 Spring IOC

1. 项目结构

myprojects 文件夹(工作空间 .idea/workspace.xml)
	spring项目
	ssm项目
	jt项目

创建 新的 工作文件夹
D:\Workspaces\myprojects
idea中使用 file -> open,打开该文件夹

创建 module
spring_1_base
spring_demo_1_ioc
spring_demo_2_factory

什么是框架
将公共的模块(功能)进行高级的抽取形成通用的代码包(jar)
使用:引入相应的jar包(class/工具方法),就可以使用框架的功能
实际意义:
简化了项目的开发,并且更好的扩展软件功能

2. Spring框架

​ https://spring.io/
​ Spring Framework 为基于 Java 的现代企业应用程序提供了一个全面的编程和配置模型——在任何类型的部署平台上。

  • Spring 的一个关键元素是应用程序级别的基础设施支持:Spring 专注于企业应用程序的“管道”,因此团队可以专注于应用程序级别的业务逻辑,而无需与特定部署环境建立不必要的联系。

  • 由Rod Johnson创建的一个开源框架
    Spring框架是一个开放源代码的J2EE应用程序框架,
    由Rod Johnson发起,是针对bean的生命周期进行管理的 轻量级容器(lightweight container)。
    Spring解决了开发者在J2EE开发中遇到的许多常见的问题,提供了功能强大IOC、AOP及Web MVC等功能。Spring可以单独应用于构筑应用程序,也可以和Struts、Webwork、Tapestry等众多Web框架组合使用,并且可以与 Swing等桌面应用程序AP组合。

  • 因此, Spring不仅仅能应用于J2EE应用程序之中,也可以应用于桌面应用程序以及小应用程序之中。
    Spring框架主要由七部分组成,分别是 Spring Core、 Spring AOP、 Spring ORM、 Spring DAO、Spring Context、 Spring Web和 Spring Web MVC。

3. 控制反转IOC

  1. 创建对象,维护对象关系的代码编写,原先是由程序员明确编写
  2. 现在,交给第三方去完成(ApplicationMain.java)
  3. 现在,交给第三方框架来完成(Spring框架)
  4. 对象创建的控制,由自己,转交给别人了!即:控制反转IOC
  • 面向对象编程中的一种设计原则
    控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。
    其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。
    IOC是控制反转,是思想:创建对象、维护对象之间的(依赖)关系。
    DI是依赖注入,是实现方式:构造参数值注入,set方法注入

使用IOC的技术,降低了对象之间的耦合度,
后期开发迭代时,易于修改和维护。

4. Spring容器启动流程(bean的创建和获得)

  1. Spring程序启动时,读取xml配置文件并解析

  2. 当解析到标签时,会使用反射机制实例化对象

    <bean id="user1" class="com.jt.pojo.User" />
    
  3. 将实例化好的对象,保存到Spring容器中超大的Map中 id属性中的值,作为key;反射实例化出的对象作为value

  4. 从容器中获取对象,就是从Map中通过id获取即可 User user = (User)contxt.getBean(“user1”);

IDEA中配置spring配置文件
打开 project structure
facets
选择 spring 功能中对应的工程
点击右侧 加号,选择工程中的application.xml配置文件
确定即可

5.工厂模式的三种方式

5.1 创建bean:静态工厂模式

StaticFactory类:

public class StaticFactory {
    public static Calendar getCalendar(){
        return Calendar.getInstance();
    }
}

application.xml配置文件

<bean id="cal1" class="com.jt.factory.StaticFactory" 
					factory-method="getInstance"/>
  • 静态方法的特点:
    静态方法调用时,通过类名直接调用
  • 静态属性:
    在内存中,静态属性只有一份
5.2 创建bean:实例化工厂模式

InstanceFactory类:

public class InstanceFactory {
    public Calendar getCalendar(){
        return Calendar.getInstance();
    }
}

application.xml配置文件

<!-- 工厂的bean实例化-->
<bean id="instanceFactory" class="com.jt.factory.InstanceFactory"/>
<!-- 调用工厂中的某个方法进行对象实例化-->
<bean id="calendar" factory-bean="instanceFactory" factory-method="getCalendar"/>
工厂是一个实例对象	??<bean id="" class=""/>
调用:对象.普通方法() ??<bean factory-method=""/>

创建bean:使用Spring工厂创建bean
以上两种的工厂,是别人提供了,无法修改,只能按照指定方式引入使用。

5.3 使用spring工厂

可按照Spring中 FactoryBean 的要求,创建Spring工厂,优点:声明简单;默认是单例的;

步骤1:编写工厂 MySpringFactory implmenets FactoryBean

public class MySpringFactory implements FactoryBean<Calendar> {
    @Override
    public Calendar getObject() throws Exception {
        return Calendar.getInstance();
    }

    @Override
    public Class<?> getObjectType() {
        return Calendar.class;
    }

    //默认情况下spring容器中都是单例对象
    //pojo中封装的是数据,张三用户,李四用户
    @Override
    public boolean isSingleton() {
        return true;
    }
}

步骤2:配置使用

<bean id="cal3" class="com.jt.factory.MySpringFactory" />

6. 单例/非单例

默认情况下Spring容器中都是单例对象(节省空间)

  • 非单例:
    pojo中封装的是数据,张三用户 李四用户 User类不是单例的
    数据源连接(java.sql.Connection)
    验证码对象

  • service.方法 dao.方法 都是算法, 届时会在自己的线程中,运行相应的算法,因此 Spring 默认java bean都是 单例的

  • 单例/非单例 通过scope属性来设定
    scope=“singleton” 默认值 单例
    scope=“prototype” 非单例(多例)

<bean id="user" class="com.jt.pojo.User"
                scope="prototype"/>
6. 1 单例对象/非单例对象创建的时机
  • 单例对象:Spring容器启动时,就创建纳入管理
  • 非单例对象:
    Spring容器启动时,不会创建非单例的对象,
    当ctx.getBean()时,才创建对象,纳入容器管理,并返回该对象
    每调用一次 ctx.getBean() 方法,就会创建产生一个新的bean

7. 懒加载 机制

​ 懒加载是指,当用户需要获取对象时,容器才创建该对象
​ 非单例就是懒加载;单例不是!
​ 懒加载适用于单例方式:
​ 默认情况下Spring容器创建单例对象就被创建,
​ 可设置单例对象,推后延迟实例化!

<bean lazy-init="true" />   <!-- true开启懒加载 -->

context.getBean(User.class)的使用
一般适用于:User对象是单例的情况下
容器中,就这一个java bean,通过类型到它,返回即可

如果User配置声明,是非单例的
	会报错吗?不会
	什么结果?产生一个实例对象

8. Bean的生命周期管理

1. 实例化对象
2. 初始化操作(资源加载、配置信息加载等)
3. 对象的使用(对象中的功能/方法被调用)
4. 对象销毁(用于释放资源 )

单例对象,有完整的生命周期管理
非单例对象:随用随销
在Spring容器关闭时,没有调用非单例对象的destroy()方法
注意:init()方法,被正常被调用执行!

二、SpringDI

Spring Core 的功能:
创建对象,维护对象之间的关系

创建对象

<bean id="" class="" />
<bean id="" class="" factory-method=""/>

维护对象之间的关系

  • 手工管理对象之间的关系
Phone("ARM", "6G")  iPhone13p
		//  参数传入的时机,是在生成构造过程中
		phone = new Phone("ARM", "6G");

Computer()         // 组装台式机
	 // 参数传入的时机,可在对象创建后,使用set方法传入
	comp = new Computer();
	comp.setMainboard("技嘉");
	comp.setCpu("英特尔");
	comp.setRam("Kingston");

Student()
	stu = new Student();
	stu.setName("张三");
	stu.setAge(23);
	stu.setPhone(phone);      // phone是引用变量(reference)
	stu.setComputer(comp);    // comp是引用变量(reference)

1. Spring管理对象之间的关系

依赖注入概念:

  • 将一起工作的具有关系的对象,
  • 通过 构造方法参数 或 set方法参数 传入建立关联,
  • 容器的工作是 创建bean 时,注入这些依赖关系

依赖注入DI全称是Dependency Injection,IOC是一种思想,DI是具体的实现手段

2. DI注入的方式:

构造器注入 和 Setter注入。

2.1 实战:注入基本值
  • 创建Module
    spring_demo_3_di

  • 创建com.jt.pojo包,包下Phone,Computer类的代码如下

    public class Computer implements Serializable {
        private String mainboard; //主板
        private String cpu;
        private String ram;
        public Computer(){
            System.out.println("执行computer()...");
        }
     //============================
    public class Phone implements Serializable {
        private String cpu;
        private String ram;
    
  • 配置application.xml

       <bean id="phone" class="com.jt.pojo.Phone" lazy-init="true">
           <!-- =================按照参数的类型=====================-->
            <!--<constructor-arg type="java.lang.String" value="六核cpu"/>
            <constructor-arg type="java.lang.String" value="12g内存"></constructor-arg>-->
             <!-- =================按照参数的变量名=====================-->
            <!--     <constructor-arg name="cpu" value="六核cpu"/>
                 <constructor-arg name="ram" value="12g内存"></constructor-arg>-->
            <!--
            java bean实例化,调用有参构造,注入参数值
            构造器注入方式,适合于创建对象,必须立即注入数据的场景
            即:手机出厂后,里面的零部件,必须已经装配好了
            -->
             <!-- =================按照参数的顺序下标=====================-->
            <constructor-arg index="0" value="六核cpu"/>
            <constructor-arg index="1" value="12g内存"></constructor-arg>
        </bean>
    
  • 测试

     @Test
        public void test1(){
            ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
            System.out.println("---------------");
            Phone phone = (Phone)context.getBean("phone");
            System.out.println(phone.getCpu());
            System.out.println(phone.getRam());
        }
    
2.2 实战:注入引用值ref
  • 创建com.jt.pojo.Student

    public class Student implements Serializable {
        private String name;
        private String gender;
        private Phone phone;
        private Computer computer;
    
  • 配置application.xml

      <bean id="student" class="com.jt.pojo.Student" autowire="byType">
            <property name="name" value="张三"/>
            <property name="gender" value=""/>
          <!-- ref的值来自前面配置bean的id-->
            <property name="phone" ref="phone"/>
            <property name="computer" ref="computer"/>
        </bean>
    
  • 测试

      @Test
        public void test3(){
            ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
            System.out.println("---------------");
            Student student = (Student)context.getBean("student");
            System.out.println(student.getName());
            System.out.println(student.getGender());
            System.out.println(student.getPhone());
            System.out.println(student.getComputer());
        }
    
2.3 实战:自动注入 autowire
<!--
取消ref的引用赋值,实现自动注入。autowire的取值有byName,byType 
-->
<bean id="student" class="com.jt.pojo.Student" autowire="byType">
        <property name="name" value="张三"/>
        <property name="gender" value=""/>

    </bean>
2.4 实战:注入基本值数据、引用对象、集合
  • 创建 com.jt.pojo.MessageBean

    public class MessageBean  implements Serializable {
        private String name;
        private Integer age;
        private Double salary;
        private Boolean state; //状态
        private Computer computer;
        private List<String> cities; //城市信息
        private Set<String> langs; //语言种类
        private Map<String,String> population; //人口
        private Properties grades; //分数
    
  • 配置 application.xml

    <!--基本类型的注入 集合-->
        <bean class="com.jt.pojo.MessageBean" id="messageBean">
            <property name="name">
                <value>五五</value>
            </property>
            <property name="age">
                <value>25</value>
            </property>
            <property name="salary" value="4500.9"/>
            <property name="state" value="true"/>
            <!--集合注入-->
            <!--spring会帮着先创建arraylist,将<value>中的数据加入到array里面-->
            <property name="cities">
                <list>
                    <value>杭州</value>
                    <value>张家界</value>
                    <value>长沙</value>
                </list>
            </property>
            <!--使用LinkedHashSet创建实现类-->
            <property name="langs">
                <set>
                    <value>c</value>
                    <value>c++</value>
                    <value>java</value>
                    <value>javascript</value>
                    <value>python</value>
                    <value>mysql</value>
                </set>
            </property>
            <!--HashMap-->
            <property name="population">
                <map>
                    <entry key="bj" value="2300万"></entry>
                    <entry key="sh" value="2500万"></entry>
                    <entry key="zjj" value="1200万"></entry>
                </map>
            </property>
            <!--Properties-->
            <property name="grades">
                <props>
                    <prop key="zs">优秀<![CDATA[>90]]></prop>
                    <prop key="lisi">良好<![CDATA[>=80]]></prop>
                </props>
            </property>
        </bean>
    
  • 测试

     @Test
        public void test1(){
            ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
            System.out.println("---------------");
            MessageBean msg = (MessageBean)context.getBean("messageBean");
            System.out.println(msg);
            Map<String, String> population = msg.getPopulation();
            System.out.println(population.getClass().getName());
            System.out.println(msg.getGrades().getClass().getName());
    
        }
    
2.5 实战:构建全局集合,并注入测试
  • 配置application.xml

     <!--
        将List,Set Map Properties 等集合,单独封装,纳入容器管理
        作用域由messageBean,扩展到整个spring容器
        以便更多的java bean来使用
        -->
        <util:list id="cityList">
            <value>张家界</value>
            <value>杭州</value>
            <value>上海</value>
        </util:list>
    
        <util:properties id="gradeProps">
            <prop key="zs">优秀</prop>
            <prop key="lisi">良好</prop>
            <prop key="ww">一般</prop>
        </util:properties>
    
  • 注入全局集合

    <bean id="msg2" class="com.jt.pojo.MessageBean">
            <property name="cities" ref="cityList"/>
            <property name="grades" ref="gradeProps"/>
        </bean>
    
  • 测试

     @Test
        public void test2(){
            ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
            System.out.println("---------------");
            MessageBean msg = (MessageBean)context.getBean("msg2");
            System.out.println(msg.getCities());
            System.out.println(msg.getGrades());
    
        }
    
2.6 实战:Spring表达式

使用Spring表达式,从另一个bean中引用获取数据 #{另一个bean的标识符.bean属性名}

  • 创建com.jt.pojo.ExpressionBean

    <bean id="msg2" class="com.jt.pojo.MessageBean">
            <property name="cities" ref="cityList"/>
            <property name="grades" ref="gradeProps"/>
    </bean>
    <!-- 获取id为msg2的bean的属性的值-->
    <bean id="exprBean" class="com.jt.pojo.ExpressionBean">
            <property name="name" value="#{messageBean.name}"/>
            <property name="city" value="#{messageBean.cities[2]}"/>
            <property name="grade" value="#{messageBean.grades[zs]}"/>
    </bean>
    
2.7 项目实战:创建DBCP数据源连接池

pom.xml中添加坐标:

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-dbcp2</artifactId>
    <version>2.6.0</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.37</version><!--  8.0.9-rc  -->
</dependency>

application-datasource.xml内容:

<!-- 从 db.properties 文件中加载数据到Properties集合中
	纳入Spring容器管理
 location可以从把resources目录下xx.properties文件里加载数据到properties集合中
-->

<util:properties id="dbProps" location="classpath:xxx.properties">
<!-- 配置dbcp的数据源的bean-->
<bean id="ds" class="数据源类型">
    <!-- 使用spring表达式从dbProps获取连接数据-->
	<property ... value="#{...}"/>
</bean>

测试:

/*
获取数据源对象
通过数据源对象,获得连接对象
面向接口编程,在编写代码时,尽可能写接口

在Spring容器中,按照类型查找,相应的java bean
因为BasicDataSource是DataSource的子类/实现类,所以,可以正常获取该java bean,并使用。
javax.sql.DataSource 是接口,org.apache.commons.dbcp2.BasicDataSource 是实现类
*/
 @Test
public void test1() throws SQLException {
    ClassPathXmlApplicationContext context =
        new ClassPathXmlApplicationContext("application-datasource.xml");
    System.out.println("------------");
    Properties dbProps =(Properties) context.getBean("dbProps");
    System.out.println(dbProps);
    BasicDataSource ds = (BasicDataSource) context.getBean("ds");
    System.out.println(ds.getConnection().getClass().getName());

}

2.8 对象属性 VS Bean属性

对象属性,就是指对象中的成员变量
Bean属性是指:

  • 和getXxx() 和 setXxx()相关的
  • 将get或set字符去除,Xxx首字母小写,得到的变量名,称作为bean属性名
//对象属性名 和 Bean属性名,通常都是一样的!

/*EL表达式中,取的都是bean属性值
${product.title}   => "product"->p   p.getTitle()
${product.price}   => "product"->p   p.getPrice()
*/
public class Computer implements Serializable {
    private String mainboard;  //主板
    private String cpu;
    private String ram;

    public Computer() {
        System.out.println("执行Computer()...");
    }

    // =========演示bean属性:mb=========
    public void setMb(String mb) {
        System.out.println("  set注入bean属性mb:" + mb);
        this.mainboard = mb;
    }

    public String getMainboard() {
        return mainboard;
    }

    public void setMainboard(String mainboard) {
        System.out.println("  set注入bean属性mainboard:" + mainboard);
        this.mainboard = mainboard;
    }

    public String getCpu() {
        return cpu;
    }

    public void setCpu(String cpu) {
        System.out.println("  set注入bean属性cpu:" + cpu);
        this.cpu = cpu;
    }

    public String getRam() {
        return ram;
    }

    public void setRam(String ram) {
        System.out.println("  set注入bean属性ram:" + ram);
        this.ram = ram;
    }
}

配置文件:

    <!-- 实例化时,调用无参构造器,再使用set方式注入参数值 到Bean属性中 -->
    <!-- 通过<property>标签,给bean属性赋值,
        Spring会根据name中提供的bean属性名,
        拼接出方法名,并调用:setXxx(value)  -->
    <bean id="computer" class="com.jt.pojo.Computer">
        <property name="mainboard" value="技嘉"/>
        <property name="cpu" value="英特尔"/>
        <property name="ram" value="Kingston"/>
        <property name="mb" value="华硕"/>
    </bean>

测试:

   @Test
    public void test2(){
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
        System.out.println("---------------");
        Computer computer = (Computer)context.getBean("computer");
        System.out.println(computer.getMainboard());
        System.out.println(computer.getCpu());
        System.out.println(computer.getRam());
    }
2.9 用spring中bean的思想来模拟mvc

所有的对象不要手动new,都是通过spring的容器来管理,并且解决其中的依赖关系。

pom依赖如下:

  <dependencies>
      <!-- spring所需依赖包如下-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.6</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>5.3.6</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>5.3.6</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-expression</artifactId>
            <version>5.3.6</version>
        </dependency>
      <!-- junit测试-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <!--导入dbcp连接池-->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-dbcp2</artifactId>
            <version>2.6.0</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.49</version>
        </dependency>
    </dependencies>

项目结构图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EqxcsST2-1659167248911)(Spring课堂笔记.assets/image-20220727093508322.png)]

三层架构的各个接口和其实现类:

  • UserDao:

    public class UserDaoImpl implements UserDao{
        private DataSource ds;
    
        public void setDs(DataSource ds) {
            this.ds = ds;
        }
    
        @Override
        public void insert(User user) {
            try {
                Connection connection = ds.getConnection();
                System.out.println("获得连接,插入数据成功!");
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
    }
    
    
  • UserService

    public class UserServiceImpl implements UserService {
        private UserDao userDao;
    
        public void setUserDao(UserDao userDao) {
            this.userDao = userDao;
        }
    
        @Override
        public void addUser(User user) {
            user.setName(user.getName()+"(加工数据)");
            userDao.insert(user);
        }
    }
    
    
  • UserController

    public class UserController {
    
        private UserService userService;
        private User user; //用于模拟页面请求传入的数据
    
    
        public void setUserService(UserService userService) {
            this.userService = userService;
        }
    
        public void setUser(User user) {
            this.user = user;
        }
    
        //user来自页面传递的数据
        public void addUser(){
            userService.addUser(user);
        }
    }
    
    

spirng的配置文件,配置bean和其依赖关系:

<!--配置数据源-->
    <!--通过locaiton从配置文件中读取-->
    <util:properties id="dbProps"
                     location="classpath*:db.properties"></util:properties>
    <!--构建数据源-->
    <bean class="org.apache.commons.dbcp2.BasicDataSource" id="ds">
        <property name="driverClassName" value="#{dbProps.driver}"/>
        <property name="url" value="#{dbProps.url}"/>
        <property name="username" value="#{dbProps.user}"/>
        <property name="password" value="#{dbProps.password}"/>
        <property name="initialSize" value="#{dbProps.initialSize}"/>
        <property name="maxTotal" value="#{dbProps.maxTotal}"/>
        <property name="maxWaitMillis" value="#{dbProps.maxWaitMilis}"/>
    </bean>
    <!--配置dao-->
    <bean id="userDao" class="com.jt.dao.UserDaoImpl">
        <property name="ds" ref="ds"/>
    </bean>
    <!--创建User对象,模拟从页面请求传入的数据-->
    <bean id="user" class="com.jt.pojo.User">
        <property name="name" value="jack"/>
        <property name="age" value="22"/>
    </bean>
    <!--配置service-->
    <bean id="userService" class="com.jt.service.impl.UserServiceImpl">
        <property name="userDao" ref="userDao"/>
    </bean>
    <!--配置controller-->
    <bean id="userController" class="com.jt.controller.UserController">
        <property name="userService" ref="userService"/>
        <property name="user" ref="user"/>
    </bean>

    <!--使用自动装配-->
    <!--配置controller-->
    <bean id="userController2"
          class="com.jt.controller.UserController"
          autowire="byType"
    />

测试:

  @Test
    public void testDao(){
        ClassPathXmlApplicationContext context =
                new ClassPathXmlApplicationContext("application.xml");
        System.out.println("--------");
        UserDao userDao = context.getBean(UserDao.class);
        System.out.println(userDao);
    }

    @Test
    public void testService(){
        ClassPathXmlApplicationContext context =
                new ClassPathXmlApplicationContext("application.xml");
        System.out.println("--------");
        UserService userService = context.getBean(UserService.class);
        userService.addUser(new User("tom",20));

    }

    @Test
    public void testController(){
        ClassPathXmlApplicationContext context =
                new ClassPathXmlApplicationContext("application.xml");
        System.out.println("--------");
        UserController userController = context.getBean(UserController.class);
        userController.addUser();
    }

    @Test
    public void testController2(){
        ClassPathXmlApplicationContext context =
                new ClassPathXmlApplicationContext("application.xml");
        System.out.println("--------");
        UserController userController = (UserController) context.getBean("userController2");
        userController.addUser();
    }

三、 Spring注解

spring为了简化xml配置方式,研发了注解方式。为了程序更加的严谨,使用不同的注解来标识不同的层级。

@Component表示一个组件,一个JavaBean对象,是一个通用的表示.

常见的功能分类如下:

  • Controller:标识Controller层的代码
  • Service:标识Service层的代码
  • Repository:用于标识持久层的代码

3.1 组件扫描

指定一个包路径,Spring会扫描该包及子包中的所有组件类,当发现组件类有特定的注解,被注解的类纳入容器管理.被注解的成员变量/方法,spring会帮着注入.

@Component功能,等同于

<bean id="xxx"class="com.jt.pojo.xxx"/>

默认情况下,会将类名首字母小写,作为java bean的标识符,

运行流程:

  • 当spring容器启动时,读取application.xml配置文件
  • 在配置文件里面声明了启用组件扫描功能,
  • spring会根据设定包的路径,扫描相应的注解组件
  • 根据指定的注解,完成相应的功能

创建Example类:

@Component("exp")  //明确声明javabean的标识符
//@Scope("prototype")
@Scope("singleton")  //指定是否单例还是多例
@Lazy //实现赖加载
public class Example {
    public Example(){
        System.out.println("执行 Example()...");
    }

    @PostConstruct     //构造器后,紧跟着init方法
    public void init(){
        System.out.println("执行Example.init()...");
    }
    @PreDestroy  //在Bean对象被销毁之前
    public void destroy(){
        System.out.println("执行Example.destroy()...");
    }
}

application.xml

 <!--开启包扫描-->
<context:component-scan base-package="com.jt"/>

3.2 使用注解实现 依赖注入

3.2.1 Spring提供的(推荐)

@Autowired
@Qualifier

  • 支持的注入方式:
    构造器注入、Setter注入、还可直接编写在成员变量上

    如果只有@Autowire注解:

  • 按照参数的类型,在容器中查找并注解

​ 如果容器中有多个实例对象 comp,superComputer,就会引起歧义,无法正常注入对象.

  • 如果按照类型查找,注入失败
  • 就按照参数名,在容器中查找并注入
  • @Qualifier(‘superComputer)
    当有该注解时,明确使用指定的字符串
    作为标识符在Spring容器中查找bean

代码如下:

  • 对构造器的注入

    电脑的父类和子类: 由于有两个电脑类,注入的时候需要指定需要哪个类,否则会导致歧义

    @Component("comp")
    public class Computer {
        public Computer(){
            System.out.println("执行 Computer()");
        }
    }
    //========子类===============
    @Component("superComputer")
    public class SuperComputer extends Computer {
     public SuperComputer(){
         System.out.println("执行 SuperComputer() ...");
     }
    }
    
使用构造器注入:
@Component
public class Programmer {  //程序员类
    private Computer computer;
    @Autowired
    //  @Qualifier("superComputer") 不支持直接用于构造器
    public Programmer(@Qualifier("superComputer") Computer computer) {
        System.out.println("执行Programmer(Computer computer) ");
        System.out.println("注入computer对象"+computer);
        this.computer = computer;
    }

    public Computer getComputer() {
        return computer;
    }
}

使用set方式注入:
@Component
public class Teacher { //老师类
    private Computer computer;

    public Teacher(){
        System.out.println("执行Teacher()...");
    }
    //set方式注入
    @Autowired
    public void setComputer(@Qualifier("comp") Computer computer) {
        System.out.println("注入computer对象"+computer);
        this.computer = computer;
    }

    public Computer getComputer() {
        return computer;
    }
}

成员变量直接注入

由于成员变量为私有的,可能会破坏其内部的服装性。

@Component
public class Student {
    @Autowired
    @Qualifier("comp")
    private Computer computer;

    public Student(){
        System.out.println("执行Student()...");
    }

    public void setComputer(Computer computer) {
        System.out.println("studnet中注入computer对象:"+computer);
        this.computer = computer;
    }

    public Computer getComputer() {
        return computer;
    }
}
3.2.2 jdk中提供的

@Resource

  • 支持的注入方式:

​ Setter注入、还可直接编写在成员变量上,不可用于构造器上。

代码如下:

set注入:
@Component
public class Manager {  //管理类
    private Computer computer;

    /*
    @Resource注解是JDK中提供的,使用比较简单,默认按照类型
    可以使用name属性,明确声明 注入指定的javabean
     */
    public Manager(){
        System.out.println("执行Manager() ...");
    }
    @Resource(name = "comp") //一般不写name属性,spring中默认都是单例的
    public void setComputer(Computer computer) {
        this.computer = computer;
    }

    public Computer getComputer() {
        return computer;
    }
}
成员变量注入:
@Component
public class Salesman { //销售员类
    @Resource(name = "comp")
    private Computer computer;

    public Computer getComputer() {
        return computer;
    }

    public void setComputer(Computer computer) {
        this.computer = computer;
    }
}

3.3 读取配置文件Properties

properties文件编码

  • 该文件的默认编码是ISO-8859-1
  • 框架默认情况下,就使用该编码读取
  • 如果文件中包含了中文,按照默认编码解析,一定会乱码!

解决方案,保证编码和解码一致即可:

//使用xml读取配置文件的时候
- 存ISO-8859-1
		使用工具,将汉字转换为Unicode编码
		C:\Program Files\Java\jdk1.8.0_311\bin>native2ascii.exe
		张三
		\u5f20\u4e09
		李四
		\u674e\u56db
- 取ISO-5559-1
	存取一致,不再乱码!
//使用注解读取配置文件的时候
- 存使用UTF-8(IDEA:Editor -> File Encoding  properties:UTF-8-@PropertySource(value="...", encoding="UTF-8")
3.3.1 xml读取配置文件

user.properties文件内容:

name=张三
age=23

application.xml文件内容

<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"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd">
    <context:component-scan base-package="com.jt"/>
    <!--从user.properties文件加载数据,并且创建Properties对象,把数据注入到Properties集合中 -->
    <util:properties id="uProps" location="classpath:user.properties"/>
</beans>

使用@Value将数据注入到变量中:

@Value(value=param),param的取值,可以是一个具体的数据,也可是来自已经加载的properties文件。可以使用#{}/${}获取。

#{} => spring表达式的获取方式,bean的id标识.属性名

${}=> 直接获取properties文件的key名称,来获取对应的value值

@Component
public class MessageBean {
    @Value("#{uProps.name}")
    private String name;
    @Value("#{uProps.age}")
    private String age;

    @Override
    public String toString() {
        return "MessageBean{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

3.4 @Bean,@Configuration的使用

@Bean注解,施加在方法上,当Spring扫描到@Bean注解时,帮着创建实例,并纳入容器管理

@Configuration 声明当前是一个配置类,相当于application.xml,省略了xml配置的繁琐

使用 Spring配置类 的形式,构建Spring容器:

@Configuration //声明当前是一个配置类,相当于application.xml
@ComponentScan(basePackages = "com.jt.pojo") //设定包扫描路径
//使用注解导入properties文件
@PropertySource(
        name = "uProps",
        value = "classpath:user.properties",
        encoding = "utf-8"
)
public class MySpringConfig {
    @Value("${name}")
    private String name;
    @Value("${age}")
    private Integer age;

    //使用方法名作为bean的标识符
    @Bean  //把当前返回的对象,放入容器管理
    public User user(){
        User user=new User();
        user.setName(name);
        user.setAge(age);
        return user;         //当前方法返回的对象,作为相应的bean实例
    }
}

基于注解类的测试:

public class TestMySpringConfig {
    /**
     * 使用Spring配置类的形式,构建Spring容器
     */
    @Test
    public  void test1(){
		//基于配置类的形式加载容器
        ApplicationContext context=new AnnotationConfigApplicationContext(MySpringConfig.class);
        System.out.println(context);
        System.out.println("------------");
       /* MessageBean messageBean = context.getBean(MessageBean.class);
        System.out.println(messageBean);*/
        User user = context.getBean(User.class);
        System.out.println(user.getName());
        System.out.println(user.getAge());
    }
}

3.5 反射机制

​ JVM运行的时候,是利用反射API管理类、方法、属性、对象
​ 管理类: 字节码文件Student.class,加载后对象的类名是:Class
​ 管理方法:方法的类型是Method(java.lang.reflect.Method)
​ 管理属性:属性的类型是Field(java.lang.reflect.Field)
​ 管理对象:Object、以及子类 Student Foo

反射是Java的自我管理机制,是自省机制
String.class Integer.class
反省在框架底层开发时,会被大量使用的
如:MyBatis、Spring…

@Value(“${age}”)
private Integer age;

//使用方法名作为bean的标识符
@Bean  //把当前返回的对象,放入容器管理
public User user(){
    User user=new User();
    user.setName(name);
    user.setAge(age);
    return user;         //当前方法返回的对象,作为相应的bean实例
}

}


基于注解类的测试:

```java
public class TestMySpringConfig {
    /**
     * 使用Spring配置类的形式,构建Spring容器
     */
    @Test
    public  void test1(){
		//基于配置类的形式加载容器
        ApplicationContext context=new AnnotationConfigApplicationContext(MySpringConfig.class);
        System.out.println(context);
        System.out.println("------------");
       /* MessageBean messageBean = context.getBean(MessageBean.class);
        System.out.println(messageBean);*/
        User user = context.getBean(User.class);
        System.out.println(user.getName());
        System.out.println(user.getAge());
    }
}

3.5 反射机制

​ JVM运行的时候,是利用反射API管理类、方法、属性、对象
​ 管理类: 字节码文件Student.class,加载后对象的类名是:Class
​ 管理方法:方法的类型是Method(java.lang.reflect.Method)
​ 管理属性:属性的类型是Field(java.lang.reflect.Field)
​ 管理对象:Object、以及子类 Student Foo

反射是Java的自我管理机制,是自省机制
String.class Integer.class
反省在框架底层开发时,会被大量使用的
如:MyBatis、Spring…

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值