bean 注入与装配的的方式有很多种,可以通过 xml,get set 方式,构造函数或者注解等。简单易用的方式就是使用 Spring 的注解了,Spring 提供了大量的注解方式。
Spring 通过一个配置文件描述 Bean 及 Bean 之间的依赖关系,利用 Java 语言的反射功能实例化Bean 并建立 Bean 之间的依赖关系。 Spring 的 IOC 容器在完成这些底层工作的基础上,还提供了 Bean 实例缓存、生命周期管理、 Bean 实例代理、事件发布、资源装载等高级服务。
Spring 启动时读取应用程序提供的 Bean 配置信息,并在 Spring 容器中生成一份相应的 Bean 配置注册表,然后根据这张注册表实例化 Bean,装配好 Bean 之间的依赖关系,为上层应用提供准备就绪的运行环境。其中 Bean 缓存池为 HashMap 实现
Spring 配置文件中每一个节点元素在 Spring 容器里都通过一个 BeanDefinition 对象表示,它描述了 Bean 的配置信息。而 BeanDefinitionRegistry 接口提供了向容器手工注册
BeanDefinition 对象的方法。
位于类结构树的顶端 ,它最主要的方法就是 getBean(String beanName),该方法从容器中返回特定名称的 Bean,BeanFactory 的功能通过其他的接口得到不断扩展
该接口定义了访问容器中 Bean 基本信息的若干方法,如查看 Bean 的个数、获取某一类型Bean 的配置名、查看容器中是否包括某一 Bean 等方法
父子级联 IOC 容器的接口, Spring 的 IOC 容器可以建立父子层级关联的容器体系,子容器可以访问父容器中的 Bean,但父容器不能访问子容器的 Bean。
Spring 使用父子容器实现了很多功能,比如在 Spring MVC 中,展现层 Bean 位于一个子容器中,而业务层和持久层的 Bean 位于父容器中。这样,展现层 Bean 就可以引用业务层和持久层的 Bean,而业务层和持久层的 Bean 则看不到展现层的 Bean。
是一个重要的接口,增强了 IOC 容器的可定制性,它定义了设置类装载器、属性编辑器、容器初始化后置处理器等方法
定义了将容器中的 Bean 按某种规则(如按名字匹配、按类型匹配等)进行自动装配的方法
定义了允许在运行期间向容器注册单实例 Bean 的方法
对于单实例( singleton)的 Bean 来说,BeanFactory 会缓存 Bean 实例,所以第二次使用 getBean() 获取 Bean 时将直接从IOC 容器的缓存中获取 Bean 实例。
Spring 在 DefaultSingletonBeanRegistry 类中提供了一个用于缓存单实例 Bean 的缓存器,它是一个用 HashMap 实现的缓存器,单实例的 Bean 以beanName 为键保存在这个 HashMap 中。
在初始化 BeanFactory 时,必须为其提供一种日志框架,比如使用 Log4J, 即在类路径下提供 Log4J 配置文件,这样启动 Spring 容器才不会报错。
ApplicationContext 由 BeanFactory派生而来,提供了更多面向实际应用的功能 ,几乎所有的应用场合我们都直接使用 ApplicationContext 而非底层的 BeanFactory。
ApplicationContext 继承了 HierarchicalBeanFactory 和 ListableBeanFactory 接口,在此基础上,还通过多个其他的接口扩展了 BeanFactory 的功能:
WebApplicationContext 是专门为 Web 应用准备的,它允许从相对于 Web 根目录的
路径中装载配置文件完成初始化工作。
从 WebApplicationContext 中可以获得ServletContext 的引用,整个 Web 应用上下文对象将作为属性放置到 ServletContext 中,以便 Web 应用环境可以访问 Spring 应用上下文。
Spring IOC 容器中只会存在一个共享的 Bean 实例,无论有多少个Bean 引用它,始终指向同一对象。该模式在多线程下是不安全的。是Spring的默认作用域
每次通过 Spring 容器获取 prototype 定义的 bean 时,容器都将创建一个新的 Bean 实例,每个 Bean 实例都有自己的属性和状态,而 singleton 全局只有一个对象。
在一次 Http 请求中,容器会返回该 Bean 的同一实例。而对不同的 Http 请求则会
产生新的 Bean,而且该 bean 仅在当前 Http Request 内有效,当前 Http 请求结束,该 bean实例也将会被销毁。
在一次 Http Session 中,容器会返回该 Bean 的同一实例。而对不同的 Session 请
求则会创建新的实例,该 bean 实例仅在当前 Session 内有效。同 Http 请求相同,每一次session 请求创建新的实例,而不同的实例之间不共享属性,且实例仅在自己的 session 请求内有效,请求结束,则实例将被销毁。
在一个全局的 Http Session 中,容器会返回该 Bean 的同一个实例,仅在使用 portlet context 时有效。
实例化一个 Bean,也就是我们常说的 new。
按照 Spring 上下文对实例化的 Bean 进行配置,也就是 IOC 注入。
如果这个 Bean 已经实现了 BeanNameAware 接口,会调用它实现的setBeanName(String)方法,此处传递的就是 Spring 配置文件中 Bean 的 id 值
如果这个 Bean 已经实现了 BeanFactoryAware 接口,会调用它实现的 setBeanFactory,setBeanFactory(BeanFactory)传递的是 Spring 工厂自身
可以用这个方式来获取其它 Bean,只需在 Spring 配置文件中配置一个普通的 Bean 就可以
如果这个 Bean 已经实现了 ApplicationContextAware 接口,会调用setApplication-Context(ApplicationContext)方法,传入 Spring 上下文
同样这个方式也可以实现步骤 4 的内容,但比 4 更好,因为 ApplicationContext 是 BeanFactory 的子接口,有更多的实现方法
如果这个 Bean 关联了 BeanPostProcessor 接口,将会调用postProcessBefore-Initialization(Object obj, String s)方法,BeanPostProcessor 经常被用作是 Bean 内容的更改,并且由于这个是在 Bean 初始化结束时调用那个的方法,也可以被应用于内存或缓存技术。
如果 Bean 在 Spring 配置文件中配置了 init-method 属性会自动调用其配置的初始化方法。
如果这个 Bean 关联了 BeanPostProcessor 接口,将会调用postProcessAfter-Initialization(Object obj, String s)方法。
以上工作完成以后就可以应用这个 Bean 了,这个 Bean 是一个 Singleton 的,所以一般情况下我们调用同一个 id 的 Bean 会是在内容地址相同的实例,当然在 Spring 配置文件中也可以配置非 Singleton
当 Bean 不再需要时,会经过清理阶段,如果 Bean 实现了 DisposableBean 这个接口,会调用那个其实现的 destroy()方法
最后,如果这个 Bean 的 Spring 配置中配置了 destroy-method 属性,会自动调用其配置的销毁方法
bean 标签有两个重要的属性(init-method 和 destroy-method)。用它们可以自己定制初始化和注销方法。它们也有相应的注解(@PostConstruct 和@PreDestroy)。
< bean id="" class="" init-method=“初始化方法” destroy-method=“销毁方法”>
所谓控制反转是指,本来被调用者的实例是由调用者来创建的,这样的缺点是耦合性太强,IOC 则是统一交给 spring 来管理创建,将对象交给容器管理,你只需要在 spring 配置文件总配置相应的 bean,以及设置相关的属性,让 spring 容器来生成类的实例对象以及管理对象。
在 spring 容器启动的时候,spring 会把你在配置文件中配置的 bean 都初始化好,然后在你需要调用的时候,就把它已经初始化好的那些 bean 分配给你需要调用这些 bean 的类。
IoC 的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过 DI(Depndency Injection,依赖注入)来实现的。
比如对象 A 需要操作数据库,以前我们总是要在 A 中自己编写代码来获得一个Conection 对象,有了 spring 我们就只需要告诉 spring,A 中需要一个 Conection,至于这个 Conection 怎么构造,何时构造,A 不需要知道。在系统运行时,spring 会在适当的时候制造一个 Conection,然后像打针一样,注射到 A 当中,这样就完成了对各个对象之间关系的控制。A 需要依赖 Conection 才能正常运行,而这个 Conection 是由 spring 注入到 A 中的,依赖注入的名字就这么来的。
那么 DI 是如何实现的呢? Java 1.3 之后一个重要特征是反射(reflction),它允许程序在运行的时候动态的生成对象、执行对象的方法、改变对象的属性,spring 就是通过反射来实现注入的。
/*带参数,方便利用构造器进行注入*/
public CatDaoImpl(String message){
this. message = message;
}
public class Id {
private int id;
public int getId() { return id; }
public void setId(int id) { this.id = id; }
}
不能直接通过"工程类.静态方法()"来获取对象,而是依然通过 spring 注入的形式获取
public class DaoFactory { //静态工厂
public static final FactoryDao getStaticFactoryDaoImpl(){
return new StaticFacotryDaoImpl();
}
}
public class SpringAction {
private FactoryDao staticFactoryDao; //注入对象
//注入对象的 set 方法
public void setStaticFactoryDao(FactoryDao staticFactoryDao) {
this.staticFactoryDao = staticFactoryDao;
}
}
需要首先 new 工厂类,再调用普通的实例方法
public class DaoFactory { //实例工厂
public FactoryDao getFactoryDaoImpl(){
return new FactoryDaoImpl();
}
}
public class SpringAction {
private FactoryDao factoryDao;
//注入对象
public void setFactoryDao(FactoryDao factoryDao) {
this.factoryDao = factoryDao;
}
}
“横切"指剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect”,即切面。
所谓"切面",简单说就是把那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。
使用"横切"技术,AOP 把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处基本相似,比如权限认证、日志、事物。AOP 的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。
在 java 的动态代理机制中,有两个重要的类或接口,一个是 InvocationHandler(Interface)、另一个则是 Proxy(Class),这两个类和接口是实现我们动态代理所必须用到的。
每一个动态代理类都必须要实现 InvocationHandler 这个接口,并且每个代理类的实例都关联到了一个 handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由 InvocationHandler 这个接口的 invoke 方法来进行调用。
通过实现该接口可以定义横切逻辑,并通过反射机制调用目标类的代码,动态将横切逻辑和业务逻辑编制在一起:
Object invoke(Object proxy, Method method, Object[] args) throws Throwable
//proxy: 指代我们所代理的那个真实对象
//method: 指代的是我们所要调用真实对象的某个方法的 Method 对象
//args: 指代的是调用真实对象某个方法时接受的参数
Proxy 利用 InvocationHandler 动态创建一个符合某一接口的实例,生成目标类的代理对象,它提供了许多的方法,但是我们用的最多的就是 newProxyInstance 这个方法:
public staic Object newProxyInstance(ClasLoader loader, Clas>[] interfaces, InvocationHandler h) throws IlegalArgumentException
//loader: 一个 ClassLoader 对象,定义了由哪个 ClassLoader 对象来对生成的代理对象进行加载
//interfaces: 一个 Interface 对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口
//如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了
//h:表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler 对象上
Spring 提供了两种方式来生成代理对象: JDKProxy 和 Cglib,具体使用哪种方式生成由
AopProxyFactory 根据 AdvisedSupport 对象的配置来决定。
默认的策略是如果目标类是接口,则使用 JDK 动态代理技术,否则使用 Cglib 来生成代理。
JDK 动态代理主要涉及到 java.lang.reflect 包中的两个类:Proxy 和InvocationHandler。
一个强大的高性能,高质量的代码生成类库,可以在运行期扩展 Java 类与实现 Java 接口,CGLib 封装了 asm,可以再运行期动态生成新的 class。
CGlib 动态代理是通过继承业务类,生成的动态代理类是业务类的子类,通过重写业务方法进行代理,因此无法代理final方法
//切面
@Aspect
public class TransactionDemo {
//切入点(连接点的断言)
@Pointcut(value="execution(* com.test.service.*.*.*(..))")
public void point(){ }
//前置通知
@Before(value="point()")
public void before(){
System.out.println("transaction begin");
}
//后置通知
@AfterReturning(value = "point()")
public void after(){
System.out.println("transaction commit");
}
//环绕通知
@Around("point()")
public void around(ProceedingJoinPoint joinPoint) throws Throwable{
System.out.println("transaction begin");
joinPoint.proceed();
System.out.println("transaction commit");
}
}
根据代理机制的不同,Spring 事务的配置又有几种不同的方式:
嵌套是子事务套在父事务中执行,子事务是父事务的一部分,在进入子事务之前,父事务建立一个回滚点,叫 save point,然后执行子事务,这个子事务的执行也算是父事务的一部分,然后子事务执行结束,父事务继续执行。
如果子事务回滚,父事务会回滚到进入子事务前建立的 save point,然后尝试其他的事务或者其他的业务逻辑,父事务之前的操作不会受到影响,更不会自动回滚。
如果父事务回滚,子事务也会跟着回滚!因为父事务结束之前,子事务是不会提交的,我们说子事务是父事务的一部分,正是这个道理。
那么:事务的提交时,子事务先提交,然后父事务再提交。子事务是父事务的一部分,由父事务统一提交。
参考博客:深入解析spring中用到的九种设计模式
又叫做静态工厂方法(StaicFactory Method)模式,简单工厂模式的实质是由一个工厂类根据传入的参数,动态决定应该创建哪一个产品类。
spring 中的 BeanFactory 就是简单工厂模式的体现,根据传入一个唯一的标识来获得 bean 对象,但是在传入参数后创建还是传入参数前创建这个要根据具体情况来定。如下配置,就是在 HelloA 类中创建一个 ABean。
Hello! 这是singletonBean!
bean>
Hello! 这是ABean!
通常由应用程序直接使用new创建新的对象,为了将对象的创建和使用相分离,采用工厂模式,即应用程序将对象的创建及初始化职责交给工厂对象。
一般情况下,应用程序有自己的工厂对象来创建bean.如果将应用程序自己的工厂对象交给Spring管理,那么Spring管理的就不是普通的bean,而是工厂Bean。
就以工厂方法中的静态方法为例讲解一下:
import java.util.Random;
public class StaticFactoryBean {
public static Integer createRandom() {
return new Integer(new Random().nextInt());
}
}
建一个config.xm配置文件,将其纳入Spring容器来管理,需要通过factory-method指定静态方法名称
//createRandom方法必须是static的,才能找到 scope="prototype"
测试:
public static void main(String[] args) {
//调用getBean()时,返回随机数.如果没有指定factory-method,会返回StaticFactoryBean的实例,即返回工厂Bean的实例
XmlBeanFactory factory = new XmlBeanFactory(new ClassPathResource("config.xml"));
System.out.println("我是IT学习者创建的实例:"+factory.getBean("random").toString());
}
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
spring 中的单例模式完成了后半句话,即提供了全局的访问点 BeanFactory。但没有从构造器级别去控制单例,这是因为 spring 管理的是任意的 java 对象。
Spring 下默认的 bean 均为 singleton,可以通过 singleton=“true|false”或scope=“?”来指定
在 Spring 的 AOP 中,使用 Advice(通知)来增强被代理类的功能。
Spring 实现 AOP 功能,对类进行方法级别的切面增强,即生成被代理类的代理类, 并在代理类的方法前,设置拦截器,通过执行拦截器重的内容增强了代理方法的功能,实现面向切面编程。
//Adapter类接口:Target
public interface AdvisorAdapter {
boolean supportsAdvice(Advice advice);
MethodInterceptor getInterceptor(Advisor advisor);
}
//MethodBeforeAdviceAdapter类,Adapter
class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {
public boolean supportsAdvice(Advice advice) {
return (advice instanceof MethodBeforeAdvice);
}
public MethodInterceptor getInterceptor(Advisor advisor) {
MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
return new MethodBeforeAdviceInterceptor(advice);
}
}
在我们的项目中遇到这样一个问题:我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库。
我们以往在spring和hibernate框架中总是配置一个数据源,因而sessionFactorydataSource属性总是指向这个数据源并且恒定不变,所有DAO在使用sessionFactory的时候都是通过这个数据源访问数据库。但是现在,由于项目的需要,我们的DAO在访问sessionFactory的时候都不得不在多个数据源中不断切换,问题就出现了:如何让sessionFactory在执行数据持久化的时候,根据客户的需求能够动态切换不同的数据源?我们能不能在spring的框架下通过少量修改得到解决?是否有什么设计模式可以利用呢?
首先想到在spring的applicationContext中配置所有的dataSource。这些dataSource可能是各种不同类型的,比如不同的数据库:Oracle、SQL Server、MySQL等,也可能是不同的数据源:比如apache 提供的org.apache.commons.dbcp.BasicDataSource、spring提供的org.springframework.jndi.JndiObjectFactoryBean等。
然后sessionFactory根据客户的每次请求,将dataSource属性设置成不同的数据源,以到达切换数据源的目的。
spring中用到的包装器模式在类名上有两种表现:一种是类名中含有Wrapper,另一种是类名中含有Decorator。基本上都是动态地给一个对象添加一些额外的职责。
为其他对象提供一种代理以控制对这个对象的访问。 从结构上来看和 Decorator 模式类似,但 Proxy 是控制,更像是一种对功能的限制,而 Decorator 是增加职责。
spring 的 Proxy 模式在 aop 中有体现,比如 JdkDynamicAopProxy 和Cglib2AopProxy。
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
spring 中 Observer 模式常用的地方是 listenr 的实现。如 AplicationListenr。
定义一系列的算法,把它们一个封装起来,并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。
spring 中在实例化对象的时候用到 Strategy 模式,在 SimpleInstantiaonStrategy 中有如下代码说明了策略模式的使用情况:
定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。Template Method 使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。Template Method 模式一般是需要继承的。
spring 中的 JdbcTemplate,在用这个类时并不想去继承这个类,因为这个类的方法太多,但是我们还是想用到 JdbcTemplate 已有的稳定的、公用的数据库连接,那么我们怎么办呢?
我们可以把变化的东西抽出来作为一个参数传入 JdbcTemplate 的方法中。但是变化的东西是一段代码,而且这段代码会用到 JdbcTemplate 中的变量。怎么办?
那我们就用回调对象吧。在这个回调对象中定义一个操纵 JdbcTemplate 中变量的方法,我们去实现这个方法,就把变化的东西集中到这里了。然后我们再传入这个回调对象到 JdbcTemplate,从而完成了调用。这可能是 Template Method 不需要继承的另一种实现方式吧。
Spring MVC 是一个基于 MVC 架构的用来简化 web 应用程序开发的应用开发框架,它是 Spring 的一个模块,无需中间整合层来整合 ,它和 Struts2 一样都属于表现层的框架。
在 web 模型中,MVC 是一种很流行的框架,通过把 Model,View,Controler 分离,把较为复杂的 web 应用分成逻辑清晰的几部分,简化开发,减少出错,方便组内开发人员之间的配合。
Spring Boot 是由 Pivotal 团队提供的全新框架,其设计目的是用来简化新 Spring 应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。
通过这种方式,Spring Boot 致力于在蓬勃发展的快速应用开发领域(rapid application development)成为领导者。其特点如下: