SpringIOC

Spring介绍

Spring(Spring Framework)是个开源框架,最早又Rod Johnson创建。
Spring是为了解决企业级开发而创建的

IOC概念

ioc控制反转(Inversion of Control)一种概念.思想.
指对 对象控制权 的转移,从 程序代码 本身反转到了 外部容器。
把对象的创建.初始化.销毁等交给了Spring容器去做.由容器控制对象的生命周期。
所有的类都会在spring容器中登记,告诉spring你是个什么东西,你需要什么东西,
然后spring会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其他需要你的东西。
所有的类的创建、销毁都由 spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是spring。
对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被spring控制,
IoC的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过DI(依赖注入)来实现的。
控制反转IoC 是说创建对象的控制权进行转移,以前创建对象的主动权和创建时机是由自己把控的,而现在这种权力转移到第三方,如IOC容器(ApplicationContext)或BeanFactory

DI依赖注入

(Dependency lnjection)
组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。
指程序运行过程中,动态的向某个对象提供它所需要的其他对象
  ●谁依赖于谁:应用程序依赖于IoC容器;
  ●为什么需要依赖:应用程序(容器管理对象)需要IoC容器来提供对象需要的外部资源;
  ●谁注入谁:IoC容器注入应用程序某个对象,应用程序依赖的对象;
  ●注入了什么:注入某个对象所需要的外部资源(包括对象、资源、常量数据)。
  
  如对象A要操作数据库,之前得再A中编写Connction对象,有了Spring就只要告诉Spring,A中需要一个Connection,至于怎么构造,何时构造,A不用知到。
在系统运行时Spring会在适当的时候造个Connection,就像打针一样,注射到A中,这就完成了对各个对象之间关系的控制。
A需要依赖Connection才能正常运行,而这个Connection是由Spring注入到A中的,名字就是怎么来的

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
 http://www.springframework.org/schema/beans/spring-beans.xsd">
 
<!-- 注册User对象-->
<bean class="com.sxt.pojo.User" name="user" id="user"/>
 
 </beans>

所谓的声明,就是将自己的信息告诉Spring容器,例如id和class,Spring容器根据class,
通过反射(默认使用的是无参构造方法)就可以创建一个名为User的User对象。
初始化容器
   通过调用容器中的getBean方法可以获取Spring容器中的对象 或 也可以通过类型直接获取一个Bean的实例,但如果出现多个相同实例 会报错

//通过id查找
@Test
public void test1(){
   ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
   User bean = (User) ac.getBean("userId");
   bean.sayId();
}

//通过Name查找
@Test
public void test2(){
   ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
   User bean = (User) ac.getBean("userName");
   bean.sayName();
}

//通过类型查找
@Test
public void test3(){
   ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
   //根据类型去Spring容器中查找对象 如果Spring容器中有两个及以上该对象的时候会报错
   User bean = ac.getBean(User.class);
   bean.sayClass1();
}

/**
* 通过name+类型
* 或者Id+类型
*/
@Test
public void test4(){
   ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
   <!--UserService bean = ac.getBean("userName", UserService.class); -->
   UserService bean = ac.getBean("userId", UserService.class);
   bean.sayClass2();
}

ApplicationContext和BeanFactory的区别
  ApplicationContext:在容器加载时实例化对象,常用
  BeanFactory:在使用时实例化对象,且BeanFactory已被弃用

静态工厂注入,动态工厂注入

很少用

<!-- 静态工厂配置 -->
<bean class="com.sxt.service.UserFactory" factory-method="getInstance" id="user"/>
<!-- 动态工厂配置 -->
<bean class="com.sxt.service.UserFactory" id="userFactory"/>  
<!-- 获取工厂创建的实例对象 -->
<bean factory-bean="userFactory" factory-method="getInstance2" id="user"/>
//创建   /动态工厂类
public class UserFactory {
    // 静态工厂  生产UserService对象
    public static UserService getInstance(){
        System.out.println("静态");
        return new UserService();
    }
    // 动态工厂  生产UserService对象
    public  UserService getInstance2(){
        System.out.println("动态");
        return new UserService();
    }
}

属性注入

  指如何给对象中的属性赋值

构造注入

 必须提供有参构造
三种方式:C域,属性,下标

       <!-- C域简写的构造注入 Spring2.x开始提供  页面下面Namespanes里勾选C-->
    <bean class="com.sxt.model.User" c:id="66" c:name="老王" c:address="阳关路">
    
     <!-- 属性 -->
    <constructor-arg name="id" value="123"/>
    <constructor-arg name="name" value="张三"/>
	<constructor-arg name="address" value="蓬莱东路"/>
        
     <!-- 下标 -->
    <constructor-arg index = "0" value="123"/>
    <constructor-arg index = "1" value = "hello"/>
    <constructor-arg index = "2" value = "深圳宝安"/>

设值注入

  有参构造方法不是必须的了,无参和GetSet方法是必须的!!!
  Java对象中提供对应的Set方法 就是利用对象属性的set方法给属性赋值
  相当于首先使用无参构造创建了个对象,然后调用对象中的Set方法给属性赋值

        <!-- 设值注入比构造注入灵活,设置注入是在对象new出来后再设置的 页面下面Namespanes里勾选P-->
    <bean class = "com.sxt.model.User" p:id="1234234" p:name="赵六" p:address="罗湖">
        <!-- 通过 设值注入 设置属性 -->
        <property name = "id" value="12345"/>
        <property name = "name" value = "李四"/>
        <property name = "address" value = "深圳福田"/>

对象,数组,集合,Map,Props注入

   <!--对象注入-->
    <bean class="com.sxt.model.Cat" id="cat">
         <property name = "nikeName" value = "阿猫"/>
         <property name = "color" value = "白色"/>
    </bean> 
     <bean class="com.sxt.model.User2">
        <!-- 通过设值注入设值cat属性  ref:引用Spring容器中的另一个对象 -->
        <property name = "cat" ref="cat"/>
     </bean> 
     
         <!--对象注入 另外写法-->
     <bean class="com.sxt.model.User2">
        <property name = "cat">
            <bean class = "com.sxt.model.Cat">
                <property name="nikeName" value="阿狗"/>
                <property name="color" value = "黄色"/>
            </bean>
        </property>
     </bean> 
     
    <bean class="com.sxt.model.User3">
         <!-- 数组 -->
         <property name="games">
            <array>
                <value>仰天大笑出门去</value>
                <value>我辈岂是蓬蒿人</value>
            </array>
         </property>
         
         <!-- 集合 -->
         <property name="cats">
            <list>
                <bean class="com.sxt.model.Cat">
                    <property name="nikeName" value="窦尔敦"/>
                    <property name="color" value="蓝脸"/>
                </bean>
                <bean class="com.sxt.model.Cat">
                    <property name="nikeName" value="关羽"/>
                    <property name="color" value="红脸"/>
                </bean>
                <bean class="com.sxt.model.Cat">
                    <property name="nikeName" value="典韦"/>
                    <property name="color" value="黄脸"/>
                </bean>
            </list>
         </property>
         
         <!-- Map -->
         <property name="map">
            <map>
                <entry key="name" value="曹操"></entry>
                <entry key="age" value="18"></entry>
                <entry key="address" value="深圳"></entry>
            </map>
         </property>
         
         <!--Props-->
         <property name="props">
            <props>
                <prop key="url">127.0.0.1:8080</prop>
                <prop key="userName">张飞</prop>
            </props>
         </property>
     </bean>
//常用类型
    //对象
    @Test
    public void test() {
        ApplicationContext ac = new ClassPathXmlApplicationContext("Commonly_used_type.xml");
        User2 user = ac.getBean(User2.class);
        System.out.println(user);
    }
    
    //数组
    @Test
    public void test2(){
        //ApplicationContext ac = new ClassPathXmlApplicationContext("Commonly_used_type.xml");
        BeanFactory ac = new XmlBeanFactory(new ClassPathResource("Commonly_used_type.xml"));//
        User3 user = ac.getBean(User3.class);
        String[] games = user.getGames();
        for(String g:games){
            System.out.println(g);
        }
        //集合
        System.out.println(user.getCats());
        
        //Map
        Map<String,Object> map = user.getMap();
        Set<String> keys = map.keySet();
        for(String key:keys){
            System.out.println(key+":"+map.get(key));
        }
        
        //Properties类型
        Properties props = user.getProps();
        String url = props.getProperty("url");
        String userName = props.getProperty("userName");
        System.out.println(url+":"+userName);
    }

Java配置方式

Java配置本质上,是用Java类来代替XML配置,这种方式在SpringBoot广泛使用
@Configuration:添加该注解 表示 该类和applicationContext.xml一样
@Bean注解 :和applicationContext.xmlbean标签 作用一样,默认的name是方法名称

/**
 * 实现Spring中的java配置
 *@Configuration 添加该注解 表示 该类和applicationContext.xml一样
 */
@Configuration
public class AppConfig {/**
     * 加了@Bean注解 的方法和 
     * applicationContext.xml 的 bean标签 作用一样
     * 默认的name是方法名称
     */
    @Bean ("user")
    public User getUser(){
        return new User();
    }
}

测试调用

@Test
public void test(){
     ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
     User user = (User) ac.getBean("user");
     user.say();
 }

自动配置

也叫自动注入,前面这几种配置方式,对于要使用的类都需要一个一个的配置。
可以通过自动配置来简化Bean的配置。
自动配置需要使用到如下注解:

  • @Component 用在身份不明的组件上
  • @Controller 用在Controller层(控制层)
  • @Service 用在Service层(业务层)
  • @Repossitroy 用在Dao层(数据访问层)

XML文件的方式

<!-- 开启扫描   配置扫描路径
context:component-scan:  在xml配置了这个标签后,spring可以自动去扫描base-pack下面或者子包下面的java文件,
                        如果扫描到有@Component @Controller @Service @Repository等这些注解的类,则把这些类注册为bean
                        这个标签可以写多个,还可以使用 *
base-package:  告诉Spring要扫描的路径,扫描的路径如果有多个,可以通过 ","连接     -->
    
<context:component-scan base-package="com.sxt.controller,com.sxt.dao.impl,com.sxt.service.impl"></context:component-scan>

Java配置的方式

@Configuration  
            //添加扫描
            //@ComponentScan("com.sxt.*")
@ComponentScans(value={
                @ComponentScan("com.sxt.controller"),
                @ComponentScan("com.sxt.serivce.impl"),
                @ComponentScan("com.sxt.dao.impl")
                })
public class JavaConfig {}

@Resource和@Autowired的区别
@Resource:默认通过name注入对象,JDK提供
@Autowired:只能通过类型注入对象,Spring提供,需要根据name注入对象需要和 @Qualifier 一块使用

use-default-filters
表示使用使用spring默认提供的过滤器,
false表示不使用,true则表示使用。
一般来说,
true结合exclude-filter标签使用,表示除去某个注解
false结合include标签使用,表示包含某个注解

<!-- 开启扫描   配置扫描路径
    context:component-scan:  在xml配置了这个标签后,spring可以自动去扫描base-pack下面或者子包下面的java文件,
    如果扫描到有@Component@Controller@Service@Repository等这些注解的类,则把这些类注册为bean
    这个标签可以写多个,还可以使用 " * " 
    base-package:  告诉Spring要扫描的路径,扫描的路径如果有多个,可以通过 ","连接
 -->
    
<context:component-scan base-package="com.sxt.controller,com.sxt.dao.impl,com.sxt.service.impl"></context:component-scan>

<!-- <context:component-scan base-package="com.sxt.controller"></context:component-scan>
    <context:component-scan base-package="com.sxt.dao.impl"></context:component-scan>
    <context:component-scan base-package="com.sxt.service.impl"></context:component-scan> -->
<!-- <context:component-scan base-package="com.sxt.*.dao"></context:component-scan> -->


<!--    use-default-filters
        表示使用spring默认提供的过滤器,会扫描指定包下的全部的标有注解的类,并注册成bean。
        false表示不使用,true则表示使用。一般来说,
        true结合<context:include-filter>标签使用,表示除去某个注解
        false结合<context:exclude-filter> 标签使用,表示包含某个注解 -->
            
<!-- 不使用默认的过滤器连  只适用org.springframework.stereotype.Controller -->
<context:component-scan base-package="com.sxt.controller" use-default-filters="false">
    <!-- 包含 -->
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

<context:component-scan base-package="com.sxt.service.impl,com.sxt.dao.impl" use-default-filters="true">
    <!-- 排除 -->
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

Profile

在实际开发中,项目即将上线时,可能需要不停的在开发环境、生产环境、测试环境…之间进行切换

//实现Spring中的Java配置方式
public class AppConfig {//@Profile注解相当于一个标记,标记当前dataSource是什么环境下的dataSource(数据源)
    
    //设置开发环境配置
    @Bean
    @Profile("dev")
    public MyDataSource getDevDataSource(){
        return new MyDataSource("127.0.1.1","sql", "root", "root");
    }
    
    //设置生产环境配置
    @Bean
    @Profile("pro")
    public MyDataSource getProDataSource(){
        return new MyDataSource("192.168.1.1","sql", "admin", "admin");
    }
    
    //设置生产环境配置
    @Bean
    @Profile("test")
    public MyDataSource getTestDataSource(){
        return new MyDataSource("192.1.1.1","sql", "pwd", "pwd");
    }
}
<!-- XML配置实现 -->
<!-- profile 配置必须放在整个配置文件的 底部 -->
    <beans profile="dev">
        <bean class="com.sxt.service.MyDataSource" p:url="http" p:driverName="dev" p:userName="dev" p:password="dev">
            <!-- <property name="url" value="http"/>
            <property name="driverName" value="dev"/>
            <property name="userName" value="dev"/>
            <property name="password" value="dev"/> -->
        </bean>
    </beans>
 
    <beans profile="pro">
        <bean class="com.sxt.service.MyDataSource" p:url="http" p:driverName="pro" p:userName="pro" p:password="pro">
            <!-- <property name="url" value="http"/>
            <property name="driverName" value="pro"/>
            <property name="userName" value="pro"/>
            <property name="password" value="pro"/> -->
        </bean>
    </beans> 
//测试
@Test
public void test(){
    //1、因为配置类,需重新指定模式(开发或是生产等),所以在新建annotation时,不用加载配置类
    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
    //2、使用Environment来指定当前运行的模式,指定的值必须和@Profile注解中自定义的值匹配。否则都不执行。
        //getEnvironment():获取一个环境变量对象
        //setActiveProfiles():设置一个活动的配置文件
    ac.getEnvironment().setActiveProfiles("dev");//
    //3、指定模式后,再注册Bean
    ac.register(AppConfig.class);
    ac.refresh();
    
    MyDataSource ds = ac.getBean(MyDataSource.class);
    System.out.println(ds);
}
@Test
public void test2(){
    ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
    ac.getEnvironment().setActiveProfiles("dev");
    ac.refresh();
    MyDataSource ds = ac.getBean(MyDataSource.class);
    System.out.println(ds);
}

条件注解

Profile实际上就是条件注解的一种特殊形式,即条件注解更加灵活,用户可以根据各种不同的条件使用不同的Bean。
条件注解在SpringBoot中使用非常广泛。SpringBoot中提供了许多自动化的配置,例如数据库配置,SpringBoot使用条件注解提前配置好许多常用的类,使用条件注解,在某一个条件满足时,这些配置就会生效。

//创建实例
public class LinuxShowCmd implements ShowCmd{
    @Override
    public String show() {
        return "Linux";
    }
}public class WindowsShowCmd implements ShowCmd{
    @Override
    public String show() {
        return "window";
    }
}
//定义条件
public class LinuxCondition implements Condition{
    /*
     * 匹配环境
     * true表示匹配上了
     * false表示没有匹配上
     */
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata atm) {
        String[] prof = context.getEnvironment().getActiveProfiles();
        for(String p:prof){
            if(p.contains("Linux")){
                return true;
            }
        }
        return false;
    }
}public class WindowsShowCondition implements Condition{
    /*
     * 匹配环境
     * true表示匹配上了
     * false表示没有匹配上
     */
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata atm) {
        String[] prof = context.getEnvironment().getActiveProfiles();
        for(String p:prof){
            if(p.contains("Window")){
                return true;
            }
        }
        return false;
    }
}
//Java配置文件
public class AppConfig2 {
    /**
     * 条件注解
     * @Conditional(value=LinuxCondition.class)
     * 中的LinuxCondition 的matchs 方法如果返回true 会调用该方法
     * 返回false就不会生效
     * @return
     */
    @Bean
    @Conditional(value=LinuxCondition.class)
    public LinuxShowCmd getLinuxShowCmd(){
        return new LinuxShowCmd();
    }
    
    @Bean
    @Conditional(value=WindowsShowCondition.class)
    public WindowsShowCmd getWindowsShowCmd(){
        return new WindowsShowCmd();
    }
}
//测试调用
@Test
public void test(){
    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
    ac.getEnvironment().setActiveProfiles("Windows");
    ac.register(AppConfig2.class);
    ac.refresh();
    
    ShowCmd sc = ac.getBean(ShowCmd.class);
    System.out.println(sc.show());
}

Bean的作用域

  • property:每次请求都会获取一个新的bean(Java原型模式)
  • singleton:单例模式,默认设置
  • request:一次请求中,bean与request生命周期同步
  • session:一次会话中,session生命周期同步
    在Spring的配置中,默认下,bean都是单例的(singleton)。获取多少次都是同一个bean
<!-- XML配置中 -->
<!-- 注册UserService
  scope: 对象在spring容器(IOC容器)中的生命周期,也可以理解为对象在spring容器中的创建方式。

             property:每次请求都会获取一个新的bean(Java原型模式)
             singleton:单例模式,默认设置
             request:一次请求中,bean与request生命周期同步
             session:一次会话中,session生命周期同步
 -->
<bean class="com.sxt.service.UserService" id="userService" scope="singleton"/>
//Java中配置
@Configuration  
@Scope(value="prototype")
public class JavaConfig {
    
}

混合配置

项目开发中,既有配置文件配置,又有Java配置存在,这时可以用@ImportResource 来实现
@ImportResource:可以导入一个配置文件

//Java中配置
@Configuration  
@ImportResource("classpath:applicationContext.xml")
public class JavaConfig {
    
}

其他注解

@Scope(value="prototype") // 单例和多例 修饰在类上
@Lazy //代表懒加载 修饰在类上
@PostConstruct //表示初始化操作 修饰在方法上
@PreDestroy //表示销毁 修饰在方法上

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值