我的博客:zhenganwen.topphp
IoC
即Inversion of Control
,控制反轉,核心思想是面向接口編程,讓上下游組件面向接口編程從而實現業務的靈活切換(上下游組件解耦)。java
DI
即Dependency Injection
,依賴注入,全部的基礎組件都面向接口編程,由Spring
幫咱們注入接口實現類對象。spring
以Controller
層和Service
層爲例,Controller
爲調用方,是爲上游。若是按照傳統方式,在Controller
中顯式地經過new
來引入某Service
進行編程,那麼這二者是緊耦合的:編程
public class UserController{
UserService userService = new UserService();
public void add(User user){
return userService.add(User user);
}
public void query(){
return userService.query();
}
}
public class UserService{
public void add(User user){
// insert user
}
public void query(){
// select user
}
}
複製代碼
如此,一旦業務變動就須要更改UserService
的代碼。如如今系統要作讀寫分離,那麼就須要將UserService
修改以下:緩存
public class UserService{
public void add(User user){
// insert user in db_1
}
public void query(){
// select user in db_2
}
}
複製代碼
這違背了「開閉原則」(對擴展開發對修改關閉)。服務器
因而在Spring
中,全部的基礎組件都面向接口編程,將基礎組件的實例交給Spring
建立和管理。經過DI
(依賴注入)來靈活地控制組件之間的依賴關係。app
首先針對特定的業務域定義統一的接口:dom
public interface IUserService{
void add();
void query();
}
複製代碼
根據接口能夠有不一樣的實現類,如讀寫同庫、讀寫分離:工具
public class UserServiceImpl implements IUserService{
public void add(User user){
// insert user in db_1
}
public void query(){
// select user in db_1
}
}
public class UserReadWriteServiceImpl implements IUserService{
public void add(User user){
// insert user in db_1
}
public void query(){
// select user in db_2
}
}
複製代碼
將上述基礎業務組件歸入SpringIoC
容器管理測試
<bean id="userServiceImpl" class="xx.xx.xx.UserServiceImpl"></bean>
<bean id="userServiceReadWriteImpl" class="xx.xx.xx.UserServiceReadWriteImpl"></bean>
複製代碼
如此在上游組件中能夠作到靈活切換:
@Controller
public class UserController{
@Resource(name = "userServiceImpl")
//@Resource(name = "userServiceReadWriteImpl")
UserService userService;
public void add(User user){
return userService.add(User user);
}
public void query(){
return userService.query();
}
}
複製代碼
這種編程模式在業務變動時,只需擴展一個UserServiceReadWriteImpl
,並經過@Resource
更改注入源就可實現業務的切換,而無需更改原有的UserServiceImpl
的代碼。
IoC容器:
package cn.tuhu.springioc.ioc;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.InputStream;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
public class CustomClassPathXmlApplicationContext {
private String xmlPath;
private static ConcurrentHashMap<String, Object> beanFactory;
/** * 實例化bean * 提取xml中的<bean></bean>節點,根據其中的class屬性實例化bean,以beanId爲緩存到beanFactory * 注入依賴 * 遍歷bean的屬性,將標註有依賴注入的屬性到beanFactory中找相關依賴賦值 * @param xmlPath */
public CustomClassPathXmlApplicationContext(String xmlPath) {
this.xmlPath = xmlPath;
beanFactory = new ConcurrentHashMap<String, Object>();
try {
initBeanFactory();
} catch (DocumentException e) {
System.out.println("xml解析失敗,請檢查編寫是否正確");
e.printStackTrace();
} catch (ClassNotFoundException e) {
System.out.println("bean配置的class不存在");
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
System.out.println("bean初始化失敗,請確保有無參構造方法");
e.printStackTrace();
}
}
public Object getBean(String beanId) {
return beanFactory.get(beanId);
}
private List<Element> parseXml() throws DocumentException {
InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(xmlPath);
SAXReader saxReader = new SAXReader();
Document document = saxReader.read(inputStream);
Element rootElement = document.getRootElement();
List<Element> beanElements = rootElement.elements();
return beanElements;
}
private void initBeanFactory() throws DocumentException, ClassNotFoundException, IllegalAccessException, InstantiationException {
List<Element> beanElements = parseXml();
for (Element beanElement : beanElements) {
String beanId = beanElement.attributeValue("id");
String className = beanElement.attributeValue("class");
Class<?> clz = Class.forName(className);
beanFactory.put(beanId, clz.newInstance());
}
}
}
複製代碼
spring.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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userService" class="cn.tuhu.springioc.service.impl.UserServiceImpl"/>
</beans>
複製代碼
組件:
package cn.tuhu.springioc.service;
public interface UserService {
void add();
}
package cn.tuhu.springioc.service.impl;
import cn.tuhu.springioc.service.UserService;
public class UserServiceImpl implements UserService {
public void add() {
System.out.println("insert user");
}
}
複製代碼
測試:
package cn.tuhu.springioc.service.ioc;
import cn.tuhu.springioc.ioc.CustomClassPathXmlApplicationContext;
import cn.tuhu.springioc.service.UserService;
import org.junit.Test;
public class CustomClassPathXmlApplicationContextTest {
@Test
public void getBean() {
CustomClassPathXmlApplicationContext context = new CustomClassPathXmlApplicationContext("spring.xml");
UserService userService = (UserService) context.getBean("userService");
userService.add();
}
}
insert user
複製代碼
組件註解
package cn.tuhu.springioc.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//能夠在類上標註
@Target(ElementType.TYPE)
//運行時保留此註解信息
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomComponent {
}
複製代碼
依賴注入
package cn.tuhu.springioc.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//能夠在成員變量上標註
@Target(ElementType.FIELD)
//運行時保留此註解信息
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomResource {
}
複製代碼
package cn.tuhu.springioc.ioc;
import cn.tuhu.springioc.annotation.CustomComponent;
import cn.tuhu.springioc.annotation.CustomResource;
import cn.tuhu.springioc.util.ClassUtils;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
public class CustomAnnotationApplicationContext {
private String basePackage;
private static ConcurrentHashMap<String, Object> beanFactory = new ConcurrentHashMap<String, Object>();
public CustomAnnotationApplicationContext(String basePackage) {
this.basePackage = basePackage;
this.initBeans();
Collection<Object> beans = beanFactory.values();
for (Object bean : beans) {
this.injectDependencies(bean);
}
}
private void injectDependencies(Object bean) {
Class<?> clz = bean.getClass();
Field[] fields = clz.getDeclaredFields();
for (Field field : fields) {
CustomResource annotation = field.getAnnotation(CustomResource.class);
if (annotation != null) {
//若是標註了@CustomResource註解,則須要依賴注入
Object obj = beanFactory.get(field.getName());
field.setAccessible(true);//若是訪問權限不夠,須要設置此項
try {
field.set(bean, obj); //依賴注入
} catch (IllegalAccessException e) { //設置了Accessible則不會拋此異常
e.printStackTrace();
}
}
}
}
private void initBeans() {
List<Class<?>> classes = ClassUtils.getClasses(basePackage); //ClassUtils工具類見下
for (Class<?> clz : classes) {
CustomComponent annotation = clz.getAnnotation(CustomComponent.class);
if (annotation != null) {
// 標註了@CustomComponent註解,須要歸入IoC容器管理
Object bean = null;
try {
bean = clz.newInstance();
} catch (InstantiationException e) {
System.out.printf("實例化bean:%s 失敗", clz.toString());
e.printStackTrace();
} catch (IllegalAccessException e) {
System.out.printf("%s訪問權限不夠", clz.toString());
e.printStackTrace();
}
String beanId = this.toLowerCaseFirstChar(clz.getSimpleName());
beanFactory.put(beanId, bean);
}
}
}
public Object getBean(String beanId) {
return beanFactory.get(beanId);
}
private String toLowerCaseFirstChar(String className) {
StringBuilder stringBuilder = new StringBuilder(className.substring(0,1).toLowerCase());
stringBuilder.append(className.substring(1));
return stringBuilder.toString();
}
}
複製代碼
經過反射掃描某package
下全部類的工具類
package cn.tuhu.springioc.util;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
public class ClassUtils {
/** * 取得某個接口下全部實現這個接口的類 */
public static List<Class> getAllClassByInterface(Class c) {
List<Class> returnClassList = null;
if (c.isInterface()) {
// 獲取當前的包名
String packageName = c.getPackage().getName();
// 獲取當前包下以及子包下因此的類
List<Class<?>> allClass = getClasses(packageName);
if (allClass != null) {
returnClassList = new ArrayList<Class>();
for (Class classes : allClass) {
// 判斷是不是同一個接口
if (c.isAssignableFrom(classes)) {
// 自己不加入進去
if (!c.equals(classes)) {
returnClassList.add(classes);
}
}
}
}
}
return returnClassList;
}
/* * 取得某一類所在包的全部類名 不含迭代 */
public static String[] getPackageAllClassName(String classLocation, String packageName) {
// 將packageName分解
String[] packagePathSplit = packageName.split("[.]");
String realClassLocation = classLocation;
int packageLength = packagePathSplit.length;
for (int i = 0; i < packageLength; i++) {
realClassLocation = realClassLocation + File.separator + packagePathSplit[i];
}
File packeageDir = new File(realClassLocation);
if (packeageDir.isDirectory()) {
String[] allClassName = packeageDir.list();
return allClassName;
}
return null;
}
/** * 從包package中獲取全部的Class * * @param pack * @return */
public static List<Class<?>> getClasses(String packageName) {
// 第一個class類的集合
List<Class<?>> classes = new ArrayList<Class<?>>();
// 是否循環迭代
boolean recursive = true;
// 獲取包的名字 並進行替換
String packageDirName = packageName.replace('.', '/');
// 定義一個枚舉的集合 並進行循環來處理這個目錄下的things
Enumeration<URL> dirs;
try {
dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
// 循環迭代下去
while (dirs.hasMoreElements()) {
// 獲取下一個元素
URL url = dirs.nextElement();
// 獲得協議的名稱
String protocol = url.getProtocol();
// 若是是以文件的形式保存在服務器上
if ("file".equals(protocol)) {
// 獲取包的物理路徑
String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
// 以文件的方式掃描整個包下的文件 並添加到集合中
findAndAddClassesInPackageByFile(packageName, filePath, recursive, classes);
} else if ("jar".equals(protocol)) {
// 若是是jar包文件
// 定義一個JarFile
JarFile jar;
try {
// 獲取jar
jar = ((JarURLConnection) url.openConnection()).getJarFile();
// 今後jar包 獲得一個枚舉類
Enumeration<JarEntry> entries = jar.entries();
// 一樣的進行循環迭代
while (entries.hasMoreElements()) {
// 獲取jar裏的一個實體 能夠是目錄 和一些jar包裏的其餘文件 如META-INF等文件
JarEntry entry = entries.nextElement();
String name = entry.getName();
// 若是是以/開頭的
if (name.charAt(0) == '/') {
// 獲取後面的字符串
name = name.substring(1);
}
// 若是前半部分和定義的包名相同
if (name.startsWith(packageDirName)) {
int idx = name.lastIndexOf('/');
// 若是以"/"結尾 是一個包
if (idx != -1) {
// 獲取包名 把"/"替換成"."
packageName = name.substring(0, idx).replace('/', '.');
}
// 若是能夠迭代下去 而且是一個包
if ((idx != -1) || recursive) {
// 若是是一個.class文件 並且不是目錄
if (name.endsWith(".class") && !entry.isDirectory()) {
// 去掉後面的".class" 獲取真正的類名
String className = name.substring(packageName.length() + 1, name.length() - 6);
try {
// 添加到classes
classes.add(Class.forName(packageName + '.' + className));
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
return classes;
}
/** * 以文件的形式來獲取包下的全部Class * * @param packageName * @param packagePath * @param recursive * @param classes */
public static void findAndAddClassesInPackageByFile(String packageName, String packagePath, final boolean recursive, List<Class<?>> classes) {
// 獲取此包的目錄 創建一個File
File dir = new File(packagePath);
// 若是不存在或者 也不是目錄就直接返回
if (!dir.exists() || !dir.isDirectory()) {
return;
}
// 若是存在 就獲取包下的全部文件 包括目錄
File[] dirfiles = dir.listFiles(new FileFilter() {
// 自定義過濾規則 若是能夠循環(包含子目錄) 或則是以.class結尾的文件(編譯好的java類文件)
public boolean accept(File file) {
return (recursive && file.isDirectory()) || (file.getName().endsWith(".class"));
}
});
// 循環全部文件
for (File file : dirfiles) {
// 若是是目錄 則繼續掃描
if (file.isDirectory()) {
findAndAddClassesInPackageByFile(packageName + "." + file.getName(), file.getAbsolutePath(), recursive,
classes);
} else {
// 若是是java類文件 去掉後面的.class 只留下類名
String className = file.getName().substring(0, file.getName().length() - 6);
try {
// 添加到集合中去
classes.add(Class.forName(packageName + '.' + className));
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
}
}
複製代碼
Service
package cn.tuhu.springioc.service.impl;
import cn.tuhu.springioc.annotation.CustomComponent;
import cn.tuhu.springioc.service.UserService;
@CustomComponent
public class UserServiceImpl implements UserService {
public void add() {
System.out.println("UserServiceImp: insert user");
}
}
複製代碼
Controller
package cn.tuhu.springioc.controller;
import cn.tuhu.springioc.annotation.CustomComponent;
import cn.tuhu.springioc.annotation.CustomResource;
import cn.tuhu.springioc.service.UserService;
@CustomComponent
public class UserController {
@CustomResource
private UserService userServiceImpl;
public void add() {
System.out.println("UserController: receive request for add user");
userServiceImpl.add();
}
}
複製代碼
package cn.tuhu.springioc.annotation;
import cn.tuhu.springioc.controller.UserController;
import cn.tuhu.springioc.ioc.CustomAnnotationApplicationContext;
import org.junit.Test;
public class UserControllerTest {
@Test
public void add() {
CustomAnnotationApplicationContext context = new CustomAnnotationApplicationContext("cn.tuhu.springioc");
UserController userController = (UserController) context.getBean("userController");
userController.add();
}
}
UserController: receive request for add user
UserServiceImp: insert user
複製代碼