赞
踩
spring的官方网址是spring.io(点这里直接进入)。但是在Java中常说的spring一般是指Spring Framework(点这里可以直接进入)。
Spring根据功能的不同,将代码划分为两类:主业务逻辑与系统级服务(交叉业务逻辑)。主业务逻辑间,及主业务逻辑与系统级服务间的耦合度是较高的。Spring对于前述两种较高的耦合度,采用了两种不同的技术进行解耦。使用IoC解耦主业务逻辑间的耦合度,使用AOP解耦主业务逻辑与系统级服务间的耦合度。
IoC,Inversion of Control,控制反转。将传统上由程序代码直接操控的对象创建权交给容器,通过容器来管理对象的生命周期。控制反转是对对象控制权的转移,从代码本身转移到了容器。
IoC是一种思想,是一个概念,其实现方式有很多。当前比较流行的实现方式有两种:依赖注入与依赖查找。
在spring中使用依赖注入之前,我们需要先向spring容器中注册被配置成单例的实例bean,然后就可以注入了。spring依赖注入方式有构造方法注入(了解)和set方法注入(重点)。set方法注入分为手动装配注入和自动装配注入。
可参考处地址(点这里)
在web应用中,web服务器启动时会加载web.xml配置文件(启动ContextLoaderListener监听器),会创建web容器,spring容器的创建和初始化是在web容器创建的过程中。
public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. // 准备更新上下文,设置开始时间,标记活动标志,初始化配置文件中的占位符 prepareRefresh(); // Tell the subclass to refresh the internal bean factory. // 一、 web工程 AbstractRefreshableApplicationContext // 将 bean 定义加载到给定的 BeanFactory 中 // 1. createBeanFactory(); 为此上下文创建内部 BeanFactory // 2. customizeBeanFactory(beanFactory); 定制 BeanFactory,是否允许 BeanDefinition 覆盖、是否允许循环引用 // 3. loadBeanDefinitions(beanFactory); 通过 BeanDefinitionReader 解析 xml 文件,解析封装信息到 BeanDefinition,并将其 register 到 BeanFactory 中 // 以 beanName为key将beanDefinition 存到 DefaultListableBeanFactory#beanDefinitionMap 中 // 二、 SpringBoot GenericApplicationContext,实际 register 过程在 invokeBeanFactoryPostProcessors 中 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. // 准备 BeanFactory 以便在此上下文中使用。 // 1. 设置 BeanFactory 的类加载器 // 2. 添加几个 BeanPostProcessor, // 3. 实例化几个特殊的 bean prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. // 在 AbstractApplicationContext#postProcessBeanFactory 为空实现,留给子类做扩展,不同 ApplicationContext 实现不同,不作详细描述 postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. // Spring 的 SPI // 先调用 BeanDefinitionRegistryPostProcessor 和 ImportBeanDefinitionRegistrar 的实现类 // 再调用 BeanFactoryPostProcessor 各个实现类的 postProcessBeanFactory(factory) 方法 // 例如:ConfigurationClassPostProcessor 会扫描 <context:component-scan/> 和 @SpringBootApplication(scanBasePackages = "") 中的Component,并且将 @Configuration 类中的 @Bean register 到 BeanFactory 中 // 扩展例如:MyBatis MapperScannerConfigurer 和 MapperScannerRegistrar,扫描Mapper register 到 BeanFactory 中 invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. // 注册 BeanPostProcessor 的实现类,不同于刚刚的 BeanFactoryPostProcessor // BeanPostProcessor 接口两个方法 postProcessBeforeInitialization 和 postProcessAfterInitialization 会在 Bean 初始化之前和之后调用 // 这边 Bean 还没初始化,下面的 finishBeanFactoryInitialization 才是真正的初始化方法 registerBeanPostProcessors(beanFactory); // Initialize message source for this context. // 初始化当前 ApplicationContext 的 MessageSource,解析消息的策略接口,用于支持消息的国际化和参数化 // Spring 两个开箱即用的实现 ResourceBundleMessageSource 和 ReloadableResourceBundleMessageSource initMessageSource(); // Initialize event multicaster for this context. // 初始化当前 ApplicationContext 的事件广播器 initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. // 典型模板方法 // 子类可以在实例化 bean 之前,做一些初始化工作,SpringBoot 会在这边启动 Web 服务 onRefresh(); // Check for listener beans and register them. // 向 initApplicationEventMulticaster() 初始化的 applicationEventMulticaster 注册事件监听器,就是实现 ApplicationListener 接口类 // 观察者模式,例如实现了 ApplicationEvent,通过 ApplicationEventPublisher#publishEvent(),可以通知到各个 ApplicationListener#onApplicationEvent registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. // 初始化所有的 singletons bean(lazy-init 的除外) // Spring bean 初始化核心方法 finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. // ApplicationEventPublisher#publishEvent() 初始化完成(ContextRefreshedEvent)事件 finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // Destroy already created singletons to avoid dangling resources. // destroy 已经创建的 singleton 避免占用资源 destroyBeans(); // Reset 'active' flag. // 重置'有效'标志 cancelRefresh(ex); // Propagate exception to caller. throw ex; } finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... // 重置Spring核心中的常见内省缓存,因为可能不再需要单例bean的元数据了... resetCommonCaches(); } }
AOP为Aspect Oriented Programming的缩写,意为:面向切面编程。AOP最早由AOP联盟的组织提出的,制定了一套规范.Spring将AOP思想引入到框架中,必须遵守AOP联盟的规范。通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
就是在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:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 目标对象类 --> <bean class="com.czy.service.UserServiceImpl" /> <!-- 该类下面是一些增强方法 --> <bean id="myAdvice" class="com.czy.advice.Myadvice" /> <aop:config> <!-- 匹配指定包和类下方法名为saveUser()且无参无返回值的方法 --> <!-- <aop:pointcut id="pointcut" expression="execution(void com.czy.service.UserServiceImpl.saveUser())"/>--> <!-- 匹配任意包中UserService开头类下参数类型为String,方法名是sava开头返回值类型为void的方法 --> <!-- <aop:pointcut id="pointcut" expression="execution(void *..*.UserService*.save*(String))"/>--> <!-- 匹配任意参数个数和类型的save开头的方法 --> <aop:pointcut id="pointcut" expression="execution(void *..*.UserService*.save*(..))"/> <aop:aspect ref="myAdvice"> <!-- 前置增强 --> <aop:before method="log" pointcut-ref="pointcut" /> <!-- 后置增强 --> <aop:after method="log2" pointcut-ref="pointcut" /> </aop:aspect> </aop:config> </beans>
使用注解方式可以把在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" xmlns:aop="http://www.springframework.org/schema/aop" 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 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 扫描注册需要被spring容器管理的bean --> <context:component-scan base-package="com.czy" /> <!-- aspectJ自动代理 --> <aop:aspectj-autoproxy /> </beans>
被注解标记的切面类
package com.czy.aspect; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; // @Aspect注解:标记该类是一个切面类 @Aspect // 将该类交给spring容器管理 @Component public class MyAspect { private static final String pointcut = "execution(* *..*.*ServiceImpl.*(..))"; @Before(value = "fn()") public void beforeAdvice() { System.out.println("前置增强..."); } @After(value = "fn()") public void afterAdvice() { System.out.println("后置增强..."); } // 定义一个通用的切点 @Pointcut(value = pointcut) public void fn(){} }
spring配置文件(省略数据库账号密码配置文件)
<?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" xmlns:tx="http://www.springframework.org/schema/tx" 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 http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <context:property-placeholder location="classpath:database.properties" /> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="${db.driverClassName}" /> <property name="url" value="${db.url}" /> <property name="username" value="${db.username}" /> <property name="password" value="${db.password}" /> </bean> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource" /> <!-- <constructor-arg name="dataSource" ref="dataSource" />--> </bean> <bean id="accountDao" class="com.czy.dao.AccountDao" /> <context:component-scan base-package="com.czy.service" /> <!-- 配置平台事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <!-- 开启事务注解配置 --> <tx:annotation-driven transaction-manager="transactionManager" /> </beans>
使用spring的JdbcTemplate访问数据库。
package com.czy.dao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; public class AccountDao { @Autowired private JdbcTemplate jdbcTemplate; public double queryMoney(String from) { double money= 0; String sql = "SELECT MONEY FROM ACCOUNT WHERE NAME = ?"; String[] strArray = new String[]{from}; money = jdbcTemplate.queryForObject(sql, strArray, Double.class); return money; } public void update(String name, double money) { Object[] args = { money, name }; // jdbcTemplate.update("UPDATE account SET money = ? WHERE name = ? ", args); jdbcTemplate.update("UPDATE account SET money = ? WHERE name = ? ", args); } }
注解事务测试的业务类
package com.czy.service; import com.czy.dao.AccountDao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; //@Transactional:标记该类的所有方法都已经被事务进行管理了,至于管理属性,不设置的话,都采取默认值 @Transactional @Service public class AccountServiceImpl { @Autowired private AccountDao accountDao; public void transfer(String from, String to, double money) { // 先查询from账户的钱 double fromMoney = accountDao.queryMoney(from); // 对from账户进行扣钱操作 accountDao.update(from, fromMoney - money); //手动制造异常 System.out.println(1/0); // 先查询from账户的钱 double toMoney = accountDao.queryMoney(to); // 对to账户进行加钱操作 accountDao.update(to, toMoney + money); } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。