2,搭建Java Maven專案
我的idea是2024.1.1版本,建立普通Maven專案如下圖:
用的jdk8,專案名可以自己改,Archetype選圖中的第一個就行,之後點 create。
建立後空的Maven專案的程式碼結構就是下圖
再修改 pom.xml檔案
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>hsp-spring</artifactId>
<version>1.0-SNAPSHOT</version>
<name>Archetype - hsp-spring</name>
<url>http://maven.apache.org</url>
<dependencies>
<!--加入spring開發的基本包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.8</version>
</dependency>
<!--加入spring開發切面程式設計需要的包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.8</version>
</dependency>
</dependencies>
</project>
重新整理後,點選最右邊的 藍色m 圖示,再點 Dependencies,就能看到出現的對應 5.3.8 版本的jar包。
測試依賴注入的程式碼結構:
UserAction.java
package com.hspedu.spring.component;
import org.springframework.stereotype.Component;
/**
* 就是一個Controller
*/
//也可以使用@Controller
//在預設情況下 我們配置@Component @Controller @Service @Repository 是單例
//@Scope(value = "prototype") 表示以多例項形式,返回UserAction bean
@Component
public class UserAction {
}
UserDao.java
package com.hspedu.spring.component;
import org.springframework.stereotype.Component;
//可以使用@Repository
@Component
public class UserDao {
public void hi() {
System.out.println("UserDao-hi()---");
}
}
UserService.java
package com.hspedu.spring.component;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
//也可以使用@Service
@Component
public class UserService {
@Autowired
//也可以使用@Resource
private UserDao userDao;
public void m1() {
userDao.hi();
}
}
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 https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--配置自動掃描的包, 同時引入對應的名稱空間-->
<!--老師說明:
1. 如果我們是普通的java專案, beans.xml 放在src下
2. 如果我們是java maven 專案, beans.xml 放在 src/main/resources
-->
<context:component-scan base-package="com.hspedu.spring.component"/>
<!--啟用基於註解方式的AOP功能-->
<aop:aspectj-autoproxy/>
</beans>
AppMain.java
package com.hspedu.spring;
import com.hspedu.spring.component.UserAction;
import com.hspedu.spring.component.UserDao;
import com.hspedu.spring.component.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AppMain {
public static void main(String[] args) {
//測試看看是否可以得到spring容器中的bean , 同時看看依賴注入是否OK
ApplicationContext ioc =
new ClassPathXmlApplicationContext("beans.xml");
UserAction userAction = (UserAction) ioc.getBean("userAction");
UserAction userAction2 = (UserAction) ioc.getBean("userAction");
System.out.println("userAction=" + userAction);
System.out.println("userAction2=" + userAction2);
UserDao userDao = (UserDao) ioc.getBean("userDao");
System.out.println("userDao=" + userDao);
UserService userService = (UserService) ioc.getBean("userService");
System.out.println("userService=" + userService);
//測試一下當前的依賴注入
userService.m1();
}
}
執行結果:
12,編寫自己的Spring容器,掃描包得到bean的class物件
spring整體架構
本節分析示意圖
類載入器
建立模組
注意路徑是同級目錄
再改 ProjectStructure 和 Settings, 如下圖所示
本節程式碼結構:
annotation包
ComponentScan.java
package com.hspedu.spring.annotation;
//自己要寫的註解,之前寫過,所以直接拿來用
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 1. @Target(ElementType.TYPE)指定我們的ComponentScan註解可以修飾 Type程式元素
* 2. @Retention(RetentionPolicy.RUNTIME) 指定ComponentScan註解 保留範圍
* 3. String value() default ""; 表示ComponentScan 可以傳入 value
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ComponentScan {
//透過value可以指定要掃描的包
String value() default "";
}
Component.java
package com.hspedu.spring.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 Component {
//透過value可以給注入的bean/物件指定名字
String value() default "";
}
Component包
MonsterService.java
package com.hspedu.spring.component;
import com.hspedu.spring.annotation.Component;
@Component("monsterDao")
public class MonsterDao {
}
MonsterDao.java
package com.hspedu.spring.component;
//引入自己定義的註解
import com.hspedu.spring.annotation.Component;
/**
* 說明MonsterService 是一個Service
* 1. 如果指定了value,那麼在注入spring容器時,以你指定為準
* 2. 如果沒有指定value ,則使用類名首字母小寫名字
*/
@Component//(value = "monsterService") //把MonsterService注入我們自己的spring容器中
//@Scope(value = "prototype")
public class MonsterService {
}
Car.java
package com.hspedu.spring.component;
public class Car {
}
ioc包
HspSpringConfig.java
package com.hspedu.spring.ioc;
//之前寫過 基於註解的spring容器
import com.hspedu.spring.annotation.ComponentScan;
/**
* 這是一個配置類, 作用類似我們原生spring的 beans.xml 容器配置檔案,value指定了要掃描的包
*/
@ComponentScan(value = "com.hspedu.spring.component")
public class HspSpringConfig {
}
HspSpringApplicationContext.java
package com.hspedu.spring.ioc;
import com.hspedu.spring.annotation.Component;
import com.hspedu.spring.annotation.ComponentScan;
import java.io.File;
import java.lang.annotation.Annotation;
import java.net.URL;
import java.util.concurrent.ConcurrentHashMap;
/**
* HspSpringApplicationContext 類的作用類似Spring原生ioc容器
*/
public class HspSpringApplicationContext {
private Class configClass;
//構造器
public HspSpringApplicationContext(Class configClass) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
this.configClass = configClass;
//獲取要掃描的包
//1. 先得到HspSpringConfig配置的@ComponentScan(value = "com.hspedu.spring.component")
ComponentScan componentScan =
(ComponentScan)this.configClass.getDeclaredAnnotation(ComponentScan.class);
//2. 透過componentScan的value=> 即要掃描的包
String path = componentScan.value();
System.out.println("要掃描的包= " + path);
//得到要掃描的包下的所有資源(類 .class)
//1.得到類的載入器->APP類載入器
ClassLoader classLoader =
HspSpringApplicationContext.class.getClassLoader();
//2. 透過類的載入器獲取到要掃描的包的資源 url=》類似一個路徑
//一定要把. 替換成 /
path = path.replace(".","/");
URL resource =
classLoader.getResource("com/hspedu/spring/component");
System.out.println("resource=" + resource);
//3. 將要載入的資源(.class) 路徑下的檔案進行遍歷=>io
File file = new File(resource.getFile());
if (file.isDirectory()) {
File[] files = file.listFiles();
for (File f : files) {
String fileAbsolutePath = f.getAbsolutePath();
//這裡我們只處理.class檔案
if (fileAbsolutePath.endsWith(".class")) {
//1. 獲取到類名
String className =
fileAbsolutePath.substring(fileAbsolutePath.lastIndexOf("\\") + 1, fileAbsolutePath.indexOf(".class"));
//2. 獲取類的完整的路徑(全類名)
//老師解讀 path.replace("/",".") => com.hspedu.spring.component.
String classFullName = path.replace("/", ".") + "." + className;
//3. 判斷該類是不是需要注入容器, 就看該類是不是有註解 @Component @Service..
Class<?> clazz = classLoader.loadClass(classFullName);
if (clazz.isAnnotationPresent(Component.class)) {
//如果該類使用了@Component,說明是Spring bean
System.out.println("是一個Spring bean =" + clazz + " 類名=" + className);
} else {
//如果該類沒有使用了@Component,說明不是Spring bean
System.out.println("不是一個Spring bean =" + clazz + " 類名=" + className);
}
}
}
System.out.println("------------------------------------");
}
}
//編寫方法返回對容器中物件
public Object getBean(String name) {
return null;
}
}
AppMain.java
package com.hspedu.spring;
import com.hspedu.spring.ioc.HspSpringApplicationContext;
import com.hspedu.spring.ioc.HspSpringConfig;
public class AppMain {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
//把 配置檔案class物件傳進去後,然後再去容器中掃描包,容器中有注入的userService,userDao的bean物件
HspSpringApplicationContext hspSpringApplicationContext =
new HspSpringApplicationContext(HspSpringConfig.class);
}
}
執行結果:
15,掃描bean資訊封裝BeanDefinition,放入Map
分析示意圖
BeanDefinition物件裡有scope和class屬性,再把key=beanName,value=BeanDefinition物件,放進BeanDefinitionMap裡去。
在 pom.xml配置要用到的jar包
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>hsp-myspring</artifactId>
<version>1.0-SNAPSHOT</version>
<name>Archetype - hsp-myspring</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
</dependencies>
</project>
有如下圖所示的包就行
程式碼結構:
annotation包
這個包只新增了Scope.java,其他java檔案不變
Scope.java
package com.hspedu.spring.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Scope 可以指定Bean的作用範圍[singleton, prototype]
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Scope {
//透過value可以指定singleton,prototype
String value() default "";
}
component包
這個包 MonsterService.java 改變了,其他java檔案不變
MonsterService.java
package com.hspedu.spring.component;
//引入自己定義的註解
import com.hspedu.spring.annotation.Component;
import com.hspedu.spring.annotation.Scope;
/**
* 說明MonsterService 是一個Service
* 1. 如果指定了value,那麼在注入spring容器時,以你指定為準
* 2. 如果沒有指定value ,則使用類名首字母小寫名字
*/
@Component//(value = "monsterService") //把MonsterService注入我們自己的spring容器中
@Scope(value = "prototype")
public class MonsterService {
}
ioc包
這個包 只有HspSpringConfig.java不變
BeanDefinition.java
package com.hspedu.spring.ioc;
/**
* BeanDefinition 用於封裝/記錄Bean的資訊[1. scope 2 Bean對應的Class物件, 反射可以生對應的物件]
*/
public class BeanDefinition {
private String scope;
private Class clazz;
//可以根據需求,進行擴充套件
public String getScope() {
return scope;
}
public void setScope(String scope) {
this.scope = scope;
}
public Class getClazz() {
return clazz;
}
public void setClazz(Class clazz) {
this.clazz = clazz;
}
@Override
public String toString() {
return "BeanDefinition{" +
"scope='" + scope + '\'' +
", clazz=" + clazz +
'}';
}
}
HspSpringApplicationContext.java
package com.hspedu.spring.ioc;
import com.hspedu.spring.annotation.Component;
import com.hspedu.spring.annotation.ComponentScan;
import com.hspedu.spring.annotation.Scope;
import org.apache.commons.lang.StringUtils;
import java.io.File;
import java.lang.annotation.Annotation;
import java.net.URL;
import java.util.concurrent.ConcurrentHashMap;
/**
* HspSpringApplicationContext 類的作用類似Spring原生ioc容器
*/
public class HspSpringApplicationContext {
private Class configClass;
//定義屬性BeanDefinitionMap -> 存放BeanDefinition物件
private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap =
new ConcurrentHashMap<>();
//定義屬性SingletonObjects -> 存放單例物件,key是String,value是不確定的型別,所以用Object
private ConcurrentHashMap<String, Object> singletonObjects =
new ConcurrentHashMap<>();
//構造器
public HspSpringApplicationContext(Class configClass) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
//完成掃描指定包
beanDefinitionByScan(configClass);
System.out.println("beanDefinitionMap=" + beanDefinitionMap);
}
//該方法完成對指定包的掃描,並將Bean資訊封裝到BeanDefinition物件,在放入到Map
public void beanDefinitionByScan(Class configClass) throws ClassNotFoundException {
this.configClass = configClass;
//獲取要掃描的包
//1. 先得到HspSpringConfig配置的@ComponentScan(value = "com.hspedu.spring.component")
ComponentScan componentScan =
(ComponentScan)this.configClass.getDeclaredAnnotation(ComponentScan.class);
//2. 透過componentScan的value=> 即要掃描的包
String path = componentScan.value();
System.out.println("要掃描的包= " + path);
//得到要掃描的包下的所有資源(類 .class)
//1.得到類的載入器->APP類載入器
ClassLoader classLoader =
HspSpringApplicationContext.class.getClassLoader();
//2. 透過類的載入器獲取到要掃描的包的資源 url=》類似一個路徑
//一定要把. 替換成 /
path = path.replace(".","/");
URL resource =
classLoader.getResource("com/hspedu/spring/component");
System.out.println("resource=" + resource);
//3. 將要載入的資源(.class) 路徑下的檔案進行遍歷=>io
File file = new File(resource.getFile());
if (file.isDirectory()) {
File[] files = file.listFiles();
for (File f : files) {
String fileAbsolutePath = f.getAbsolutePath();
//這裡我們只處理.class檔案
if (fileAbsolutePath.endsWith(".class")) {
//1. 獲取到類名
String className =
fileAbsolutePath.substring(fileAbsolutePath.lastIndexOf("\\") + 1, fileAbsolutePath.indexOf(".class"));
//2. 獲取類的完整的路徑(全類名)
//老師解讀 path.replace("/",".") => com.hspedu.spring.component.
String classFullName = path.replace("/", ".") + "." + className;
//3. 判斷該類是不是需要注入容器, 就看該類是不是有註解 @Component @Service..
Class<?> clazz = classLoader.loadClass(classFullName);
if (clazz.isAnnotationPresent(Component.class)) {
//如果該類使用了@Component,說明是Spring bean
System.out.println("是一個Spring bean =" + clazz + " 類名=" + className);
System.out.println("------------------------------------");
//先得到beanName
//1. 得到Component註解
Component componentAnnotation = clazz.getDeclaredAnnotation(Component.class);
//2. 得到配置value值, 疑問 如果程式設計師沒有配置value[後面處理..]
String beanName = componentAnnotation.value();
if ("".equals(beanName)) {//如果沒有寫value
//將該類的類名首字母小寫作為beanName
beanName = StringUtils.uncapitalize(className);
}
//3.將Bean的資訊封裝到BeanDefinition物件->放入到BeanDefinitionMap
BeanDefinition beanDefinition = new BeanDefinition();
beanDefinition.setClazz(clazz);
//4. 獲取Scope值
if (clazz.isAnnotationPresent(Scope.class)) {
//如果配置了Scope, 獲取他配置的值
Scope scopeAnnotation = clazz.getDeclaredAnnotation(Scope.class);
beanDefinition.setScope(scopeAnnotation.value());
} else {
//如果沒有配置Scope, 就預設的值singleton
beanDefinition.setScope("singleton");
}
//將beanDefinition 物件放入到Map
beanDefinitionMap.put(beanName, beanDefinition);
} else {
//如果該類沒有使用了@Component,說明不是Spring bean
System.out.println("不是一個Spring bean =" + clazz + " 類名=" + className);
System.out.println("------------------------------------");
}
}
}
}
}
//編寫方法返回對容器中物件
public Object getBean(String name) {
return null;
}
}
AppMain.java
package com.hspedu.spring;
import com.hspedu.spring.ioc.HspSpringApplicationContext;
import com.hspedu.spring.ioc.HspSpringConfig;
public class AppMain {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
//把 配置檔案class物件傳進去後,然後再去容器中掃描包,容器中有注入的userService,userDao的bean物件
HspSpringApplicationContext hspSpringApplicationContext =
new HspSpringApplicationContext(HspSpringConfig.class);
System.out.println("ok");
}
}
執行結果:
有一部分太長了
Debug結果:
20,初始化Bean單例池,並完成getBean,createBean方法
分析示意圖
程式碼結構不變,ioc包的HspSpringApplicationContext.java和 AppMain.java變了
ioc包
HspSpringApplicationContext.java
package com.hspedu.spring.ioc;
import com.hspedu.spring.annotation.Component;
import com.hspedu.spring.annotation.ComponentScan;
import com.hspedu.spring.annotation.Scope;
import org.apache.commons.lang.StringUtils;
import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.Enumeration;
import java.util.concurrent.ConcurrentHashMap;
/**
* HspSpringApplicationContext 類的作用類似Spring原生ioc容器
*/
public class HspSpringApplicationContext {
private Class configClass;
//定義屬性BeanDefinitionMap -> 存放BeanDefinition物件
private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap =
new ConcurrentHashMap<>();
//定義屬性SingletonObjects -> 存放單例物件,key是String,value是不確定的型別,所以用Object
private ConcurrentHashMap<String, Object> singletonObjects =
new ConcurrentHashMap<>();
//構造器
public HspSpringApplicationContext(Class configClass) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
//完成掃描指定包
beanDefinitionByScan(configClass);
System.out.println("beanDefinitionMap=" + beanDefinitionMap);
//透過beanDefinitionMap , 初始化singletonObjects 單例池
//封裝成方法
//遍歷所有的beanDefinition物件
//這裡是java基礎->集合和列舉
Enumeration<String> keys = beanDefinitionMap.keys();//把key=beanName拿到了
while (keys.hasMoreElements()){
//得到beanName
String beanName = keys.nextElement();
//透過beanName 得到對應的beanDefinition物件
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
//判斷該bean是singleton還是prototype
if ("singleton".equalsIgnoreCase(beanDefinition.getScope())) {
//將該bean例項放入到singletonObjects 集合
Object bean = createBean(beanDefinition);
singletonObjects.put(beanName, bean);
}
}
System.out.println("singletonObjects 單例池=" + singletonObjects);
System.out.println("beanDefinitionMap=" + beanDefinitionMap);
}
//該方法完成對指定包的掃描,並將Bean資訊封裝到BeanDefinition物件,在放入到Map
public void beanDefinitionByScan(Class configClass) throws ClassNotFoundException {
this.configClass = configClass;
//獲取要掃描的包
//1. 先得到HspSpringConfig配置的@ComponentScan(value = "com.hspedu.spring.component")
ComponentScan componentScan =
(ComponentScan)this.configClass.getDeclaredAnnotation(ComponentScan.class);
//2. 透過componentScan的value=> 即要掃描的包
String path = componentScan.value();
System.out.println("要掃描的包= " + path);
//得到要掃描的包下的所有資源(類 .class)
//1.得到類的載入器->APP類載入器
ClassLoader classLoader =
HspSpringApplicationContext.class.getClassLoader();
//2. 透過類的載入器獲取到要掃描的包的資源 url=》類似一個路徑
//一定要把. 替換成 /
path = path.replace(".","/");
URL resource =
classLoader.getResource("com/hspedu/spring/component");
System.out.println("resource=" + resource);
//3. 將要載入的資源(.class) 路徑下的檔案進行遍歷=>io
File file = new File(resource.getFile());
if (file.isDirectory()) {
File[] files = file.listFiles();
for (File f : files) {
String fileAbsolutePath = f.getAbsolutePath();
//這裡我們只處理.class檔案
if (fileAbsolutePath.endsWith(".class")) {
//1. 獲取到類名
String className =
fileAbsolutePath.substring(fileAbsolutePath.lastIndexOf("\\") + 1, fileAbsolutePath.indexOf(".class"));
//2. 獲取類的完整的路徑(全類名)
//老師解讀 path.replace("/",".") => com.hspedu.spring.component.
String classFullName = path.replace("/", ".") + "." + className;
//3. 判斷該類是不是需要注入容器, 就看該類是不是有註解 @Component @Service..
Class<?> clazz = classLoader.loadClass(classFullName);
if (clazz.isAnnotationPresent(Component.class)) {
//如果該類使用了@Component,說明是Spring bean
System.out.println("是一個Spring bean =" + clazz + " 類名=" + className);
System.out.println("------------------------------------");
//先得到beanName
//1. 得到Component註解
Component componentAnnotation = clazz.getDeclaredAnnotation(Component.class);
//2. 得到配置value值, 疑問 如果程式設計師沒有配置value[後面處理..]
String beanName = componentAnnotation.value();
if ("".equals(beanName)) {//如果沒有寫value
//將該類的類名首字母小寫作為beanName
beanName = StringUtils.uncapitalize(className);
}
//3.將Bean的資訊封裝到BeanDefinition物件->放入到BeanDefinitionMap
BeanDefinition beanDefinition = new BeanDefinition();
beanDefinition.setClazz(clazz);
//4. 獲取Scope值
if (clazz.isAnnotationPresent(Scope.class)) {
//如果配置了Scope, 獲取他配置的值
Scope scopeAnnotation = clazz.getDeclaredAnnotation(Scope.class);
beanDefinition.setScope(scopeAnnotation.value());
} else {
//如果沒有配置Scope, 就預設的值singleton
beanDefinition.setScope("singleton");
}
//將beanDefinition 物件放入到Map
beanDefinitionMap.put(beanName, beanDefinition);
} else {
//如果該類沒有使用了@Component,說明不是Spring bean
System.out.println("不是一個Spring bean =" + clazz + " 類名=" + className);
System.out.println("------------------------------------");
}
}
}
}
}
//完成createBean(BeanDefinition beanDefinition) 方法
//老師說明,目前,我們先簡單實現
private Object createBean(BeanDefinition beanDefinition){
//得到Bean的clazz物件
Class clazz = beanDefinition.getClazz();
try {
//使用反射得到例項
Object instance = clazz.getDeclaredConstructor().newInstance();
return instance;
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
//如果反射建立物件失敗
return null;
}
//編寫方法返回對容器中物件
public Object getBean(String name) {
//老師加一個判斷,傳入的beanName是否在beanDefinitionMap中存在..
if (beanDefinitionMap.containsKey(name)) {//如果存在
BeanDefinition beanDefinition = beanDefinitionMap.get(name);
//得到beanDefinition的scope, 分別進行處理
if ("singleton".equalsIgnoreCase(beanDefinition.getScope())) {
//說明是單例配置, 就直接從單例池獲取
return singletonObjects.get(name);
} else {//如果不是單例的,我就呼叫createBean, 反射一個物件
return createBean(beanDefinition);
}
} else {//如果不存在
//丟擲一個空指標異常-小夥伴也可以自定義-Java基礎異常
throw new NullPointerException("沒有該bean");
}
}
}
AppMain.java
package com.hspedu.spring;
import com.hspedu.spring.component.MonsterDao;
import com.hspedu.spring.component.MonsterService;
import com.hspedu.spring.ioc.HspSpringApplicationContext;
import com.hspedu.spring.ioc.HspSpringConfig;
public class AppMain {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
//把 配置檔案class物件傳進去後,然後再去容器中掃描包,容器中有注入的userService,userDao的bean物件
HspSpringApplicationContext hspSpringApplicationContext =
new HspSpringApplicationContext(HspSpringConfig.class);
MonsterService monsterService = (MonsterService) hspSpringApplicationContext.getBean("monsterService");
MonsterService monsterService2 = (MonsterService) hspSpringApplicationContext.getBean("monsterService");
System.out.println("monsterService=" + monsterService);
System.out.println("monsterService2=" + monsterService2);
MonsterDao monsterDao = (MonsterDao) hspSpringApplicationContext.getBean("monsterDao");
MonsterDao monsterDao2 = (MonsterDao) hspSpringApplicationContext.getBean("monsterDao");
System.out.println("monsterDao=" + monsterDao);
System.out.println("monsterDao2=" + monsterDao2);
System.out.println("ok");
}
}
執行結果:
23,實現依賴注入
分析示意圖:
1,增加註解 @Autowired
2,在MonsterService類裡增加屬性MonsterDao
3,在createBean方法裡增加依賴注入的業務
程式碼結構:
以下程式碼改變了,其餘不變
annotation包
介面Autowired.java
package com.hspedu.spring.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {
}
component包
MonsterDao.java
package com.hspedu.spring.component;
import com.hspedu.spring.annotation.Component;
@Component("monsterDao")
public class MonsterDao {
public void hi() {
System.out.println("MonsterDao-hi()");
}
}
MonsterService.java
package com.hspedu.spring.component;
//引入自己定義的註解
import com.hspedu.spring.annotation.Autowired;
import com.hspedu.spring.annotation.Component;
import com.hspedu.spring.annotation.Scope;
/**
* 說明MonsterService 是一個Service
* 1. 如果指定了value,那麼在注入spring容器時,以你指定為準
* 2. 如果沒有指定value ,則使用類名首字母小寫名字
*/
@Component//(value = "monsterService") //把MonsterService注入我們自己的spring容器中
@Scope(value = "prototype")
public class MonsterService {
//這裡我們使用自己的@Autowired來修飾屬性
//表示該屬性,是透過容器完成依賴注入
//說明: 我們實現按照名字來進行組裝即可
@Autowired
private MonsterDao monsterDao;
public void m1() {
monsterDao.hi();
}
}
ioc包
HspSpringApplicationContext.java
package com.hspedu.spring.ioc;
import com.hspedu.spring.annotation.Autowired;
import com.hspedu.spring.annotation.Component;
import com.hspedu.spring.annotation.ComponentScan;
import com.hspedu.spring.annotation.Scope;
import org.apache.commons.lang.StringUtils;
import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.Enumeration;
import java.util.concurrent.ConcurrentHashMap;
/**
* HspSpringApplicationContext 類的作用類似Spring原生ioc容器
*/
public class HspSpringApplicationContext {
private Class configClass;
//定義屬性BeanDefinitionMap -> 存放BeanDefinition物件
private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap =
new ConcurrentHashMap<>();
//定義屬性SingletonObjects -> 存放單例物件,key是String,value是不確定的型別,所以用Object
private ConcurrentHashMap<String, Object> singletonObjects =
new ConcurrentHashMap<>();
//構造器
public HspSpringApplicationContext(Class configClass) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
//完成掃描指定包
beanDefinitionByScan(configClass);
System.out.println("beanDefinitionMap=" + beanDefinitionMap);
//透過beanDefinitionMap , 初始化singletonObjects 單例池
//封裝成方法
//遍歷所有的beanDefinition物件
//這裡是java基礎->集合和列舉
Enumeration<String> keys = beanDefinitionMap.keys();//把key=beanName拿到了
while (keys.hasMoreElements()){
//得到beanName
String beanName = keys.nextElement();
//透過beanName 得到對應的beanDefinition物件
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
//判斷該bean是singleton還是prototype
if ("singleton".equalsIgnoreCase(beanDefinition.getScope())) {
//將該bean例項放入到singletonObjects 集合
Object bean = createBean(beanDefinition);
singletonObjects.put(beanName, bean);
}
}
System.out.println("singletonObjects 單例池=" + singletonObjects);
System.out.println("beanDefinitionMap=" + beanDefinitionMap);
}
//該方法完成對指定包的掃描,並將Bean資訊封裝到BeanDefinition物件,在放入到Map
public void beanDefinitionByScan(Class configClass) throws ClassNotFoundException {
this.configClass = configClass;
//獲取要掃描的包
//1. 先得到HspSpringConfig配置的@ComponentScan(value = "com.hspedu.spring.component")
ComponentScan componentScan =
(ComponentScan)this.configClass.getDeclaredAnnotation(ComponentScan.class);
//2. 透過componentScan的value=> 即要掃描的包
String path = componentScan.value();
System.out.println("要掃描的包= " + path);
//得到要掃描的包下的所有資源(類 .class)
//1.得到類的載入器->APP類載入器
ClassLoader classLoader =
HspSpringApplicationContext.class.getClassLoader();
//2. 透過類的載入器獲取到要掃描的包的資源 url=》類似一個路徑
//一定要把. 替換成 /
path = path.replace(".","/");
URL resource =
classLoader.getResource("com/hspedu/spring/component");
System.out.println("resource=" + resource);
//3. 將要載入的資源(.class) 路徑下的檔案進行遍歷=>io
File file = new File(resource.getFile());
if (file.isDirectory()) {
File[] files = file.listFiles();
for (File f : files) {
String fileAbsolutePath = f.getAbsolutePath();
//這裡我們只處理.class檔案
if (fileAbsolutePath.endsWith(".class")) {
//1. 獲取到類名
String className =
fileAbsolutePath.substring(fileAbsolutePath.lastIndexOf("\\") + 1, fileAbsolutePath.indexOf(".class"));
//2. 獲取類的完整的路徑(全類名)
//老師解讀 path.replace("/",".") => com.hspedu.spring.component.
String classFullName = path.replace("/", ".") + "." + className;
//3. 判斷該類是不是需要注入容器, 就看該類是不是有註解 @Component @Service..
Class<?> clazz = classLoader.loadClass(classFullName);
if (clazz.isAnnotationPresent(Component.class)) {
//如果該類使用了@Component,說明是Spring bean
System.out.println("是一個Spring bean =" + clazz + " 類名=" + className);
System.out.println("------------------------------------");
//先得到beanName
//1. 得到Component註解
Component componentAnnotation = clazz.getDeclaredAnnotation(Component.class);
//2. 得到配置value值, 疑問 如果程式設計師沒有配置value[後面處理..]
String beanName = componentAnnotation.value();
if ("".equals(beanName)) {//如果沒有寫value
//將該類的類名首字母小寫作為beanName
beanName = StringUtils.uncapitalize(className);
}
//3.將Bean的資訊封裝到BeanDefinition物件->放入到BeanDefinitionMap
BeanDefinition beanDefinition = new BeanDefinition();
beanDefinition.setClazz(clazz);
//4. 獲取Scope值
if (clazz.isAnnotationPresent(Scope.class)) {
//如果配置了Scope, 獲取他配置的值
Scope scopeAnnotation = clazz.getDeclaredAnnotation(Scope.class);
beanDefinition.setScope(scopeAnnotation.value());
} else {
//如果沒有配置Scope, 就預設的值singleton
beanDefinition.setScope("singleton");
}
//將beanDefinition 物件放入到Map
beanDefinitionMap.put(beanName, beanDefinition);
} else {
//如果該類沒有使用了@Component,說明不是Spring bean
System.out.println("不是一個Spring bean =" + clazz + " 類名=" + className);
System.out.println("------------------------------------");
}
}
}
}
}
//完成createBean(BeanDefinition beanDefinition) 方法
//老師說明,目前,我們先簡單實現
private Object createBean(BeanDefinition beanDefinition){
//得到Bean的clazz物件
Class clazz = beanDefinition.getClazz();
try {
//使用反射得到例項
Object instance = clazz.getDeclaredConstructor().newInstance();
//老師分析: 這裡老韓會加入依賴注入的業務邏輯!!!
//1. 遍歷當前要建立的物件的所有欄位,看看哪個欄位有@Autowired
for (Field declaredField : clazz.getDeclaredFields()) {
//2. 判斷這個欄位是否有@Autowired
if (declaredField.isAnnotationPresent(Autowired.class)) {
//3. 得到這個欄位名字
String name = declaredField.getName();
//4. 透過getBean方法來獲取要組裝物件
Object bean = getBean(name);
//5. 進行組裝
declaredField.setAccessible(true);//因為屬性是pirvate, 需要暴破
declaredField.set(instance, bean);
}
}
return instance;
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
//如果反射建立物件失敗
return null;
}
//編寫方法返回對容器中物件
public Object getBean(String name) {
//老師加一個判斷,傳入的beanName是否在beanDefinitionMap中存在..
if (beanDefinitionMap.containsKey(name)) {//如果存在
BeanDefinition beanDefinition = beanDefinitionMap.get(name);
//得到beanDefinition的scope, 分別進行處理
if ("singleton".equalsIgnoreCase(beanDefinition.getScope())) {
//說明是單例配置, 就直接從單例池獲取
return singletonObjects.get(name);
} else {//如果不是單例的,我就呼叫createBean, 反射一個物件
return createBean(beanDefinition);
}
} else {//如果不存在
//丟擲一個空指標異常-小夥伴也可以自定義-Java基礎異常
throw new NullPointerException("沒有該bean");
}
}
}
AppMain.java
package com.hspedu.spring;
import com.hspedu.spring.component.MonsterDao;
import com.hspedu.spring.component.MonsterService;
import com.hspedu.spring.ioc.HspSpringApplicationContext;
import com.hspedu.spring.ioc.HspSpringConfig;
public class AppMain {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
//把 配置檔案class物件傳進去後,然後再去容器中掃描包,容器中有注入的userService,userDao的bean物件
HspSpringApplicationContext hspSpringApplicationContext =
new HspSpringApplicationContext(HspSpringConfig.class);
//測試一下依賴注入的功能
MonsterService monsterService =
(MonsterService)hspSpringApplicationContext.getBean("monsterService");
monsterService.m1();
}
}
執行結果:
26,實現BeanPostProcessor機制
程式碼結構:
annotation包
介面Autowired.java
package com.hspedu.spring.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {
}
Component包
Car.java
package com.hspedu.spring.component;
import com.hspedu.spring.annotation.Component;
import com.hspedu.spring.processor.InitializingBean;
@Component
public class Car implements InitializingBean {
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("Car的初始化方法...");
}
}
MonsterDao.java
package com.hspedu.spring.component;
import com.hspedu.spring.annotation.Component;
import com.hspedu.spring.processor.InitializingBean;
@Component("monsterDao")
public class MonsterDao implements InitializingBean {
public void hi() {
System.out.println("MonsterDao-hi()");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("MonsterDao 初始化方法被呼叫...");
}
}
MonsterService.java
package com.hspedu.spring.component;
//引入自己定義的註解
import com.hspedu.spring.annotation.Autowired;
import com.hspedu.spring.annotation.Component;
import com.hspedu.spring.annotation.Scope;
import com.hspedu.spring.processor.InitializingBean;
/**
* 說明MonsterService 是一個Service
* 1. 如果指定了value,那麼在注入spring容器時,以你指定為準
* 2. 如果沒有指定value ,則使用類名首字母小寫名字
*/
@Component//(value = "monsterService") //把MonsterService注入我們自己的spring容器中
@Scope(value = "prototype")
public class MonsterService implements InitializingBean {
//這裡我們使用自己的@Autowired來修飾屬性
//表示該屬性,是透過容器完成依賴注入
//說明: 我們實現按照名字來進行組裝即可
@Autowired
private MonsterDao monsterDao;
public void m1() {
monsterDao.hi();
}
/**
* 老師解讀
* 1. afterPropertiesSet就是在bean的setter方法執行完畢後被spring容器呼叫
* 2 即就是初始化方法
* @throws Exception
*/
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("MonsterService 初始化方法被呼叫 程式設計師在這裡加入初始化的業務..");
}
}
HspBeanPostProcessor.java
package com.hspedu.spring.component;
import com.hspedu.spring.annotation.Component;
import com.hspedu.spring.processor.BeanPostProcessor;
/**
* 說明
* 1. 這是我們自己的一個後置處理器
* 2. 實現了BeanPostProcessor
* 3. 我們可以重寫before和after方法
* 4. 在Spring容器中,仍然把HspBeanPostProcessor當做一個Bean物件, 要在注入到容器
* 5. @Component 標識
* 6. 我們要讓HspBeanPostProcessor成為真正的後置處理器, 需要在容器中加入業務程式碼
* 7. 還要考慮多個後置處理器物件注入到容器問題
*/
@Component
public class HspBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
//這裡請小夥伴一定要體會到,後置處理器是會容器的建立的bean生效
//,相當於是可以對多個物件程式設計, 切面程式設計
//日誌,許可權,身份, 事務.......
if (bean instanceof Car) {
System.out.println("這是一個Car物件, 我可以處理");
//((Car)bean)
}
System.out.println("後置處理器HspBeanPostProcessor Before呼叫 bean型別="
+ bean.getClass() + " bean的名字=" + beanName);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
System.out.println("後置處理器HspBeanPostProcessor After呼叫 bean型別="
+ bean.getClass() + " bean的名字=" + beanName);
return bean;
}
}
ioc包
HspSpringApplicationContext.java
package com.hspedu.spring.ioc;
import com.hspedu.spring.annotation.Autowired;
import com.hspedu.spring.annotation.Component;
import com.hspedu.spring.annotation.ComponentScan;
import com.hspedu.spring.annotation.Scope;
import com.hspedu.spring.processor.BeanPostProcessor;
import com.hspedu.spring.processor.InitializingBean;
import org.apache.commons.lang.StringUtils;
import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
/**
* HspSpringApplicationContext 類的作用類似Spring原生ioc容器
*/
public class HspSpringApplicationContext {
private Class configClass;
//定義屬性BeanDefinitionMap -> 存放BeanDefinition物件
private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap =
new ConcurrentHashMap<>();
//定義屬性SingletonObjects -> 存放單例物件,key是String,value是不確定的型別,所以用Object
private ConcurrentHashMap<String, Object> singletonObjects =
new ConcurrentHashMap<>();
//定義一個屬性beanPostProcessorList, => 存放後置處理器
private List<BeanPostProcessor> beanPostProcessorList =
new ArrayList<>();
//構造器
public HspSpringApplicationContext(Class configClass) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
//完成掃描指定包
beanDefinitionByScan(configClass);
System.out.println("beanDefinitionMap=" + beanDefinitionMap);
//透過beanDefinitionMap , 初始化singletonObjects 單例池
//封裝成方法
//遍歷所有的beanDefinition物件
//這裡是java基礎->集合和列舉
Enumeration<String> keys = beanDefinitionMap.keys();//把key=beanName拿到了
while (keys.hasMoreElements()){
//得到beanName
String beanName = keys.nextElement();
//透過beanName 得到對應的beanDefinition物件
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
//判斷該bean是singleton還是prototype
if ("singleton".equalsIgnoreCase(beanDefinition.getScope())) {
//將該bean例項放入到singletonObjects 集合
Object bean = createBean(beanName, beanDefinition);
singletonObjects.put(beanName, bean);
}
}
// System.out.println("singletonObjects 單例池=" + singletonObjects);
// System.out.println("beanDefinitionMap=" + beanDefinitionMap);
}
//該方法完成對指定包的掃描,並將Bean資訊封裝到BeanDefinition物件,在放入到Map
public void beanDefinitionByScan(Class configClass) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
this.configClass = configClass;
//獲取要掃描的包
//1. 先得到HspSpringConfig配置的@ComponentScan(value = "com.hspedu.spring.component")
ComponentScan componentScan =
(ComponentScan)this.configClass.getDeclaredAnnotation(ComponentScan.class);
//2. 透過componentScan的value=> 即要掃描的包
String path = componentScan.value();
System.out.println("要掃描的包= " + path);
//得到要掃描的包下的所有資源(類 .class)
//1.得到類的載入器->APP類載入器
ClassLoader classLoader =
HspSpringApplicationContext.class.getClassLoader();
//2. 透過類的載入器獲取到要掃描的包的資源 url=》類似一個路徑
//一定要把. 替換成 /
path = path.replace(".","/");
URL resource =
classLoader.getResource("com/hspedu/spring/component");
System.out.println("resource=" + resource);
//3. 將要載入的資源(.class) 路徑下的檔案進行遍歷=>io
File file = new File(resource.getFile());
if (file.isDirectory()) {
File[] files = file.listFiles();
for (File f : files) {
String fileAbsolutePath = f.getAbsolutePath();
//這裡我們只處理.class檔案
if (fileAbsolutePath.endsWith(".class")) {
//1. 獲取到類名
String className =
fileAbsolutePath.substring(fileAbsolutePath.lastIndexOf("\\") + 1, fileAbsolutePath.indexOf(".class"));
//2. 獲取類的完整的路徑(全類名)
//老師解讀 path.replace("/",".") => com.hspedu.spring.component.
String classFullName = path.replace("/", ".") + "." + className;
//3. 判斷該類是不是需要注入容器, 就看該類是不是有註解 @Component @Service..
Class<?> clazz = classLoader.loadClass(classFullName);
if (clazz.isAnnotationPresent(Component.class)) {
//如果該類使用了@Component,說明是Spring bean
System.out.println("是一個Spring bean =" + clazz + " 類名=" + className);
//老師說明
//1. 為了方便,老韓這裡將後置處理器放入到一個ArrayList
//2. 如果發現是一個後置處理器, 放入到 beanPostProcessorList
//3. 在原生的Spring容器中, 對後置處理器還是走的getBean, createBean
// , 但是需要我們在singletonObjects 加入相應的業務邏輯
//4. 因為這裡我們是為了講解後置處理去的機制,我就簡化
//5. 如果小夥伴們,仍然走以前的邏輯,也可以,就是要麻煩一點
//判斷當前的這個clazz有沒有實現BeanPostProcessor
//說明, 這裡我們不能使用 instanceof 來判斷clazz是否實現了BeanPostProcessor
//原因: clazz不是一個例項物件,而是一個類物件/clazz, 使用isAssignableFrom
//小夥伴將其當做一個語法理解
if (BeanPostProcessor.class.isAssignableFrom(clazz)) {
BeanPostProcessor beanPostProcessor =
(BeanPostProcessor) clazz.newInstance();
//放入到beanPostProcessorList
beanPostProcessorList.add(beanPostProcessor);
continue;
}
System.out.println("-------------------------------------------------[pyuyu");
//先得到beanName
//1. 得到Component註解
Component componentAnnotation = clazz.getDeclaredAnnotation(Component.class);
//2. 得到配置value值, 疑問 如果程式設計師沒有配置value[後面處理..]
String beanName = componentAnnotation.value();
if ("".equals(beanName)) {//如果沒有寫value
//將該類的類名首字母小寫作為beanName
beanName = StringUtils.uncapitalize(className);
}
//3.將Bean的資訊封裝到BeanDefinition物件->放入到BeanDefinitionMap
BeanDefinition beanDefinition = new BeanDefinition();
beanDefinition.setClazz(clazz);
//4. 獲取Scope值
if (clazz.isAnnotationPresent(Scope.class)) {
//如果配置了Scope, 獲取他配置的值
Scope scopeAnnotation = clazz.getDeclaredAnnotation(Scope.class);
beanDefinition.setScope(scopeAnnotation.value());
} else {
//如果沒有配置Scope, 就預設的值singleton
beanDefinition.setScope("singleton");
}
//將beanDefinition 物件放入到Map
beanDefinitionMap.put(beanName, beanDefinition);
} else {
//如果該類沒有使用了@Component,說明不是Spring bean
System.out.println("不是一個Spring bean =" + clazz + " 類名=" + className);
System.out.println("------------------------------------");
}
}
}
}
}
//完成createBean(BeanDefinition beanDefinition) 方法
//老師說明,目前,我們先簡單實現
private Object createBean(String beanName, BeanDefinition beanDefinition){
//得到Bean的clazz物件
Class clazz = beanDefinition.getClazz();
try {
//使用反射得到例項
Object instance = clazz.getDeclaredConstructor().newInstance();
//老師分析: 這裡老韓會加入依賴注入的業務邏輯!!!
//1. 遍歷當前要建立的物件的所有欄位,看看哪個欄位有@Autowired
for (Field declaredField : clazz.getDeclaredFields()) {
//2. 判斷這個欄位是否有@Autowired
if (declaredField.isAnnotationPresent(Autowired.class)) {
//3. 得到這個欄位名字
String name = declaredField.getName();
//4. 透過getBean方法來獲取要組裝物件
Object bean = getBean(name);
//5. 進行組裝
declaredField.setAccessible(true);//因為屬性是pirvate, 需要暴破
declaredField.set(instance, bean);
}
}
System.out.println("=====建立好例項====" + instance);
//我們在Bean的初始化方法前,呼叫後置處理器的before方法
for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
//在後置處理器的before方法,可以對容器的bean例項進行處理
//然後返回處理後新的bean例項, 相當於做一個前置處理
Object current =
beanPostProcessor.postProcessBeforeInitialization(instance, beanName);
//不是空,就還是原先的instance
if (current != null) {
instance = current;
}
}
//這裡判斷是否要執行Bean初始化方法
//1. 判斷當前建立的Bean物件是否實現了InitializingBean
//2. instanceof java基礎中講 表判斷某個物件的執行型別是不是某個型別或者
// 某個型別的子型別
//3. 這裡就使用到介面程式設計
if (instance instanceof InitializingBean) {
//3.將instance轉成InitializingBean型別
try {
((InitializingBean) instance).afterPropertiesSet();
} catch (Exception e) {
e.printStackTrace();
}
}
//我們在Bean的初始化方法後,呼叫後置處理器的after方法
for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
//在後置處理器的after方法,可以對容器的bean例項進行處理
//然後返回處理後的bean例項, 相當於做一個後置處理
//原生Spring容器,比我們這個還要複雜
Object current =
beanPostProcessor.postProcessAfterInitialization(instance, beanName);
if(current != null) {
instance = current;
}
}
System.out.println("----------------------------------------------");
return instance;
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
//如果反射建立物件失敗
return null;
}
//編寫方法返回對容器中物件
public Object getBean(String name) {
//老師加一個判斷,傳入的beanName是否在beanDefinitionMap中存在..
if (beanDefinitionMap.containsKey(name)) {//如果存在
BeanDefinition beanDefinition = beanDefinitionMap.get(name);
//得到beanDefinition的scope, 分別進行處理
if ("singleton".equalsIgnoreCase(beanDefinition.getScope())) {
//說明是單例配置, 就直接從單例池獲取
return singletonObjects.get(name);
} else {//如果不是單例的,我就呼叫createBean, 反射一個物件
return createBean(name, beanDefinition);
}
} else {//如果不存在
//丟擲一個空指標異常-小夥伴也可以自定義-Java基礎異常
throw new NullPointerException("沒有該bean");
}
}
}
processor包
InitializingBean.java
package com.hspedu.spring.processor;
/**
* 老師解讀
* 1. 我們根據原生Spring 定義了一個InitializingBean
* 2. 該InitializingBean介面有一個方法void afterPropertiesSet() throws Exception;
* 3. afterPropertiesSet() 在Bean的 setter後執行,即就是我們原來的初始化方法
* 4. 當一個Bean實現這個介面後,就實現afterPropertiesSet() , 這個方法就是初始化方法
*/
public interface InitializingBean {
void afterPropertiesSet() throws Exception;
}
BeanPostProcessor.java
package com.hspedu.spring.processor;
/**
* 老師解讀
* 1. 參考原生Spring容器定義一個介面BeanPostProcessor
* 2. 該介面有兩個方法postProcessBeforeInitialization 和 postProcessAfterInitialization
* 3. 這兩個方法,會對Spring容器的所有Bean生效, 已經是切面程式設計的概念.
*/
public interface BeanPostProcessor {
/**
* 老師說明
* 1. postProcessBeforeInitialization在Bean的初始化方法前呼叫
* @param bean
* @param beanName
* @return
*/
default Object postProcessBeforeInitialization(Object bean, String beanName) {
return bean;
}
/**
* 1. postProcessAfterInitialization在Bean的初始化方法後呼叫
* @param bean
* @param beanName
* @return
*/
default Object postProcessAfterInitialization(Object bean, String beanName) {
return bean;
}
}
AppMain.java
package com.hspedu.spring;
import com.hspedu.spring.component.MonsterDao;
import com.hspedu.spring.component.MonsterService;
import com.hspedu.spring.ioc.HspSpringApplicationContext;
import com.hspedu.spring.ioc.HspSpringConfig;
public class AppMain {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
//把 配置檔案class物件傳進去後,然後再去容器中掃描包,容器中有注入的userService,userDao的bean物件
HspSpringApplicationContext hspSpringApplicationContext =
new HspSpringApplicationContext(HspSpringConfig.class);
//測試一下依賴注入的功能
MonsterService monsterService =
(MonsterService)hspSpringApplicationContext.getBean("monsterService");
monsterService.m1();
}
}
執行結果:
35,實現AOP機制
AOP機制需要 Bean後置處理機制和動態代理機制
程式碼結構:
annotation包
註解Aspect.java
package com.hspedu.spring.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface Aspect {
String value() default "";
}
component包
SmartAnimalable.java
package com.hspedu.spring.component;
public interface SmartAnimalable {
float getSum(float i, float j);
float getSub(float i, float j);
}
SmartDog.java
package com.hspedu.spring.component;
import com.hspedu.spring.annotation.Component;
@Component(value = "smartDog")
public class SmartDog implements SmartAnimalable{
@Override
public float getSum(float i, float j) {
float res = i + j;
System.out.println("SmartDog-getSum-res=" + res);
return res;
}
@Override
public float getSub(float i, float j) {
float res = i - j;
System.out.println("SmartDog-getSub-res=" + res);
return res;
}
}
SmartAnimalAspect.java
package com.hspedu.spring.component;
import com.hspedu.spring.annotation.Aspect;
import com.hspedu.spring.annotation.Component;
/**
* 老師說明:SmartAnimalAspect當做一個切面類來使用
* ,後面老師再分析如何做的更加靈活
*/
@Aspect //我們的註解
@Component //這是實現了
public class SmartAnimalAspect {
public static void showBeginLog() {
System.out.println("前置通知..");
}
public static void showSuccessLog() {
System.out.println("返回通知..");
}
}
processor包
BeanPostProcessor.java
package com.hspedu.spring.component;
import com.hspedu.spring.annotation.Component;
import com.hspedu.spring.processor.BeanPostProcessor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 說明
* 1. 這是我們自己的一個後置處理器
* 2. 實現了BeanPostProcessor
* 3. 我們可以重寫before和after方法
* 4. 在Spring容器中,仍然把HspBeanPostProcessor當做一個Bean物件, 要在注入到容器
* 5. @Component 標識
* 6. 我們要讓HspBeanPostProcessor成為真正的後置處理器, 需要在容器中加入業務程式碼
* 7. 還要考慮多個後置處理器物件注入到容器問題
*/
@Component
public class HspBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
//這裡請小夥伴一定要體會到,後置處理器是會容器的建立的bean生效
//,相當於是可以對多個物件程式設計, 切面程式設計
//日誌,許可權,身份, 事務.......
if (bean instanceof Car) {
System.out.println("這是一個Car物件, 我可以處理");
//((Car)bean)
}
System.out.println("後置處理器HspBeanPostProcessor Before呼叫 bean型別="
+ bean.getClass() + " bean的名字=" + beanName);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
System.out.println("後置處理器HspBeanPostProcessor After呼叫 bean型別="
+ bean.getClass() + " bean的名字=" + beanName);
//實現AOP, 返回代理物件, 即對Bean進行包裝
if ("smartDog".equals(beanName)) {
//使用Jdk的動態代理,返回返回bean的代理物件
//如果沒有印象的小夥伴,回去看老韓講過的動態代理
Object proxyInstance = Proxy.newProxyInstance(HspBeanPostProcessor.class.getClassLoader(),
bean.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("method=" + method.getName());
Object result = null;
//假如我們進行前置通知+返回通知 處理的方法是getSum
//後面可以透過註解來做的更加靈活
if ("getSum".equals(method.getName())) {
SmartAnimalAspect.showBeginLog();
result = method.invoke(bean, args);//執行目標方法
//進行返回通知的處理
SmartAnimalAspect.showSuccessLog();
} else {
result = method.invoke(bean, args);//執行目標方法
}
return result;
}
});
//如果bean是需要返回代理物件的, 這裡就直接return proxyInstance
return proxyInstance;
}
//如果不需要AOP, 返回 bean
return bean;
}
}
AppMain.java
package com.hspedu.spring;
import com.hspedu.spring.component.MonsterDao;
import com.hspedu.spring.component.MonsterService;
import com.hspedu.spring.component.SmartAnimalable;
import com.hspedu.spring.ioc.HspSpringApplicationContext;
import com.hspedu.spring.ioc.HspSpringConfig;
public class AppMain {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
//把 配置檔案class物件傳進去後,然後再去容器中掃描包,容器中有注入的userService,userDao的bean物件
HspSpringApplicationContext hspSpringApplicationContext =
new HspSpringApplicationContext(HspSpringConfig.class);
//這裡我們測試一下AOP機制是否生效了
SmartAnimalable smartDog = (SmartAnimalable)hspSpringApplicationContext.getBean("smartDog");
//System.out.println("smartDog=" + smartDog.getClass());
smartDog.getSum(10, 2);
smartDog.getSub(10,2);
System.out.println("ok");
}
}
執行結果:
截了一部分
44,JdbcTemplate使用
1. 引入使用 JdbcTemplate 需要的 jar 包
2. 建立資料庫 spring 和表 monster
注意:資料庫 mysql 版本是5.7.19。
在 SQLyog 軟體裡 建立資料庫和表,一句一句執行。
-- 建立資料庫
CREATE DATABASE spring
USE spring
-- 建立表 monster
CREATE TABLE monster(
id INT PRIMARY KEY,
`name` VARCHAR(64) NOT NULL DEFAULT '',
skill VARCHAR(64) NOT NULL DEFAULT ''
)CHARSET=utf8
INSERT INTO monster VALUES(100, '青牛怪', '吐火');
INSERT INTO monster VALUES(200, '黃袍怪', '吐煙');
INSERT INTO monster VALUES(300, '蜘蛛怪', '吐絲');
建立結果:
3. 建立配置檔案 src/jdbc.properties
jdbc.properties
改成自己的密碼
jdbc.user=root
jdbc.pwd=123
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring?useUnicode=true&characterEncoding=UTF-8
4. 建立配置檔案 src/JdbcTemplate_ioc.xml
JdbcTemplate_ioc.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 引入外部屬性檔案 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 配置資料來源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="${jdbc.userName}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="driverClass" value="${jdbc.driverClass}"></property>
<property name="jdbcUrl" value="${jdbc.url}"></property>
</bean>
</beans>
5,測試是否可以正確得到資料來源
JdbcTemplateTest.java
package com.hspedu.spring.test;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
public class JdbcTemplateTest {
@Test
public void testDatasourceByJdbcTemplate() throws SQLException {
//獲取到容器
ApplicationContext ioc =
new ClassPathXmlApplicationContext("JdbcTemplate_ioc.xml");
DataSource dataSource = ioc.getBean(DataSource.class);
Connection connection = dataSource.getConnection();
System.out.println("獲取到connection= " + connection);
connection.close();
System.out.println("ok");
}
}
執行結果:
46,JdbcTemplate-新增資料
1,配置 JdbcTemplate_ioc.xml,將資料來源分配給 JdbcTemplate bean
JdbcTemplate_ioc.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--引入外部的jdbc.properties檔案-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--配置資料來源物件-DataSoruce-->
<bean class="com.mchange.v2.c3p0.ComboPooledDataSource" id="dataSource">
<!--給資料來源物件配置屬性值-->
<property name="user" value="${jdbc.user}"/>
<property name="password" value="${jdbc.pwd}"/>
<property name="driverClass" value="${jdbc.driver}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
</bean>
<!--配置JdbcTemplate物件-->
<bean class="org.springframework.jdbc.core.JdbcTemplate" id="jdbcTemplate">
<!--給JdbcTemplate物件配置屬性dataSource, 注意:後面是引用的上面的資料來源物件dataSource-->
<property name="dataSource" ref="dataSource"/>
</bean>
</beans>
2. 修改 JdbcTemplateTest.java,新增一個新的 monster
JdbcTemplateTest.java
package com.hspedu.spring.test;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
public class JdbcTemplateTest {
//測試透過JdbcTemplate物件完成新增資料
@Test
public void addDataByJdbcTemplate() {
//獲取到容器
ApplicationContext ioc =
new ClassPathXmlApplicationContext("JdbcTemplate_ioc.xml");
//獲取JdbcTemplate物件
JdbcTemplate jdbcTemplate = ioc.getBean(JdbcTemplate.class);
//1. 新增方式1
//String sql = "INSERT INTO monster VALUES(400, '紅孩兒', '槍法')";
//jdbcTemplate.execute(sql);
//2. 新增方式2,?用來佔位
String sql = "INSERT INTO monster VALUES(?, ?, ?)";
//affected表示 執行後表受影響的記錄數
int affected = jdbcTemplate.update(sql, 500, "紅孩兒2", "槍法2");
System.out.println("add ok affected=" + affected);
}
}
執行結果:
sqlyog資料庫結果:
47,JdbcTemplate-修改資料
1,修改 JdbcTemplateTest.java,更新一個 monster 的 skill
JdbcTemplateTest.java
package com.hspedu.spring.test;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
public class JdbcTemplateTest {
//測試透過JdbcTemplate物件完成修改資料
@Test
public void updateDataByJdbcTemplate() {
//獲取到容器
ApplicationContext ioc =
new ClassPathXmlApplicationContext("JdbcTemplate_ioc.xml");
//獲取JdbcTemplate物件
JdbcTemplate jdbcTemplate = ioc.getBean(JdbcTemplate.class);
//組織SQL
String sql = "UPDATE monster SET skill=? WHERE id=?";
int affected = jdbcTemplate.update(sql, "美女計", 500);
System.out.println("update ok affected= " + affected);
}
}
執行結果:
sqlyog資料庫結果:
48,JdbcTemplate-批次處理
1,修改 JdbcTemplateTest.java,批次新增二個 monster 白蛇精和青蛇精
JdbcTemplateTest.java
package com.hspedu.spring.test;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
public class JdbcTemplateTest {
//批次新增二個monster 白蛇精和青蛇精
//這裡有一個使用API的技巧
/**
* 老師說明
* 1. 對於某個類, 有很多API, 使用的步驟
* 2. 老韓的使用技巧(1) 先確定API名字 (2) 根據API提供相應的引數 [組織引數]
* (3) 把自己的呼叫思路清晰 (4) 根據API, 可以推測類似的用法和功能
*/
/**
* batch add data
* 批次新增二個monster 白蛇精和青蛇精-update(sql,List<Object[]>)
*/
@Test
public void addBatchDataByJdbcTemplate() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("JdbcTemplate_ioc.xml");
//得到JdbcTemplate bean
JdbcTemplate jdbcTemplate = ioc.getBean(JdbcTemplate.class);//新增..
//1. 先確定,猜測API名稱 batchUpdate[如果出現問題,才重新玩]
//public int[] batchUpdate(String sql, List<Object[]> batchArgs){}
//2. 準備引數
String sql = "INSERT INTO monster VALUES(?, ?, ?)";
List<Object[]> batchArgs = new ArrayList<>();
batchArgs.add(new Object[]{600, "老鼠精", "偷吃糧食"});
batchArgs.add(new Object[]{700, "老貓精", "抓老鼠"});
//3. 呼叫
//說明:返回結果是一個陣列,每個元素對應上面的sql語句對錶的影響記錄數
int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);
//輸出
for (int anInt : ints) {
System.out.println("anInt=" + anInt);
}
System.out.println("batch add ok..");
}
}
執行結果:
sqlyog資料庫結果:
49,JdbcTemplate-查詢後封裝成物件
1. 查詢 id=100 的 monster 並封裝到 Monster 實體物件
JdbcTemplateTest.java
package com.hspedu.spring.test;
import com.hspedu.spring.bean.Monster;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
public class JdbcTemplateTest {
//查詢id=100的monster並封裝到Monster實體物件[在實際開發中,非常有用]
@Test
public void selectDataByJdbcTemplate() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("JdbcTemplate_ioc.xml");
//得到JdbcTemplate bean
JdbcTemplate jdbcTemplate = ioc.getBean(JdbcTemplate.class);
//組織SQL
//透過BeanPropertyRowMapper獲取rowmapper 是一個介面,可以將查詢的結果,封裝到你指定的Monster物件中.
//1. 確定API : queryForObject()
//public <T> T queryForObject(String sql, RowMapper<T> rowMapper, @Nullable Object... args)
//2.準備引數
String sql = "SELECT id AS monsterId, NAME, skill FROM monster WHERE id = 100";
//使用RowMapper 介面來對返回的資料,進行一個封裝-》底層使用的反射->setter
//這裡有一個細節: 你查詢的記錄的表的欄位需要和 Monster的物件欄位名保持一致
RowMapper<Monster> rowMapper = new BeanPropertyRowMapper<>(Monster.class);
//jdbcTemplate
Monster monster = jdbcTemplate.queryForObject(sql, rowMapper);
System.out.println("monster= " + monster);
System.out.println("查詢ok");
}
}
執行結果:
50,JdbcTemplate-查詢後封裝成物件集合
1. 查詢 id>=100 的 monster 並封裝到 Monster 實體物件
JdbcTemplateTest.java
package com.hspedu.spring.test;
import com.hspedu.spring.bean.Monster;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
public class JdbcTemplateTest {
//查詢id>=200的monster並封裝到Monster實體物件
/**
* 查詢多條記錄
*/
@Test
public void selectMulDataByJdbcTemplate() {
ApplicationContext ioc =
new ClassPathXmlApplicationContext("JdbcTemplate_ioc.xml");
//得到JdbcTemplate bean
JdbcTemplate jdbcTemplate = ioc.getBean(JdbcTemplate.class);
//組織SQL
//透過BeanPropertyRowMapper獲取rowmapper 是一個介面,可以將查詢的結果,封裝到你指定的Monster物件中.
//1. 確定API
//public <T> T query(String sql, RowMapper<T> rowMapper, Object... args){}
//2. 組織引數
String sql = "SELECT id AS monsterId, NAME, skill FROM monster WHERE id >= ?";
RowMapper<Monster> rowMapper = new BeanPropertyRowMapper<>(Monster.class);
//3. 呼叫
List<Monster> monsterList = jdbcTemplate.query(sql, rowMapper, 100);
for (Monster monster : monsterList) {
System.out.println("monster= " + monster);
}
}
}
執行結果:
51,JdbcTemplate-返回單行單列
1. 查詢返回結果只有一行一列的值,比如查詢 id=100 的怪物名
JdbcTemplateTest.java
package com.hspedu.spring.test;
import com.hspedu.spring.bean.Monster;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
public class JdbcTemplateTest {
//查詢返回結果只有一行一列的值,比如查詢id=100的怪物名
/**
* 查詢返回結果只有一行一列的值
*/
@Test
public void selectScalarByJdbcTemplate() {
ApplicationContext ioc =
new ClassPathXmlApplicationContext("JdbcTemplate_ioc.xml");
//得到JdbcTemplate bean
JdbcTemplate jdbcTemplate = ioc.getBean(JdbcTemplate.class);
//1. 確定API
//public <T> T queryForObject(String sql, Class<T> requiredType)
//2. 提供引數
String sql = "SELECT NAME FROM monster WHERE id = 100";
//Class<T> requiredType 表示你返回的單行單列的資料型別
String name =
jdbcTemplate.queryForObject(sql, String.class);
System.out.println("返回name= " + name);
}
}
執行結果:
52,JdbcTemplate-具名引數
1. 使用 Map 傳入具名引數完成操作,比如新增 螞蟻精.:name 就是具名引數形式需要使用 NamedParameterJdbcTemplate 類
JdbcTemplateTest.java
package com.hspedu.spring.test;
import com.hspedu.spring.bean.Monster;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class JdbcTemplateTest {
//使用Map傳入具名引數完成操作,比如新增 螞蟻精.:name 就是具名引數形式需要使用NamedParameterJdbcTemplate 類,
// 語句形式: String sql = "INSERT INTO monster VALUES(:my_id, :name, :skill)";
/**
* 使用Map傳入具名引數完成操作,比如新增
*/
@Test
public void testDataByNamedParameterJdbcTemplate() {
ApplicationContext ioc =
new ClassPathXmlApplicationContext("JdbcTemplate_ioc.xml");
//得到NamedParameterJdbcTemplate bean
NamedParameterJdbcTemplate namedParameterJdbcTemplate =
ioc.getBean(NamedParameterJdbcTemplate.class);
//1. 確定使用API
//public int update(String sql, Map<String, ?> paramMap)
//2. 準備引數 [:my_id, :name, :skill] 要求按照規定的名字來設定引數
String sql = "INSERT INTO monster VALUES(:id, :name, :skill)";
Map<String, Object> paramMap = new HashMap<>();
//給paramMap填寫資料
paramMap.put("id", 800);
paramMap.put("name", "螞蟻精");
paramMap.put("skill", "喜歡打洞");
//3. 呼叫
int affected = namedParameterJdbcTemplate.update(sql, paramMap);
System.out.println("add ok affected=" + affected);
}
}
執行結果:
sqlyog資料庫結果:
53,JdbcTemplate-sqlparametersoruce
1. 使用 sqlparametersoruce 來封裝具名引數,還是新增一個 Monster 狐狸精
JdbcTemplateTest.java
package com.hspedu.spring.test;
import com.hspedu.spring.bean.Monster;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class JdbcTemplateTest {
//使用sqlparametersoruce 來封裝具名引數,還是新增一個Monster 狐狸精
@Test
public void operDataBySqlparametersoruce() {
ApplicationContext ioc =
new ClassPathXmlApplicationContext("JdbcTemplate_ioc.xml");
//得到NamedParameterJdbcTemplate bean
NamedParameterJdbcTemplate namedParameterJdbcTemplate =
ioc.getBean(NamedParameterJdbcTemplate.class);
//確定API
//public int update(String sql, SqlParameterSource paramSource)
//public BeanPropertySqlParameterSource(Object object)
//準備引數
String sql = "INSERT INTO monster VALUES(:monsterID, :name, :skill)";
Monster monster = new Monster(900, "大象精", "搬運木頭");
SqlParameterSource sqlParameterSource =
new BeanPropertySqlParameterSource(monster);
//呼叫
int affected =
namedParameterJdbcTemplate.update(sql, sqlParameterSource);
System.out.println("add ok affected= " + affected);
}
}
執行結果:
sqlyog資料庫結果:
54,DAO使用JdbcTemplate完成對資料庫的操作
程式碼結構:
MonsterDao.java
package com.hspedu.spring.jdbctemplate.dao;
import com.hspedu.spring.bean.Monster;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import javax.annotation.Resource;
@Repository //將MonsterDao 注入到spring容器
public class MonsterDao {
//注入一個屬性
@Resource
private JdbcTemplate jdbcTemplate;
//完成儲存任務
public void save(Monster monster) {
//組織sql
String sql = "INSERT INTO monster VALUES(?,?,?)";
int affected = jdbcTemplate.update
(sql, monster.getMonsterID(), monster.getName(), monster.getSkill());
System.out.println("affected= " + affected);
}
}
JdbcTemplate_ioc.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--配置要掃描包-->
<context:component-scan
base-package="com.hspedu.spring.jdbctemplate.dao"/>
<!--引入外部的jdbc.properties檔案-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--配置資料來源物件-DataSoruce-->
<bean class="com.mchange.v2.c3p0.ComboPooledDataSource" id="dataSource">
<!--給資料來源物件配置屬性值-->
<property name="user" value="${jdbc.user}"/>
<property name="password" value="${jdbc.pwd}"/>
<property name="driverClass" value="${jdbc.driver}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
</bean>
<!--配置JdbcTemplate物件-->
<bean class="org.springframework.jdbc.core.JdbcTemplate" id="jdbcTemplate">
<!--給JdbcTemplate物件配置屬性dataSource, 注意:後面是引用的上面的資料來源物件dataSource-->
<property name="dataSource" ref="dataSource"/>
</bean>
<!--配置NamedParameterJdbcTemplate物件-->
<bean class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate"
id="namedParameterJdbcTemplate">
<!--透過構造器,設定資料來源-->
<constructor-arg name="dataSource" ref="dataSource"/>
</bean>
</beans>
JdbcTemplateTest.java
package com.hspedu.spring.test;
import com.hspedu.spring.bean.Monster;
import com.hspedu.spring.jdbctemplate.dao.MonsterDao;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class JdbcTemplateTest {
//測試MonsterDAO
@Test
public void monsterDaoSave() {
ApplicationContext ioc =
new ClassPathXmlApplicationContext("JdbcTemplate_ioc.xml");
MonsterDao monsterDao = ioc.getBean(MonsterDao.class);
Monster monster = new Monster(1000, "小鴨精", "吃魚");
monsterDao.save(monster);
System.out.println("MonsterDAO儲存 ok ..");
}
}
執行結果:
sqlyog資料庫結果: