答:IoC叫控制反转,是Inversion of Control的缩写,DI(Dependency Injection)叫依赖注入,是对IoC更简单的诠释。控制反转是把传统上由程序代码直接操控的对象的调用权交给容器,经过容器来实现对象组件的装配和管理。所谓的”控制反转”就是对组件对象控制权的转移,从程序代码自己转移到了外部容器,由容器来建立对象并管理对象之间的依赖关系。IoC体现了好莱坞原则 – “Don’t call me, we will call you”。依赖注入的基本原则是应用组件不该该负责查找资源或者其余依赖的协做对象。配置对象的工做应该由容器负责,查找资源的逻辑应该从应用组件的代码中抽取出来,交给容器来完成。DI是对IoC更准确的描述,即组件之间的依赖关系由容器在运行期决定,形象的来讲,即由容器动态的将某种依赖关系注入到组件之中。html
举个例子:一个类A须要用到接口B中的方法,那么就须要为类A和接口B创建关联或依赖关系,最原始的方法是在类A中建立一个接口B的实现类C的实例,但这种方法须要开发人员自行维护两者的依赖关系,也就是说当依赖关系发生变更的时候须要修改代码并从新构建整个系统。若是经过一个容器来管理这些对象以及对象的依赖关系,则只须要在类A中定义好用于关联接口B的方法(构造器或setter方法),将类A和接口B的实现类C放入容器中,经过对容器的配置来实现两者的关联。java
依赖注入能够经过setter方法注入(设值注入)、构造器注入和接口注入三种方式来实现,Spring支持setter注入和构造器注入,一般使用构造器注入来注入必须的依赖关系,对于可选的依赖关系,则setter注入是更好的选择,setter注入须要类提供无参构造器或者无参的静态工厂方法来建立对象。程序员
14七、Spring中Bean的做用域有哪些?spring
答:在Spring的早期版本中,仅有两个做用域:singleton和prototype,前者表示Bean以单例的方式存在;后者表示每次从容器中调用Bean时,都会返回一个新的实例,prototype一般翻译为原型。sql
补充:设计模式中的建立型模式中也有一个原型模式,原型模式也是一个经常使用的模式,例如作一个室内设计软件,全部的素材都在工具箱中,而每次从工具箱中取出的都是素材对象的一个原型,能够经过对象克隆来实现原型模式。数据库
Spring 2.x中针对WebApplicationContext新增了3个做用域,分别是:request(每次HTTP请求都会建立一个新的Bean)、session(同一个HttpSession共享同一个Bean,不一样的HttpSession使用不一样的Bean)和globalSession(同一个全局Session共享一个Bean)。apache
说明:单例模式和原型模式都是重要的设计模式。通常状况下,无状态或状态不可变的类适合使用单例模式。在传统开发中,因为DAO持有Connection这个非线程安全对象于是没有使用单例模式;但在Spring环境下,全部DAO类对能够采用单例模式,由于Spring利用AOP和Java API中的ThreadLocal对非线程安全的对象进行了特殊处理。编程
ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。ThreadLocal,顾名思义是线程的一个本地化对象,当工做于多线程中的对象使用ThreadLocal维护变量时,ThreadLocal为每一个使用该变量的线程分配一个独立的变量副本,因此每个线程均可以独立的改变本身的副本,而不影响其余线程所对应的副本。从线程的角度看,这个变量就像是线程的本地变量。设计模式
ThreadLocal类很是简单好用,只有四个方法,能用上的也就是下面三个方法:安全
- void set(T value):设置当前线程的线程局部变量的值。
- T get():得到当前线程所对应的线程局部变量的值。
- void remove():删除当前线程中线程局部变量的值。
ThreadLocal是如何作到为每个线程维护一份独立的变量副本的呢?在ThreadLocal类中有一个Map,键为线程对象,值是其线程对应的变量的副本,本身要模拟实现一个ThreadLocal类其实并不困难,代码以下所示:
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public class MyThreadLocal<T> {
private Map<Thread, T> map = Collections.synchronizedMap(new HashMap<Thread, T>());
public void set(T newValue) {
map.put(Thread.currentThread(), newValue);
}
public T get() {
return map.get(Thread.currentThread());
}
public void remove() {
map.remove(Thread.currentThread());
}
}
14八、解释一下什么叫AOP(面向切面编程)?
答:AOP(Aspect-Oriented Programming)指一种程序设计范型,该范型以一种称为切面(aspect)的语言构造为基础,切面是一种新的模块化机制,用来描述分散在对象、类或方法中的横切关注点(crosscutting concern)。
14九、你是如何理解”横切关注”这个概念的?
答:”横切关注”是会影响到整个应用程序的关注功能,它跟正常的业务逻辑是正交的,没有必然的联系,可是几乎全部的业务逻辑都会涉及到这些关注功能。一般,事务、日志、安全性等关注就是应用中的横切关注功能。
150、你如何理解AOP中的链接点(Joinpoint)、切点(Pointcut)、加强(Advice)、引介(Introduction)、织入(Weaving)、切面(Aspect)这些概念?
答:
a. 链接点(Joinpoint):程序执行的某个特定位置(如:某个方法调用前、调用后,方法抛出异常后)。一个类或一段程序代码拥有一些具备边界性质的特定点,这些代码中的特定点就是链接点。Spring仅支持方法的链接点。
b. 切点(Pointcut):若是链接点至关于数据中的记录,那么切点至关于查询条件,一个切点能够匹配多个链接点。Spring AOP的规则解析引擎负责解析切点所设定的查询条件,找到对应的链接点。
c. 加强(Advice):加强是织入到目标类链接点上的一段程序代码。Spring提供的加强接口都是带方位名的,如:BeforeAdvice、AfterReturningAdvice、ThrowsAdvice等。不少资料上将加强译为“通知”,这明显是个词不达意的翻译,让不少程序员困惑了许久。
说明: Advice在国内的不少书面资料中都被翻译成”通知”,可是很显然这个翻译没法表达其本质,有少许的读物上将这个词翻译为”加强”,这个翻译是对Advice较为准确的诠释,咱们经过AOP将横切关注功能加到原有的业务逻辑上,这就是对原有业务逻辑的一种加强,这种加强能够是前置加强、后置加强、返回后加强、抛异常时加强和包围型加强。
d. 引介(Introduction):引介是一种特殊的加强,它为类添加一些属性和方法。这样,即便一个业务类本来没有实现某个接口,经过引介功能,能够动态的未该业务类添加接口的实现逻辑,让业务类成为这个接口的实现类。
e. 织入(Weaving):织入是将加强添加到目标类具体链接点上的过程,AOP有三种织入方式:①编译期织入:须要特殊的Java编译期(例如AspectJ的ajc);②装载期织入:要求使用特殊的类加载器,在装载类的时候对类进行加强;③运行时织入:在运行时为目标类生成代理实现加强。Spring采用了动态代理的方式实现了运行时织入,而AspectJ采用了编译期织入和装载期织入的方式。
f. 切面(Aspect):切面是由切点和加强(引介)组成的,它包括了对横切关注功能的定义,也包括了对链接点的定义。
补充:代理模式是GoF提出的23种设计模式中最为经典的模式之一,代理模式是对象的结构模式,它给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。简单的说,代理对象能够完成比原对象更多的职责,当须要为原对象添加横切关注功能时,就可使用原对象的代理对象。咱们在打开Office系列的Word文档时,若是文档中有插图,当文档刚加载时,文档中的插图都只是一个虚框占位符,等用户真正翻到某页要查看该图片时,才会真正加载这张图,这其实就是对代理模式的使用,代替真正图片的虚框就是一个虚拟代理;Hibernate的load方法也是返回一个虚拟代理对象,等用户真正须要访问对象的属性时,才向数据库发出SQL语句得到真实对象。
下面用一个找枪手代考的例子演示代理模式的使用:
/**
* 参考人员接口
* @author 骆昊
*
*/
public interface Candidate {
/**
* 答题
*/
public void answerTheQuestions();
}
/**
* 懒学生
* @author 骆昊
*
*/
public class LazyStudent implements Candidate {
private String name; // 姓名
public LazyStudent(String name) {
this.name = name;
}
@Override
public void answerTheQuestions() {
// 懒学生只能写出本身的名字不会答题
System.out.println("姓名: " + name);
}
}
/**
* 枪手
* @author 骆昊
*
*/
public class Gunman implements Candidate {
private Candidate target; // 被代理对象
public Gunman(Candidate target) {
this.target = target;
}
@Override
public void answerTheQuestions() {
// 枪手要写上代考的学生的姓名
target.answerTheQuestions();
// 枪手要帮助懒学生答题并交卷
System.out.println("奋笔疾书正确答案");
System.out.println("交卷");
}
}
public class ProxyTest1 {
public static void main(String[] args) {
Candidate c = new Gunman(new LazyStudent("王小二"));
c.answerTheQuestions();
}
}
说明:从JDK 1.3开始,Java提供了动态代理技术,容许开发者在运行时建立接口的代理实例,主要包括Proxy类和InvocationHandler接口。下面的例子使用动态代理为ArrayList编写一个代理,在添加和删除元素时,在控制台打印添加或删除的元素以及ArrayList的大小:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.List;
public class ListProxy<T> implements InvocationHandler {
private List<T> target;
public ListProxy(List<T> target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object retVal = null;
System.out.println("[" + method.getName() + ": " + args[0] + "]");
retVal = method.invoke(target, args);
System.out.println("[size=" + target.size() + "]");
return retVal;
}
}
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;
public class ProxyTest2 {
@SuppressWarnings("unchecked")
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
Class<?> clazz = list.getClass();
ListProxy<String> myProxy = new ListProxy<String>(list);
List<String> newList = (List<String>)
Proxy.newProxyInstance(clazz.getClassLoader(),
clazz.getInterfaces(), myProxy);
newList.add("apple");
newList.add("banana");
newList.add("orange");
newList.remove("banana");
}
}
说明:使用Java的动态代理有一个局限性就是代理的类必需要实现接口,虽然面向接口编程是每一个优秀的Java程序都知道的规则,但现实每每不尽如人意,对于没有实现接口的类如何为其生成代理呢?继承!继承是最经典的扩展已有代码能力的手段,虽然继承经常被初学者滥用,但继承也经常被进阶的程序员忽视。CGLib采用很是底层的字节码生成技术,经过为一个类建立子类来生成代理,它弥补了Java动态代理的不足,所以Spring中动态代理和CGLib都是建立代理的重要手段,对于实现了接口的类就用动态代理为其生成代理类,而没有实现接口的类就用CGLib经过继承的方式为其建立代理。
15一、Spring中自动装配的方式有哪些?
答:
- no:不进行自动装配,手动设置Bean的依赖关系。
- byName:根据Bean的名字进行自动装配。
- byType:根据Bean的类型进行自动装配。
- constructor:相似于byType,不过是应用于构造器的参数,若是正好有一个Bean与构造器的参数类型相同则能够自动装配,不然会致使错误。
- autodetect:若是有默认的构造器,则经过constructor的方式进行自动装配,不然使用byType的方式进行自动装配。
说明:自动装配没有自定义装配方式那么精确,并且不能自动装配简单属性(基本类型、字符串等),在使用时应注意。
15二、Spring中如何使用注解来配置Bean?有哪些相关的注解?
答:首先须要在Spring配置文件中增长以下配置:
<context:component-scan base-package="org.example"/>
而后能够用@Component、@Controller、@Service、@Repository注解来标注须要由Spring IoC容器进行对象托管的类。这几个注解没有本质区别,只不过@Controller一般用于控制器,@Service一般用于业务逻辑类,@Repository一般用于仓储类(例如咱们的DAO实现类),普通的类用@Component来标注。
15三、Spring支持的事务管理类型有哪些?你在项目中使用哪一种方式?
答:Spring支持编程式事务管理和声明式事务管理。许多Spring框架的用户选择声明式事务管理,由于这种方式和应用程序的关联较少,所以更加符合轻量级容器的概念。声明式事务管理要优于编程式事务管理,尽管在灵活性方面它弱于编程式事务管理,由于编程式事务容许你经过代码控制业务。
事务分为全局事务和局部事务。全局事务由应用服务器管理,须要底层服务器JTA支持(如WebLogic、WildFly等)。局部事务和底层采用的持久化方案有关,例如使用JDBC进行持久化时,须要使用Connetion对象来操做事务;而采用Hibernate进行持久化时,须要使用Session对象来操做事务。
Spring提供了以下所示的事务管理器。
这些事务的父接口都是PlatformTransactionManager。Spring的事务管理机制是一种典型的策略模式,PlatformTransactionManager表明事务管理接口,该接口定义了三个方法,该接口并不知道底层如何管理事务,可是它的实现类必须提供getTransaction()方法(开启事务)、commit()方法(提交事务)、rollback()方法(回滚事务)的多态实现,这样就能够用不一样的实现类表明不一样的事务管理策略。使用JTA全局事务策略时,须要底层应用服务器支持,而不一样的应用服务器所提供的JTA全局事务可能存在细节上的差别,所以实际配置全局事务管理器是可能须要使用JtaTransactionManager的子类,如:WebLogicJtaTransactionManager(Oracle的WebLogic服务器提供)、UowJtaTransactionManager(IBM的WebSphere服务器提供)等。
编程式事务管理以下所示。
<?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:p="http://www.springframework.org/schema/p"
xmlns:p="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">
<context:component-scan base-package="com.jackfrued"/>
<bean id="propertyConfig"
class="org.springframework.beans.factory.config.
PropertyPlaceholderConfigurer">
<property name="location">
<value>jdbc.properties</value>
</property>
</bean>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName">
<value>${db.driver}</value>
</property>
<property name="url">
<value>${db.url}</value>
</property>
<property name="username">
<value>${db.username}</value>
</property>
<property name="password">
<value>${db.password}</value>
</property>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource">
<ref bean="dataSource" />
</property>
</bean>
<!-- JDBC事务管理器 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.
DataSourceTransactionManager" scope="singleton">
<property name="dataSource">
<ref bean="dataSource" />
</property>
</bean>
<!-- 声明事务模板 -->
<bean id="transactionTemplate"
class="org.springframework.transaction.support.
TransactionTemplate">
<property name="transactionManager">
<ref bean="transactionManager" />
</property>
</bean>
</beans>
package com.jackfrued.dao.impl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import com.jackfrued.dao.EmpDao;
import com.jackfrued.entity.Emp;
@Repository
public class EmpDaoImpl implements EmpDao {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public boolean save(Emp emp) {
String sql = "insert into emp values (?,?,?)";
return jdbcTemplate.update(sql, emp.getId(), emp.getName(), emp.getBirthday()) == 1;
}
}
package com.jackfrued.biz.impl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import com.jackfrued.biz.EmpService;
import com.jackfrued.dao.EmpDao;
import com.jackfrued.entity.Emp;
@Service
public class EmpServiceImpl implements EmpService {
@Autowired
private TransactionTemplate txTemplate;
@Autowired
private EmpDao empDao;
@Override
public void addEmp(final Emp emp) {
txTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus txStatus) {
empDao.save(emp);
}
});
}
}