Spring介绍和IOC的xml中<bean>配置

什么是Spring(面试):
                    Spring是一个分层的一站式的开源框架,可以将各个层集成在一起,提高开发效率,
Spring可以给我们解耦,比如平常我们要一个对象是要new一个对象,如果这个对象被成百上千次使用,那么突然有一天要换一个对象怎么办?成百上千个文件都要换?这个时候就体现出Spring的IOC的思想的好处,如果用了ioc思想就可以将想要的对象的全域名添加到<bean>中,用的时候通过id获得,然后如果想要换的时候就可以去通过修改xmld的bean的全域名去更改,这就是方便了解耦,使得我们不再频繁更改代码就可以实现更改
          

IOC思想: 也叫控制反转,由主动初始化到被动由工厂创建的这个过程就是控制反转   控制:对象的创建和销毁         反转:对象的控制权从开发者给了工厂,工厂+反色+配置文件
IOC代码思想的转变:
         

//需求:创建service层对象

/* 1. 主动创建对象 */ 
   UserServiceImpl  userService = new UserServiceImpl();
   // 存在的问题:耦合严重。   当service层类需要变动,左右两遍都要修改。


/* 2. 面向接口编程 (多态) */ 
   UserSerivce  userService = new UserServiceImpl();
   // 好处:解耦。   当dao层实现类需要变动,等号左边代码不需要修改。
   // 缺点:等号右边代码还要变动。 
       

/* 3. 反射 + 配置文件 */ 
   ResourceBundle bundle = ResourceBundle.getBundle("service");//读取配置文件
   String userServiceClassName = bundle.getString("userService");//获取配置文件中信息
   Class userServiceCalss = Class.forName(userServiceClassName);//反射获取类的Class对象
   UserService userService = (UserService) userServiceCalss.newInstance();//实例化
   // 好处: 进一步解耦。  当service层实现类需要变动,不需要修改代码(直接修改配置文件即可)
   // 缺点: 代码多。 (对程序员来讲)


/* 4. 工厂模式 (工厂+反射+配置文件) */ 
   UserService userService = (UserService) BeanFactory.getBean("userService");
   // 好处: 封装了创建对象的方法,更好的管理对象。

那么他的配置文件*.XML文件是怎么加载的呢?
                       是使用Application接口的实现类ClassPathXmlApplication去解析xml
接下来是一个简单的通过工厂创建对象的案例:
      

//测试
@Test
    public void test01() {
        //通过ClassPathXmlApplicationContext来解析xml配置文件
        ClassPathXmlApplicationContext cpxac = new ClassPathXmlApplicationContext("ApplicationContext.xml");
        //通过给出配置文件的唯一标识符id的名称来获取对象
        UserService userService = (UserService) cpxac.getBean("UserService");
        userService.test();

    }


//接口
public interface UserService {
    void test();
}

//接口的实现类
public class UserServiceImpl implements UserService {
    public void test() {
        System.out.println("这是test,开始打印这句话.....");
    }
}

他的xml文件为

<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--这里的id是唯一标识符,可以通过id的名称去获取工厂创建的的对象,也可以写name也是唯一标识,
        但是name可以有多个值,如下name中有两个值,和id用法一样但是这两个参数可以获得工厂的对象

        工厂对象创建的原理:class为全域名,用来反射
           1):Class clazz = Class.forName("com.itheima.service.impl.UserServiceImpl");
           2):UserService userService = clazz.newInstance();创建对象
           3): map.put("UserService",userService);
           创建完对象以后在将id为key对象为value田间道map中,所以下次再创建这个对象的时候会先去看map中有没有id为UserService
           如果有那么就直接去除如果没有就说明第一次创建就创建一个对象,所以他是一个单例模式
           
           class中的全域名必须有一个无参的构造方法,如果没有那么就会报
           No default constructor found; nested exception is java.lang.NoSuchMethodException: com.itheima.service.impl.UserServiceImpl.<init>()
           这个错
    -->
    <bean id="UserService" name="userService1,userService2" class="com.itheima.service.impl.UserServiceImpl"></bean>
</beans>

Spring的bean标签
          在Spring中,默认是单例模式(饿汉模式):在加载配置文件的时候就会创建这个对象,并且是同一个对象。而懒汉模式是创建的时候会创建这个对象,并且不是同一个对象。
在SPring的<bean>标签可以设置他是饿汉模式还是懒汉模式,通过scope来设置
 

 @Test
    public void test01() {
        //通过ClassPathXmlApplicationContext来解析xml配置文件
        ClassPathXmlApplicationContext cpxac = new ClassPathXmlApplicationContext("ApplicationContext.xml");
        //通过给出配置文件的唯一标识符id的名称来获取对象
        UserService userService = (UserService) cpxac.getBean("UserService");
        System.out.println("userService = " + userService);

        UserService userService1 = (UserService) cpxac.getBean("UserService");
        System.out.println("userService1 = " + userService1);
    }

通过输出可以发现
userService = com.itheima.service.impl.UserServiceImpl@36b4cef0
userService1 = com.itheima.service.impl.UserServiceImpl@36b4cef0
他们的地址值是一个,说明他们是同一个对象,这是在xml默认的情况下,而且他是在加载的时候就创建了,也就是没有调用就已经随着xml文件加载被创建
将bean中用scope去改一下他的模式
<bean id="UserService" scope="prototype" name="userService1,userService2" class="com.itheima.service.impl.UserServiceImpl"></bean>
scope有两个值:prototype懒汉       singleton:饿汉,或者不写默认就是singleton
输入结果是:
userService = com.itheima.service.impl.UserServiceImpl@7a187f14
userService1 = com.itheima.service.impl.UserServiceImpl@6f195bc3
地址值不一样的,而且用了他以后就是调用的时候才回去加载他的构造方法

Spring-IOC的生命周期:就是对象的创建到销毁的过程
          单例对象:scope="singleton"
          生命周期:
                 创建:当应用加载,创建容器的时候就被创建了
                 活着:容器在就一直活着
                 死亡:容器销毁了对象也就销毁了
          多例对象:scope="prototype"
          生命周期:
                 创建:当使用对象时创建
                  活着:对象使用就会活着
                 销毁: 对象长时间不用就会被java的gc销毁,所以他的销毁不归spring容器管
可以在bean标签中,使用init-methoddestroy-method两个属性,分别定义bean对象在初始化或销毁时要完成的工作
<bean id="UserService1" class="com.itheima.service.impl.UserServiceImpl1" init-method="init" destroy-method="des"></bean>
是表示创建UserServiceImpl1对象的时候就会调用他里面的init(_方法,如果时单例的话,当容器销毁或者关闭了就会调用des()方法,他这个方法是自己定义调用的,前提是UserServiceImpl1里有这个方法。如果是多例的话创建的时候会调用init()方法,销毁的时候不会调用,因为多例销毁不归容器管
 

Spring的IOC配置-bean对象创建方式:
       1):方式一:直接配置
       2):方式二:静态工厂
       3):方式三:实例工厂

直接配置就是之前通常的配置:
         <!-- 将UserService实现类装配到容器中 -->
        <bean id="userService" class="com.itheima.spring.bean.UserServiceImpl"/>
        通过bean标签的配置,spring会利用反射来创建这个对象并装配到容器中,通过反射创建这个对象时是默认使用类的无参构造方法来实例化bean的
        应用场景:在配置的时候,知道实现类的全限定名 (一般自己写bean)
        底层原理:无参构造 (要求类中必须有无参构造方法)
        缺点:开发者需要知道类名

方式二:静态工厂
    步骤:先写一个静态工厂类,然后里面有一个静态方法,通过静态方法去得到这个对象
     

public class UserServiceFactory {
    public static UserServiceImpl getBean(){
        return new UserServiceImpl();
    }
}
<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    
    <!--他是在UserServiceFactory有一个方法getBean得到UserServiceImpl对象
          底层:  
             1)Class clazz = Class.forName("com.itheima.Factory.UserServiceFactory");
             2) Method method = clazz.getMethod("getBean")
             3) UserService userService = method.invoke(null);
             4)map.put("UserService",userService)
             
             他是通过反射拿到factory-method="getBean"的参数也就是静态工厂的方法名称去调用他的方法,但是invoke为什么时null?
               这是因为他是静态方法,他在创建的时候就已经创建了所以时null
     -->
    <bean id="UserService" class="com.itheima.Factory.UserServiceFactory" factory-method="getBean"></bean>
</beans>

方式三:实例工厂:
      使用实例工厂的形式创建bean
 

public class InstanceFactory {
    //非静态方法
    public UserService getBean() {
        UserService userService = new UserServiceImpl2();
        return userService;
    }
}

<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--
        # 实例工厂的原理
            clazz =  Class.forName("com.itheima.factory.InstanceFactory");
            if = clazz.newInstance();
            getBean =  clazz.getMethod("getBean")
            UserService service = getBean.invoke(if);
            map.put("userService3",service);
    -->
    <!-- 实例工厂对象 -->
    <bean id="if" class="com.itheima.spring.bean.factory.InstanceFactory"/>
    <!-- UserService对象 -->
    <bean id="userService3" factory-bean="if" factory-method="getBean"/>

</beans>


依赖注入:使用Spring的容器可以降低耦合,但是不能完全消除依赖,比如一个类中有一个参数是一个自定义类型,然后想要给他赋值还要通过工厂创建对象然后去接收。注入就可以实现不去接收也可以在创建这个对象的时候去给他赋值
使用前的代码:
   

//业务层
public class UserServiceImpl3 implements UserService {
    //dao层对象
    private UserDao userDao;//成员变量

    //无参构造方法
    public UserServiceImpl3(){
        //1.加载配置文件
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");

        //2.获取Spring容器中的资源
        userDao = (UserDao) ctx.getBean("userDao");//仅仅只是让Spring创建bean对象,降低了依赖关系。  但还是需要自己书写代码接收Spring创建的bean对象。
    }

    public void save(User user) {
        //调用dao层对象中的添加用户方法
        userDao.save(user);
    }
}

常用的注入有两种方式给成员变量赋值:
           1:setter方法、    2:构造方法


   1:setter方法:
            

<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--
        set注入
        1. 原理 : 空参构造 + set方法
           clazz = Class.forName("com.itheima.service.impl.UserServiceImpl3");
           service = class.newInstance(); //
           setName = clazz.getMethod("setName")
           setName.invoke(service,"zs");
           // service.setName("zs");

        2. 配置 : bean标签内子标签property
            1). name : bean中的属性名
            2). 值
                  value : 写基本类型、字符串、包装类
                  ref: 引用类型
    -->
    <!-- UserDao对象 -->
    <bean id="userDao" class="com.itheima.spring.bean.UserDao"/>
    <!-- 日期类型 -->
    <bean id="birthday" class="java.util.Date"/>
    <!-- UserService对象 -->
    <bean id="userService4" class="com.itheima.spring.bean.UserServiceImpl3">
        <!-- 注入属性值 -->
        <property name="num" value="100"/>
        <property name="name" value="hmh"/>
        <property name="birthday" ref="birthday"/>
        <property name="userDao" ref="userDao"/>
    </bean>

</beans>
public class UserServiceImpl3 implements UserService {
    private int num; //基本类型
    private String name; //String类型
    private UserDao userDao; //dao层对象
    private Date birthday; //Date类型

    public int getNum() {
        return num;
    }
    public void setNum(int num) {
        this.num = num;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public UserDao getUserDao() {
        return userDao;
    }
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
    public Date getBirthday() {
        return birthday;
    }
    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }
}
}

这是上边修改的实现类,必须要写构造方法

 

2:构造器注入:
       

public class UserServiceImpl3 implements UserService {
    private int num; //基本类型
    private String name; //String类型
    private UserDao userDao; //dao层对象
    private Date birthday; //Date类型

    //有参构造方法
    public UserServiceImpl3(int num , String name, UserDao userDao, Date date){
        this.num = num;
        this.name = name;
        this.userDao= userDao;
        this.birthday = date;
    }
    //无参构造方法
    public UserServiceImpl3(){
    }

    public void save(User user) {
        System.out.println("成员变量 num:"+num);
        System.out.println("成员变量 name:"+name);
        System.out.println("成员变量 birthday:"+birthday);

        //调用dao层对象中的添加用户方法
        userDao.save(user);
    }
}
<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd">
  
    <!--
        构造器注入 (了解)
        1. 原理
           clazz = Class.forName("com.itheima.service.impl.UserServiceImpl3");
           contructor = clazz.getConstructor(String.class,int.class,UserDao.class,Date.class);
           service = contructor.newInstance("zs",18,userDao,myDate);
           map.put("userService44",service);

        2. 配置:bean标签的子标签constructor-arg
                name属性 : 构造方法中参数的名字
                value属性: 非引用类型值
                ref属性:   引用类型值
    -->
    <bean id="userDao" class="com.itheima.spring.bean.UserDao"/>
    <bean id="birthday" class="java.util.Date"/>
    <bean id="userService4" class="com.itheima.spring.bean.UserServiceImpl3">
        <constructor-arg name="num" value="100"/>
        <constructor-arg name="name" value="hmh"/>
        <constructor-arg name="userDao" ref="userDao"/>
        <!-- 构造方法中参数名:date -->
        <constructor-arg name="date" ref="birthday"/>
    </bean>

</beans>

集合类型的注入:

   

 原理:
         clazz = Class.forName("com.itheima.service.impl.UserServiceImpl5")
         service = clazz.newInstance();

         setList = service.getMethod("setList",List.class);

         List list = new ArrayList();
         list.add("zs");
         list.add("ls");
         list.add("ww");

         setList.invoke(service,list);// service.setList(list)
    -->

<!-- List类型数据注入 -->
<bean id="beanId" class="className">
    <property name="成员变量名">
        <list>
             <value>元素</value>
        </list>
    </property>
</bean>

<!-- Set类型数据注入 -->
<bean id="beanId" class="className">
    <property name="成员变量名">
        <set>
            <value>元素</value>
        </set>
    </property>
</bean>

<!-- Map类型数据注入 -->
<bean id="beanId" class="className">
     <property name="成员变量名">
         <map>
             <entry key="key元素" value="value元素"/>
         </map>
     </property>
</bean>

<!-- Properties类型数据注入 -->
<bean id="beanId" class="className">
    <property name="成员变量名">
         <props>
              <prop key="key元素">value元素</prop>
         </props>
    </property>
</bean>

<!-- 数组类型数据注入 -->
<bean id="beanId" class="className">
    <property name="成员变量名">
         <array>
              <value>元素值</value>
         </array>
    </property>
</bean>

Spring的IOC的EL表达式:
    就是数据引用,在配置mybatis的时候就有#{}   ${}   这两个符号,而${}这个是可以读取properties文件的值,比如   username=root    就可以通过在xml中写${username}得到他的值,他在bean中的用法就是:<bean id="beanId" class="className">
                                                <property  name="成员变量名" value="EL表达式"/>
                                    </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
        https://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--
        Spring EL
        1. ${表达式}
                  引入配置文件中的数据
        2. #{表达式}
                   强调的是把内容赋值给属性

            #{'字符串'}
            #{数字}
            #{beanId}
     如下的userDao和birthday就是bean的id也是可以引用的
    -->
    <bean id="userDao" class="com.itheima.spring.bean.UserDao"/>
    <bean id="birthday" class="java.util.Date"/>
    <bean id="userService6" class="com.itheima.spring.bean.UserServiceImpl3">
        <!-- 使用EL表达式注入属性值 -->
        <property name="num" value="#{100}"/>
        <property name="name" value="#{'hmh'}"/>
        <property name="birthday" value="#{birthday}"/>
        <property name="userDao" value="#{userDao}"/>     
    </bean>

</beans>

使用properties文件的值,这里用${}

       先准备context命名空间支持:
                     xmlns:context="http://www.springframework.org/schema/context"
      加载指定的properties   文件
                     <context:property-placeholder location="classpath:filename.properties"/>
      接下来就能使用里面的值了:
                      <property name="成员变量名" value="${propertiesName}"/>

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值