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数据库结果:

spring下 -spring整体架构,JdbcTemplate笔记的更多相关文章

  1. CentOS6.8下MySQL MHA架构搭建笔记

    转载请注明出处,本文地址:http://www.cnblogs.com/ajiangg/p/6552855.html 以下是CentOS6.8下MySQL MHA架构搭建笔记 IP资源规划: 192. ...

  2. Spring Cloud 微服务架构学习笔记与示例

    本文示例基于Spring Boot 1.5.x实现,如对Spring Boot不熟悉,可以先学习我的这一篇:<Spring Boot 1.5.x 基础学习示例>.关于微服务基本概念不了解的 ...

  3. Spring Framework(框架)整体架构 变迁

    Spring Framework(框架)整体架构 2018年04月24日 11:16:41 阅读数:1444 标签: Spring框架架构 更多 个人分类: Spring框架   版权声明:本文为博主 ...

  4. Spring框架 - Spring和Spring框架组成

    Spring框架 - Spring和Spring框架组成 Spring是什么?它是怎么诞生的?有哪些主要的组件和核心功能呢? 本文通过这几个问题帮助你构筑Spring和Spring Framework ...

  5. Spring技术内幕:设计理念和整体架构概述(转)

    程序员都很崇拜技术大神,很大一部分是因为他们发现和解决问题的能力,特别是线上出现紧急问题时,总是能够快速定位和解决. 一方面,他们有深厚的技术基础,对应用的技术知其所以然,另一方面,在采坑的过程中不断 ...

  6. Spring的设计理念和整体架构

    1.Spring的各个子项目 Spring Framework(Core):这是我们熟知的Spring项目的核心.Spring Framework(Core)中包含了一系列Ioc容器的设计,提供了依赖 ...

  7. spring源码深度解析—Spring的整体架构和环境搭建

    概述 Spring是一个开放源代码的设计层面框架,他解决的是业务逻辑层和其他各层的松耦合问题,因此它将面向接口的编程思想贯穿整个系统应用.Spring是于2003 年兴起的一个轻量级的Java 开发框 ...

  8. Spring cloud微服务安全实战-4-3常见的微服务安全整体架构

    整体架构 这个图适合中小公司.麻雀虽小 五脏俱全.微服务架构所需要做的事在这个图里基本都有了. 绿色的不讲,主要讲的是这三块(橘黄色的).后面的和运维相关,会讲,不会讲的太深 订单服务 首先来写一个订 ...

  9. Spring学习(一)--Spring的设计与整体架构

    之前只是在学校里大概的学习了一下Spring框架的使用以及一些最基本.浅显的原理,并没有做出深入的学习,等到工作之后想提升自己的时候发现所掌握的Spring框架的简直烂如狗屎,为监督自己的学习进度,立 ...

  10. 【Spring源码深度解析系列 】Spring整体架构

    一.Spring的整体架构和模块 二.模块分类: 1.Core Container Core Container包含有Core .Beans.Context.和Expression  Language ...

随机推荐

  1. 【牛客刷题】HJ3 明明的随机数

    题目链接 这题有两个要编码解决的问题,首先是去重,其次是排序. 最开始想着就用Java的TreeSet解决了,简单好用,去重排序都一并解决了,编码只需要考虑input的逻辑就可以,代码如下: impo ...

  2. 存储过程中调用EXECUTE IMMEDIATE的“权限不足”

    EXECUTE IMMEDIATE是Oracle中使用动态SQL的一种方法,可以直接执行,也可以在存储过程中调用.然而在存储过程中调用可能会遇到权限不足的问题,如在存储过程中执行重建索引语句: sql ...

  3. bat 随笔

    bat 获取文件名 %%~nxi bat 变量去除空字符 BAT批处理中的字符串处理详解(字符串截取)

  4. THUPC2024 初赛

    <南开大学数分I月考III在初赛开始四十分钟时结束> 早晨试图速成泰勒展开失败了 考试前 zsy 把 yzf 接到学校了,应该是国赛后第一次见 yzf 考完试发现 yzf 已经买好 KFC ...

  5. 如何将一个模块文件编译到Linux内核中?

    很多粉丝在群里提问,如何把一个模块文件编译到内核中或者独立变异成ko文件.本文给大家详解讲解. 1. 内核目录 Linux内核源代码非常庞大,随着版本的发展不断增加.它使用目录树结构,并且使用Make ...

  6. 【Python】之pip安装第三方库失败

    一直报错:Could not fetch URL https://pypi.org/simple/pygame/: There was a problem confirming the ssl cer ...

  7. Kubernetes-3:使用kubeadm部署k8s环境及常见报错解决方法

    k8s集群安装 环境说明: k8s-Master-Centos8 ip:192.168.152.53 k8s-Node1-Centos7 ip:192.168.152.253 k8s-Node2-Ce ...

  8. JVM笔记九-GC收集器日志信息学习

    在上一篇文章中,我们通过代码运行结果,查看到JVM的堆内存逻辑上分区是三部分,物理上分区是2部分,以及是新生代分区三部分,占比分布是8/1/1.而且我们还通过代码和堆JVM参数配置,制造出了OOM异常 ...

  9. Figma 学习笔记 – Prototype

    挺简单的, 只要知道它有什么, 基本上就会用了 监听 Event Type 监听 Callback Action 过度 Animation Frame Scrolling

  10. DTO转VO工具

    data工具,实现了对象拷贝 DTO -> VO 只需要实现一个类即可 data-utils data工具,实现了对象拷贝DTO -> VO 解决的问题 Mapstruct需要安插件!!! ...