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数据库结果:
标签:hspedu,java,spring,JdbcTemplate,import,架构,com,class From: https://www.cnblogs.com/romantichuaner/p/18472927