工厂模式是我们最常用的实例化对象模式了,它是用工厂中的方法代替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来保证事务的提交时机等等。
它的含义为:控制反转。它不是一个技术,而是一种思想。其作用是用于削减代码间的耦合。它的实现思想就是利用了工厂设计模式,把创建对象代码从具体类中剥离出去,交由工厂来完成,从而降低代码间的依赖关系。 耦合有如下分类: (1) 内容耦合。当一个模块直接修改或操作另一个模块的数据时,或一个模块不通过正常入口而转入另一个模块时,这样的耦合被称为内容耦合。内容耦合是最高程度的耦合,应该避免使用之。 (2) 公共耦合。两个或两个以上的模块共同引用一个全局数据项,这种耦合被称为公共耦合。在具有大量公共耦合的结构中,确定究竟是哪个模块给全局变量赋了一个特定的值是十分困难的。 (3) 外部耦合 。一组模块都访问同一全局简单变量而不是同一全局数据结构,而且不是通过参数表传递该全局变量的信息,则称之为外部耦合。 (4) 控制耦合 。一个模块通过接口向另一个模块传递一个控制信号,接受信号的模块根据信号值而进行适当的动作,这种耦合被称为控制耦合。 (5) 标记耦合 。若一个模块A通过接口向两个模块B和C传递一个公共参数,那么称模块B和C之间存在一个标记耦合。 (6) 数据耦合。模块之间通过参数来传递数据,那么被称为数据耦合。数据耦合是最低的一种耦合形式,系统中一般都存在这种类型的耦合,因为为了完成一些有意义的功能,往往需要将某些模块的输出数据作为另一些模块的输入数据。 (7) 非直接耦合 。两个模块之间没有直接关系,它们之间的联系完全是通过主模块的控制和调用来实现的。 为什么要解耦: 耦合是影响软件复杂程度和设计质量的一个重要因素,在设计上我们应采用以下原则:如果模块间必须存在耦合,就尽量使用数据耦合,少用控制耦合,限制公共耦合的范围,尽量避免使用内容耦合。
它的全称是依赖注入。是spring框架核心ioc的具体实现。 举例说明:我们的程序在编写时,通过控制反转,把对象的创建交给了spring,但是代码中不可能出现没有依赖的情况。ioc解耦只是降低他们的依赖关系,但不会消除。例如:我们的业务层仍会调用持久层的方法。 那这种业务层和持久层的依赖关系,在使用spring之后,就让spring来维护了。 简单的说,就是坐等框架把持久层对象传入业务层,而不用我们自己去获取。
spring在2.5版本引入了注解配置的支持,同时从Spring 3版本开始,Spring JavaConfig项目提供的许多特性成为核心Spring框架的一部分。因此,可以使用Java而不是XML文件来定义应用程序类外部的bean。在这里面官方文档为我们提供了四个基本注解@Configuration,@Bean,@Import,@DependsOn
1.需求: 实现保存一条数据到数据库。 2.表结构: create table account( id int primary key auto_increment, name varchar(50), money double(7,2) ); 3.要求: 使用spring框架中的JdbcTemplate和DriverManagerDataSource 使用纯注解配置spring的ioc
<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>
/** * 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; } }
jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/spring_day01 jdbc.username=root jdbc.password=1234
/** * @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); } }
@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 ""; }
作用: 它是在spring3.0版本之后加入的。此注解是spring支持注解驱动开发的一个标志。表明当前类是spring的一个配置类,作用是替代spring的applicationContext.xml。但其本质就是@Component注解,被此注解修饰的类,也会被存入spring的ioc容器。 属性: value: 用于存入spring的Ioc容器中Bean的id。 使用场景: 在注解驱动开发时,用于编写配置的类,通常可以使用此注解。一般情况下,我们的配置也会分为主从配置,@Configuration一般出现在主配置类上。例如,入门案例中的SpringConfiguration类上。值得注意的是,如果我们在注解驱动开发时,构建ioc容器使用的是传入字节码的构造函数,此注解可以省略。但是如果传入的是一个包,此注解则不能省略。
在注解驱动的入门案例中,由于没有了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 { }
@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 {}; } }
作用: 用于指定创建容器时要扫描的包。该注解在指定扫描的位置时,可以指定包名,也可以指定扫描的类。同时支持定义扫描规则,例如包含哪些或者排除哪些。同时,它还支持自定义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。
在入门案例中,如果我们加入了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 { }