zoukankan      html  css  js  c++  java
  • 【Spring学习笔记】IOC容器及Spring配置详解

    【Spring学习笔记】IOC容器及Spring配置详解

    IOC底层原理

    1. 什么是IOC?
      (1)Inversion of Control,控制反转,是面向对象的一种设计原则,可降低代码耦合度。
      (2)作用:把对象创建和对象之间的调用过程交给Spring进行管理,直接理解起来相对抽象,下面将通过一系列的实例来带你慢慢理解什么是Spring的IOC。
    2. IOC底层如何实现?
      (1)技术底层:xml解析、工厂模式、反射。
      (2)实例对比
    • 原始方式:直接创建对象,调用方法,耦合度高。
    • 工厂模式:创建一个工厂类Factory,在类中创建另一个类时调用工厂类中创建它的方法,但耦合度还没有降低到最低限度。
    • IOC(进一步降低耦合度,仅需要修改配置文件):
      • xml配置文件,配置创建的对象(
      • 有service类和dao类,创建工厂类
    class UserFactory {
    	public static UserDao getDao() {
    		//1. xml解析
    		String classValue = class属性值;
    		//2. 通过反射创建对象
    		Class clazz = Class.forName(classValue);
    		return (UserDao)clazz.newInstance();
    	}
    }
    

    IOC接口(BeanFactory)

    1. IOC思想基于IOC容器完成,IOC容器底层就是对象工厂。
    2. Spring提供IOC容器实现两种方式(两个接口):
      (1)BeanFactory:IOC容器基本实现,是Spring内部的使用接口,一般不提供开发人员使用。
    • 懒加载,加载配置文件时不会创建对象,在获取对象(使用)时才去创建对象。
      (2)ApplicationContext:BeanFactory接口的子接口,提供更多更强大的功能,一般由开发人员使用。
    • 饿加载,加载配置文件时就会创建对象,服务器会在启动时完成一系列耗时的动作,更常使用。
    1. ApplicationContext接口的实现类
    • FileSystemXmlApplicationContext:使用文件的系统绝对路径
    • ClassPathXmlApplicationContext:使用src下类路径
    • ConfigurableApplicationContext:包含一些相关的扩展功能。

    IOC操作Bean管理

    1. 什么是Bean管理
      (1)Spring创建对象
      (2)Spring注入属性
    2. Bean管理操作的两种方式

    1. 基于xml配置文件方式创建对象

    (1)在Spring配置文件中,使用bean标签 ,添加对应属性
    (2)bean标签中的常用属性

    id:bean对象的标识符
    class:类的全路径
    name:与id类似,可以使用特殊符号,不推荐使用
    scope:生存周期(singleton单例,默认、prototype多例、request、session、global session)
    init-method:指定初始化方法
    destroy-method:指定销毁方法

    (3)创建对象时,默认使用无参构造器完成对象的创建
    (4)property中的常用属性

    name:属性名,set方法后的字符串(开头小写)
    value:属性值
    ref:引用容器中的bean对象作为value 的类型
    赋多值时使用子标签list/map/props等,再用value/map/prop标签进行赋值

    2. 基于xml配置文件方式注入属性

    (1)DI:依赖注入,就是注入属性

    • 第一种方式:通过set方法注入,在Spring配置文件配置对象创建和属性注入
      Book.java
    package com.example.demo;
    
    public class Book {
    
    	//创建属性
    	private String bname;
    	private String bauthor;
    	//创建属性对应的set方法
    	public void setBname(String bname) {
    		this.bname = bname;
    	}
    	
    	public void setBauthor(String bauthor) {
    		this.bauthor = bauthor;
    	}
    
    	public void testDemo() {
    		System.out.println(bname + ": " + bauthor);
    	}
    	/*
    	public static void main(String[] args) {
    		Book book = new Book();
    		book.setBname("Hello World");
    	}
    	*/
    
    }
    

    bean1.xml

    <bean id="book" class="com.example.demo.Book">
    	<!-- 使用property完成属性注入 
    		name:类里面的属性名称
    		value:向属性里注入的值
    	-->
    	<property name="bname" value="Hello World"></property>
    	<property name="bauthor" value="Tuzk1"></property>
    </bean>
    

    TestSpring5.java

    public class TestSpring5 {
    	
    	@Test
    	public void testBook1() {
    		//1. 加载spring配置文件
    		ApplicationContext context = 
    			new ClassPathXmlApplicationContext("bean1.xml");
    		//2. 获取配置创建的对象
    		Book book = context.getBean("book", Bool.class);
    		
    		System.out.println(book);
    		book.testDemo();
    	}
    	
    }
    
    • 第二种方式:通过有参构造注入
      Orders.java
    public class Orders {
    	
    	private String oname;
    	private String address;
    	
    	public Orders(String oname, String address) {
    		this.oname = oname;
    		this.address = address;
    	}
    	
    }
    

    bean1.xml

    <bean id="book" class="com.example.demo.Orders">
    	<constructor-arg name="oname" value="book"></constructor-arg>
    	<constructor-arg name="address" value="China"></constructor-arg>
    	<!-- 有参构造注入,也可以通过参数索引进行注入
    	<constructor-arg index="0" value="book"></constructor-arg>
    	-->
    </bean>
    

    TestSpring5.java

    @Test
    public void testBook1() {
        //1. 加载spring配置文件
        ApplicationContext context = 
        new ClassPathXmlApplicationContext("bean1.xml");
        //2. 获取配置创建的对象
        Orders orders = context.getBean("orders", Orders.class);
    
        System.out.println(Orders);
        orders.testDemo();
    }
    
    • 第三种方式:p名称空间注入(了解,用于简化基于xml配置)
    1. 添加p名称空间在配置文件beans标签中:
    <beans xmlns="http://www.springframework.org/schema/beans"
    	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xmlns:p="http://www.springframework.org/schema/p"
    	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
    1. 在beans标签中
    <bean id="book" class="com.example.demo.Book" p:bname="九阳神功" p:bauthor="无名氏"></bean>
    

    (2)注入字面量

    1. Null值(不注入也是Null)
    <property name="address">
    	<null/>
    </property>
    
    1. 特殊符号
    <!-- 
    1. 转义字符代替:&lt;
    2. CDATA:演示如下
    -->
    <property name="address" >
    	<value><![CDATA[<<Tuzk1>>]]></value>
    </property>
    

    (3)import引入其他配置文件(分模块开发)

    <import resource="applicationContext-user.xml">
    

    3. 基于xml配置数据库连接池(以Druid为例,其他连接源类似)

    接着,下面将演示三种方式实现数据库连接池的配置,分别是:

    • 手动配置
    	@Test
        //手动创建Druid数据源
        public void test1() throws Exception {
            DruidDataSource dataSource = new DruidDataSource();
            dataSource.setDriverClassName("com.mysql.jdbc.Driver");
            dataSource.setUrl("jdbc:mysql://localhost:3306/test");
            dataSource.setUsername("root");
            dataSource.setPassword("123456");
    
            Connection connection = dataSource.getConnection();
            System.out.println(connection);
            connection.close();
        }
    
    • 使用xml配置
    	@Test
        //手动创建c3p0数据源,读取配置文件
        public void test3() throws Exception {
            ResourceBundle rb = ResourceBundle.getBundle("jdbc");
            String driver = rb.getString("jdbc.driver");
            String url = rb.getString("jdbc.url");
            String username = rb.getString("jdbc.username");
            String password = rb.getString("jdbc.password");
    
            DruidDataSource dataSource = new DruidDataSource();
            dataSource.setDriverClassName(driver);
            dataSource.setUrl(url);
            dataSource.setUsername(username);
            dataSource.setPassword(password);
    
            Connection connection = dataSource.getConnection();
            System.out.println(connection);
            connection.close();
        }
    

    这里通过引入外部配置文件来注入数据库连接源的属性,其优点是可以进一步解耦合,以下是xml和properties配置文件。
    applicationContext.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"
           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">
    
        <!-- 加载外部properties文件 -->
        <context:property-placeholder location="classpath:jdbc.properties"/>
    
        <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
            <property name="driverClassName" value="${jdbc.driver}"></property>
            <property name="url" value="${jdbc.url}"></property>
            <property name="username" value="${jdbc.username}"></property>
            <property name="password" value="${jdbc.password}"></property>
        </bean>
    <beans>
    

    jdbc.properties

    jdbc.driver=com.mysql.jdbc.Driver
    jdbc.url=jdbc:mysql://localhost:3306/test
    jdbc.username=root
    jdbc.password=123456
    
    • 使用Spring配置
    	@Test
        //Spring配置Druid数据源
        public void test3() throws Exception {
            ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
            DataSource dataSource = app.getBean(DataSource.class);
            Connection conn = dataSource.getConnection();
            System.out.println(conn);
            conn.close();
        }
    

    4. 基于注解方式实现

    通过以上的实例我们可以知道,Spring是轻代码而中配置的框架,其配置相当繁重,十分影响我们的开发效率,因此注解开发便应运而生,代替xml配置成为更受欢迎的配置方法。

    注解分为原始注解和新注解(其实只是出现时间不同),这里我们先大致了解一下Spring中常见的一些原始注解。

    注解 说明
    @Component 使用在类上用于实例化Bean。
    @Controller 使用在web层上用于实例化Bean。
    @Service 使用在service层上用于实例化Bean。
    @Reponsitory 使用在dao层上用于实例化Bean。
    @Autowired 使用在字段上用于根据类型依赖注入。
    @Qualifier 结合@Autowired一起使用根据名称进行依赖注入。
    @Resource 相当于@Autowired+@Qualifier,按照名称进行注入。
    @Value 注入普通属性。
    @Scope 标注Bean的作用范围。
    @PostConstruct 使用在方法上标注该方法是Bean的初始化方法。
    @PreDestroy 使用在方法上标注该方法是Bean的销毁方法。

    例如,我们可以在dao层的类上使用@Repository或@Component(@Controller、@Service和@Repository跟@Component的作用是一样的,但使用前面三个注解可以提高可读性,因此更推荐使用)来代替xml中的bean标签;

    @Repository("userDAO")
    public class UserDaoImpl implements UserDAO {
        public void save() {
            System.out.println("save...");
        }
    }
    
    

    在service层的类上使用@Service或@Component来代替xml中的bean标签,并使用@Autowired配合@Qualifier进行属性注入。

    //这是原本xml中配置该类的bean标签
    //<bean id="userService" class="com.water.service.impl.UserServiceImpl">
    //    <property name="userDao" ref="userDAO"></property>
    //</bean>
    @Service("userService")
    //@Scope("prototype")或@Scope("singleton"),标注其生存周期
    //@PostConstruct或@PreDestory,对应bean中的init-method和destroy-method属性(需要在该类中编写相关方法,此处略过)
    public class UserServiceImpl implements UserService {
        @Autowired	//按照数据类型从Spring容器中进行匹配,这里会自动匹配UserDAO类,可省略Qualifier,但不推荐
        @Qualifier("userDAO")	//根据id值从容器中进行匹配,主要结合@Autowired一起使用
        //@Resource(name="userDAO"),可以使用该注解代替以上注解,更推荐使用
        private UserDAO userDao;
        
        //@value主要结合SpEL使用
        @value("${jdbc.driver}")
        private String driver;
        
        public void setUserDao(UserDAO userDAO) {
            this.userDao = userDAO;
        }
    
        public void save() {
            userDao.save();
        }
    }
    
    

    当然,Spring并不能自己知道我们使用了注解,我们需要在xml配置文件中让它知道这件事,代码如下(需要引入xmlns:context,引入方式在上面已有介绍):

    <!-- Spring的组件扫描器将会扫描base-package包下可能包含注解的所有文件 -->
    <context:component-scan base-package="com.water" />
    

    但是,原始注解并不能代替xml中所有配置,于是就需要有新注解,需要使用新注解替代的配置如下:

    我们先大致了解下Spring新注解:

    注解 说明
    @Configuration 用于指定当前类是一个Spring配置类,当创建容器时会从该类上加载注解。
    @ConponentScan 用于指定Spring在初始化容器时需要扫描的包。
    @Bean 使用在方法上,标注将该方法返回值存储到Spring容器中。
    @PropertySource 用于加载.properties文件中的配置。
    @import 用于导入其他xml配置。

    然后下面用几个例子来演示一下新注解的用法:

    这里,继续以数据库连接源Druid的配置为例,我在com.water包下创建了一个config包,用来存放Spring相关配置文件。

    DataSourceConfiguration.java

    //配置组件扫描
    @ComponentScan("com.water")
    //读取properties配置文件
    @PropertySource("classpath:jdbc.properties")
    public class DataSourceConfiguration {
    
        //使用SpEL获取配置文件中的属性
        @Value("${jdbc.driver}")
        private String driver;
        @Value("${jdbc.url}")
        private String url;
        @Value("${jdbc.username}")
        private String username;
        @Value("${jdbc.password}")
        private String password;
    
        @Bean("DruidDataSource")    //Spring会将当前方法的返回值以指定名称存储到Spring容器中
        public DruidDataSource getDruidDataSource() throws PropertyVetoException {
            DruidDataSource dataSource = new DruidDataSource();
            dataSource.setDriverClassName(driver);
            dataSource.setUrl(url);
            dataSource.setUsername(username);
            dataSource.setPassword(password);
    
            return dataSource;
        }
    
    }
    

    SpringConfiguration.java

    //标志该类是Spring的核心配置类
    @Configuration
    //<import resource="" />,导入以上的配置文件
    @Import({DataSourceConfiguration.class})
    public class SpringConfiguration {
    }
    

    至此,我们已经经历了从xml配置到使用Spring注解配置的完全转换,不再需要读取之前的xml配置文件,因此在com.water.web包下的UserController.java中,我们不再需要再读取xml文件了,转而读取Spring的核心注解配置文件,代码如下:

    public class UserController {
    
        public static void main(String[] args) {
            //ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
            ApplicationContext app = new AnnotationConfigApplicationContext(SpringConfiguration.class);
            UserService userService = app.getBean(UserService.class);
            userService.save();
        }
    
    }
    

    至此,我们已经把Spring从xml配置到注解配置完整地过了一遍,文章也到此结束。
    最后,非常感谢黑马程序员的Spring学习视频。

  • 相关阅读:
    Java实现AES加密
    spring定时任务详解(@Scheduled注解)
    springBoot 项目war包部署及改为war包后资源路径错误问题
    (转)如何在maven的pom.xml中添加本地jar包
    HttpClient MultipartEntityBuilder 上传文件
    Java BigDecimal详解
    mysq带条件的分页查询数据结果错误
    jstack生成的Thread Dump日志线程 分析
    jquery将表单序列化
    java jdk动态代理学习记录
  • 原文地址:https://www.cnblogs.com/tuzkizki/p/14841052.html
Copyright © 2011-2022 走看看