Java教程

Day51~(spring注解高级IOC)51

本文主要是介绍Day51~(spring注解高级IOC)51,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

第一部分:Spring的IOC

一、设计模式-工厂模式

    工厂模式是我们最常用的实例化对象模式了,它是用工厂中的方法代替new创建对象的一种设计模式。
    我们以Mybatis的SqlSession接口为例,它有一个实现类DefaultSqlSession,如果我们要创建该接口的实例对象:SqlSession sqlSession=new DefaultSqlSession();
    可是,实际情况是,通常我们都要在创建SqlSession实例时做点初始化的工作,比如解析XML,封装连接数据库的信息等等。
     在创建对象时,如果有一些不得不做的初始化操作时,我们首先到的是,可以使用构造函数,这样生成实例就写成:SqlSession sqlSession=new DefaultSqlSession(传入配置文件的路径);
     但是,如果创建sqlSession实例时所做的初始化工作不是像赋值这样简单的事,可能是很长一段代码,如果也写入构造函数中,那你的代码很难看了(就需要Refactor重构)。
     为什么说代码很难看,初学者可能没有这种感觉,我们分析如下,初始化工作如果是很长一段代码,说明要做的工作很多,将很多工作装入一个方法中,相当于将很多鸡蛋放在一个篮子里,是很危险的,这也是有悖于Java面向对象的原则,面向对象的封装(Encapsulation)和分派(Delegation)告诉我们,尽量将长的代码分派“切割”成每段,将每段再“封装”起来(减少段和段之间耦合联系性),这样,就会将风险分散,以后如果需要修改,只要更改每段,不会再发生牵一动百的事情。
    所以,Mybatis框架在使用时为我们提供了SqlSessionFactory工厂类,通过方法获取到SqlSession对象。同时方法有很多重载,用于实现不同的需求。这个方法就是openSession(),它支持传入Connection参数来保证连接的一致性;支持传入true|false来保证事务的提交时机等等。

二、IOC和DI

1、IOC-Inversion of Control

    它的含义为:控制反转。它不是一个技术,而是一种思想。其作用是用于削减代码间的耦合。它的实现思想就是利用了工厂设计模式,把创建对象代码从具体类中剥离出去,交由工厂来完成,从而降低代码间的依赖关系。
    耦合有如下分类:
        (1) 内容耦合。当一个模块直接修改或操作另一个模块的数据时,或一个模块不通过正常入口而转入另一个模块时,这样的耦合被称为内容耦合。内容耦合是最高程度的耦合,应该避免使用之。
        (2) 公共耦合。两个或两个以上的模块共同引用一个全局数据项,这种耦合被称为公共耦合。在具有大量公共耦合的结构中,确定究竟是哪个模块给全局变量赋了一个特定的值是十分困难的。
        (3) 外部耦合 。一组模块都访问同一全局简单变量而不是同一全局数据结构,而且不是通过参数表传递该全局变量的信息,则称之为外部耦合。
        (4) 控制耦合 。一个模块通过接口向另一个模块传递一个控制信号,接受信号的模块根据信号值而进行适当的动作,这种耦合被称为控制耦合。
        (5) 标记耦合 。若一个模块A通过接口向两个模块B和C传递一个公共参数,那么称模块B和C之间存在一个标记耦合。
        (6) 数据耦合。模块之间通过参数来传递数据,那么被称为数据耦合。数据耦合是最低的一种耦合形式,系统中一般都存在这种类型的耦合,因为为了完成一些有意义的功能,往往需要将某些模块的输出数据作为另一些模块的输入数据。
        (7) 非直接耦合 。两个模块之间没有直接关系,它们之间的联系完全是通过主模块的控制和调用来实现的。
    为什么要解耦:
        耦合是影响软件复杂程度和设计质量的一个重要因素,在设计上我们应采用以下原则:如果模块间必须存在耦合,就尽量使用数据耦合,少用控制耦合,限制公共耦合的范围,尽量避免使用内容耦合。  

2、DI-Dependency Injection

    它的全称是依赖注入。是spring框架核心ioc的具体实现。
    举例说明:我们的程序在编写时,通过控制反转,把对象的创建交给了spring,但是代码中不可能出现没有依赖的情况。ioc解耦只是降低他们的依赖关系,但不会消除。例如:我们的业务层仍会调用持久层的方法。
那这种业务层和持久层的依赖关系,在使用spring之后,就让spring来维护了。
    简单的说,就是坐等框架把持久层对象传入业务层,而不用我们自己去获取。

三、Spring注解驱动开发入门

1、写在最前

spring在2.5版本引入了注解配置的支持,同时从Spring 3版本开始,Spring JavaConfig项目提供的许多特性成为核心Spring框架的一部分。因此,可以使用Java而不是XML文件来定义应用程序类外部的bean。在这里面官方文档为我们提供了四个基本注解@Configuration,@Bean,@Import,@DependsOn

ioc-1-1

2、注解驱动入门案例介绍

1.需求:
    实现保存一条数据到数据库。
2.表结构:
    create table account(
        id int primary key auto_increment,
        name varchar(50),
        money double(7,2)
    );
3.要求:
    使用spring框架中的JdbcTemplate和DriverManagerDataSource
    使用纯注解配置spring的ioc

3、案例实现

3.1、导入坐标

<dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.1.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.45</version>
        </dependency>
    </dependencies>

3.2、编写配置类

/**
 * spring的配置类
 * 用于替代xml配置
 * @author 黑马程序员
 * @Company http://www.itheima.com
 */
@Configuration
@Import(JdbcConfig.class)
@PropertySource("classpath:jdbc.properties")
public class SpringConfiguration {
}
​
​
/**
 * 连接数据库的配置
 * @author 黑马程序员
 * @Company http://www.itheima.com
 */
public class JdbcConfig {
​
    @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("jdbcTemplate")
    public JdbcTemplate createJdbcTemplate(DataSource dataSource){
        return new JdbcTemplate(dataSource);
    }
​
​
    @Bean("dataSource")
    public DataSource createDataSource(){
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(driver);
        dataSource.setUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        return dataSource;
    }
}

3.3、编写配置文件

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring_day01
jdbc.username=root
jdbc.password=1234

3.4、编写测试类

/**
 * @author 黑马程序员
 * @Company http://www.itheima.com
 */
public class SpringAnnotationDrivenTest {
​
    /**
     * 测试
     * @param args
     */
    public static void main(String[] args) {
        //1.获取容器
        ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
        //2.根据id获取对象
        JdbcTemplate jdbcTemplate = ac.getBean("jdbcTemplate",JdbcTemplate.class);
        //3.执行操作
        jdbcTemplate.update("insert into account(name,money)values(?,?)","test",12345);
    }
}

四、IOC的常用注解分析

1、用于注解驱动的注解

1.1、@Configuration

1.1.1、源码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
​
    /**
     * Explicitly specify the name of the Spring bean definition associated with the
     * {@code @Configuration} class. If left unspecified (the common case), a bean
     * name will be automatically generated.
     * <p>The custom name applies only if the {@code @Configuration} class is picked
     * up via component scanning or supplied directly to an
     * {@link AnnotationConfigApplicationContext}. If the {@code @Configuration} class
     * is registered as a traditional XML bean definition, the name/id of the bean
     * element will take precedence.
     * @return the explicit component name, if any (or empty String otherwise)
     * @see org.springframework.beans.factory.support.DefaultBeanNameGenerator
     */
    @AliasFor(annotation = Component.class)
    String value() default "";
}
1.1.2、说明
作用:
    它是在spring3.0版本之后加入的。此注解是spring支持注解驱动开发的一个标志。表明当前类是spring的一个配置类,作用是替代spring的applicationContext.xml。但其本质就是@Component注解,被此注解修饰的类,也会被存入spring的ioc容器。
属性:
    value:
        用于存入spring的Ioc容器中Bean的id。
使用场景:
    在注解驱动开发时,用于编写配置的类,通常可以使用此注解。一般情况下,我们的配置也会分为主从配置,@Configuration一般出现在主配置类上。例如,入门案例中的SpringConfiguration类上。值得注意的是,如果我们在注解驱动开发时,构建ioc容器使用的是传入字节码的构造函数,此注解可以省略。但是如果传入的是一个包,此注解则不能省略。
1.1.3、示例
    在注解驱动的入门案例中,由于没有了applicationContext.xml,就没法在xml中配置spring创建容器要扫描的包了。那么,我们自己写的一些类,通过注解配置到ioc容器中也无法实现了。此时就可以使用此注解来代替spring的配置文件。
/**
 * spring的配置类
 * 用于替代xml配置
 * @author 黑马程序员
 * @Company http://www.itheima.com
 */
@Configuration
@Import(JdbcConfig.class)
@PropertySource("classpath:jdbc.properties")
@ComponentScan("com.itheima")
public class SpringConfiguration {
}

1.2、@ComponentScan

1.2.1、源码
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
    /**
     */
    @AliasFor("basePackages")
    String[] value() default {};
​
    /**
     */
    @AliasFor("value")
    String[] basePackages() default {};
​
    /**
     */
    Class<?>[] basePackageClasses() default {};
​
    /**
     */
    Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
​
    /**
     */
    Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;
​
    /**
     */
    ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;
​
    /**
     */
    String resourcePattern() default ClassPathScanningCandidateComponentProvider.DEFAULT_RESOURCE_PATTERN;
​
    /**
     */
    boolean useDefaultFilters() default true;
​
    /**
     */
    Filter[] includeFilters() default {};
​
    /**
     */
    Filter[] excludeFilters() default {};
​
    /**
     */
    boolean lazyInit() default false;
​
​
    /**
     * Declares the type filter to be used as an {@linkplain ComponentScan#includeFilters
     * include filter} or {@linkplain ComponentScan#excludeFilters exclude filter}.
     */
    @Retention(RetentionPolicy.RUNTIME)
    @Target({})
    @interface Filter {
        /**
         */
        FilterType type() default FilterType.ANNOTATION;
​
        /**
         */
        @AliasFor("classes")
        Class<?>[] value() default {};
      
        /**
         */
        @AliasFor("value")
        Class<?>[] classes() default {};
      
        /**
         */
        String[] pattern() default {};
    }
}
1.2.2、说明
作用:
    用于指定创建容器时要扫描的包。该注解在指定扫描的位置时,可以指定包名,也可以指定扫描的类。同时支持定义扫描规则,例如包含哪些或者排除哪些。同时,它还支持自定义Bean的命名规则
属性:
    value:
        用于指定要扫描的包。当指定了包的名称之后,spring会扫描指定的包及其子包下的所有类。
    basePackages:
        它和value作用是一样的。
    basePackageClasses:
        指定具体要扫描的类的字节码。
    nameGenrator:
        指定扫描bean对象存入容器时的命名规则。详情请参考第五章第4小节的BeanNameGenerator及其实现类。
    scopeResolver:
        用于处理并转换检测到的Bean的作用范围。
    soperdProxy:
        用于指定bean生成时的代理方式。默认是Default,则不使用代理。详情请参考第五章第5小节ScopedProxyMode枚举。
    resourcePattern:
        用于指定符合组件检测条件的类文件,默认是包扫描下的  **/*.class
    useDefaultFilters:
        是否对带有@Component @Repository @Service @Controller注解的类开启检测,默认是开启的。
    includeFilters:
        自定义组件扫描的过滤规则,用以扫描组件。
        FilterType有5种类型:
            ANNOTATION, 注解类型 默认
            ASSIGNABLE_TYPE,指定固定类
            ASPECTJ, ASPECTJ类型
            REGEX,正则表达式
            CUSTOM,自定义类型
        详细用法请参考第五章第6小节自定义组件扫描过滤规则
    excludeFilters:
        自定义组件扫描的排除规则。
    lazyInit:
        组件扫描时是否采用懒加载 ,默认不开启。
使用场景:
    在注解驱动开发时,我们自己编写的类都使用注解的方式进行配置,要想让spring添加到ioc容器中,就需要使用此注解来实现组件的扫描。
细节:
    在spring4.3版本之后还加入了一个@ComponentScans的注解,该注解就是支持配置多个@ComponentScan。
1.2.3、示例
    在入门案例中,如果我们加入了dao或者记录日志的工具类,这些使用了@Component或其衍生注解配置的bean,要想让他们进入ioc容器,就少不了使用@ComponentScan
​
package com.itheima.dao.impl;
import com.itheima.dao.AccountDao;      
/**
 * @author 黑马程序员
 * @Company http://www.itheima.com
 */
@Repository("accountDao")
public class AccountDaoImpl implements AccountDao{
    //持久层开发(此处没有考虑mybatis代理dao方式或者其他持久层技术,因为不希望和其他框架技术关联)
}      
​
/**
 * spring的配置类
 * 用于替代xml配置
 * @author 黑马程序员
 * @Company http://www.itheima.com
 */
@Configuration
@Import(JdbcConfig.class)
@PropertySource("classpath:jdbc.properties")
@ComponentScan("com.itheima")
public class SpringConfiguration {
}

 

 

 

这篇关于Day51~(spring注解高级IOC)51的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!